[IMP] microsoft_outlook, fetchmail_outlook *: improve usability
* fetchmail, google_gmail, fetchmail_gmail Purpose ======= Improve the usability of the outlook modules. Specifications ============== Remove the checkbox "Use Outlook" and instead use the smtp_authentication and the server_type, to be consistent with Gmail. Hide the password field for Outlook / Gmail mail servers. Add constraints on the outgoing mail server to force the user to use the right configuration (e.g. the from_filter, so the sending does not fail). Add an option in the mail module to install Outlook. Show a message for the outgoing mail servers to explain each authentication methods. Task-2811567 Part-of: odoo/odoo#88215
This commit is contained in:
@@ -47,6 +47,7 @@ class FetchmailServer(models.Model):
|
||||
('pop', 'POP Server'),
|
||||
('local', 'Local Server'),
|
||||
], string='Server Type', index=True, required=True, default='pop')
|
||||
server_type_info = fields.Text('Server Type Info', compute='_compute_server_type_info')
|
||||
is_ssl = fields.Boolean('SSL/TLS', help="Connections are encrypted with SSL/TLS through a dedicated port (default: IMAPS=993, POP3S=995)")
|
||||
attach = fields.Boolean('Keep Attachments', help="Whether attachments should be downloaded. "
|
||||
"If not enabled, incoming emails will be stripped of any attachments before being processed", default=True)
|
||||
@@ -64,6 +65,14 @@ class FetchmailServer(models.Model):
|
||||
configuration = fields.Text('Configuration', readonly=True)
|
||||
script = fields.Char(readonly=True, default='/mail/static/scripts/odoo-mailgate.py')
|
||||
|
||||
@api.depends('server_type')
|
||||
def _compute_server_type_info(self):
|
||||
for server in self:
|
||||
if server.server_type == 'local':
|
||||
server.server_type_info = _('Use a local script to fetch your emails and create new records.')
|
||||
else:
|
||||
server.server_type_info = False
|
||||
|
||||
@api.onchange('server_type', 'is_ssl', 'object_id')
|
||||
def onchange_server_type(self):
|
||||
self.port = 0
|
||||
|
||||
@@ -29,10 +29,20 @@
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="date"/>
|
||||
<field name="server_type" widget="radio"/>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger"
|
||||
attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="server_type" widget="radio" attrs="{'readonly': [('state', '=', 'done')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date" attrs="{'invisible': [('date', '=', False)]}"/>
|
||||
<div class="text-muted font-italic" colspan="8" role="alert"
|
||||
attrs="{'invisible': [('server_type_info', '=', False)]}">
|
||||
<field name="server_type_info"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Server & Login" name="server_login_details">
|
||||
@@ -47,7 +57,8 @@
|
||||
</group>
|
||||
<group attrs="{'invisible' : [('server_type', '=', 'local')]}" string="Login Information">
|
||||
<field name="user" attrs="{'required' : [('server_type', '!=', 'local')]}"/>
|
||||
<field name="password" password="True" attrs="{'required' : [('server_type', '!=', 'local')]}"/>
|
||||
<field name="password" password="True"
|
||||
attrs="{'required' : [('server_type', 'in', ('imap', 'pop'))], 'invisible' : [('server_type', 'not in', ('imap', 'pop'))]}"/>
|
||||
</group>
|
||||
<group string="Actions to Perform on Incoming Mails">
|
||||
<field name="object_id"/>
|
||||
@@ -63,7 +74,7 @@
|
||||
<field name="priority"/>
|
||||
<field name="attach"/>
|
||||
<field name="original"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class FetchmailServer(models.Model):
|
||||
@@ -10,6 +10,14 @@ class FetchmailServer(models.Model):
|
||||
|
||||
server_type = fields.Selection(selection_add=[('gmail', 'Gmail OAuth Authentication')], ondelete={'gmail': 'set default'})
|
||||
|
||||
def _compute_server_type_info(self):
|
||||
gmail_servers = self.filtered(lambda server: server.server_type == 'gmail')
|
||||
gmail_servers.server_type_info = _(
|
||||
'Connect your Gmail account with the OAuth Authentication process. \n'
|
||||
'You will be redirected to the Gmail login page where you will '
|
||||
'need to accept the permission.')
|
||||
super(FetchmailServer, self - gmail_servers)._compute_server_type_info()
|
||||
|
||||
@api.onchange('server_type', 'is_ssl', 'object_id')
|
||||
def onchange_server_type(self):
|
||||
"""Set the default configuration for a IMAP Gmail server."""
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<field name="user" position="after">
|
||||
<field name="google_gmail_uri" invisible="1"/>
|
||||
<field name="google_gmail_refresh_token" invisible="1"/>
|
||||
<div></div>
|
||||
<div attrs="{'invisible': [('server_type', '!=', 'gmail')]}">
|
||||
<div attrs="{'invisible': [('server_type', '!=', 'gmail')]}"
|
||||
class="d-flex flex-row align-items-center" colspan="8">
|
||||
<span attrs="{'invisible': ['|', ('server_type', '!=', 'gmail'), ('google_gmail_refresh_token', '=', False)]}"
|
||||
class="badge text-bg-success">
|
||||
Gmail Token Valid
|
||||
@@ -22,22 +22,18 @@
|
||||
Connect your Gmail account
|
||||
</button>
|
||||
<button type="object"
|
||||
name="open_google_gmail_uri" class="btn-link px-0"
|
||||
name="open_google_gmail_uri" class="btn-link px-0 ml-2"
|
||||
attrs="{'invisible': ['|', '|', ('google_gmail_uri', '=', False), ('server_type', '!=', 'gmail'), ('google_gmail_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog"/>
|
||||
Edit Settings
|
||||
<i class="fa fa-cog" title="Edit Settings"/>
|
||||
</button>
|
||||
<div class="alert alert-warning" role="alert"
|
||||
<button class="alert alert-warning d-block mt-2 text-left"
|
||||
icon="fa-arrow-right" type="action" role="alert"
|
||||
name="%(base.res_config_setting_act_window)d"
|
||||
attrs="{'invisible': ['|', ('server_type', '!=', 'gmail'), ('google_gmail_uri', '!=', False)]}">
|
||||
Setup your Gmail API credentials in the general settings to link a Gmail account.
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
<field name="password" position="attributes">
|
||||
<attribute name="attrs">
|
||||
{'required' : [('server_type', '!=', 'local'), ('server_type', '!=', 'gmail'), ('password', '!=', False)], 'invisible' : [('server_type', '=', 'gmail')]}
|
||||
</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@@ -13,35 +13,40 @@ class FetchmailServer(models.Model):
|
||||
|
||||
_OUTLOOK_SCOPE = 'https://outlook.office.com/IMAP.AccessAsUser.All'
|
||||
|
||||
@api.constrains('use_microsoft_outlook_service', 'server_type', 'password', 'is_ssl')
|
||||
server_type = fields.Selection(selection_add=[('outlook', 'Outlook OAuth Authentication')], ondelete={'outlook': 'set default'})
|
||||
|
||||
def _compute_server_type_info(self):
|
||||
outlook_servers = self.filtered(lambda server: server.server_type == 'outlook')
|
||||
outlook_servers.server_type_info = _(
|
||||
'Connect your personal Outlook account using OAuth. \n'
|
||||
'You will be redirected to the Outlook login page to accept '
|
||||
'the permissions.')
|
||||
super(FetchmailServer, self - outlook_servers)._compute_server_type_info()
|
||||
|
||||
@api.depends('server_type')
|
||||
def _compute_is_microsoft_outlook_configured(self):
|
||||
outlook_servers = self.filtered(lambda server: server.server_type == 'outlook')
|
||||
(self - outlook_servers).is_microsoft_outlook_configured = False
|
||||
super(FetchmailServer, outlook_servers)._compute_is_microsoft_outlook_configured()
|
||||
|
||||
@api.constrains('server_type', 'is_ssl')
|
||||
def _check_use_microsoft_outlook_service(self):
|
||||
for server in self:
|
||||
if not server.use_microsoft_outlook_service:
|
||||
continue
|
||||
if server.server_type == 'outlook' and not server.is_ssl:
|
||||
raise UserError(_('SSL is required for the server %r.', server.name))
|
||||
|
||||
if server.server_type != 'imap':
|
||||
raise UserError(_('Outlook mail server %r only supports IMAP server type.', server.name))
|
||||
|
||||
if server.password:
|
||||
raise UserError(_(
|
||||
'Please leave the password field empty for Outlook mail server %r. '
|
||||
'The OAuth process does not require it', server.name))
|
||||
|
||||
if not server.is_ssl:
|
||||
raise UserError(_('SSL is required .', server.name))
|
||||
|
||||
@api.onchange('use_microsoft_outlook_service')
|
||||
def _onchange_use_microsoft_outlook_service(self):
|
||||
@api.onchange('server_type')
|
||||
def onchange_server_type(self):
|
||||
"""Set the default configuration for a IMAP Outlook server."""
|
||||
if self.use_microsoft_outlook_service:
|
||||
if self.server_type == 'outlook':
|
||||
self.server = 'imap.outlook.com'
|
||||
self.server_type = 'imap'
|
||||
self.is_ssl = True
|
||||
self.port = 993
|
||||
else:
|
||||
self.microsoft_outlook_refresh_token = False
|
||||
self.microsoft_outlook_access_token = False
|
||||
self.microsoft_outlook_access_token_expiration = False
|
||||
super(FetchmailServer, self).onchange_server_type()
|
||||
|
||||
def _imap_login(self, connection):
|
||||
"""Authenticate the IMAP connection.
|
||||
@@ -49,9 +54,16 @@ class FetchmailServer(models.Model):
|
||||
If the mail server is Outlook, we use the OAuth2 authentication protocol.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.use_microsoft_outlook_service:
|
||||
if self.server_type == 'outlook':
|
||||
auth_string = self._generate_outlook_oauth2_string(self.user)
|
||||
connection.authenticate('XOAUTH2', lambda x: auth_string)
|
||||
connection.select('INBOX')
|
||||
else:
|
||||
super()._imap_login(connection)
|
||||
|
||||
def _get_connection_type(self):
|
||||
"""Return which connection must be used for this mail server (IMAP or POP).
|
||||
The Outlook mail server used an IMAP connection.
|
||||
"""
|
||||
self.ensure_one()
|
||||
return 'imap' if self.server_type == 'outlook' else super()._get_connection_type()
|
||||
|
||||
@@ -21,12 +21,11 @@ class TestFetchmailOutlook(TransactionCase):
|
||||
|
||||
mail_server = self.env['fetchmail.server'].create({
|
||||
'name': 'Test server',
|
||||
'use_microsoft_outlook_service': True,
|
||||
'server_type': 'outlook',
|
||||
'user': 'test@example.com',
|
||||
'microsoft_outlook_access_token': 'test_access_token',
|
||||
'microsoft_outlook_access_token_expiration': time.time() + 1000000,
|
||||
'password': '',
|
||||
'server_type': 'imap',
|
||||
'is_ssl': True,
|
||||
})
|
||||
|
||||
@@ -45,15 +44,6 @@ class TestFetchmailOutlook(TransactionCase):
|
||||
with self.assertRaises(UserError, msg='Should ensure that the password is empty'):
|
||||
self.env['fetchmail.server'].create({
|
||||
'name': 'Test server',
|
||||
'use_microsoft_outlook_service': True,
|
||||
'server_type': 'outlook',
|
||||
'password': 'test',
|
||||
'server_type': 'imap',
|
||||
})
|
||||
|
||||
with self.assertRaises(UserError, msg='Should ensure that the server type is IMAP'):
|
||||
self.env['fetchmail.server'].create({
|
||||
'name': 'Test server',
|
||||
'use_microsoft_outlook_service': True,
|
||||
'password': '',
|
||||
'server_type': 'pop',
|
||||
})
|
||||
|
||||
@@ -6,42 +6,36 @@
|
||||
<field name="priority">1000</field>
|
||||
<field name="inherit_id" ref="fetchmail.view_email_server_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="server" position="before">
|
||||
<field name="use_microsoft_outlook_service" string="Outlook"
|
||||
attrs="{'readonly': [('state', '=', 'done')]}"/>
|
||||
</field>
|
||||
<field name="user" position="after">
|
||||
<field name="is_microsoft_outlook_configured" invisible="1"/>
|
||||
<field name="microsoft_outlook_refresh_token" invisible="1"/>
|
||||
<field name="microsoft_outlook_access_token" invisible="1"/>
|
||||
<field name="microsoft_outlook_access_token_expiration" invisible="1"/>
|
||||
<div></div>
|
||||
<div attrs="{'invisible': [('use_microsoft_outlook_service', '=', False)]}">
|
||||
<span attrs="{'invisible': ['|', ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '=', False)]}"
|
||||
<div attrs="{'invisible': [('server_type', '!=', 'outlook')]}"
|
||||
class="d-flex flex-row align-items-center" colspan="8">
|
||||
<span attrs="{'invisible': ['|', ('server_type', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '=', False)]}"
|
||||
class="badge text-bg-success">
|
||||
Outlook Token Valid
|
||||
</span>
|
||||
<button type="object"
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '!=', False)]}">
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('server_type', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '!=', False)]}">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
Connect your Outlook account
|
||||
</button>
|
||||
<button type="object"
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog"/>
|
||||
Edit Settings
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0 ml-2"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('server_type', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog" title="Edit Settings"/>
|
||||
</button>
|
||||
<div class="alert alert-warning" role="alert"
|
||||
attrs="{'invisible': ['|', ('is_microsoft_outlook_configured', '=', True), ('use_microsoft_outlook_service', '=', False)]}">
|
||||
<button class="alert alert-warning d-block mt-2 text-left"
|
||||
icon="fa-arrow-right" type="action" role="alert"
|
||||
name="%(base.res_config_setting_act_window)d"
|
||||
attrs="{'invisible': ['|', ('is_microsoft_outlook_configured', '=', True), ('server_type', '!=', 'outlook')]}">
|
||||
Setup your Outlook API credentials in the general settings to link a Outlook account.
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
<field name="password" position="attributes">
|
||||
<attribute name="attrs">{}</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -131,7 +131,7 @@ class GoogleService(models.AbstractModel):
|
||||
raise UserError(_('Google %s is not yet configured.', service.title()))
|
||||
|
||||
if not refresh_token:
|
||||
raise UserError(_('The refresh token for authentication is not set.'))
|
||||
raise UserError(_('Your Google account is not connected.'))
|
||||
|
||||
try:
|
||||
result = requests.post(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
{
|
||||
"name": "Google Gmail",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"category": "Hidden",
|
||||
"description": "Gmail support for incoming / outgoing mail servers",
|
||||
"depends": [
|
||||
@@ -16,4 +16,9 @@
|
||||
],
|
||||
"auto_install": True,
|
||||
"license": "LGPL-3",
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"google_gmail/static/src/scss/google_gmail.scss",
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import base64
|
||||
|
||||
from odoo import fields, models, api
|
||||
from odoo import _, fields, models, api
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class IrMailServer(models.Model):
|
||||
@@ -16,6 +17,14 @@ class IrMailServer(models.Model):
|
||||
selection_add=[('gmail', 'Gmail OAuth Authentication')],
|
||||
ondelete={'gmail': 'set default'})
|
||||
|
||||
def _compute_smtp_authentication_info(self):
|
||||
gmail_servers = self.filtered(lambda server: server.smtp_authentication == 'gmail')
|
||||
gmail_servers.smtp_authentication_info = _(
|
||||
'Connect your Gmail account with the OAuth Authentication process. \n'
|
||||
'By default, only a user with a matching email address will be able to use this server. '
|
||||
'To extend its use, you should set a "mail.default.from" system parameter.')
|
||||
super(IrMailServer, self - gmail_servers)._compute_smtp_authentication_info()
|
||||
|
||||
@api.onchange('smtp_encryption')
|
||||
def _onchange_encryption(self):
|
||||
"""Do not change the SMTP configuration if it's a Gmail server
|
||||
@@ -24,7 +33,7 @@ class IrMailServer(models.Model):
|
||||
super(IrMailServer, self)._onchange_encryption()
|
||||
|
||||
@api.onchange('smtp_authentication')
|
||||
def _onchange_smtp_authentication(self):
|
||||
def _onchange_smtp_authentication_gmail(self):
|
||||
if self.smtp_authentication == 'gmail':
|
||||
self.smtp_host = 'smtp.gmail.com'
|
||||
self.smtp_encryption = 'starttls'
|
||||
@@ -35,6 +44,31 @@ class IrMailServer(models.Model):
|
||||
self.google_gmail_access_token = False
|
||||
self.google_gmail_access_token_expiration = False
|
||||
|
||||
@api.onchange('smtp_user', 'smtp_authentication')
|
||||
def _on_change_smtp_user_gmail(self):
|
||||
"""The Gmail mail servers can only be used for the user personal email address."""
|
||||
if self.smtp_authentication == 'gmail':
|
||||
self.from_filter = self.smtp_user
|
||||
|
||||
@api.constrains('smtp_authentication', 'smtp_pass', 'smtp_encryption', 'from_filter', 'smtp_user')
|
||||
def _check_use_google_gmail_service(self):
|
||||
gmail_servers = self.filtered(lambda server: server.smtp_authentication == 'gmail')
|
||||
for server in gmail_servers:
|
||||
if server.smtp_pass:
|
||||
raise UserError(_(
|
||||
'Please leave the password field empty for Gmail mail server %r. '
|
||||
'The OAuth process does not require it', server.name))
|
||||
|
||||
if server.smtp_encryption != 'starttls':
|
||||
raise UserError(_(
|
||||
'Incorrect Connection Security for Gmail mail server %r. '
|
||||
'Please set it to "TLS (STARTTLS)".', server.name))
|
||||
|
||||
if server.from_filter != server.smtp_user:
|
||||
raise UserError(_(
|
||||
'This server %r can only be used for your personal email address. '
|
||||
'Please fill the "from_filter" field with %r.', server.name, server.smtp_user))
|
||||
|
||||
def _smtp_login(self, connection, smtp_user, smtp_password):
|
||||
if len(self) == 1 and self.smtp_authentication == 'gmail':
|
||||
auth_string = self._generate_oauth2_string(smtp_user, self.google_gmail_refresh_token)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg height="48px" viewBox="0 0 48 48" width="48px" xmlns="http://www.w3.org/2000/svg"><path d="M45,16.2l-5,2.75l-5,4.75L35,40h7c1.657,0,3-1.343,3-3V16.2z" fill="#4caf50"/><path d="M3,16.2l3.614,1.71L13,23.7V40H6c-1.657,0-3-1.343-3-3V16.2z" fill="#1e88e5"/><polygon fill="#e53935" points="35,11.2 24,19.45 13,11.2 12,17 13,23.7 24,31.95 35,23.7 36,17"/><path d="M3,12.298V16.2l10,7.5V11.2L9.876,8.859C9.132,8.301,8.228,8,7.298,8h0C4.924,8,3,9.924,3,12.298z" fill="#c62828"/><path d="M45,12.298V16.2l-10,7.5V11.2l3.124-2.341C38.868,8.301,39.772,8,40.702,8h0 C43.076,8,45,9.924,45,12.298z" fill="#fbc02d"/></svg>
|
||||
|
After Width: | Height: | Size: 632 B |
@@ -0,0 +1,4 @@
|
||||
span.o_field_char[name="google_gmail_authorization_code"] {
|
||||
max-width: 150px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
<field name="smtp_user" position="after">
|
||||
<field name="google_gmail_uri" invisible="1"/>
|
||||
<field name="google_gmail_refresh_token" invisible="1"/>
|
||||
<div></div>
|
||||
<div attrs="{'invisible': [('smtp_authentication', '!=', 'gmail')]}">
|
||||
<div attrs="{'invisible': [('smtp_authentication', '!=', 'gmail')]}"
|
||||
class="d-flex flex-row align-items-center" colspan="8">
|
||||
<span attrs="{'invisible': ['|', ('smtp_authentication', '!=', 'gmail'), ('google_gmail_refresh_token', '=', False)]}"
|
||||
class="badge text-bg-success">
|
||||
Gmail Token Valid
|
||||
@@ -21,22 +21,23 @@
|
||||
Connect your Gmail account
|
||||
</button>
|
||||
<button type="object"
|
||||
name="open_google_gmail_uri" class="btn-link px-0"
|
||||
name="open_google_gmail_uri" class="btn-link px-0 ml-2"
|
||||
attrs="{'invisible': ['|', '|', ('google_gmail_uri', '=', False), ('smtp_authentication', '!=', 'gmail'), ('google_gmail_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog"/>
|
||||
Edit Settings
|
||||
<i class="fa fa-cog" title="Edit Settings"/>
|
||||
</button>
|
||||
<div class="alert alert-warning" role="alert"
|
||||
<button class="alert alert-warning d-block mt-2 text-left"
|
||||
icon="fa-arrow-right" type="action" role="alert"
|
||||
name="%(base.res_config_setting_act_window)d"
|
||||
attrs="{'invisible': ['|', ('smtp_authentication', '!=', 'gmail'), ('google_gmail_uri', '!=', False)]}">
|
||||
Setup your Gmail API credentials in the general settings to link a Gmail account.
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
<field name="smtp_user" position="attributes">
|
||||
<attribute name="attrs">{'invisible' : [('smtp_authentication', '!=', 'login'), ('smtp_authentication', '!=', 'gmail')]}</attribute>
|
||||
</field>
|
||||
<field name="smtp_pass" position="attributes">
|
||||
<attribute name="attrs">{'invisible' : [('smtp_authentication', '=', 'gmail')]}</attribute>
|
||||
<field name="smtp_authentication_info" position="after">
|
||||
<a href="https://www.odoo.com/documentation/15.0/applications/general/email_communication/email_servers.html?highlight=outgoing email server#use-a-default-from-email-address"
|
||||
attrs="{'invisible': [('smtp_authentication', '!=', 'gmail')]}" target="_blank">
|
||||
Read More
|
||||
</a>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_module_google_gmail" position="replace">
|
||||
<div class="row mt16" id="gmail_client_identifier">
|
||||
<label string="Client ID" for="google_gmail_client_identifier" class="col-lg-3 o_light_label"/>
|
||||
<field name="google_gmail_client_identifier" class="ms-2"/>
|
||||
<label string="ID" for="google_gmail_client_identifier" class="col-lg-3 o_light_label"/>
|
||||
<field name="google_gmail_client_identifier" class="ms-2"
|
||||
placeholder="ID of your Google app"/>
|
||||
</div>
|
||||
<div class="row mt16" id="gmail_client_secret">
|
||||
<label string="Client Secret" for="google_gmail_client_secret" class="col-lg-3 o_light_label"/>
|
||||
<field name="google_gmail_client_secret" class="ms-2"/>
|
||||
<label string="Secret" for="google_gmail_client_secret" class="col-lg-3 o_light_label"/>
|
||||
<field name="google_gmail_client_secret" class="ms-2"
|
||||
placeholder="Secret of your Google app"/>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
|
||||
@@ -16,6 +16,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
'Alias Domain', config_parameter='mail.catchall.domain',
|
||||
help="If you have setup a catch-all email domain redirected to the Odoo server, enter the domain name here.")
|
||||
module_google_gmail = fields.Boolean('Support Gmail Authentication')
|
||||
module_microsoft_outlook = fields.Boolean('Support Outlook Authentication')
|
||||
restrict_template_rendering = fields.Boolean(
|
||||
'Restrict Template Rendering',
|
||||
config_parameter='mail.restrict.template.rendering',
|
||||
|
||||
@@ -36,13 +36,43 @@
|
||||
<div class="text-muted" id="external_email_server_default">
|
||||
Configure your own email servers
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<div class="content-group mb-3" attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<div class="mt16" id="mail_alias_domain">
|
||||
<label for="alias_domain" class="o_light_label"/>
|
||||
<span>@</span>
|
||||
<field name="alias_domain" placeholder='e.g. "mycompany.com"'/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex" attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<field name="module_google_gmail"/>
|
||||
<div>
|
||||
<label string="Gmail Credentials" for="module_google_gmail"/>
|
||||
<a href="https://console.developers.google.com/" title="Get Gmail API credentials" class="o_doc_link" target="_blank"/>
|
||||
<div class="text-muted">
|
||||
Send and receive emails through your Gmail account.
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('module_google_gmail','=',False)]}" id="msg_module_google_gmail">
|
||||
<div class="mt16 text-warning"><strong>Save</strong> this page and come back here to set up the feature.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex" attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<field name="module_microsoft_outlook"/>
|
||||
<div>
|
||||
<label string="Outlook Credentials" for="module_microsoft_outlook"/>
|
||||
<a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app"
|
||||
title="Get Outlook API credentials" class="o_doc_link" target="_blank"/>
|
||||
<div class="text-muted">
|
||||
Send and receive emails through your Outlook account.
|
||||
</div>
|
||||
<div class="content-group" id="msg_module_microsoft_outlook"
|
||||
attrs="{'invisible': [('module_microsoft_outlook','=',False)]}">
|
||||
<div class="mt16 text-warning">
|
||||
<strong>Save</strong> this page and come back here to set up the feature.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
@@ -66,22 +96,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="google_gmail_setting"
|
||||
attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_google_gmail"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label string="Gmail Credentials" for="module_google_gmail"/>
|
||||
<a href="https://console.developers.google.com/" title="Get Gmail API credentials" class="o_doc_link" target="_blank"/>
|
||||
<div class="text-muted">
|
||||
Send and receive emails with your Gmail account.
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('module_google_gmail','=',False)]}" id="msg_module_google_gmail">
|
||||
<div class="mt16 text-warning"><strong>Save</strong> this page and come back here to set up the feature.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Custom ICE server list</span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
{
|
||||
"name": "Microsoft Outlook",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"category": "Hidden",
|
||||
"description": "Outlook support for outgoing mail servers",
|
||||
"depends": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import base64
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@@ -15,12 +15,28 @@ class IrMailServer(models.Model):
|
||||
|
||||
_OUTLOOK_SCOPE = 'https://outlook.office.com/SMTP.Send'
|
||||
|
||||
@api.constrains('use_microsoft_outlook_service', 'smtp_pass', 'smtp_encryption')
|
||||
def _check_use_microsoft_outlook_service(self):
|
||||
for server in self:
|
||||
if not server.use_microsoft_outlook_service:
|
||||
continue
|
||||
smtp_authentication = fields.Selection(
|
||||
selection_add=[('outlook', 'Outlook OAuth Authentication')],
|
||||
ondelete={'outlook': 'set default'})
|
||||
|
||||
@api.depends('smtp_authentication')
|
||||
def _compute_is_microsoft_outlook_configured(self):
|
||||
outlook_servers = self.filtered(lambda server: server.smtp_authentication == 'outlook')
|
||||
(self - outlook_servers).is_microsoft_outlook_configured = False
|
||||
super(IrMailServer, outlook_servers)._compute_is_microsoft_outlook_configured()
|
||||
|
||||
def _compute_smtp_authentication_info(self):
|
||||
outlook_servers = self.filtered(lambda server: server.smtp_authentication == 'outlook')
|
||||
outlook_servers.smtp_authentication_info = _(
|
||||
'Connect your Outlook account with the OAuth Authentication process. \n'
|
||||
'By default, only a user with a matching email address will be able to use this server. '
|
||||
'To extend its use, you should set a "mail.default.from" system parameter.')
|
||||
super(IrMailServer, self - outlook_servers)._compute_smtp_authentication_info()
|
||||
|
||||
@api.constrains('smtp_authentication', 'smtp_pass', 'smtp_encryption', 'from_filter', 'smtp_user')
|
||||
def _check_use_microsoft_outlook_service(self):
|
||||
outlook_servers = self.filtered(lambda server: server.smtp_authentication == 'outlook')
|
||||
for server in outlook_servers:
|
||||
if server.smtp_pass:
|
||||
raise UserError(_(
|
||||
'Please leave the password field empty for Outlook mail server %r. '
|
||||
@@ -31,17 +47,22 @@ class IrMailServer(models.Model):
|
||||
'Incorrect Connection Security for Outlook mail server %r. '
|
||||
'Please set it to "TLS (STARTTLS)".', server.name))
|
||||
|
||||
if server.from_filter != server.smtp_user:
|
||||
raise UserError(_(
|
||||
'This server %r can only be used for your personal email address. '
|
||||
'Please fill the "from_filter" field with %r.', server.name, server.smtp_user))
|
||||
|
||||
@api.onchange('smtp_encryption')
|
||||
def _onchange_encryption(self):
|
||||
"""Do not change the SMTP configuration if it's a Outlook server
|
||||
|
||||
(e.g. the port which is already set)"""
|
||||
if not self.use_microsoft_outlook_service:
|
||||
if self.smtp_authentication != 'outlook':
|
||||
super()._onchange_encryption()
|
||||
|
||||
@api.onchange('use_microsoft_outlook_service')
|
||||
def _onchange_use_microsoft_outlook_service(self):
|
||||
if self.use_microsoft_outlook_service:
|
||||
@api.onchange('smtp_authentication')
|
||||
def _onchange_smtp_authentication_outlook(self):
|
||||
if self.smtp_authentication == 'outlook':
|
||||
self.smtp_host = 'smtp.outlook.com'
|
||||
self.smtp_encryption = 'starttls'
|
||||
self.smtp_port = 587
|
||||
@@ -50,11 +71,17 @@ class IrMailServer(models.Model):
|
||||
self.microsoft_outlook_access_token = False
|
||||
self.microsoft_outlook_access_token_expiration = False
|
||||
|
||||
@api.onchange('smtp_user', 'smtp_authentication')
|
||||
def _on_change_smtp_user_outlook(self):
|
||||
"""The Outlook mail servers can only be used for the user personal email address."""
|
||||
if self.smtp_authentication == 'outlook':
|
||||
self.from_filter = self.smtp_user
|
||||
|
||||
def _smtp_login(self, connection, smtp_user, smtp_password):
|
||||
if len(self) == 1 and self.use_microsoft_outlook_service:
|
||||
if len(self) == 1 and self.smtp_authentication == 'outlook':
|
||||
auth_string = self._generate_outlook_oauth2_string(smtp_user)
|
||||
oauth_param = base64.b64encode(auth_string.encode()).decode()
|
||||
connection.ehlo()
|
||||
connection.docmd('AUTH', 'XOAUTH2 %s' % oauth_param)
|
||||
connection.docmd('AUTH', f'XOAUTH2 {oauth_param}')
|
||||
else:
|
||||
super()._smtp_login(connection, smtp_user, smtp_password)
|
||||
|
||||
@@ -23,7 +23,6 @@ class MicrosoftOutlookMixin(models.AbstractModel):
|
||||
_OUTLOOK_SCOPE = None
|
||||
_OUTLOOK_ENDPOINT = 'https://login.microsoftonline.com/common/oauth2/v2.0/'
|
||||
|
||||
use_microsoft_outlook_service = fields.Boolean('Outlook Authentication')
|
||||
is_microsoft_outlook_configured = fields.Boolean('Is Outlook Credential Configured',
|
||||
compute='_compute_is_microsoft_outlook_configured')
|
||||
microsoft_outlook_refresh_token = fields.Char(string='Outlook Refresh Token',
|
||||
@@ -35,14 +34,13 @@ class MicrosoftOutlookMixin(models.AbstractModel):
|
||||
microsoft_outlook_uri = fields.Char(compute='_compute_outlook_uri', string='Authentication URI',
|
||||
help='The URL to generate the authorization code from Outlook', groups='base.group_system')
|
||||
|
||||
@api.depends('use_microsoft_outlook_service')
|
||||
def _compute_is_microsoft_outlook_configured(self):
|
||||
Config = self.env['ir.config_parameter'].sudo()
|
||||
microsoft_outlook_client_id = Config.get_param('microsoft_outlook_client_id')
|
||||
microsoft_outlook_client_secret = Config.get_param('microsoft_outlook_client_secret')
|
||||
self.is_microsoft_outlook_configured = microsoft_outlook_client_id and microsoft_outlook_client_secret
|
||||
|
||||
@api.depends('use_microsoft_outlook_service')
|
||||
@api.depends('is_microsoft_outlook_configured')
|
||||
def _compute_outlook_uri(self):
|
||||
Config = self.env['ir.config_parameter'].sudo()
|
||||
base_url = self.get_base_url()
|
||||
@@ -50,7 +48,7 @@ class MicrosoftOutlookMixin(models.AbstractModel):
|
||||
OUTLOOK_ENDPOINT = Config.get_param('microsoft.outlook.endpoint', self._OUTLOOK_ENDPOINT)
|
||||
|
||||
for record in self:
|
||||
if not record.id or not record.use_microsoft_outlook_service or not record.is_microsoft_outlook_configured:
|
||||
if not record.id or not record.is_microsoft_outlook_configured:
|
||||
record.microsoft_outlook_uri = False
|
||||
continue
|
||||
|
||||
@@ -80,7 +78,7 @@ class MicrosoftOutlookMixin(models.AbstractModel):
|
||||
if not self.env.user.has_group('base.group_system'):
|
||||
raise AccessError(_('Only the administrator can link an Outlook mail server.'))
|
||||
|
||||
if not self.use_microsoft_outlook_service or not self.is_microsoft_outlook_configured:
|
||||
if not self.is_microsoft_outlook_configured:
|
||||
raise UserError(_('Please configure your Outlook credentials.'))
|
||||
|
||||
return {
|
||||
@@ -161,7 +159,7 @@ class MicrosoftOutlookMixin(models.AbstractModel):
|
||||
or not self.microsoft_outlook_access_token_expiration \
|
||||
or self.microsoft_outlook_access_token_expiration < now_timestamp:
|
||||
if not self.microsoft_outlook_refresh_token:
|
||||
raise UserError(_('Please login your Outlook mail server before using it.'))
|
||||
raise UserError(_('Please connect with your Outlook account before using it.'))
|
||||
(
|
||||
self.microsoft_outlook_access_token,
|
||||
self.microsoft_outlook_access_token_expiration,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" ?><svg id="Capa_1" style="enable-background:new 0 0 128 128;" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style type="text/css">
|
||||
.st0{fill:#21A365;}
|
||||
.st1{fill:#107C41;}
|
||||
.st2{fill:#185B37;}
|
||||
.st3{fill:#33C481;}
|
||||
.st4{fill:#17864C;}
|
||||
.st5{fill:#FFFFFF;}
|
||||
.st6{fill:#036C70;}
|
||||
.st7{fill:#1A9BA1;}
|
||||
.st8{fill:#37C6D0;}
|
||||
.st9{fill:#04878B;}
|
||||
.st10{fill:#4F59CA;}
|
||||
.st11{fill:#7B82EA;}
|
||||
.st12{fill:#4C53BB;}
|
||||
.st13{fill:#0F78D5;}
|
||||
.st14{fill:#29A7EB;}
|
||||
.st15{fill:#0358A8;}
|
||||
.st16{fill:#0F79D6;}
|
||||
.st17{fill:#038387;}
|
||||
.st18{fill:#048A8E;}
|
||||
.st19{fill:#C8421D;}
|
||||
.st20{fill:#FF8F6A;}
|
||||
.st21{fill:#ED6B47;}
|
||||
.st22{fill:#891323;}
|
||||
.st23{fill:#AF2131;}
|
||||
.st24{fill:#C94E60;}
|
||||
.st25{fill:#E08195;}
|
||||
.st26{fill:#B42839;}
|
||||
.st27{fill:#0464B8;}
|
||||
.st28{fill:#0377D4;}
|
||||
.st29{fill:#4FD8FF;}
|
||||
.st30{fill:#1681D7;}
|
||||
.st31{fill:#0178D4;}
|
||||
.st32{fill:#042071;}
|
||||
.st33{fill:#168FDE;}
|
||||
.st34{fill:#CA64EA;}
|
||||
.st35{fill:#7E1FAF;}
|
||||
.st36{fill:#AE4BD5;}
|
||||
.st37{fill:#9332BF;}
|
||||
.st38{fill:#7719AA;}
|
||||
.st39{fill:#0078D4;}
|
||||
.st40{fill:#1490DF;}
|
||||
.st41{fill:#0364B8;}
|
||||
.st42{fill:#28A8EA;}
|
||||
.st43{fill:#41A5ED;}
|
||||
.st44{fill:#2C7BD5;}
|
||||
.st45{fill:#195ABE;}
|
||||
.st46{fill:#103E91;}
|
||||
.st47{fill:#2166C3;}
|
||||
.st48{opacity:0.2;}
|
||||
</style><g><path class="st27" d="M120.2,22.8H34.2V9.1C34.2,6.8,36,5,38.2,5h77.9c2.3,0,4.1,1.8,4.1,4.1V22.8z"/><rect class="st28" height="26.6" width="28.7" x="34.2" y="22.8"/><rect class="st14" height="26.6" width="28.7" x="62.9" y="22.8"/><rect class="st29" height="26.6" width="28.7" x="91.6" y="22.8"/><rect class="st30" height="26.6" width="28.7" x="34.2" y="49.4"/><rect class="st31" height="26.6" width="28.7" x="62.9" y="49.4"/><rect class="st14" height="26.6" width="28.7" x="91.6" y="49.4"/><rect class="st27" height="26.6" width="28.7" x="62.9" y="75.9"/><rect class="st30" height="26.6" width="28.7" x="91.6" y="75.9"/></g><path class="st32" d="M126.9,69.1l-6.6,3.9V61.8l6.5,3.6C128.3,66.1,128.3,68.2,126.9,69.1z"/><path class="st33" d="M126.9,69.1l-0.6,0.4l0,0l-0.7,0.4l-5.3,3.1v-0.1l-88.4,50.6h89.1c3.8,0,6.8-3.1,6.8-6.8l0.1-49.4 C128,67.9,127.6,68.7,126.9,69.1z"/><g><path class="st14" d="M122,123.5H32.8c-3.8,0-6.8-3.1-6.8-6.8V68.5L122,123.5z"/></g><path class="st30" d="M59,96.5h-53c-3.5,0-6.4-2.9-6.4-6.4V37.9c0-3.5,2.9-6.4,6.4-6.4h53c3.5,0,6.4,2.9,6.4,6.4v52.2 C65.4,93.6,62.6,96.5,59,96.5z"/><g><path class="st5" d="M32.5,82.9c-10.3,0-16.8-7.8-16.8-18.2c0-11,7.1-18.8,17.3-18.8c10.6,0,16.8,7.9,16.8,18.1 C49.9,76.1,42.5,82.9,32.5,82.9L32.5,82.9z M32.9,77.7c6.4,0,10-5.9,10-13.4c0-6.8-3.4-13.1-10-13.1s-10.1,6.2-10.1,13.4 S26.4,77.7,32.9,77.7L32.9,77.7z"/></g><path class="st48" d="M65.5,37.3c0,0.2,0,0.4,0,0.6v52.2c0,3.5-2.9,6.4-6.4,6.4H26v5.7h38.4c3.5,0,6.4-2.9,6.4-6.4V43.6 C70.8,40.4,68.5,37.7,65.5,37.3z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -5,38 +5,42 @@
|
||||
<field name="model">ir.mail_server</field>
|
||||
<field name="inherit_id" ref="base.ir_mail_server_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="smtp_host" position="before">
|
||||
<field name="use_microsoft_outlook_service" string="Outlook"/>
|
||||
</field>
|
||||
<field name="smtp_user" position="after">
|
||||
<field name="is_microsoft_outlook_configured" invisible="1"/>
|
||||
<field name="microsoft_outlook_refresh_token" invisible="1"/>
|
||||
<field name="microsoft_outlook_access_token" invisible="1"/>
|
||||
<field name="microsoft_outlook_access_token_expiration" invisible="1"/>
|
||||
<div></div>
|
||||
<div attrs="{'invisible': [('use_microsoft_outlook_service', '=', False)]}">
|
||||
<span attrs="{'invisible': ['|', ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '=', False)]}"
|
||||
<div attrs="{'invisible': [('smtp_authentication', '!=', 'outlook')]}"
|
||||
class="d-flex flex-row align-items-center" colspan="8">
|
||||
<span attrs="{'invisible': ['|', ('smtp_authentication', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '=', False)]}"
|
||||
class="badge text-bg-success">
|
||||
Outlook Token Valid
|
||||
</span>
|
||||
<button type="object"
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '!=', False)]}">
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('smtp_authentication', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '!=', False)]}">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
Connect your Outlook account
|
||||
</button>
|
||||
<button type="object"
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('use_microsoft_outlook_service', '=', False), ('microsoft_outlook_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog"/>
|
||||
Edit Settings
|
||||
name="open_microsoft_outlook_uri" class="btn-link px-0 ml-2"
|
||||
attrs="{'invisible': ['|', '|', ('is_microsoft_outlook_configured', '=', False), ('smtp_authentication', '!=', 'outlook'), ('microsoft_outlook_refresh_token', '=', False)]}">
|
||||
<i class="fa fa-cog" title="Edit Settings"/>
|
||||
</button>
|
||||
<div class="alert alert-warning" role="alert"
|
||||
attrs="{'invisible': ['|', ('is_microsoft_outlook_configured', '=', True), ('use_microsoft_outlook_service', '=', False)]}">
|
||||
<button class="alert alert-warning d-block mt-2 text-left"
|
||||
icon="fa-arrow-right" type="action" role="alert"
|
||||
name="%(base.res_config_setting_act_window)d"
|
||||
attrs="{'invisible': ['|', ('is_microsoft_outlook_configured', '=', True), ('smtp_authentication', '!=', 'outlook')]}">
|
||||
Setup your Outlook API credentials in the general settings to link a Outlook account.
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
<field name="smtp_authentication_info" position="after">
|
||||
<a href="https://www.odoo.com/documentation/15.0/applications/general/email_communication/email_servers.html?highlight=outgoing email server#use-a-default-from-email-address"
|
||||
attrs="{'invisible': [('smtp_authentication', '!=', 'outlook')]}" target="_blank">
|
||||
Read More
|
||||
</a>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -6,27 +6,18 @@
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="emails" position="inside">
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="microsoft_outlook_setting"
|
||||
attrs="{'invisible': [('external_email_server_default', '=', False)]}">
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Outlook Credentials</span>
|
||||
<div class="text-muted">
|
||||
Send and receive email with your Outlook account.
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16" id="outlook_client_identifier">
|
||||
<label string="Client ID" for="microsoft_outlook_client_identifier"
|
||||
class="col-lg-3 o_light_label"/>
|
||||
<field name="microsoft_outlook_client_identifier" class="ms-2"/>
|
||||
</div>
|
||||
<div class="row mt16" id="outlook_client_secret">
|
||||
<label string="Client Secret" for="microsoft_outlook_client_secret"
|
||||
class="col-lg-3 o_light_label"/>
|
||||
<field name="microsoft_outlook_client_secret" class="ms-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="msg_module_microsoft_outlook" position="replace">
|
||||
<div class="row mt16" id="outlook_client_identifier">
|
||||
<label string="ID" for="microsoft_outlook_client_identifier"
|
||||
class="col-lg-3 o_light_label"/>
|
||||
<field name="microsoft_outlook_client_identifier" class="ms-2"
|
||||
placeholder="ID of your Outlook app"/>
|
||||
</div>
|
||||
<div class="row mt16" id="outlook_client_secret">
|
||||
<label string="Secret" for="microsoft_outlook_client_secret"
|
||||
class="col-lg-3 o_light_label"/>
|
||||
<field name="microsoft_outlook_client_secret" class="ms-2"
|
||||
placeholder="Secret of your Outlook app"/>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
|
||||
@@ -95,6 +95,7 @@ class IrMailServer(models.Model):
|
||||
smtp_host = fields.Char(string='SMTP Server', required=True, help="Hostname or IP of SMTP server")
|
||||
smtp_port = fields.Integer(string='SMTP Port', required=True, default=25, help="SMTP Port. Usually 465 for SSL, and 25 or 587 for other cases.")
|
||||
smtp_authentication = fields.Selection([('login', 'Username'), ('certificate', 'SSL Certificate')], string='Authenticate with', required=True, default='login')
|
||||
smtp_authentication_info = fields.Text('Authentication Info', compute='_compute_smtp_authentication_info')
|
||||
smtp_user = fields.Char(string='Username', help="Optional username for SMTP authentication", groups='base.group_system')
|
||||
smtp_pass = fields.Char(string='Password', help="Optional password for SMTP authentication", groups='base.group_system')
|
||||
smtp_encryption = fields.Selection([('none', 'None'),
|
||||
@@ -118,6 +119,21 @@ class IrMailServer(models.Model):
|
||||
"is used. Default priority is 10 (smaller number = higher priority)")
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@api.depends('smtp_authentication')
|
||||
def _compute_smtp_authentication_info(self):
|
||||
for server in self:
|
||||
if server.smtp_authentication == 'login':
|
||||
server.smtp_authentication_info = _(
|
||||
'Connect to your server through your usual username and password. \n'
|
||||
'This is the most basic SMTP authentication process and '
|
||||
'may not be accepted by all providers. \n')
|
||||
elif server.smtp_authentication == 'certificate':
|
||||
server.smtp_authentication_info = _(
|
||||
'Authenticate by using SSL certificates, belonging to your domain name. \n'
|
||||
'SSL certificates allow you to authenticate your mail server for the entire domain name.')
|
||||
else:
|
||||
server.smtp_authentication = False
|
||||
|
||||
@api.constrains('smtp_ssl_certificate', 'smtp_ssl_private_key')
|
||||
def _check_smtp_ssl_files(self):
|
||||
"""We must provided both files or none."""
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
name="test_smtp_connection" class="btn-primary"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger"
|
||||
attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" placeholder="e.g. My Outgoing Server"/>
|
||||
@@ -17,24 +19,38 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="smtp_authentication" widget="radio"/>
|
||||
<field name="smtp_user" attrs="{'invisible': [('smtp_authentication', '!=', 'login')]}" force_save="1"/>
|
||||
<field name="smtp_pass" attrs="{'invisible': [('smtp_authentication', '!=', 'login')]}" password="True" force_save="1"/>
|
||||
<field name="smtp_ssl_certificate" attrs="{'invisible': [('smtp_authentication', '!=', 'certificate')]}" force_save="1"/>
|
||||
<field name="smtp_ssl_private_key" attrs="{'invisible': [('smtp_authentication', '!=', 'certificate')]}" force_save="1"/>
|
||||
</group>
|
||||
<group string="Connection">
|
||||
<field name="smtp_encryption" widget="radio"/>
|
||||
<field name="smtp_host"/>
|
||||
<field name="smtp_port" options="{'format': false}"/>
|
||||
<field name="smtp_debug" groups="base.group_no_one"/>
|
||||
<group>
|
||||
<div class="text-muted font-italic" role="alert"
|
||||
attrs="{'invisible': [('smtp_authentication_info', '=', False)]}">
|
||||
<field name="smtp_authentication_info"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page name="connection" string="Connection">
|
||||
<group>
|
||||
<group>
|
||||
<field name="smtp_encryption" widget="radio"/>
|
||||
<field name="smtp_host"/>
|
||||
<field name="smtp_port" options="{'format': false}"/>
|
||||
<field name="smtp_debug" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="smtp_user" attrs="{'invisible': [('smtp_authentication', '=', 'certificate')]}" force_save="1"/>
|
||||
<field name="smtp_pass" attrs="{'invisible': [('smtp_authentication', '!=', 'login')]}" password="True" force_save="1"/>
|
||||
<field name="smtp_ssl_certificate" attrs="{'invisible': [('smtp_authentication', '!=', 'certificate')]}" force_save="1"/>
|
||||
<field name="smtp_ssl_private_key" attrs="{'invisible': [('smtp_authentication', '!=', 'certificate')]}" force_save="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user