confirguration smtp client

This commit is contained in:
wilsongomez 2022-09-02 09:06:03 -05:00
parent b665e950ea
commit a9664f93dc
7 changed files with 203 additions and 96 deletions

View file

@ -3,10 +3,11 @@
# the full copyright notices and license terms. # the full copyright notices and license terms.
from trytond.pool import Pool from trytond.pool import Pool
from . import smtp from . import smtp
from . import email_
def register(): def register():
Pool.register( Pool.register(
smtp.SmtpServer, smtp.SmtpConnectionMail,
smtp.SmtpServerModel, email_.Email,
module='smtp', type_='model') module='smtp', type_='model')

141
email_.py Normal file
View file

@ -0,0 +1,141 @@
import heapq
import mimetypes
import re
from email.encoders import encode_base64
from email.header import Header
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
from email.mime.text import MIMEText
from email.utils import formataddr, getaddresses
try:
import html2text
except ImportError:
html2text = None
from genshi.template import TextTemplate
# from trytond.i18n import gettext
from trytond.config import config
from trytond.pool import Pool, PoolMeta
from trytond.report import Report
from trytond.ir.email_ import HTML_EMAIL, _get_emails
from trytond.sendmail import sendmail_transactional, SMTPDataManager
from trytond.transaction import Transaction
from .sendmail import _SMTPDataManager
class Email(metaclass=PoolMeta):
"Email"
__name__ = 'ir.email'
@classmethod
def send(cls, to='', cc='', bcc='', subject='', body='',
files=None, record=None, reports=None, attachments=None):
pool = Pool()
User = pool.get('res.user')
ActionReport = pool.get('ir.action.report')
Attachment = pool.get('ir.attachment')
transaction = Transaction()
user = User(transaction.user)
SmtpConnection = pool.get('smtp.connection.mail')
smtps = SmtpConnection.search([('user', '=', user.id)])
uri = None
user_email = None
if smtps:
uri = smtps[0].get_uri()
user_email = smtps[0].smtp_email
Model = pool.get(record[0])
record = Model(record[1])
body_html = HTML_EMAIL % {
'subject': subject,
'body': body,
'signature': user.signature or '',
}
content = MIMEMultipart('alternative')
if html2text:
body_text = HTML_EMAIL % {
'subject': subject,
'body': body,
'signature': '',
}
converter = html2text.HTML2Text()
body_text = converter.handle(body_text)
if user.signature:
body_text += '\n-- \n' + converter.handle(user.signature)
part = MIMEText(body_text, 'plain', _charset='utf-8')
content.attach(part)
part = MIMEText(body_html, 'html', _charset='utf-8')
content.attach(part)
if files or reports or attachments:
msg = MIMEMultipart('mixed')
msg.attach(content)
if files is None:
files = []
else:
files = list(files)
for report_id in (reports or []):
report = ActionReport(report_id)
Report = pool.get(report.report_name, type='report')
ext, content, _, title = Report.execute(
[record.id], {
'action_id': report.id,
})
name = '%s.%s' % (title, ext)
if isinstance(content, str):
content = content.encode('utf-8')
files.append((name, content))
if attachments:
files += [
(a.name, a.data) for a in Attachment.browse(attachments)]
for name, data in files:
mimetype, _ = mimetypes.guess_type(name)
if mimetype:
attachment = MIMENonMultipart(*mimetype.split('/'))
attachment.set_payload(data)
encode_base64(attachment)
else:
attachment = MIMEApplication(data)
attachment.add_header(
'Content-Disposition', 'attachment',
filename=('utf-8', '', name))
msg.attach(attachment)
else:
msg = content
msg['From'] = from_ = user_email
if user.email:
if user.name:
user_email = formataddr((user.name, user.email))
else:
user_email = user.email
msg['Behalf-Of'] = user_email
msg['Reply-To'] = user_email
msg['To'] = ', '.join(formataddr(a) for a in getaddresses([to]))
msg['Cc'] = ', '.join(formataddr(a) for a in getaddresses([cc]))
msg['Subject'] = Header(subject, 'utf-8')
to_addrs = list(filter(None, map(
str.strip,
_get_emails(to) + _get_emails(cc) + _get_emails(bcc))))
sendmail_transactional(
from_, to_addrs, msg, datamanager=SMTPDataManager(uri=uri, strict=True))
email = cls(
recipients=to,
recipients_secondary=cc,
recipients_hidden=bcc,
addresses=[{'address': a} for a in to_addrs],
subject=subject,
body=body,
resource=record)
email.save()
with Transaction().set_context(_check_access=False):
attachments_ = []
for name, data in files:
attachments_.append(
Attachment(resource=email, name=name, data=data))
Attachment.save(attachments_)
return email

View file

@ -90,25 +90,15 @@ setup(name=name,
'Intended Audience :: Financial and Insurance Industry', 'Intended Audience :: Financial and Insurance Industry',
'Intended Audience :: Legal Industry', 'Intended Audience :: Legal Industry',
'License :: OSI Approved :: GNU General Public License (GPL)', 'License :: OSI Approved :: GNU General Public License (GPL)',
'Natural Language :: Bulgarian',
'Natural Language :: Catalan',
'Natural Language :: Chinese (Simplified)',
'Natural Language :: Czech', 'Natural Language :: Czech',
'Natural Language :: Dutch', 'Natural Language :: Dutch',
'Natural Language :: English', 'Natural Language :: English',
'Natural Language :: French',
'Natural Language :: German',
'Natural Language :: Hungarian',
'Natural Language :: Italian',
'Natural Language :: Portuguese (Brazilian)',
'Natural Language :: Russian',
'Natural Language :: Slovenian',
'Natural Language :: Spanish', 'Natural Language :: Spanish',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Office/Business', 'Topic :: Office/Business',
@ -125,8 +115,4 @@ setup(name=name,
test_suite='tests', test_suite='tests',
test_loader='trytond.test_loader:Loader', test_loader='trytond.test_loader:Loader',
tests_require=tests_require, tests_require=tests_require,
use_2to3=True,
convert_2to3_doctests=[
'tests/scenario.rst',
],
) )

102
smtp.py
View file

@ -8,9 +8,9 @@ from trytond.pool import Pool
from trytond.pyson import Eval from trytond.pyson import Eval
class SmtpServer(ModelSQL, ModelView): class SmtpConnectionMail(ModelSQL, ModelView):
'SMTP Servers' 'SMTP Conection Mail'
__name__ = 'smtp.server' __name__ = 'smtp.connection.mail'
name = fields.Char('Name', required=True) name = fields.Char('Name', required=True)
smtp_server = fields.Char('Server', required=True, smtp_server = fields.Char('Server', required=True,
states={ states={
@ -28,18 +28,10 @@ class SmtpServer(ModelSQL, ModelView):
states={ states={
'readonly': (Eval('state') != 'draft'), 'readonly': (Eval('state') != 'draft'),
}, depends=['state']) }, depends=['state'])
smtp_user = fields.Char('User',
states={
'readonly': (Eval('state') != 'draft'),
}, depends=['state'])
smtp_password = fields.Char('Password', smtp_password = fields.Char('Password',
states={ states={
'readonly': (Eval('state') != 'draft'), 'readonly': (Eval('state') != 'draft'),
}, depends=['state']) }, depends=['state'])
smtp_use_email = fields.Boolean('Use email',
states={
'readonly': (Eval('state') != 'draft'),
}, depends=['state'], help='Force to send emails using this email')
smtp_email = fields.Char('Email', required=True, smtp_email = fields.Char('Email', required=True,
states={ states={
'readonly': (Eval('state') != 'draft'), 'readonly': (Eval('state') != 'draft'),
@ -49,24 +41,11 @@ class SmtpServer(ModelSQL, ModelView):
('draft', 'Draft'), ('draft', 'Draft'),
('done', 'Done'), ('done', 'Done'),
], 'State', readonly=True, required=True) ], 'State', readonly=True, required=True)
default = fields.Boolean('Default') user = fields.Many2One('res.user', 'User')
models = fields.Many2Many('smtp.server-ir.model',
'server', 'model', 'Models',
states={
'readonly': Eval('state').in_(['done']),
},
depends=['state'])
@classmethod @classmethod
def __setup__(cls): def __setup__(cls):
super(SmtpServer, cls).__setup__() super(SmtpConnectionMail, cls).__setup__()
cls._error_messages.update({
'smtp_successful': 'SMTP Test Connection Was Successful',
'smtp_test_details': 'SMTP Test Connection Details:\n%s',
'smtp_error': 'SMTP Test Connection Failed.',
'server_model_not_found': (
'There are not SMTP server related at model %s'),
})
cls._buttons.update({ cls._buttons.update({
'get_smtp_test': {}, 'get_smtp_test': {},
'draft': { 'draft': {
@ -81,9 +60,6 @@ class SmtpServer(ModelSQL, ModelView):
def check_xml_record(cls, records, values): def check_xml_record(cls, records, values):
return True return True
@staticmethod
def default_default():
return True
@staticmethod @staticmethod
def default_smtp_ssl(): def default_smtp_ssl():
@ -97,6 +73,15 @@ class SmtpServer(ModelSQL, ModelView):
def default_state(): def default_state():
return 'draft' return 'draft'
def get_uri(self):
option = 'smtp'
if self.smtp_tls:
option = 'smtp+tls'
elif self.smtp_ssl:
option = 'smtps'
# smtps://cuenta@dominio.com:miclave@mail.dominio.com:465
return option + '://' + self.smtp_email + ':' + self.smtp_password + '@' + self.smtp_server + ':' + str(self.smtp_port)
@classmethod @classmethod
@ModelView.button @ModelView.button
def draft(cls, servers): def draft(cls, servers):
@ -116,13 +101,14 @@ class SmtpServer(ModelSQL, ModelView):
def get_smtp_test(cls, servers): def get_smtp_test(cls, servers):
"""Checks SMTP credentials and confirms if outgoing connection works""" """Checks SMTP credentials and confirms if outgoing connection works"""
for server in servers: for server in servers:
try: # try:
server.get_smtp_server() server = server.get_smtp_server()
except Exception, message: print(server, 'server')
cls.raise_user_error('smtp_test_details', message) # except e:
except: # cls.raise_user_error('smtp_test_details', message)
cls.raise_user_error('smtp_error') # except:
cls.raise_user_error('smtp_successful') # cls.raise_user_error('smtp_error')
# cls.raise_user_error('smtp_successful')
def get_smtp_server(self): def get_smtp_server(self):
""" """
@ -144,28 +130,28 @@ class SmtpServer(ModelSQL, ModelView):
return smtp_server return smtp_server
@classmethod # @classmethod
def get_smtp_server_from_model(self, model): # def get_smtp_server_from_model(self, model):
""" # """
Return Server from Models # Return Server from Models
:param model: str Model name # :param model: str Model name
return object server # return object server
""" # """
model = Pool().get('ir.model').search([('model', '=', model)])[0] # model = Pool().get('ir.model').search([('model', '=', model)])[0]
servers = Pool().get('smtp.server-ir.model').search([ # servers = Pool().get('smtp.server-ir.model').search([
('model', '=', model), # ('model', '=', model),
], limit=1) # ], limit=1)
if not servers: # if not servers:
self.raise_user_error('server_model_not_found', model.name) # self.raise_user_error('server_model_not_found', model.name)
return servers[0].server # return servers[0].server
class SmtpServerModel(ModelSQL): # class SmtpServerModel(ModelSQL):
'SMTP Server - Model' # 'SMTP Server - Model'
__name__ = 'smtp.server-ir.model' # __name__ = 'smtp.server-ir.model'
_table = 'smtp_server_ir_model' # _table = 'smtp_server_ir_model'
server = fields.Many2One('smtp.server', 'Server', ondelete='CASCADE', # server = fields.Many2One('smtp.server', 'Server', ondelete='CASCADE',
select=True, required=True) # select=True, required=True)
model = fields.Many2One('ir.model', 'Model', ondelete='RESTRICT', # model = fields.Many2One('ir.model', 'Model', ondelete='RESTRICT',
select=True, required=True) # select=True, required=True)

View file

@ -18,20 +18,20 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<!-- smtp.server --> <!-- smtp.server -->
<record model="ir.ui.view" id="server_view_form"> <record model="ir.ui.view" id="server_view_form">
<field name="model">smtp.server</field> <field name="model">smtp.connection.mail</field>
<field name="type">form</field> <field name="type">form</field>
<field name="name">smtp_server_form</field> <field name="name">smtp_connection_mail_form</field>
</record> </record>
<record model="ir.ui.view" id="server_view_tree"> <record model="ir.ui.view" id="server_view_tree">
<field name="model">smtp.server</field> <field name="model">smtp.connection.mail</field>
<field name="type">tree</field> <field name="type">tree</field>
<field name="name">smtp_server_tree</field> <field name="name">smtp_connection_mail_tree</field>
</record> </record>
<!-- Actions / Menu --> <!-- Actions / Menu -->
<record model="ir.action.act_window" id="act_server_form"> <record model="ir.action.act_window" id="act_server_form">
<field name="name">Servers</field> <field name="name">Smtp Connection Users</field>
<field name="res_model">smtp.server</field> <field name="res_model">smtp.connection.mail</field>
</record> </record>
<record model="ir.action.act_window.view" id="act_server_form_view1"> <record model="ir.action.act_window.view" id="act_server_form_view1">
<field name="sequence" eval="10"/> <field name="sequence" eval="10"/>
@ -67,7 +67,7 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
id="menu_server_form"/> id="menu_server_form"/>
<!-- Access --> <!-- Access -->
<record model="ir.model.access" id="access_smtp_server"> <!-- <record model="ir.model.access" id="access_smtp_server">
<field name="model" search="[('model', '=', 'smtp.server')]"/> <field name="model" search="[('model', '=', 'smtp.server')]"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>
@ -104,6 +104,6 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/> <field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/> <field name="perm_delete" eval="True"/>
</record> </record> -->
</data> </data>
</tryton> </tryton>

View file

@ -4,8 +4,8 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<form> <form>
<label name="name"/> <label name="name"/>
<field name="name"/> <field name="name"/>
<label name="default"/> <label name="user"/>
<field name="default"/> <field name="user"/>
<notebook colspan="4"> <notebook colspan="4">
<page string="Server" id="server"> <page string="Server" id="server">
<label name="smtp_server"/> <label name="smtp_server"/>
@ -16,12 +16,8 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<field name="smtp_ssl"/> <field name="smtp_ssl"/>
<label name="smtp_tls"/> <label name="smtp_tls"/>
<field name="smtp_tls"/> <field name="smtp_tls"/>
<label name="smtp_user"/>
<field name="smtp_user"/>
<label name="smtp_password"/> <label name="smtp_password"/>
<field name="smtp_password" widget="password"/> <field name="smtp_password" widget="password"/>
<label name="smtp_use_email"/>
<field name="smtp_use_email"/>
<label name="smtp_email"/> <label name="smtp_email"/>
<field name="smtp_email"/> <field name="smtp_email"/>
<group col="6" colspan="6" id="server_buttons"> <group col="6" colspan="6" id="server_buttons">
@ -32,8 +28,5 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<button name="get_smtp_test" string="Test Connection" icon="tryton-ok"/> <button name="get_smtp_test" string="Test Connection" icon="tryton-ok"/>
</group> </group>
</page> </page>
<page string="Models" id="model">
<field name="models"/>
</page>
</notebook> </notebook>
</form> </form>

View file

@ -2,8 +2,8 @@
<!-- This file is part smtp module for Tryton. <!-- This file is part smtp module for Tryton.
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. --> The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
<tree> <tree>
<field name="user"/>
<field name="name"/> <field name="name"/>
<field name="smtp_server"/> <field name="smtp_server"/>
<field name="smtp_user"/>
<field name="state"/> <field name="state"/>
</tree> </tree>