Setup your Gmail API credentials in the general settings to link a Gmail account.
-
+
-
-
- {'required' : [('server_type', '!=', 'local'), ('server_type', '!=', 'gmail'), ('password', '!=', False)], 'invisible' : [('server_type', '=', 'gmail')]}
-
-
diff --git a/addons/fetchmail_outlook/models/fetchmail_server.py b/addons/fetchmail_outlook/models/fetchmail_server.py
index 1e36ebe9fe0..52df4eaf582 100644
--- a/addons/fetchmail_outlook/models/fetchmail_server.py
+++ b/addons/fetchmail_outlook/models/fetchmail_server.py
@@ -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()
diff --git a/addons/fetchmail_outlook/tests/test_fetchmail_outlook.py b/addons/fetchmail_outlook/tests/test_fetchmail_outlook.py
index 8cc07d1764d..143f379a598 100644
--- a/addons/fetchmail_outlook/tests/test_fetchmail_outlook.py
+++ b/addons/fetchmail_outlook/tests/test_fetchmail_outlook.py
@@ -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',
})
diff --git a/addons/fetchmail_outlook/views/fetchmail_server_views.xml b/addons/fetchmail_outlook/views/fetchmail_server_views.xml
index 30d432d74e0..a572fd3e448 100644
--- a/addons/fetchmail_outlook/views/fetchmail_server_views.xml
+++ b/addons/fetchmail_outlook/views/fetchmail_server_views.xml
@@ -6,42 +6,36 @@
1000
-
-
-
-
-
-
+
Outlook Token Valid
-
+
+
-
- {}
-
diff --git a/addons/google_account/models/google_service.py b/addons/google_account/models/google_service.py
index ec644bd131b..09bcd919091 100644
--- a/addons/google_account/models/google_service.py
+++ b/addons/google_account/models/google_service.py
@@ -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(
diff --git a/addons/google_gmail/__manifest__.py b/addons/google_gmail/__manifest__.py
index 76a8727f44e..a91d4fd0ed8 100644
--- a/addons/google_gmail/__manifest__.py
+++ b/addons/google_gmail/__manifest__.py
@@ -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",
+ ]
+ },
}
diff --git a/addons/google_gmail/models/ir_mail_server.py b/addons/google_gmail/models/ir_mail_server.py
index eb338bc1c55..57835f6ebb6 100644
--- a/addons/google_gmail/models/ir_mail_server.py
+++ b/addons/google_gmail/models/ir_mail_server.py
@@ -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)
diff --git a/addons/google_gmail/static/description/icon.png b/addons/google_gmail/static/description/icon.png
new file mode 100644
index 00000000000..f28d0ab573f
Binary files /dev/null and b/addons/google_gmail/static/description/icon.png differ
diff --git a/addons/google_gmail/static/description/icon.svg b/addons/google_gmail/static/description/icon.svg
new file mode 100644
index 00000000000..7965971e95e
--- /dev/null
+++ b/addons/google_gmail/static/description/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/addons/google_gmail/static/src/scss/google_gmail.scss b/addons/google_gmail/static/src/scss/google_gmail.scss
new file mode 100644
index 00000000000..1bc54662b5c
--- /dev/null
+++ b/addons/google_gmail/static/src/scss/google_gmail.scss
@@ -0,0 +1,4 @@
+span.o_field_char[name="google_gmail_authorization_code"] {
+ max-width: 150px !important;
+ overflow: hidden;
+}
diff --git a/addons/google_gmail/views/ir_mail_server_views.xml b/addons/google_gmail/views/ir_mail_server_views.xml
index 910a77efa46..b8a25334623 100644
--- a/addons/google_gmail/views/ir_mail_server_views.xml
+++ b/addons/google_gmail/views/ir_mail_server_views.xml
@@ -8,8 +8,8 @@
-
-
diff --git a/addons/mail/models/res_config_settings.py b/addons/mail/models/res_config_settings.py
index 04e6769c969..bfc67e9252e 100644
--- a/addons/mail/models/res_config_settings.py
+++ b/addons/mail/models/res_config_settings.py
@@ -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',
diff --git a/addons/mail/views/res_config_settings_views.xml b/addons/mail/views/res_config_settings_views.xml
index 513be01ce90..ebe37cbe8fb 100644
--- a/addons/mail/views/res_config_settings_views.xml
+++ b/addons/mail/views/res_config_settings_views.xml
@@ -36,13 +36,43 @@
Configure your own email servers
-
+
@
+
+
+
+
+
+
+ Send and receive emails through your Gmail account.
+
+
+
Save this page and come back here to set up the feature.
+
+
+
+
+
+
+
+
+
+ Send and receive emails through your Outlook account.
+
+
+
+ Save this page and come back here to set up the feature.
+
+
+
+
@@ -66,22 +96,6 @@
-
-
-
-
-
-
-
-
- Send and receive emails with your Gmail account.
-
-
-
Save this page and come back here to set up the feature.
-
-
-
Custom ICE server list
diff --git a/addons/microsoft_outlook/__manifest__.py b/addons/microsoft_outlook/__manifest__.py
index b1070571605..0af1221b549 100644
--- a/addons/microsoft_outlook/__manifest__.py
+++ b/addons/microsoft_outlook/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Microsoft Outlook",
- "version": "1.0",
+ "version": "1.1",
"category": "Hidden",
"description": "Outlook support for outgoing mail servers",
"depends": [
diff --git a/addons/microsoft_outlook/models/ir_mail_server.py b/addons/microsoft_outlook/models/ir_mail_server.py
index c01ecf9ab44..f28f7fbd08a 100644
--- a/addons/microsoft_outlook/models/ir_mail_server.py
+++ b/addons/microsoft_outlook/models/ir_mail_server.py
@@ -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)
diff --git a/addons/microsoft_outlook/models/microsoft_outlook_mixin.py b/addons/microsoft_outlook/models/microsoft_outlook_mixin.py
index a534cf475e5..b74afa76e77 100644
--- a/addons/microsoft_outlook/models/microsoft_outlook_mixin.py
+++ b/addons/microsoft_outlook/models/microsoft_outlook_mixin.py
@@ -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,
diff --git a/addons/microsoft_outlook/static/description/icon.png b/addons/microsoft_outlook/static/description/icon.png
new file mode 100644
index 00000000000..6c454d3f896
Binary files /dev/null and b/addons/microsoft_outlook/static/description/icon.png differ
diff --git a/addons/microsoft_outlook/static/description/icon.svg b/addons/microsoft_outlook/static/description/icon.svg
new file mode 100644
index 00000000000..9408317cf0e
--- /dev/null
+++ b/addons/microsoft_outlook/static/description/icon.svg
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/addons/microsoft_outlook/views/ir_mail_server_views.xml b/addons/microsoft_outlook/views/ir_mail_server_views.xml
index 8870b9419e1..5fd7c5db638 100644
--- a/addons/microsoft_outlook/views/ir_mail_server_views.xml
+++ b/addons/microsoft_outlook/views/ir_mail_server_views.xml
@@ -5,38 +5,42 @@
ir.mail_server
-
-
-
-
-
- Send and receive email with your Outlook account.
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/odoo/addons/base/models/ir_mail_server.py b/odoo/addons/base/models/ir_mail_server.py
index 39fddc7f733..de7ea18eebd 100644
--- a/odoo/addons/base/models/ir_mail_server.py
+++ b/odoo/addons/base/models/ir_mail_server.py
@@ -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."""
diff --git a/odoo/addons/base/views/ir_mail_server_views.xml b/odoo/addons/base/views/ir_mail_server_views.xml
index 8fe837d721a..553486cdd20 100644
--- a/odoo/addons/base/views/ir_mail_server_views.xml
+++ b/odoo/addons/base/views/ir_mail_server_views.xml
@@ -10,6 +10,8 @@
name="test_smtp_connection" class="btn-primary"/>
+
@@ -17,24 +19,38 @@
-
+
-
-
-
-
-
-
-
-
-
+
+