[IMP] fetchmail_gmail, google_gmail: simplify the mail server form view

Purpose
=======
A field `use_google_gmail_service` has been used in stable to define
a mail server which use Gmail authentication.

But now that the fields `smtp_authentication` exists, we want to use it
to simplify the mail server form view. For the incoming mail server,
the field `server_type` will be used for the same purpose.

Add a new field to have the option to install `google_gmail` in the
main settings page.

Task-2170676

closes odoo/odoo#83413

Related: odoo/upgrade#3199
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
std-odoo
2022-02-01 15:32:16 +00:00
parent e5f35a6074
commit d497dbe919
13 changed files with 92 additions and 74 deletions
+19 -8
View File
@@ -100,13 +100,14 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
def connect(self):
self.ensure_one()
if self.server_type == 'imap':
connection_type = self._get_connection_type()
if connection_type == 'imap':
if self.is_ssl:
connection = IMAP4_SSL(self.server, int(self.port))
else:
connection = IMAP4(self.server, int(self.port))
self._imap_login(connection)
elif self.server_type == 'pop':
elif connection_type == 'pop':
if self.is_ssl:
connection = POP3_SSL(self.server, int(self.port))
else:
@@ -148,9 +149,10 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
finally:
try:
if connection:
if server.server_type == 'imap':
connection_type = server._get_connection_type()
if connection_type == 'imap':
connection.close()
elif server.server_type == 'pop':
elif connection_type == 'pop':
connection.quit()
except Exception:
# ignored, just a consequence of the previous exception
@@ -160,7 +162,7 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
@api.model
def _fetch_mails(self):
""" Method called by cron to fetch mails from servers """
return self.search([('state', '=', 'done'), ('server_type', 'in', ['pop', 'imap'])]).fetch_mail()
return self.search([('state', '=', 'done'), ('server_type', '!=', 'local')]).fetch_mail()
def fetch_mail(self):
""" WARNING: meant for cron usage only - will commit() after each email! """
@@ -174,7 +176,8 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
count, failed = 0, 0
imap_server = None
pop_server = None
if server.server_type == 'imap':
connection_type = server._get_connection_type()
if connection_type == 'imap':
try:
imap_server = server.connect()
imap_server.select()
@@ -198,7 +201,7 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
if imap_server:
imap_server.close()
imap_server.logout()
elif server.server_type == 'pop':
elif connection_type == 'pop':
try:
while True:
failed_in_loop = 0
@@ -231,6 +234,14 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
server.write({'date': fields.Datetime.now()})
return True
def _get_connection_type(self):
"""Return which connection must be used for this mail server (IMAP or POP).
Can be overridden in sub-module to define which connection to use for a specific
"server_type" (e.g. Gmail server).
"""
self.ensure_one()
return self.server_type
@api.model
def _update_cron(self):
if self.env.context.get('fetchmail_cron_running'):
@@ -238,6 +249,6 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
try:
# Enabled/Disable cron based on the number of 'done' server of type pop or imap
cron = self.env.ref('fetchmail.ir_cron_mail_gateway_action')
cron.toggle(model=self._name, domain=[('state', '=', 'done'), ('server_type', 'in', ['pop', 'imap'])])
cron.toggle(model=self._name, domain=[('state', '=', 'done'), ('server_type', '!=', 'local')])
except ValueError:
pass
+1 -1
View File
@@ -31,8 +31,8 @@
<sheet>
<group col="4">
<field name="name"/>
<field name="server_type"/>
<field name="date"/>
<field name="server_type" widget="radio"/>
</group>
<notebook>
<page string="Server &amp; Login" name="server_login_details">
+1 -1
View File
@@ -3,7 +3,7 @@
{
"name": "Fetchmail Gmail",
"version": "1.0",
"version": "1.1",
"category": "Hidden",
"description": "Google authentication for incoming mail server",
"depends": [
@@ -1,34 +1,28 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, _
from odoo.exceptions import UserError
from odoo import api, fields, models
class FetchmailServer(models.Model):
_name = 'fetchmail.server'
_inherit = ['fetchmail.server', 'google.gmail.mixin']
@api.constrains('use_google_gmail_service', 'server_type')
def _check_use_google_gmail_service(self):
if any(server.use_google_gmail_service and server.server_type != 'imap' for server in self):
raise UserError(_('Gmail authentication only supports IMAP server type.'))
server_type = fields.Selection(selection_add=[('gmail', 'Gmail OAuth Authentication')], ondelete={'gmail': 'set default'})
@api.onchange('use_google_gmail_service')
def _onchange_use_google_gmail_service(self):
@api.onchange('server_type', 'is_ssl', 'object_id')
def onchange_server_type(self):
"""Set the default configuration for a IMAP Gmail server."""
if self.use_google_gmail_service:
if self.server_type == 'gmail':
self.server = 'imap.gmail.com'
self.server_type = 'imap'
self.is_ssl = True
self.port = 993
else:
self.server_type = 'pop'
self.is_ssl = False
self.google_gmail_authorization_code = False
self.google_gmail_refresh_token = False
self.google_gmail_access_token = False
self.google_gmail_access_token_expiration = False
super(FetchmailServer, self).onchange_server_type()
def _imap_login(self, connection):
"""Authenticate the IMAP connection.
@@ -36,9 +30,16 @@ class FetchmailServer(models.Model):
If the mail server is Gmail, we use the OAuth2 authentication protocol.
"""
self.ensure_one()
if self.use_google_gmail_service:
if self.server_type == 'gmail':
auth_string = self._generate_oauth2_string(self.user, self.google_gmail_refresh_token)
connection.authenticate('XOAUTH2', lambda x: auth_string)
connection.select('INBOX')
else:
super(FetchmailServer, self)._imap_login(connection)
def _get_connection_type(self):
"""Return which connection must be used for this mail server (IMAP or POP).
The Gmail mail server used an IMAP connection.
"""
self.ensure_one()
return 'imap' if self.server_type == 'gmail' else super()._get_connection_type()
@@ -5,26 +5,25 @@
<field name="model">fetchmail.server</field>
<field name="inherit_id" ref="fetchmail.view_email_server_form"/>
<field name="arch" type="xml">
<field name="server" position="before">
<field name="use_google_gmail_service" string="Gmail" attrs="{'readonly': [('state', '=', 'done')]}"/>
</field>
<field name="user" position="after">
<field string="Authorization Code" name="google_gmail_authorization_code" password="True"
attrs="{'required': [('use_google_gmail_service', '=', True)], 'invisible': [('use_google_gmail_service', '=', False)], 'readonly': [('state', '=', 'done')]}"
attrs="{'required': [('server_type', '=', 'gmail')], 'invisible': [('server_type', '!=', 'gmail')], 'readonly': [('state', '=', 'done')]}"
style="word-break: break-word;"/>
<field name="google_gmail_uri"
class="fa fa-arrow-right oe_edit_only"
widget="url"
text=" Get an Authorization Code"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '=', False)]}"
attrs="{'invisible': ['|', ('server_type', '!=', 'gmail'), ('google_gmail_uri', '=', False)]}"
nolabel="1"/>
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials in the general settings to link a Gmail account.
attrs="{'invisible': ['|', ('server_type', '!=', 'gmail'), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials to link a Gmail account.
</div>
</field>
<field name="password" position="attributes">
<attribute name="attrs">{'required' : [('server_type', '!=', 'local'), ('use_google_gmail_service', '=', False), ('password', '!=', False)], 'invisible' : [('use_google_gmail_service', '=', True)]}</attribute>
<attribute name="attrs">
{'required' : [('server_type', '!=', 'local'), ('server_type', '!=', 'gmail'), ('password', '!=', False)], 'invisible' : [('server_type', '=', 'gmail')]}
</attribute>
</field>
</field>
</record>
+1 -1
View File
@@ -3,7 +3,7 @@
{
"name": "Google Gmail",
"version": "1.0",
"version": "1.1",
"category": "Hidden",
"description": "Gmail support for incoming / outgoing mail servers",
"depends": [
@@ -16,7 +16,6 @@ class GoogleGmailMixin(models.AbstractModel):
_SERVICE_SCOPE = 'https://mail.google.com/'
use_google_gmail_service = fields.Boolean('Gmail Authentication')
google_gmail_authorization_code = fields.Char(string='Authorization Code', groups='base.group_system')
google_gmail_refresh_token = fields.Char(string='Refresh Token', groups='base.group_system')
google_gmail_access_token = fields.Char(string='Access Token', groups='base.group_system')
+12 -9
View File
@@ -3,7 +3,7 @@
import base64
from odoo import models, api
from odoo import fields, models, api
class IrMailServer(models.Model):
@@ -12,17 +12,20 @@ class IrMailServer(models.Model):
_name = 'ir.mail_server'
_inherit = ['ir.mail_server', 'google.gmail.mixin']
smtp_authentication = fields.Selection(
selection_add=[('gmail', 'Gmail OAuth Authentication')],
ondelete={'gmail': 'set default'})
@api.onchange('smtp_encryption')
def _onchange_encryption(self):
"""Do not change the SMTP configuration if it's a Gmail server
(e.g. the port which is already set)"""
if not self.use_google_gmail_service:
super()._onchange_encryption()
if self.smtp_authentication != 'gmail':
super(IrMailServer, self)._onchange_encryption()
@api.onchange('use_google_gmail_service')
def _onchange_use_google_gmail_service(self):
if self.use_google_gmail_service:
@api.onchange('smtp_authentication')
def _onchange_smtp_authentication(self):
if self.smtp_authentication == 'gmail':
self.smtp_host = 'smtp.gmail.com'
self.smtp_encryption = 'starttls'
self.smtp_port = 587
@@ -34,10 +37,10 @@ class IrMailServer(models.Model):
self.google_gmail_access_token_expiration = False
def _smtp_login(self, connection, smtp_user, smtp_password):
if len(self) == 1 and self.use_google_gmail_service:
if len(self) == 1 and self.smtp_authentication == 'gmail':
auth_string = self._generate_oauth2_string(smtp_user, self.google_gmail_refresh_token)
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(IrMailServer, self)._smtp_login(connection, smtp_user, smtp_password)
@@ -5,26 +5,25 @@
<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_google_gmail_service" string="Gmail"/>
</field>
<field name="smtp_user" position="after">
<field string="Authorization Code" name="google_gmail_authorization_code" password="True"
attrs="{'required': [('use_google_gmail_service', '=', True)], 'invisible': [('use_google_gmail_service', '=', False)]}"
style="word-break: break-word;"/>
attrs="{'required': [('smtp_authentication', '=', 'gmail')], 'invisible': [('smtp_authentication', '!=', 'gmail')]}"/>
<field name="google_gmail_uri"
class="fa fa-arrow-right oe_edit_only"
widget="url"
text=" Get an Authorization Code"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '=', False)]}"
attrs="{'invisible': ['|', ('smtp_authentication', '!=', 'gmail'), ('google_gmail_uri', '=', False)]}"
nolabel="1"/>
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials in the general settings to link a Gmail account.
attrs="{'invisible': ['|', ('smtp_authentication', '!=', 'gmail'), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials to link a Gmail account.
</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' : [('use_google_gmail_service', '=', True)]}</attribute>
<attribute name="attrs">{'invisible' : [('smtp_authentication', '=', 'gmail')]}</attribute>
</field>
</field>
</record>
@@ -6,25 +6,14 @@
<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="google_gmail_setting"
attrs="{'invisible': [('external_email_server_default', '=', False)]}">
<div class="o_setting_right_pane">
<span class="o_form_label">Gmail Credentials</span>
<div class="text-muted">
Send and receive email with your Gmail account.
</div>
<div class="content-group">
<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="ml-2"/>
</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="ml-2"/>
</div>
</div>
</div>
<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="ml-2" attrs="{'required': [('module_google_gmail', '=', True), ('external_email_server_default', '=', True)]}"/>
</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="ml-2" attrs="{'required': [('module_google_gmail', '=', True), ('external_email_server_default', '=', True)]}"/>
</div>
</div>
</field>
@@ -15,6 +15,7 @@ class ResConfigSettings(models.TransientModel):
alias_domain = fields.Char(
'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')
restrict_template_rendering = fields.Boolean(
'Restrict Template Rendering',
config_parameter='mail.restrict.template.rendering',
@@ -66,6 +66,22 @@
</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>
+4 -4
View File
@@ -233,12 +233,12 @@ class IrMailServer(models.Model):
if mail_server:
smtp_server = mail_server.smtp_host
smtp_port = mail_server.smtp_port
if mail_server.smtp_authentication == "login":
smtp_user = mail_server.smtp_user
smtp_password = mail_server.smtp_pass
else:
if mail_server.smtp_authentication == "certificate":
smtp_user = None
smtp_password = None
else:
smtp_user = mail_server.smtp_user
smtp_password = mail_server.smtp_pass
smtp_encryption = mail_server.smtp_encryption
smtp_debug = smtp_debug or mail_server.smtp_debug
from_filter = mail_server.from_filter