diff options
| author | Olivier Dony <odo@openerp.com> | 2011-07-22 18:34:57 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@openerp.com> | 2011-07-22 18:34:57 +0200 |
| commit | c996efa2fe65e87ef62e4f0f4520ee83ed9ea5cd (patch) | |
| tree | ae027c0da3c798f0047d92ade88ff2288fbe4729 /addons/mail/wizard | |
| parent | f9a932fac28341ec1abfcf7cbf1bf1607bfbce01 (diff) | |
[IMP] mail: another pass of cleanup/refactoring of mail features - finish renaming
bzr revid: odo@openerp.com-20110722163457-7g9ngdb2p0bixcst
Diffstat (limited to 'addons/mail/wizard')
| -rw-r--r-- | addons/mail/wizard/__init__.py | 4 | ||||
| -rw-r--r-- | addons/mail/wizard/email_compose_message.py | 196 | ||||
| -rw-r--r-- | addons/mail/wizard/mail_compose_message.py | 248 | ||||
| -rw-r--r-- | addons/mail/wizard/mail_compose_message_view.xml (renamed from addons/mail/wizard/email_compose_message_view.xml) | 16 |
4 files changed, 258 insertions, 206 deletions
diff --git a/addons/mail/wizard/__init__.py b/addons/mail/wizard/__init__.py index 16ba392b2d7..88b9581f68e 100644 --- a/addons/mail/wizard/__init__.py +++ b/addons/mail/wizard/__init__.py @@ -19,6 +19,6 @@ # ############################################################################## -import email_compose_message +import mail_compose_message -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/email_compose_message.py b/addons/mail/wizard/email_compose_message.py deleted file mode 100644 index b542d2e2dbb..00000000000 --- a/addons/mail/wizard/email_compose_message.py +++ /dev/null @@ -1,196 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv -from osv import fields -import tools -from tools.safe_eval import safe_eval as eval -import re - -class email_compose_message(osv.osv_memory): - _name = 'email.compose.message' - _inherit = 'email.message.common' - _description = 'This is the wizard for Compose E-mail' - - def default_get(self, cr, uid, fields, context=None): - """ - Returns default values for fields - @param fields: list of fields, for which default values are required to be read - @param context: context arguments, like lang, time zone - - @return: Returns a dictionary that contains default values for fields - """ - if context is None: - context = {} - result = super(email_compose_message, self).default_get(cr, uid, fields, context=context) - vals = {} - if context.get('mass_mail'): - return result - if context.get('active_model') and context.get('active_id') and not context.get('mail')=='reply': - vals = self.get_value(cr, uid, context.get('active_model'), context.get('active_id'), context) - - elif context.get('mail')=='reply' and context.get('active_id', False): - vals = self.get_message_data(cr, uid, int(context.get('active_id', False)), context) - - else: - result['model'] = context.get('active_model', False) - - if not vals: - return result - - for field in fields: - result.update({field : vals.get(field, False)}) - return result - - _columns = { - 'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), - 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), - 'filter_id': fields.many2one('ir.filters', 'Filters'), - } - - def get_value(self, cr, uid, model, res_id, context=None): - return {} - - def get_message_data(self, cr, uid, message_id, context=None): - ''' - Called by default_get() to get message detail - @param message_id: Id of the email message - ''' - if context is None: - context = {} - result = {} - message_pool = self.pool.get('email.message') - if message_id: - message_data = message_pool.browse(cr, uid, message_id, context) - subject = tools.ustr(message_data and message_data.subject or '') - description = message_data and message_data.body or '' - if context.get('mail','') == 'reply': - header = '-------- Original Message --------' - sender = 'From: %s' % tools.ustr(message_data.email_from or '') - email_to = 'To: %s' % tools.ustr(message_data.email_to or '') - sentdate = 'Date: %s' % message_data.date - desc = '\n > \t %s' % tools.ustr(description.replace('\n', "\n > \t") or '') - description = '\n'.join([header, sender, email_to, sentdate, desc]) - if not subject.startswith('Re: '): - subject = "Re: " + subject - - result.update({ - 'body' : description, - 'subject' : subject, - 'message_id' : message_data and message_data.message_id or False, - 'attachment_ids' : [], - 'res_id' : message_data and message_data.res_id or False, - 'email_from' : message_data and message_data.email_to or False, - 'email_to' : message_data and message_data.email_from or False, - 'email_cc' : message_data and message_data.email_cc or False, - 'email_bcc' : message_data and message_data.email_bcc or False, - 'reply_to' : message_data and message_data.reply_to or False, - 'model' : message_data and message_data.model or False, - 'user_id' : message_data and message_data.user_id and message_data.user_id.id or False, - 'references' : message_data and message_data.references and tools.ustr(message_data.references) or False, - 'sub_type' : message_data and message_data.sub_type or False, - 'headers' : message_data and message_data.headers or False, - 'priority' : message_data and message_data.priority or False, - 'debug': message_data and message_data.debug or False - }) - - return result - - def send_mail(self, cr, uid, ids, context=None): - ''' - Sends the email - ''' - if context is None: - context = {} - - email_message_pool = self.pool.get('email.message') - attachment = {} - email_ids = [] - for mail in self.browse(cr, uid, ids, context=context): - for attach in mail.attachment_ids: - attachment[attach.datas_fname] = attach.datas - references = False - message_id = False - - # Reply Email - if context.get('mail') == 'reply' and mail.message_id: - references = mail.references and mail.references + "," + mail.message_id or mail.message_id - else: - message_id = mail.message_id - - # Mass mailing - if context.get('mass_mail', False): - if context['active_ids'] and context['active_model']: - active_ids = context['active_ids'] - active_model = context['active_model'] - - else: - active_model = mail.model - active_model_pool = self.pool.get(active_model) - active_ids = active_model_pool.search(cr, uid, eval(mail.filter_id.domain), context=eval(mail.filter_id.context)) - - for active_id in active_ids: - subject = self.get_template_value(cr, uid, mail.subject, active_model, active_id) - body = self.get_template_value(cr, uid, mail.body, active_model, active_id) - email_to = self.get_template_value(cr, uid, mail.email_to, active_model, active_id) - email_from = self.get_template_value(cr, uid, mail.email_from, active_model, active_id) - email_cc = self.get_template_value(cr, uid, mail.email_cc, active_model, active_id) - email_bcc = self.get_template_value(cr, uid, mail.email_bcc, active_model, active_id) - reply_to = self.get_template_value(cr, uid, mail.reply_to, active_model, active_id) - - email_id = email_message_pool.schedule_with_attach(cr, uid, email_from, email_to, subject, body, - model=mail.model, email_cc=email_cc, email_bcc=email_bcc, reply_to=reply_to, - attach=attachment, message_id=message_id, references=references, openobject_id=int(mail.res_id), - subtype=mail.sub_type, x_headers=mail.headers, priority=mail.priority, smtp_server_id=mail.smtp_server_id and mail.smtp_server_id.id, - auto_delete=mail.auto_delete or False, context=context) - email_ids.append(email_id) - - else: - email_id = email_message_pool.schedule_with_attach(cr, uid, mail.email_from, mail.email_to, mail.subject, mail.body, - model=mail.model, email_cc=mail.email_cc, email_bcc=mail.email_bcc, reply_to=mail.reply_to, - attach=attachment, message_id=message_id, references=references, openobject_id=int(mail.res_id), - subtype=mail.sub_type, x_headers=mail.headers, priority=mail.priority, smtp_server_id=mail.smtp_server_id and mail.smtp_server_id.id, - auto_delete=mail.auto_delete, context=context) - email_ids.append(email_id) - return {'type': 'ir.actions.act_window_close'} - - - def get_template_value(self, cr, uid, message, model, resource_id, context=None): - if context is None: - context = {} - def merge(match): - exp = str(match.group()[2:-1]).strip() - result = eval(exp, - { - 'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context), - 'context': dict(context), # copy context to prevent side-effects of eval - 'object' : self.pool.get(model).browse(cr, uid, resource_id), - }) - if result in (None, False): - return str("") - return tools.ustr(result) - - com = re.compile('(\$\{.+?\})') - return message and com.sub(merge, message) - -email_compose_message() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py new file mode 100644 index 00000000000..0cdd5a8b7ba --- /dev/null +++ b/addons/mail/wizard/mail_compose_message.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import re + +import tools +from mail.mail_message import to_email +from osv import osv +from osv import fields +from tools.safe_eval import safe_eval as eval +from tools.safe_eval import literal_eval +from tools.translate import _ + +# main mako-like expression pattern +EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') + +class mail_compose_message(osv.osv_memory): + """Generic E-mail composition wizard. This wizard is meant to be inherited + at model and view level to provide specific wizard features. + + The behavior of the wizard can be modified through the use of context + parameters, among which are: + + * mass_mail: turns multi-recipient mode, where the mail details can + contain template placeholders that will be merged with + actual data before being sent to each recipient, as + determined via ``context['active_model']`` and + ``context['active_ids']``. + * mail: if set to 'reply', the wizard will be in mail reply mode + * active_model: model name of the document to which the mail being + composed is related + * active_id: id of the document to which the mail being composed is + related, or id of the message to which user is replying, + in case mail == 'reply'. + * active_ids: ids of the documents to which the mail being composed is + related, in case ``context['mass_mail']`` is set. + """ + _name = 'mail.compose.message' + _inherit = 'mail.message.common' + _description = 'E-mail composition wizard' + + def default_get(self, cr, uid, fields, context=None): + """Overridden to provide specific defaults depending on the context + parameters. + + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + """ + if context is None: + context = {} + result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + vals = {} + if context.get('mass_mail'): + return result + if context.get('active_model') and context.get('active_id') and not context.get('mail')=='reply': + vals = self.get_value(cr, uid, context.get('active_model'), context.get('active_id'), context) + elif context.get('mail')=='reply' and context.get('active_id'): + vals = self.get_message_data(cr, uid, int(context['active_id']), context) + else: + result['model'] = context.get('active_model', False) + if not vals: + return result + for field in fields: + result.update({field : vals.get(field, False)}) + return result + + _columns = { + 'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), + 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), + 'filter_id': fields.many2one('ir.filters', 'Filters'), + } + + def get_value(self, cr, uid, model, res_id, context=None): + """Returns a defaults-like dict with initial values for the composition + wizard when sending an email related to the document record identified + by ``model`` and ``res_id``. + + The default implementation returns an empty dictionary, and is meant + to be overridden by subclasses. + + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + """ + return {} + + def get_message_data(self, cr, uid, message_id, context=None): + """Returns a defaults-like dict with initial values for the composition + wizard when replying to the given message (e.g. including the quote + of the initial message, and the correct recipient). + Should not be called unless ``context['mail'] == 'reply'``. + + :param int message_id: id of the mail.message to which the user + is replying. + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + When calling this method, the ``'mail'`` value + in the context should be ``'reply'``. + """ + if context is None: + context = {} + result = {} + mail_message = self.pool.get('mail.message') + if message_id: + message_data = mail_message.browse(cr, uid, message_id, context) + subject = tools.ustr(message_data.subject or '') + # we use the plain text version of the original mail, by default, + # as it is easier to quote than the HTML version. + # XXX TODO: make it possible to switch to HTML on the fly + description = message_data.body_text or '' + if context.get('mail') == 'reply': + header = _('-------- Original Message --------') + sender = _('From: %s') % tools.ustr(message_data.email_from or '') + email_to = _('To: %s') % tools.ustr(message_data.email_to or '') + sentdate = _('Date: %s') % message_data.date + desc = '\n > \t %s' % tools.ustr(description.replace('\n', "\n > \t") or '') + description = '\n'.join([header, sender, email_to, sentdate, desc]) + re_prefix = _("Re:") + if not (subject.startswith('Re:') or subject.startswith(re_prefix)): + subject = "%s %s" % (re_prefix, subject) + result.update({ + 'subtype' : 'plain', # default to the text version due to quoting + 'body_text' : description, + 'subject' : subject, + 'message_id' : message_data.message_id or False, + 'attachment_ids' : [], + 'res_id' : message_data.res_id or False, + 'email_from' : message_data.email_to or False, + 'email_to' : message_data.email_from or False, + 'email_cc' : message_data.email_cc or False, + 'email_bcc' : message_data.email_bcc or False, + 'reply_to' : message_data.reply_to or False, + 'model' : message_data.model or False, + 'user_id' : message_data.user_id and message_data.user_id.id or False, + 'references' : message_data.references and tools.ustr(message_data.references) or False, + 'headers' : message_data.headers or False, + }) + return result + + def send_mail(self, cr, uid, ids, context=None): + '''Process the wizard contents and proceed with sending the corresponding + email(s), rendering any template patterns on the fly if needed. + The resulting email(s) are scheduled for being sent the next time the + mail.message scheduler runs, or the next time + ``mail.message.process_email_queue`` is called. + + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + ''' + if context is None: + context = {} + mail_message = self.pool.get('mail.message') + for mail in self.browse(cr, uid, ids, context=context): + attachment = {} + for attach in mail.attachment_ids: + attachment[attach.datas_fname] = attach.datas + references = False + message_id = False + + # Reply Email + if context.get('mail') == 'reply' and mail.message_id: + references = mail.references and mail.references + "," + mail.message_id or mail.message_id + else: + message_id = mail.message_id + + if context.get('mass_mail'): + # Mass mailing: must render the template patterns + if context.get('active_ids') and context.get('active_model'): + active_ids = context['active_ids'] + active_model = context['active_model'] + else: + active_model = mail.model + active_model_pool = self.pool.get(active_model) + active_ids = active_model_pool.search(cr, uid, literal_eval(mail.filter_id.domain), context=literal_eval(mail.filter_id.context)) + + for active_id in active_ids: + subject = self.render_template(cr, uid, mail.subject, active_model, active_id) + + body = mail.body_html if mail.subtype == 'html' else mail.body_text + body = self.render_template(cr, uid, body, active_model, active_id) + email_from = self.render_template(cr, uid, mail.email_from, active_model, active_id) + email_to = self.render_template(cr, uid, mail.email_to, active_model, active_id) + email_cc = self.render_template(cr, uid, mail.email_cc, active_model, active_id) + email_bcc = self.render_template(cr, uid, mail.email_bcc, active_model, active_id) + reply_to = self.render_template(cr, uid, mail.reply_to, active_model, active_id) + + mail_message.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, body, + model=mail.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to, + attachments=attachment, message_id=message_id, references=references, res_id=int(mail.res_id), + subtype=mail.subtype, headers=mail.headers, auto_delete=mail.auto_delete, context=context) + else: + # normal mode - no mass-mailing + mail_message.schedule_with_attach(cr, uid, mail.email_from, to_email(mail.email_to), mail.subject, mail.body, + model=mail.model, email_cc=to_email(mail.email_cc), email_bcc=to_email(mail.email_bcc), reply_to=mail.reply_to, + attachments=attachment, message_id=message_id, references=references, res_id=int(mail.res_id), + subtype=mail.subtype, headers=mail.headers, auto_delete=mail.auto_delete, context=context) + + return {'type': 'ir.actions.act_window_close'} + + def render_template(self, cr, uid, template, model, res_id, context=None): + """Render the given template text, replace mako-like expressions ``${expr}`` + with the result of passing these expressions ``through safe_eval()`` with + an evaluation context containing: + + * ``user``: browse_record of the current user + * ``object``: browse_record of the document record this mail is + related to + * ``context``: the context passed to the mail composition wizard + + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + """ + if context is None: + context = {} + def merge(match): + exp = str(match.group()[2:-1]).strip() + result = eval(exp, + { + 'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context), + 'object' : self.pool.get(model).browse(cr, uid, res_id, context=context), + 'context': dict(context), # copy context to prevent side-effects of eval + }) + if result in (None, False): + return "" + return tools.ustr(result) + return template and EXPRESSION_PATTERN.sub(merge, template) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/email_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 0cc90f3e7c9..ec2a038f073 100644 --- a/addons/mail/wizard/email_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -3,15 +3,14 @@ <data> <record model="ir.ui.view" id="email_compose_message_wizard_form"> - <field name="name">email.compose.message.form</field> - <field name="model">email.compose.message</field> + <field name="name">mail.compose.message.form</field> + <field name="model">mail.compose.message</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="Compose Email"> <group col="6" colspan="4"> <field name="model" invisible="context.get('active_model',False)"/> <field name='filter_id' invisible="context.get('active_model',False)"/> - <field name="smtp_server_id" widget="selection" colspan="4" invisible="1"/> <field name="email_from" colspan="4" required="1"/> <field name="email_to" colspan="4" required="1"/> <field name="email_cc" colspan="4"/> @@ -23,7 +22,7 @@ <separator string="" colspan="4"/> <notebook colspan="4"> <page string="Body"> - <field name="body" colspan="4" nolabel="1"/> + <field name="body_text" colspan="4" nolabel="1"/> </page> <page string="Attachments"> <label string="Add here all attachments of the current document you want to include in the Email." colspan="4"/> @@ -41,16 +40,17 @@ <record id="action_email_compose_message_wizard" model="ir.actions.act_window"> <field name="name">Compose E-mail</field> - <field name="res_model">email.compose.message</field> - <field name="src_model">email.compose.message</field> + <field name="res_model">mail.compose.message</field> + <field name="src_model">mail.compose.message</field> <field name="type">ir.actions.act_window</field> <field name="view_type">form</field> <field name="view_mode">form</field> <field name="target">new</field> </record> - <act_window name="Mass Mailing" - res_model="email.compose.message" + <!-- Replace the default mass-mailing wizard in base with the composition wizard --> + <act_window name="Mass Mailing" + res_model="mail.compose.message" src_model="res.partner" view_mode="form" target="new" |
