diff --git a/addons/account_followup/__openerp__.py b/addons/account_followup/__openerp__.py index ae6fb8e4fce..62624fa8801 100644 --- a/addons/account_followup/__openerp__.py +++ b/addons/account_followup/__openerp__.py @@ -43,7 +43,7 @@ """, 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', - 'depends': ['account'], + 'depends': ['account', 'emails'], 'init_xml': [], 'update_xml': [ 'security/ir.model.access.csv', diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py index 5d9c2da860f..64822e1d4cf 100644 --- a/addons/account_followup/wizard/account_followup_print.py +++ b/addons/account_followup/wizard/account_followup_print.py @@ -205,6 +205,7 @@ class account_followup_print_all(osv.osv_memory): move_obj = self.pool.get('account.move.line') user_obj = self.pool.get('res.users') line_obj = self.pool.get('account_followup.stat') + email_message_obj = self.pool.get('email.message') if context is None: context = {} @@ -276,7 +277,7 @@ class account_followup_print_all(osv.osv_memory): msg = '' if dest: try: - tools.email_send(src, dest, sub, body) + email_message_obj.email_send(cr, uid, src, dest, sub, body, model='account.followup.print.all') msg_sent += partner.name + '\n' except Exception, e: raise osv.except_osv('Error !', e ) diff --git a/addons/base_action_rule/__openerp__.py b/addons/base_action_rule/__openerp__.py index 7717890afba..57818dddc5c 100644 --- a/addons/base_action_rule/__openerp__.py +++ b/addons/base_action_rule/__openerp__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # @@ -15,7 +15,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## @@ -26,12 +26,12 @@ 'description': "This module allows to implement action rules for any object.", 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', - 'depends': ['base'], + 'depends': ['base', 'emails'], 'init_xml': [ 'base_action_rule_data.xml' ], 'update_xml': [ - 'base_action_rule_view.xml', + 'base_action_rule_view.xml', 'security/ir.model.access.csv', ], 'demo_xml': [], diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 2a4e2c0e4e1..a2432050f1b 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -24,7 +24,7 @@ from tools.translate import _ from datetime import datetime from datetime import timedelta from tools.safe_eval import safe_eval -import pooler +import pooler import re import time import tools @@ -39,7 +39,7 @@ class base_action_rule(osv.osv): _name = 'base.action.rule' _description = 'Action Rules' - + def _state_get(self, cr, uid, context=None): """ Get State @param self: The object pointer @@ -55,7 +55,7 @@ class base_action_rule(osv.osv): @param uid: the current user’s ID for security checks, @param context: A standard dictionary for contextual values """ return [('', '')] - + def priority_get(self, cr, uid, context=None): """ Get Priority @param self: The object pointer @@ -65,57 +65,57 @@ class base_action_rule(osv.osv): return [('', '')] _columns = { - 'name': fields.char('Rule Name', size=64, required=True), - 'model_id': fields.many2one('ir.model', 'Object', required=True), - 'create_date': fields.datetime('Create Date', readonly=1), + 'name': fields.char('Rule Name', size=64, required=True), + 'model_id': fields.many2one('ir.model', 'Object', required=True), + 'create_date': fields.datetime('Create Date', readonly=1), 'active': fields.boolean('Active', help="If the active field is set to False,\ - it will allow you to hide the rule without removing it."), + it will allow you to hide the rule without removing it."), 'sequence': fields.integer('Sequence', help="Gives the sequence order \ -when displaying a list of rules."), +when displaying a list of rules."), 'trg_date_type': fields.selection([ - ('none', 'None'), - ('create', 'Creation Date'), - ('action_last', 'Last Action Date'), - ('date', 'Date'), - ('deadline', 'Deadline'), - ], 'Trigger Date', size=16), + ('none', 'None'), + ('create', 'Creation Date'), + ('action_last', 'Last Action Date'), + ('date', 'Date'), + ('deadline', 'Deadline'), + ], 'Trigger Date', size=16), 'trg_date_range': fields.integer('Delay after trigger date', \ help="Delay After Trigger Date,\ specifies you can put a negative number. If you need a delay before the \ -trigger date, like sending a reminder 15 minutes before a meeting."), +trigger date, like sending a reminder 15 minutes before a meeting."), 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), \ - ('day', 'Days'), ('month', 'Months')], 'Delay type'), - 'trg_user_id': fields.many2one('res.users', 'Responsible'), - 'trg_partner_id': fields.many2one('res.partner', 'Partner'), - 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), - 'trg_state_from': fields.selection(_state_get, 'State', size=16), - 'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16), + ('day', 'Days'), ('month', 'Months')], 'Delay type'), + 'trg_user_id': fields.many2one('res.users', 'Responsible'), + 'trg_partner_id': fields.many2one('res.partner', 'Partner'), + 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), + 'trg_state_from': fields.selection(_state_get, 'State', size=16), + 'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16), - 'act_method': fields.char('Call Object Method', size=64), - 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), - 'act_state': fields.selection(_state_get, 'Set State to', size=16), + 'act_method': fields.char('Call Object Method', size=64), + 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), + 'act_state': fields.selection(_state_get, 'Set State to', size=16), 'act_email_cc': fields.char('Add Watchers (Cc)', size=250, help="\ These people will receive a copy of the future communication between partner \ -and users by email"), +and users by email"), 'act_remind_partner': fields.boolean('Remind Partner', help="Check \ -this if you want the rule to send a reminder by email to the partner."), +this if you want the rule to send a reminder by email to the partner."), 'act_remind_user': fields.boolean('Remind Responsible', help="Check \ -this if you want the rule to send a reminder by email to the user."), - 'act_reply_to': fields.char('Reply-To', size=64), - 'act_remind_attach': fields.boolean('Remind with Attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."), +this if you want the rule to send a reminder by email to the user."), + 'act_reply_to': fields.char('Reply-To', size=64), + 'act_remind_attach': fields.boolean('Remind with Attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."), 'act_mail_to_user': fields.boolean('Mail to Responsible', help="Check\ - this if you want the rule to send an email to the responsible person."), - 'act_mail_to_watchers': fields.boolean('Mail to Watchers (CC)', + this if you want the rule to send an email to the responsible person."), + 'act_mail_to_watchers': fields.boolean('Mail to Watchers (CC)', help="Check this if you want \ -the rule to mark CC(mail to any other person defined in actions)."), +the rule to mark CC(mail to any other person defined in actions)."), 'act_mail_to_email': fields.char('Mail to these Emails', size=128, \ - help="Email-id of the persons whom mail is to be sent"), - 'act_mail_body': fields.text('Mail body', help="Content of mail"), + help="Email-id of the persons whom mail is to be sent"), + 'act_mail_body': fields.text('Mail body', help="Content of mail"), 'regex_name': fields.char('Regex on Resource Name', size=128, help="Regular expression for matching name of the resource\ \ne.g.: 'urgent.*' will search for records having name starting with the string 'urgent'\ -\nNote: This is case sensitive search."), - 'server_action_id': fields.many2one('ir.actions.server', 'Server Action', help="Describes the action name.\neg:on which object which action to be taken on basis of which condition"), - 'filter_id':fields.many2one('ir.filters', 'Filter', required=False), +\nNote: This is case sensitive search."), + 'server_action_id': fields.many2one('ir.actions.server', 'Server Action', help="Describes the action name.\neg:on which object which action to be taken on basis of which condition"), + 'filter_id':fields.many2one('ir.filters', 'Filter', required=False), 'act_email_from' : fields.char('Email From', size=64, required=False, help="Use a python expression to specify the right field on which one than we will use for the 'From' field of the header"), 'act_email_to' : fields.char('Email To', size=64, required=False, @@ -124,17 +124,17 @@ the rule to mark CC(mail to any other person defined in actions)."), } _defaults = { - 'active': lambda *a: True, - 'trg_date_type': lambda *a: 'none', - 'trg_date_range_type': lambda *a: 'day', - 'act_mail_to_user': lambda *a: 0, - 'act_remind_partner': lambda *a: 0, - 'act_remind_user': lambda *a: 0, - 'act_mail_to_watchers': lambda *a: 0, + 'active': lambda *a: True, + 'trg_date_type': lambda *a: 'none', + 'trg_date_range_type': lambda *a: 'day', + 'act_mail_to_user': lambda *a: 0, + 'act_remind_partner': lambda *a: 0, + 'act_remind_user': lambda *a: 0, + 'act_mail_to_watchers': lambda *a: 0, } - + _order = 'sequence' - + def onchange_model_id(self, cr, uid, ids, name): #This is not a good solution as it will affect the domain only on onchange res = {'domain':{'filter_id':[]}} @@ -174,7 +174,7 @@ the rule to mark CC(mail to any other person defined in actions)."), self.pre_action(cr, uid, [new_id], model, context=context) return new_id return make_call_old - + def _write(self, old_write, model, context=None): if context is None: context = {} @@ -202,9 +202,9 @@ the rule to mark CC(mail to any other person defined in actions)."), return True def create(self, cr, uid, vals, context=None): res_id = super(base_action_rule, self).create(cr, uid, vals, context=context) - self._register_hook(cr, uid, [res_id], context=context) + self._register_hook(cr, uid, [res_id], context=context) return res_id - + def write(self, cr, uid, ids, vals, context=None): res = super(base_action_rule, self).write(cr, uid, ids, vals, context=context) self._register_hook(cr, uid, ids, context=context) @@ -272,18 +272,18 @@ the rule to mark CC(mail to any other person defined in actions)."), @param self: The object pointer """ data = { - 'object_id': obj.id, - 'object_subject': hasattr(obj, 'name') and obj.name or False, - 'object_date': hasattr(obj, 'date') and obj.date or False, - 'object_description': hasattr(obj, 'description') and obj.description or False, - 'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/', + 'object_id': obj.id, + 'object_subject': hasattr(obj, 'name') and obj.name or False, + 'object_date': hasattr(obj, 'date') and obj.date or False, + 'object_description': hasattr(obj, 'description') and obj.description or False, + 'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/', 'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \ - obj.user_id.address_id and obj.user_id.address_id.email) or '/', + obj.user_id.address_id and obj.user_id.address_id.email) or '/', 'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and\ - obj.user_id.address_id and obj.user_id.address_id.phone) or '/', - 'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/', + obj.user_id.address_id and obj.user_id.address_id.phone) or '/', + 'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/', 'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\ - obj.partner_address_id.email) or '/', + obj.partner_address_id.email) or '/', } return self.format_body(body % data) @@ -302,6 +302,7 @@ the rule to mark CC(mail to any other person defined in actions)."), if context is None: context = {} + email_message_obj = self.pool.get('email.message') body = self.format_mail(obj, body) if not emailfrom: if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and\ @@ -312,9 +313,9 @@ the rule to mark CC(mail to any other person defined in actions)."), emailfrom = tools.ustr(emailfrom) reply_to = emailfrom if not emailfrom: - raise osv.except_osv(_('Error!'), + raise osv.except_osv(_('Error!'), _("No E-Mail ID Found for your Company address!")) - return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id)) + return email_message_obj.email_send(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, openobject_id=str(obj.id)) def do_check(self, cr, uid, action, obj, context=None): @@ -325,7 +326,7 @@ the rule to mark CC(mail to any other person defined in actions)."), @param context: A standard dictionary for contextual values """ if context is None: context = {} - ok = True + ok = True if action.filter_id: if action.model_id.model == action.filter_id.model_id: context.update(eval(action.filter_id.context)) @@ -480,15 +481,15 @@ the rule to mark CC(mail to any other person defined in actions)."), return True _constraints = [ - (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), + (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), ] base_action_rule() class ir_cron(osv.osv): - _inherit = 'ir.cron' - + _inherit = 'ir.cron' + def _poolJobs(self, db_name, check=False): try: db = pooler.get_db(db_name) diff --git a/addons/base_calendar/__openerp__.py b/addons/base_calendar/__openerp__.py index 6a52e30de27..9c0e4857d13 100644 --- a/addons/base_calendar/__openerp__.py +++ b/addons/base_calendar/__openerp__.py @@ -20,9 +20,9 @@ ############################################################################## { - "name" : "Basic Calendar Functionality", - "version" : "1.0", - "depends" : ["base"], + "name" : "Basic Calendar Functionality", + "version" : "1.0", + "depends" : ["base", "emails"], 'description': """Full featured calendar system that supports: - Calendar of events - Alerts (create requests) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index e5cff943f2f..db53d88c911 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -411,7 +411,7 @@ property or property parameter."), cal = vobject.iCalendar() event = cal.add('vevent') if not event_obj.date_deadline or not event_obj.date: - raise osv.except_osv(_('Warning !'),_("Couldn't Invite because date is not specified!")) + raise osv.except_osv(_('Warning !'),_("Couldn't Invite because date is not specified!")) event.add('created').value = ics_datetime(time.strftime('%Y-%m-%d %H:%M:%S')) event.add('dtstart').value = ics_datetime(event_obj.date) event.add('dtend').value = ics_datetime(event_obj.date_deadline) @@ -456,7 +456,7 @@ property or property parameter."), trigger.value = delta # Compute other details valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP' - + for attendee in event_obj.attendee_ids: attendee_add = event.add('attendee') attendee_add.params['CUTYPE'] = [str(attendee.cutype)] @@ -480,6 +480,7 @@ property or property parameter."), context = {} company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name + email_message_obj = self.pool.get('email.message') for att in self.browse(cr, uid, ids, context=context): sign = att.sent_by_uid and att.sent_by_uid.signature or '' sign = '
'.join(sign and sign.split('\n') or []) @@ -506,11 +507,12 @@ property or property parameter."), body = html_invitation % body_vals if mail_to and email_from: attach = self.get_ics_file(cr, uid, res_obj, context=context) - tools.email_send( + email_message_obj.email_send(cr, uid, email_from, mail_to, sub, body, + model='calendar.attendee', attach=attach and [('invitation.ics', attach)] or None, subtype='html', reply_to=email_from @@ -674,7 +676,7 @@ true, it will allow you to hide the event alarm information without removing it. new_res_alarm = alarm_ids[0] cr.execute('UPDATE %s ' % model_obj._table + \ ' SET base_calendar_alarm_id=%s, alarm_id=%s ' \ - ' WHERE id=%s', + ' WHERE id=%s', (cal_alarm.id, new_res_alarm, data.id)) self.do_alarm_unlink(cr, uid, [data.id], model) @@ -809,6 +811,7 @@ class calendar_alarm(osv.osv): return True # XXX FIXME REMOVE THIS AFTER FIXING get_recurrent_dates!! if context is None: context = {} + email_message_obj = self.pool.get('email.message') current_datetime = datetime.now() request_obj = self.pool.get('res.request') alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context) @@ -890,11 +893,12 @@ From: for att in alarm.attendee_ids: mail_to.append(att.user_id.address_id.email) if mail_to: - tools.email_send( + email_message_obj.email_send(cr, uid, tools.config.get('email_from', False), mail_to, sub, - body + body, + model='calendar.alarm' ) if next_trigger_date: update_vals.update({'trigger_date': next_trigger_date}) @@ -966,7 +970,7 @@ class calendar_event(osv.osv): value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S") elif end_date and duration and not allday: # we have both, keep them synchronized: - # set duration based on end_date (arbitrary decision: this avoid + # set duration based on end_date (arbitrary decision: this avoid # getting dates like 06:31:48 instead of 06:32:00) end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S") diff = end - start @@ -1130,7 +1134,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=-1SU'), 'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \ ('weekly', 'Weekly'), ('monthly', 'Monthly'), \ - ('yearly', 'Yearly'),], + ('yearly', 'Yearly'),], 'Recurrency', states={'done': [('readonly', True)]}, help="Let the event automatically repeat at that interval"), 'alarm_id': fields.many2one('res.alarm', 'Alarm', states={'done': [('readonly', True)]}, @@ -1148,7 +1152,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ ('weekly', 'Weeks'), ('monthly', 'Months'), ('yearly', 'Years'), ], 'Frequency'), - + 'end_type' : fields.selection([('forever', 'Forever'), ('count', 'Fix amout of times'), ('end_date','End date')], 'Way to end reccurency'), 'interval': fields.integer('Repeat every', help="Repeat every (Days/Week/Month/Year)"), 'count': fields.integer('Repeat', help="Repeat x times"), @@ -1159,7 +1163,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ 'fr': fields.boolean('Fri'), 'sa': fields.boolean('Sat'), 'su': fields.boolean('Sun'), - 'select1': fields.selection([('date', 'Date of month'), + 'select1': fields.selection([('date', 'Date of month'), ('day', 'Day of month')], 'Option'), 'day': fields.integer('Date of month'), 'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \ @@ -1176,8 +1180,8 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ 'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}), 'active': fields.boolean('Active', help="If the active field is set to \ true, it will allow you to hide the event alarm information without removing it."), - 'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"), - 'edit_all': fields.boolean('Edit All', help="Edit all Occurrences of recurrent Meeting."), + 'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"), + 'edit_all': fields.boolean('Edit All', help="Edit all Occurrences of recurrent Meeting."), } def default_organizer(self, cr, uid, context=None): user_pool = self.pool.get('res.users') @@ -1204,12 +1208,12 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ def onchange_edit_all(self, cr, uid, ids, rrule_type,edit_all, context=None): if not context: context = {} - + value = {} if edit_all and rrule_type: for id in ids: base_calendar_id2real_id(id) - return value + return value def modify_all(self, cr, uid, event_ids, defaults, context=None, *args): """ @@ -1342,7 +1346,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ freq=datas.get('rrule_type') if freq == 'none': return '' - + interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or '' if freq == 'weekly': @@ -1358,7 +1362,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ elif datas.get('select1')=='date': monthstring = ';BYMONTHDAY=' + str(datas.get('day')) - + if datas.get('end_date'): datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z' enddate = (datas.get('count') and (';COUNT=' + str(datas.get('count'))) or '') +\ @@ -1403,7 +1407,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ res = self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit) return res - + def get_edit_all(self, cr, uid, id, vals=None): """ @@ -1414,10 +1418,10 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ if(vals and 'edit_all' in vals): #we jsut check edit_all return vals['edit_all'] else: #it's a recurrent event and edit_all is already check - return meeting['recurrency'] and meeting['edit_all'] + return meeting['recurrency'] and meeting['edit_all'] + - def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): """ @@ -1440,12 +1444,12 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ res = False for event_id in select: real_event_id = base_calendar_id2real_id(event_id) - + if(self.get_edit_all(cr, uid, event_id, vals=vals)): event_id = real_event_id - - + + if len(str(event_id).split('-')) > 1: data = self.read(cr, uid, event_id, ['date', 'date_deadline', \ 'rrule', 'duration', 'exdate']) @@ -1459,15 +1463,15 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ 'edit_all': False, 'recurrency' : False, }) - + new_id = self.copy(cr, uid, real_event_id, default=data, context=context) - + date_new = event_id.split('-')[1] date_new = time.strftime("%Y%m%dT%H%M%S", \ time.strptime(date_new, "%Y%m%d%H%M%S")) exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new res = self.write(cr, uid, [real_event_id], {'exdate': exdate}) - + context.update({'active_id': new_id, 'active_ids': [new_id]}) continue if not real_event_id in new_ids: @@ -1590,10 +1594,10 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\ res = False for event_datas in self.read(cr, uid, ids, ['date', 'rrule', 'exdate'], context=context): event_id = event_datas['id'] - + if self.get_edit_all(cr, uid, event_id, vals=None): event_id = base_calendar_id2real_id(event_id) - + if isinstance(event_id, (int, long)): res = super(calendar_event, self).unlink(cr, uid, event_id, context=context) self.pool.get('res.alarm').do_alarm_unlink(cr, uid, [event_id], self._name) @@ -1711,7 +1715,7 @@ class calendar_todo(osv.osv): @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. @param context: A standard dictionary for contextual values """ - + assert name == 'date' return self.write(cr, uid, id, { 'date_start': value }, context=context) @@ -1743,9 +1747,9 @@ class ir_attachment(osv.osv): for arg in args: args1.append(map(lambda x:str(x).split('-')[0], arg)) return super(ir_attachment, self).search_count(cr, user, args1, context) - - - + + + def create(self, cr, uid, vals, context=None): if context: id = context.get('default_res_id', False) diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index db69144b408..d1500850afb 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -56,7 +56,7 @@ Create dashboard for CRM that includes: 'base', 'base_action_rule', 'process', - 'mail_gateway', + 'email_gateway', 'base_calendar', 'resource', 'board' diff --git a/addons/crm/crm.py b/addons/crm/crm.py index edfee9e7614..4463cf0c114 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -49,7 +49,7 @@ class crm_case(object): def _find_lost_stage(self, cr, uid, type, section_id): return self._find_percent_stage(cr, uid, 0.0, type, section_id) - + def _find_won_stage(self, cr, uid, type, section_id): return self._find_percent_stage(cr, uid, 100.0, type, section_id) @@ -60,14 +60,14 @@ class crm_case(object): stage_pool = self.pool.get('crm.case.stage') if section_id : ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type), ("section_ids", 'in', [section_id])]) - else : + else : ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type)]) - + if ids: return ids[0] return False - - + + def _find_first_stage(self, cr, uid, type, section_id): """ return the first stage that has a sequence number equal or higher than sequence @@ -75,9 +75,9 @@ class crm_case(object): stage_pool = self.pool.get('crm.case.stage') if section_id : ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type), ("section_ids", 'in', [section_id])]) - else : + else : ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type)]) - + if ids: stages = stage_pool.browse(cr, uid, ids) stage_min = stages[0] @@ -85,9 +85,9 @@ class crm_case(object): if stage_min.sequence > stage.sequence: stage_min = stage return stage_min.id - else : + else : return False - + def onchange_stage_id(self, cr, uid, ids, stage_id, context={}): """ @param self: The object pointer @@ -95,7 +95,7 @@ class crm_case(object): @param uid: the current user’s ID for security checks, @param ids: List of stage’s IDs @stage_id: change state id on run time """ - + if not stage_id: return {'value':{}} @@ -151,19 +151,19 @@ class crm_case(object): default = {} default.update({ - 'message_ids': [], + 'message_ids': [], }) if hasattr(self, '_columns'): if self._columns.get('date_closed'): default.update({ - 'date_closed': False, + 'date_closed': False, }) if self._columns.get('date_open'): default.update({ 'date_open': False }) return super(osv.osv, self).copy(cr, uid, id, default, context=context) - + def _get_default_email(self, cr, uid, context=None): """Gives default email address for current user @param self: The object pointer @@ -214,7 +214,7 @@ class crm_case(object): return next_stage else : return self._find_next_stage(cr, uid, stage_list, index + 1, current_seq, stage_pool) - + def stage_change(self, cr, uid, ids, context=None, order='sequence'): if context is None: context = {} @@ -226,12 +226,12 @@ class crm_case(object): for case in self.browse(cr, uid, ids, context=context): next_stage = False value = {} - if case.section_id.id : + if case.section_id.id : domain = [('type', '=', stage_type),('section_ids', '=', case.section_id.id)] else : domain = [('type', '=', stage_type)] - + stages = stage_pool.search(cr, uid, domain, order=order) current_seq = case.stage_id.sequence index = -1 @@ -239,18 +239,18 @@ class crm_case(object): index = stages.index(case.stage_id.id) next_stage = self._find_next_stage(cr, uid, stages, index, current_seq, stage_pool, context=context) - + if next_stage: next_stage_id = next_stage.id value.update({'stage_id': next_stage.id}) if next_stage.on_change: value.update({'probability': next_stage.probability}) self.write(cr, uid, [case.id], value, context=context) - - + + return next_stage_id #FIXME should return a list of all id - - + + def stage_next(self, cr, uid, ids, context=None): """This function computes next stage for case from its current stage using available stage for that case type @@ -259,9 +259,9 @@ class crm_case(object): @param uid: the current user’s ID for security checks, @param ids: List of case IDs @param context: A standard dictionary for contextual values""" - + return self.stage_change(cr, uid, ids, context=context, order='sequence') - + def stage_previous(self, cr, uid, ids, context=None): """This function computes previous stage for case from its current stage using available stage for that case type @@ -283,7 +283,7 @@ class crm_case(object): """ if not part: return {'value': {'partner_address_id': False, - 'email_from': False, + 'email_from': False, 'phone': False }} addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) @@ -321,7 +321,7 @@ class crm_case(object): @param ids: List of case Ids @param *args: Tuple Value for additional Params """ - + cases = self.browse(cr, uid, ids) self._history(cr, uid, cases, _('Open')) for case in cases: @@ -329,9 +329,9 @@ class crm_case(object): if not case.user_id: data['user_id'] = uid self.write(cr, uid, case.id, data) - - - self._action(cr, uid, cases, 'open') + + + self._action(cr, uid, cases, 'open') return True def case_close(self, cr, uid, ids, *args): @@ -450,6 +450,7 @@ class crm_case(object): @param context: A standard dictionary for contextual values """ + email_message_obj = self.pool.get('email.message') for case in self.browse(cr, uid, ids, context=context): if not case.section_id.reply_to: raise osv.except_osv(_('Error!'), ("Reply To is not specified in the sales team")) @@ -480,11 +481,12 @@ class crm_case(object): # Send an email subject = "Reminder: [%s] %s" % (str(case.id), case.name, ) - tools.email_send( + email_message_obj.email_send(cr, uid, src, [dest], - subject, + subject, body, + model='crm.case', reply_to=case.section_id.reply_to, openobject_id=str(case.id), attach=attach_to_send @@ -550,9 +552,9 @@ class crm_case_stage(osv.osv): _description = "Stage of case" _rec_name = 'name' _order = "sequence" - - - + + + def _get_type_value(self, cr, user, context): return [('lead','Lead'),('opportunity','Opportunity')] @@ -566,8 +568,8 @@ class crm_case_stage(osv.osv): 'requirements': fields.text('Requirements'), 'type': fields.selection(_get_type_value, 'Type'), } - - + + def _find_stage_type(self, cr, uid, context=None): """Finds type of stage according to object. @param self: The object pointer @@ -612,7 +614,7 @@ class crm_case_section(osv.osv): 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'), 'resource_calendar_id': fields.many2one('resource.calendar', "Working Time"), 'note': fields.text('Description'), - 'working_hours': fields.float('Working Hours', digits=(16,2 )), + 'working_hours': fields.float('Working Hours', digits=(16,2 )), 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), } @@ -708,7 +710,7 @@ class crm_case_stage(osv.osv): _columns = { 'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', 'Sections'), } - + crm_case_stage() diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 477f9132fab..d877c074020 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -37,25 +37,26 @@ class base_action_rule(osv.osv): """ Base Action Rule """ _inherit = 'base.action.rule' _description = 'Action Rules' - + _columns = { - 'trg_section_id': fields.many2one('crm.case.section', 'Sales Team'), - 'trg_max_history': fields.integer('Maximum Communication History'), - 'trg_categ_id': fields.many2one('crm.case.categ', 'Category'), - 'regex_history' : fields.char('Regular Expression on Case History', size=128), - 'act_section_id': fields.many2one('crm.case.section', 'Set Team to'), - 'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'), + 'trg_section_id': fields.many2one('crm.case.section', 'Sales Team'), + 'trg_max_history': fields.integer('Maximum Communication History'), + 'trg_categ_id': fields.many2one('crm.case.categ', 'Category'), + 'regex_history' : fields.char('Regular Expression on Case History', size=128), + 'act_section_id': fields.many2one('crm.case.section', 'Set Team to'), + 'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'), 'act_mail_to_partner': fields.boolean('Mail to Partner', help="Check \ -this if you want the rule to send an email to the partner."), +this if you want the rule to send an email to the partner."), } - + def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from', False), context=None): + email_message_obj = self.pool.get('email.message') body = self.format_mail(obj, body) if not emailfrom: if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email: emailfrom = obj.user_id.address_id.email - + name = '[%d] %s' % (obj.id, tools.ustr(obj.name)) emailfrom = tools.ustr(emailfrom) if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.reply_to: @@ -63,10 +64,10 @@ this if you want the rule to send an email to the partner."), else: reply_to = emailfrom if not emailfrom: - raise osv.except_osv(_('Error!'), + raise osv.except_osv(_('Error!'), _("No E-Mail ID Found for your Company address!")) - return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id)) - + return email_message_obj.email_send(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, openobject_id=str(obj.id)) + def do_check(self, cr, uid, action, obj, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @@ -79,7 +80,7 @@ this if you want the rule to send an email to the partner."), if hasattr(obj, 'categ_id'): ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==obj.categ_id.id) - #Cheking for history + #Cheking for history regex = action.regex_history result_history = True if regex: @@ -109,7 +110,7 @@ this if you want the rule to send an email to the partner."), @param context: A standard dictionary for contextual values """ res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) write = {} - + if hasattr(action, 'act_section_id') and action.act_section_id: obj.section_id = action.act_section_id write['section_id'] = action.act_section_id.id diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index b055943d785..60e2f447c0d 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -100,7 +100,7 @@ class crm_lead(crm_case, osv.osv): _columns = { # Overridden from res.partner.address: - 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', + 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', select=True, help="Optional linked partner, usually after conversion of the lead"), # From crm.case @@ -124,7 +124,7 @@ class crm_lead(crm_case, osv.osv): domain="['|',('section_id','=',section_id),('section_id','=',False)]"), 'channel_id': fields.many2one('res.partner.canal', 'Channel'), - 'contact_name': fields.char('Contact Name', size=64), + 'contact_name': fields.char('Contact Name', size=64), 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner that will be created while converting the into opportunity'), 'optin': fields.boolean('Opt-In', help="If opt-in is checked, this contact has accepted to receive emails."), 'optout': fields.boolean('Opt-Out', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), @@ -147,10 +147,10 @@ class crm_lead(crm_case, osv.osv): help='The state is set to \'Draft\', when a case is created.\ \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ - \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), + 'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } - + _defaults = { 'active': lambda *a: 1, @@ -163,8 +163,8 @@ class crm_lead(crm_case, osv.osv): 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], #'stage_id': _get_stage_id, } - - + + def onchange_partner_address_id(self, cr, uid, ids, add, email=False): """This function returns value of partner email based on Partner Address @@ -189,10 +189,10 @@ class crm_lead(crm_case, osv.osv): @param *args: Give Tuple Value """ leads = self.browse(cr, uid, ids) - - - - for i in xrange(0, len(ids)): + + + + for i in xrange(0, len(ids)): if leads[i].state == 'draft': value = {} if not leads[i].stage_id : @@ -203,7 +203,7 @@ class crm_lead(crm_case, osv.osv): self.log_open( cr, uid, leads[i]) res = super(crm_lead, self).case_open(cr, uid, ids, *args) return res - + def log_open(self, cr, uid, case): if case.type == 'lead': message = _("The lead '%s' has been opened.") % case.name @@ -291,11 +291,11 @@ class crm_lead(crm_case, osv.osv): def write(self, cr, uid, ids, vals, context=None): if not context: context = {} - + if 'date_closed' in vals: return super(crm_lead,self).write(cr, uid, ids, vals, context=context) return super(crm_lead,self).write(cr, uid, ids, vals, context) - + def stage_historize(self, cr, uid, ids, stage, context=None): stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage, context=context) self.history(cr, uid, ids, _('Stage'), details=stage_obj.name) @@ -306,20 +306,20 @@ class crm_lead(crm_case, osv.osv): message = _("The stage of opportunity '%s' has been changed to '%s'.") % (case.name, stage_obj.name) self.log(cr, uid, case.id, message) return True - + def stage_next(self, cr, uid, ids, context=None): stage = super(crm_lead, self).stage_next(cr, uid, ids, context=context) if stage: self.stage_historize(cr, uid, ids, stage, context=context) return stage - + def stage_previous(self, cr, uid, ids, context=None): stage = super(crm_lead, self).stage_previous(cr, uid, ids, context) if stage: - self.stage_historize(cr, uid, ids, stage, context=context) + self.stage_historize(cr, uid, ids, stage, context=context) return stage - - + + def message_new(self, cr, uid, msg, context=None): """ Automatically calls when new email message arrives @@ -369,7 +369,7 @@ class crm_lead(crm_case, osv.osv): @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, - @param ids: List of update mail’s IDs + @param ids: List of update mail’s IDs """ if isinstance(ids, (str, int, long)): ids = [ids] diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py index 57ce46b52cc..5ac7948d067 100644 --- a/addons/crm/crm_meeting.py +++ b/addons/crm/crm_meeting.py @@ -45,12 +45,12 @@ class crm_meeting(crm_case, osv.osv): _inherit = ['mailgate.thread',"calendar.event"] _columns = { # From crm.case - 'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}), - 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}), + 'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}), + 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ - domain="[('partner_id','=',partner_id)]", states={'done': [('readonly', True)]}), + domain="[('partner_id','=',partner_id)]", states={'done': [('readonly', True)]}), 'section_id': fields.many2one('crm.case.section', 'Sales Team', states={'done': [('readonly', True)]}, \ - select=True, help='Sales team to which Case belongs to.'), + select=True, help='Sales team to which Case belongs to.'), 'email_from': fields.char('Email', size=128, states={'done': [('readonly', True)]}, help="These people will receive email."), 'id': fields.integer('ID'), 'create_date': fields.datetime('Creation Date' , readonly=True), @@ -67,7 +67,7 @@ class crm_meeting(crm_case, osv.osv): 'event_id', 'attendee_id', 'Attendees', states={'done': [('readonly', True)]}), 'date_closed': fields.datetime('Closed', readonly=True), 'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]), 'state': fields.selection([('open', 'Confirmed'), ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'), @@ -75,7 +75,7 @@ class crm_meeting(crm_case, osv.osv): size=16, readonly=True), } _defaults = { - 'state': 'draft', + 'state': 'draft', 'active': 1, 'user_id': lambda self, cr, uid, ctx: uid, } @@ -147,7 +147,7 @@ class res_users(osv.osv): except: # Tolerate a missing shortcut. See product/product.py for similar code. logging.getLogger('orm').debug('Skipped meetings shortcut for user "%s"', data.get('name',' 3: - res = '\n\t'.join(data_list[:3]) + '...' - else: - res = '\n\t'.join(data_list) - return res - - def _get_display_text(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} - tz = context.get('tz') - result = {} - for message in self.browse(cr, uid, ids, context=context): - msg_txt = '' - if message.history: - msg_txt += (message.email_from or '/') + _(' wrote on ') + format_date_tz(message.date, tz) + ':\n\t' - if message.description: - msg_txt += self.truncate_data(cr, uid, message.description, context=context) - else: - msg_txt = (message.user_id.name or '/') + _(' on ') + format_date_tz(message.date, tz) + ':\n\t' - if message.name == _('Opportunity'): - msg_txt += _("Converted to Opportunity") - elif message.name == _('Note'): - msg_txt = (message.user_id.name or '/') + _(' added note on ') + format_date_tz(message.date, tz) + ':\n\t' - msg_txt += self.truncate_data(cr, uid, message.description, context=context) - elif message.name == _('Stage'): - msg_txt += _("Changed Stage to: ") + message.description - else: - msg_txt += _("Changed Status to: ") + message.name - result[message.id] = msg_txt - return result - - _name = 'mailgate.message' - _description = 'Mailgateway Message' - _order = 'date desc' - _columns = { - 'name':fields.text('Subject', readonly=True), - 'model': fields.char('Object Name', size=128, select=1, readonly=True), - 'res_id': fields.integer('Resource ID', select=1, readonly=True), - 'ref_id': fields.char('Reference Id', size=256, readonly=True, help="Message Id in Email Server.", select=True), - 'date': fields.datetime('Date', readonly=True), - 'history': fields.boolean('Is History?', readonly=True), - 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), - 'message': fields.text('Description', readonly=True), - 'email_from': fields.char('From', size=128, help="Email From", readonly=True), - 'email_to': fields.char('To', help="Email Recipients", size=256, readonly=True), - 'email_cc': fields.char('Cc', help="Carbon Copy Email Recipients", size=256, readonly=True), - 'email_bcc': fields.char('Bcc', help='Blind Carbon Copy Email Recipients', size=256, readonly=True), - 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email.", select=True), - 'references': fields.text('References', readonly=True, help="References emails."), - 'description': fields.text('Description', readonly=True), - 'partner_id': fields.many2one('res.partner', 'Partner', required=False), - 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments', readonly=True), - 'display_text': fields.function(_get_display_text, method=True, type='text', size="512", string='Display Text'), - } - - def init(self, cr): - cr.execute("""SELECT indexname - FROM pg_indexes - WHERE indexname = 'mailgate_message_res_id_model_idx'""") - if not cr.fetchone(): - cr.execute("""CREATE INDEX mailgate_message_res_id_model_idx - ON mailgate_message (model, res_id)""") - -mailgate_message() - class mailgate_tool(osv.osv_memory): _name = 'email.server.tools' @@ -324,7 +206,7 @@ class mailgate_tool(osv.osv_memory): if isinstance(res_ids, (int, long)): res_ids = [res_ids] - msg_pool = self.pool.get('mailgate.message') + msg_pool = self.pool.get('email.message') for res_id in res_ids: case = self.pool.get(model).browse(cr, uid, res_id, context=context) partner_id = hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False diff --git a/addons/email_gateway/email_gateway_view.xml b/addons/email_gateway/email_gateway_view.xml new file mode 100644 index 00000000000..9fc708eac0f --- /dev/null +++ b/addons/email_gateway/email_gateway_view.xml @@ -0,0 +1,90 @@ + + + + + + mailgate.thread.form + mailgate.thread + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + mailgate.thread.tree + mailgate.thread + tree + + + + + + + + + + Mailgateway Threads + mailgate.thread + tree,form + form + + + + + + tree + + + + + + form + + + + + + + tree + + + + + + form + + + + +
+
\ No newline at end of file diff --git a/addons/mail_gateway/scripts/__init__.py b/addons/email_gateway/scripts/__init__.py similarity index 100% rename from addons/mail_gateway/scripts/__init__.py rename to addons/email_gateway/scripts/__init__.py diff --git a/addons/mail_gateway/scripts/openerp_mailgate/__init__.py b/addons/email_gateway/scripts/openerp_mailgate/__init__.py similarity index 100% rename from addons/mail_gateway/scripts/openerp_mailgate/__init__.py rename to addons/email_gateway/scripts/openerp_mailgate/__init__.py diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/email_gateway/scripts/openerp_mailgate/openerp_mailgate.py similarity index 100% rename from addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py rename to addons/email_gateway/scripts/openerp_mailgate/openerp_mailgate.py diff --git a/addons/email_gateway/security/ir.model.access.csv b/addons/email_gateway/security/ir.model.access.csv new file mode 100644 index 00000000000..1a934671ae8 --- /dev/null +++ b/addons/email_gateway/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_mailgate_thread","mailgate.thread","model_mailgate_thread",,1,0,0,0 \ No newline at end of file diff --git a/addons/email_template/__init__.py b/addons/email_template/__init__.py index dd87232679d..15a99722203 100644 --- a/addons/email_template/__init__.py +++ b/addons/email_template/__init__.py @@ -20,8 +20,8 @@ # ############################################################################## -import email_template_account import email_template -import email_template_mailbox import wizard +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/email_template/__openerp__.py b/addons/email_template/__openerp__.py index 520109663dc..2f21631b7d7 100644 --- a/addons/email_template/__openerp__.py +++ b/addons/email_template/__openerp__.py @@ -26,17 +26,15 @@ "author" : "Openlabs", "website" : "http://openerp.com", "category" : "Added functionality", - "depends" : ['marketing', 'base_tools'], + "depends" : [ 'emails'], "description": """ Email Template is extraction of Power Email basically just to send the emails. """, - "init_xml": ['email_template_scheduler_data.xml'], + "init_xml": [], "update_xml": [ 'security/email_template_security.xml', - 'email_template_workflow.xml', - 'email_template_account_view.xml', + 'wizard/email_template_preview_view.xml', 'email_template_view.xml', - 'email_template_mailbox_view.xml', 'wizard/email_template_send_wizard_view.xml', 'security/ir.model.access.csv' ], diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index ee94b4c7c37..cad467a37f9 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -20,37 +20,21 @@ # ############################################################################## +from osv import osv +from osv import fields import base64 import random import netsvc import logging import re - -TEMPLATE_ENGINES = [] - -from osv import osv, fields from tools.translate import _ +import tools +import pooler try: from mako.template import Template as MakoTemplate - TEMPLATE_ENGINES.append(('mako', 'Mako Templates')) except ImportError: logging.getLogger('init').warning("module email_template: Mako templates not installed") - -try: - from django.template import Context, Template as DjangoTemplate - #Workaround for bug: - #http://code.google.com/p/django-tagging/issues/detail?id=110 - from django.conf import settings - settings.configure() - #Workaround ends - TEMPLATE_ENGINES.append(('django', 'Django Template')) -except ImportError: - logging.getLogger('init').warning("module email_template: Django templates not installed") - -import tools -import pooler -import logging def get_value(cursor, user, recid, message=None, template=None, context=None): """ @@ -74,18 +58,9 @@ def get_value(cursor, user, recid, message=None, template=None, context=None): env = { 'user':pool.get('res.users').browse(cursor, user, user, context=context), 'db':cursor.dbname - } - if template.template_language == 'mako': - templ = MakoTemplate(message, input_encoding='utf-8') - reply = MakoTemplate(message).render_unicode(object=object, - peobject=object, - env=env, - format_exceptions=True) - elif template.template_language == 'django': - templ = DjangoTemplate(message) - env['object'] = object - env['peobject'] = object - reply = templ.render(Context(env)) + } + templ = MakoTemplate(message, input_encoding='utf-8') + reply = MakoTemplate(message).render_unicode(object=object, peobject=object, env=env, format_exceptions=True) return reply or False except Exception: logging.exception("can't render %r", message) @@ -100,24 +75,17 @@ class email_template(osv.osv): _description = 'Email Templates for Models' def change_model(self, cursor, user, ids, object_name, context=None): + mod_name = False if object_name: - mod_name = self.pool.get('ir.model').read( - cursor, - user, - object_name, - ['model'], context)['model'] - else: - mod_name = False - return { - 'value':{'model_int_name':mod_name} - } + mod_name = self.pool.get('ir.model').browse(cursor, user, object_name, context).model + return {'value':{'model_int_name':mod_name}} _columns = { 'name' : fields.char('Name', size=100, required=True), 'object_name':fields.many2one('ir.model', 'Resource'), 'model_int_name':fields.char('Model Internal Name', size=200,), 'from_account':fields.many2one( - 'email_template.account', + 'email.smtp_server', string="Email Account", help="Emails will be sent from this approved account."), 'def_to':fields.char( @@ -245,17 +213,7 @@ This is useful for CRM leads for example"), help="Copy this html code to your HTML message" " body for displaying the info in your mail.", store=False), - #Template language(engine eg.Mako) specifics - 'template_language':fields.selection( - TEMPLATE_ENGINES, - 'Templating Language', - required=True - ) - } - - _defaults = { - 'template_language' : lambda *a:'mako', - + 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), } _sql_constraints = [ @@ -266,31 +224,32 @@ This is useful for CRM leads for example"), vals = {} if context is None: context = {} - template_obj = self.browse(cr, uid, ids, context=context)[0] - src_obj = template_obj.object_name.model - vals['ref_ir_act_window'] = self.pool.get('ir.actions.act_window').create(cr, uid, { - 'name': template_obj.name, - 'type': 'ir.actions.act_window', - 'res_model': 'email_template.send.wizard', - 'src_model': src_obj, - 'view_type': 'form', - 'context': "{'src_model':'%s','template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template_obj.id), - 'view_mode':'form,tree', - 'view_id': self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'email_template.send.wizard.form')], context=context)[0], - 'target': 'new', - 'auto_refresh':1 - }, context) - vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, { - 'name': _('Send Mail (%s)') % template_obj.name, - 'model': src_obj, - 'key2': 'client_action_multi', - 'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']), - 'object': True, - }, context) + action_obj = self.pool.get('ir.actions.act_window') + for template in self.browse(cr, uid, ids, context=context): + src_obj = template.object_name.model + vals['ref_ir_act_window'] = action_obj.create(cr, uid, { + 'name': template.name, + 'type': 'ir.actions.act_window', + 'res_model': 'email_template.send.wizard', + 'src_model': src_obj, + 'view_type': 'form', + 'context': "{'src_model':'%s','template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template.id), + 'view_mode':'form,tree', + 'view_id': self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'email_template.send.wizard.form')], context=context)[0], + 'target': 'new', + 'auto_refresh':1 + }, context) + vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, { + 'name': _('Send Mail (%s)') % template.name, + 'model': src_obj, + 'key2': 'client_action_multi', + 'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']), + 'object': True, + }, context) self.write(cr, uid, ids, { - 'ref_ir_act_window': vals['ref_ir_act_window'], - 'ref_ir_value': vals['ref_ir_value'], - }, context) + 'ref_ir_act_window': vals.get('ref_ir_act_window',False), + 'ref_ir_value': vals.get('ref_ir_value',False), + }, context) return True def unlink_action(self, cr, uid, ids, context=None): @@ -323,125 +282,128 @@ This is useful for CRM leads for example"), default.update({'name':new_name}) return super(email_template, self).copy(cr, uid, id, default, context) - def build_expression(self, field_name, sub_field_name, null_value, template_language='mako'): + def build_expression(self, field_name, sub_field_name, null_value): """ Returns a template expression based on data provided @param field_name: field name @param sub_field_name: sub field name (M2O) @param null_value: default value if the target value is empty - @param template_language: name of template engine @return: computed expression """ - expression = '' - if template_language == 'mako': - if field_name: - expression = "${object." + field_name - if sub_field_name: - expression += "." + sub_field_name - if null_value: - expression += " or '''%s'''" % null_value - expression += "}" - elif template_language == 'django': - if field_name: - expression = "{{object." + field_name - if sub_field_name: - expression += "." + sub_field_name - if null_value: - expression += "|default: '''%s'''" % null_value - expression += "}}" + if field_name: + expression = "${object." + field_name + if sub_field_name: + expression += "." + sub_field_name + if null_value: + expression += " or '''%s'''" % null_value + expression += "}" return expression +# +# def onchange_model_object_field(self, cr, uid, ids, model_object_field, context=None): +# if not model_object_field: +# return {} +# result = {} +# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) +# #Check if field is relational +# if field_obj.ttype in ['many2one', 'one2many', 'many2many']: +# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) +# if res_ids: +# result['sub_object'] = res_ids[0] +# result['copyvalue'] = self.build_expression(False, False, False) +# result['sub_model_object_field'] = False +# result['null_value'] = False +# else: +# #Its a simple field... just compute placeholder +# result['sub_object'] = False +# result['copyvalue'] = self.build_expression(field_obj.name, False, False) +# result['sub_model_object_field'] = False +# result['null_value'] = False +# return {'value':result} +# +# def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, context=None): +# if not model_object_field or not sub_model_object_field: +# return {} +# result = {} +# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) +# if field_obj.ttype in ['many2one', 'one2many', 'many2many']: +# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) +# sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) +# if res_ids: +# result['sub_object'] = res_ids[0] +# result['copyvalue'] = self.build_expression(field_obj.name, sub_field_obj.name, False) +# result['sub_model_object_field'] = sub_model_object_field +# result['null_value'] = False +# else: +# #Its a simple field... just compute placeholder +# result['sub_object'] = False +# result['copyvalue'] = self.build_expression(field_obj.name, False, False) +# result['sub_model_object_field'] = False +# result['null_value'] = False +# return {'value':result} +# +# +# def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None): +# if not model_object_field and not null_value: +# return {} +# result = {} +# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) +# if field_obj.ttype in ['many2one', 'one2many', 'many2many']: +# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) +# sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) +# if res_ids: +# result['sub_object'] = res_ids[0] +# result['copyvalue'] = self.build_expression(field_obj.name, +# sub_field_obj.name, +# null_value, +# template_language +# ) +# result['sub_model_object_field'] = sub_model_object_field +# result['null_value'] = null_value +# else: +# #Its a simple field... just compute placeholder +# result['sub_object'] = False +# result['copyvalue'] = self.build_expression(field_obj.name, +# False, +# null_value, +# template_language +# ) +# result['sub_model_object_field'] = False +# result['null_value'] = null_value +# return {'value':result} - def onchange_model_object_field(self, cr, uid, ids, model_object_field, template_language, context=None): - if not model_object_field: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - #Check if field is relational - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(False, - False, - False, - template_language) - result['sub_model_object_field'] = False - result['null_value'] = False - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - False, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = False - return {'value':result} - - def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, template_language, context=None): - if not model_object_field or not sub_model_object_field: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(field_obj.name, - sub_field_obj.name, - False, - template_language - ) - result['sub_model_object_field'] = sub_model_object_field - result['null_value'] = False - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - False, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = False - return {'value':result} - - def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None): - if not model_object_field and not null_value: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(field_obj.name, - sub_field_obj.name, - null_value, - template_language - ) - result['sub_model_object_field'] = sub_model_object_field - result['null_value'] = null_value - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - null_value, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = null_value + def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, null_value=None, context=None): + result = { + 'sub_object': False, + 'copyvalue': False, + 'sub_model_object_field': False, + 'null_value': False + } + if model_object_field: + fields_obj = self.pool.get('ir.model.fields') + field_value = fields_obj.browse(cr, uid, model_object_field, context) + if field_value.ttype in ['many2one', 'one2many', 'many2many']: + res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_value.relation)], context=context) + sub_field_value = False + if sub_model_object_field: + sub_field_value = fields_obj.browse(cr, uid, sub_model_object_field, context) + if res_ids: + result.update({ + 'sub_object': res_ids[0], + 'copyvalue': self.build_expression(field_value.name, sub_field_value and sub_field_value.name or False, null_value or False), + 'sub_model_object_field': sub_model_object_field or False, + 'null_value': null_value or False + }) + else: + result.update({ + 'copyvalue': self.build_expression(field_value.name, False, null_value or False), + 'null_value': null_value or False + }) return {'value':result} def _add_attachment(self, cursor, user, mailbox_id, name, data, filename, context=None): """ Add an attachment to a given mailbox entry. - :param data: base64 encoded attachment data to store """ attachment_obj = self.pool.get('ir.attachment') @@ -450,31 +412,19 @@ This is useful for CRM leads for example"), 'datas': data, 'datas_fname': filename, 'description': name or _('No Description'), - 'res_model':'email_template.mailbox', + 'res_model':'email.message', 'res_id': mailbox_id, } - attachment_id = attachment_obj.create(cursor, - user, - attachment_data, - context) + attachment_id = attachment_obj.create(cursor, user, attachment_data, context) if attachment_id: - self.pool.get('email_template.mailbox').write( - cursor, - user, - mailbox_id, + self.pool.get('email.message').write(cursor, user, mailbox_id, { 'attachments_ids':[(4, attachment_id)], 'mail_type':'multipart/mixed' }, context) - def generate_attach_reports(self, - cursor, - user, - template, - record_id, - mail, - context=None): + def generate_attach_reports(self, cursor, user, template, record_id, mail, context=None): """ Generate report to be attached and attach it to the email, and add any directly attached files as well. @@ -490,21 +440,12 @@ This is useful for CRM leads for example"), @return: True """ if template.report_template: - reportname = 'report.' + \ - self.pool.get('ir.actions.report.xml').read( - cursor, - user, - template.report_template.id, - ['report_name'], - context)['report_name'] + reportname = 'report.' + self.pool.get('ir.actions.report.xml').browse(cursor, + user, template.report_template.id, context).report_name service = netsvc.LocalService(reportname) data = {} data['model'] = template.model_int_name - (result, format) = service.create(cursor, - user, - [record_id], - data, - context) + (result, format) = service.create(cursor, user, [record_id], data, context) fname = tools.ustr(get_value(cursor, user, record_id, template.file_name, template, context) or 'Report') @@ -519,12 +460,7 @@ This is useful for CRM leads for example"), return True - def _generate_mailbox_item_from_template(self, - cursor, - user, - template, - record_id, - context=None): + def _generate_mailbox_item_from_template(self, cursor, user, template, record_id, context=None): """ Generates an email from the template for record record_id of target object @@ -542,25 +478,14 @@ This is useful for CRM leads for example"), context = {} #If account to send from is in context select it, else use enforced account if 'account_id' in context.keys(): - from_account = self.pool.get('email_template.account').read( - cursor, - user, - context.get('account_id'), - ['name', 'email_id'], - context - ) + from_account = self.pool.get('email.smtp_server').read(cursor, user, context.get('account_id'), ['name', 'email_id'], context) else: from_account = { 'id':template.from_account.id, 'name':template.from_account.name, 'email_id':template.from_account.email_id } - lang = get_value(cursor, - user, - record_id, - template.lang, - template, - context) + lang = get_value(cursor, user, record_id, template.lang, template, context) if lang: ctx = context.copy() ctx.update({'lang':lang}) @@ -622,11 +547,11 @@ This is useful for CRM leads for example"), template.def_body_html, template, context), - 'account_id' :from_account['id'], #This is a mandatory field when automatic emails are sent 'state':'na', 'folder':'drafts', 'mail_type':'multipart/alternative', + 'template_id': template.id } if template['message_id']: @@ -636,187 +561,55 @@ This is useful for CRM leads for example"), elif template['track_campaign_item']: # get appropriate message-id mailbox_values.update({'message_id': tools.misc.generate_tracking_message_id(record_id)}) - - if not mailbox_values['account_id']: - raise Exception("Unable to send the mail. No account linked to the template.") +# +# if not mailbox_values['account_id']: +# raise Exception("Unable to send the mail. No account linked to the template.") #Use signatures if allowed if template.use_sign: - sign = self.pool.get('res.users').read(cursor, - user, - user, - ['signature'], - context)['signature'] + sign = self.pool.get('res.users').read(cursor, user, user, ['signature'], context)['signature'] if mailbox_values['body_text']: mailbox_values['body_text'] += sign if mailbox_values['body_html']: mailbox_values['body_html'] += sign - mailbox_id = self.pool.get('email_template.mailbox').create( - cursor, - user, - mailbox_values, - context) + mailbox_id = self.pool.get('email.message').create(cursor, user, mailbox_values, context) return mailbox_id - def generate_mail(self, - cursor, - user, - template_id, - record_ids, - context=None): + def generate_mail(self, cursor, user, template_id, record_ids, context=None): if context is None: context = {} template = self.browse(cursor, user, template_id, context=context) if not template: raise Exception("The requested template could not be loaded") result = True - mailbox_obj = self.pool.get('email_template.mailbox') + mailbox_obj = self.pool.get('email.message') for record_id in record_ids: - mailbox_id = self._generate_mailbox_item_from_template( - cursor, - user, - template, - record_id, - context) - mail = mailbox_obj.browse( - cursor, - user, - mailbox_id, - context=context - ) + mailbox_id = self._generate_mailbox_item_from_template(cursor, user, template, record_id, context) + mail = mailbox_obj.browse(cursor, user, mailbox_id, context=context) if template.report_template or template.attachment_ids: - self.generate_attach_reports( - cursor, - user, - template, - record_id, - mail, - context - ) - - self.pool.get('email_template.mailbox').write( - cursor, - user, - mailbox_id, - {'folder':'outbox'}, - context=context - ) - # TODO : manage return value of all the records - result = self.pool.get('email_template.mailbox').send_this_mail(cursor, user, [mailbox_id], context) + self.generate_attach_reports(cursor, user, template, record_id, mail, context ) + mailbox_obj.write(cursor, user, mailbox_id, {'folder':'outbox', 'state': 'waiting'}, context=context) return result email_template() +class email_message(osv.osv): + _inherit = 'email.message' + _columns = { + 'template_id': fields.many2one('email.template', 'Email-Template', readonly=True), + } -## FIXME: this class duplicates a lot of features of the email template send wizard, -## one of the 2 should inherit from the other! - -class email_template_preview(osv.osv_memory): - _name = "email_template.preview" - _description = "Email Template Preview" - - def _get_model_recs(self, cr, uid, context=None): - if context is None: - context = {} - #Fills up the selection box which allows records from the selected object to be displayed - self.context = context - if 'template_id' in context: - ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context) - ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model'] - model_obj = self.pool.get(ref_obj_name) - ref_obj_ids = model_obj.search(cr, uid, [], 0, 20, 'id', context=context) - if not ref_obj_ids: - ref_obj_ids = [] - - # also add the default one if requested, otherwise it won't be available for selection: - default_id = context.get('default_rel_model_ref') - if default_id and default_id not in ref_obj_ids: - ref_obj_ids.insert(0, default_id) - return model_obj.name_get(cr, uid, ref_obj_ids, context) - return [] - - def default_get(self, cr, uid, fields, context=None): - if context is None: - context = {} - result = super(email_template_preview, self).default_get(cr, uid, fields, context=context) - if (not fields or 'rel_model_ref' in fields) and 'template_id' in context \ - and not result.get('rel_model_ref'): - selectables = self._get_model_recs(cr, uid, context=context) - result['rel_model_ref'] = selectables and selectables[0][0] or False + def process_email_queue(self, cr, uid, ids=None, context=None): + result = super(email_message, self).process_email_queue(cr, uid, ids, context) + attachment_obj = self.pool.get('ir.attachment') + for message in self.browse(cr, uid, result, context): + if message.template_id and message.template_id.auto_delete: + self.unlink(cr, uid, [id], context=context) + attachment_ids = [x.id for x in message.attachments_ids] + attachment_obj.unlink(cr, uid, attachment_ids, context=context) return result - def _default_model(self, cursor, user, context=None): - """ - Returns the default value for model field - @param cursor: Database Cursor - @param user: ID of current user - @param context: OpenERP Context - """ - return self.pool.get('email.template').read( - cursor, - user, - context['template_id'], - ['object_name'], - context).get('object_name', False) - - _columns = { - 'ref_template':fields.many2one( - 'email.template', - 'Template', readonly=True), - 'rel_model':fields.many2one('ir.model', 'Model', readonly=True), - 'rel_model_ref':fields.selection(_get_model_recs, 'Referred Document'), - 'to':fields.char('To', size=250, readonly=True), - 'cc':fields.char('CC', size=250, readonly=True), - 'bcc':fields.char('BCC', size=250, readonly=True), - 'reply_to':fields.char('Reply-To', - size=250, - help="The address recipients should reply to," - " if different from the From address." - " Placeholders can be used here."), - 'message_id':fields.char('Message-ID', - size=250, - help="The Message-ID header value, if you need to" - "specify it, for example to automatically recognize the replies later." - " Placeholders can be used here."), - 'subject':fields.char('Subject', size=200, readonly=True), - 'body_text':fields.text('Body', readonly=True), - 'body_html':fields.text('Body', readonly=True), - 'report':fields.char('Report Name', size=100, readonly=True), - } - _defaults = { - 'ref_template': lambda self, cr, uid, ctx:ctx['template_id'] or False, - 'rel_model': _default_model, - } - def on_change_ref(self, cr, uid, ids, rel_model_ref, context=None): - if context is None: - context = {} - if not rel_model_ref: - return {} - vals = {} - if context == {}: - context = self.context - template = self.pool.get('email.template').browse(cr, uid, context['template_id'], context) - #Search translated template - lang = get_value(cr, uid, rel_model_ref, template.lang, template, context) - if lang: - ctx = context.copy() - ctx.update({'lang':lang}) - template = self.pool.get('email.template').browse(cr, uid, context['template_id'], ctx) - vals['to'] = get_value(cr, uid, rel_model_ref, template.def_to, template, context) - vals['cc'] = get_value(cr, uid, rel_model_ref, template.def_cc, template, context) - vals['bcc'] = get_value(cr, uid, rel_model_ref, template.def_bcc, template, context) - vals['reply_to'] = get_value(cr, uid, rel_model_ref, template.reply_to, template, context) - if template.message_id: - vals['message_id'] = get_value(cr, uid, rel_model_ref, template.message_id, template, context) - elif template.track_campaign_item: - vals['message_id'] = tools.misc.generate_tracking_message_id(rel_model_ref) - vals['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context) - vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context) - vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context) - vals['report'] = get_value(cr, uid, rel_model_ref, template.file_name, template, context) - return {'value':vals} - -email_template_preview() +email_message() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_account.py b/addons/email_template/email_template_account.py deleted file mode 100644 index 462baa1672d..00000000000 --- a/addons/email_template/email_template_account.py +++ /dev/null @@ -1,470 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2004-2010 OpenERP SA () -# -# 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 -# -############################################################################## - -from osv import osv, fields -import re -import smtplib -import base64 -from email import Encoders -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import decode_header, Header -from email.utils import formatdate -import netsvc -import datetime -from tools.translate import _ -import tools -import logging - -EMAIL_PATTERN = re.compile(r'([^()\[\] ,<:\\>@";]+@[^()\[\] ,<:\\>@";]+)') # See RFC822 -def extract_emails(emails_str): - """ - Returns a list of email addresses recognized in a string, ignoring the rest of the string. - extract_emails('a@b.com,c@bcom, "John Doe" , e@b.com') -> ['a@b.com','c@bcom', 'd@b.com', 'e@b.com']" - """ - return EMAIL_PATTERN.findall(emails_str) - - -def extract_emails_from_dict(addresses={}): - """ - Extracts email addresses from a dictionary with comma-separated address string values, handling - separately the To, CC, BCC and Reply-To addresses. - - :param addresses: a dictionary of addresses in the form {'To': 'a@b.com,c@bcom; d@b.com;e@b.com' , 'CC': 'e@b.com;f@b.com', ... } - :return: a dictionary with a list of separate addresses for each header (To, CC, BCC), with an additional key 'all-recipients' - containing all addresses for the 'To', 'CC', 'BCC' entries. - """ - result = {'all-recipients':[]} - keys = ['To', 'CC', 'BCC', 'Reply-To'] - for each in keys: - emails = extract_emails(addresses.get(each, u'')) - while u'' in emails: - emails.remove(u'') - result[each] = emails - if each != 'Reply-To': - result['all-recipients'].extend(emails) - return result - -class email_template_account(osv.osv): - """ - Object to store email account settings - """ - _name = "email_template.account" - _known_content_types = ['multipart/mixed', - 'multipart/alternative', - 'multipart/related', - 'text/plain', - 'text/html' - ] - _columns = { - 'name': fields.char('Description', - size=64, required=True, - readonly=True, select=True, - help="The description is used as the Sender name along with the provided From Email, \ -unless it is already specified in the From Email, e.g: John Doe ", - states={'draft':[('readonly', False)]}), - 'auto_delete': fields.boolean('Auto Delete', size=64, readonly=True, - help="Permanently delete emails after sending", - states={'draft':[('readonly', False)]}), - 'user':fields.many2one('res.users', - 'Related User', required=True, - readonly=True, states={'draft':[('readonly', False)]}), - 'email_id': fields.char('From Email', - size=120, required=True, - readonly=True, states={'draft':[('readonly', False)]} , - help="eg: 'john@doe.com' or 'John Doe '"), - 'smtpserver': fields.char('Server', - size=120, required=True, - readonly=True, states={'draft':[('readonly', False)]}, - help="Enter name of outgoing server, eg: smtp.yourdomain.com"), - 'smtpport': fields.integer('SMTP Port', - size=64, required=True, - readonly=True, states={'draft':[('readonly', False)]}, - help="Enter port number, eg: 25 or 587"), - 'smtpuname': fields.char('User Name', - size=120, required=False, - readonly=True, states={'draft':[('readonly', False)]}, - help="Specify the username if your SMTP server requires authentication, " - "otherwise leave it empty."), - 'smtppass': fields.char('Password', - size=120, invisible=True, - required=False, readonly=True, - states={'draft':[('readonly', False)]}), - 'smtptls':fields.boolean('TLS', - states={'draft':[('readonly', False)]}, readonly=True), - - 'smtpssl':fields.boolean('SSL/TLS (only in python 2.6)', - states={'draft':[('readonly', False)]}, readonly=True), - 'send_pref':fields.selection([ - ('html', 'HTML, otherwise Text'), - ('text', 'Text, otherwise HTML'), - ('alternative', 'Both HTML & Text (Alternative)'), - ('mixed', 'Both HTML & Text (Mixed)') - ], 'Mail Format', required=True), - 'company':fields.selection([ - ('yes', 'Yes'), - ('no', 'No') - ], 'Corporate', - readonly=True, - help="Select if this mail account does not belong " \ - "to specific user but to the organization as a whole. " \ - "eg: info@companydomain.com", - required=True, states={ - 'draft':[('readonly', False)] - }), - - 'state':fields.selection([ - ('draft', 'Initiated'), - ('suspended', 'Suspended'), - ('approved', 'Approved') - ], - 'State', required=True, readonly=True), - } - - _defaults = { - 'name':lambda self, cursor, user, context:self.pool.get( - 'res.users' - ).read( - cursor, - user, - user, - ['name'], - context - )['name'], - 'state':lambda * a:'draft', - 'smtpport':lambda *a:25, - 'smtpserver':lambda *a:'localhost', - 'company':lambda *a:'yes', - 'user':lambda self, cursor, user, context:user, - 'send_pref':lambda *a: 'html', - 'smtptls':lambda *a:True, - } - - _sql_constraints = [ - ( - 'email_uniq', - 'unique (email_id)', - 'Another setting already exists with this email ID !') - ] - - def name_get(self, cr, uid, ids, context=None): - return [(a["id"], "%s (%s)" % (a['email_id'], a['name'])) for a in self.read(cr, uid, ids, ['name', 'email_id'], context=context)] - - def _constraint_unique(self, cursor, user, ids, context=None): - """ - This makes sure that you dont give personal - users two accounts with same ID (Validated in sql constaints) - However this constraint exempts company accounts. - Any no of co accounts for a user is allowed - """ - if self.read(cursor, user, ids, ['company'])[0]['company'] == 'no': - accounts = self.search(cursor, user, [ - ('user', '=', user), - ('company', '=', 'no') - ]) - if len(accounts) > 1 : - return False - else : - return True - else: - return True - - _constraints = [ - (_constraint_unique, - 'Error: You are not allowed to have more than 1 account.', - []) - ] - - def get_outgoing_server(self, cursor, user, ids, context=None): - """ - Returns the Out Going Connection (SMTP) object - - @attention: DO NOT USE except_osv IN THIS METHOD - @param cursor: Database Cursor - @param user: ID of current user - @param ids: ID/list of ids of current object for - which connection is required - First ID will be chosen from lists - @param context: Context - - @return: SMTP server object or Exception - """ - #Type cast ids to integer - if type(ids) == list: - ids = ids[0] - this_object = self.browse(cursor, user, ids, context=context) - if this_object: - if this_object.smtpserver and this_object.smtpport: - try: - if this_object.smtpssl: - serv = smtplib.SMTP_SSL(this_object.smtpserver, this_object.smtpport) - else: - serv = smtplib.SMTP(this_object.smtpserver, this_object.smtpport) - if this_object.smtptls: - serv.ehlo() - serv.starttls() - serv.ehlo() - except Exception, error: - raise error - try: - if serv.has_extn('AUTH') or this_object.smtpuname or this_object.smtppass: - serv.login(str(this_object.smtpuname), str(this_object.smtppass)) - except Exception, error: - raise error - return serv - raise Exception(_("SMTP SERVER or PORT not specified")) - raise Exception(_("Core connection for the given ID does not exist")) - - def check_outgoing_connection(self, cursor, user, ids, context=None): - """ - checks SMTP credentials and confirms if outgoing connection works - (Attached to button) - @param cursor: Database Cursor - @param user: ID of current user - @param ids: list of ids of current object for - which connection is required - @param context: Context - """ - try: - self.get_outgoing_server(cursor, user, ids, context) - raise osv.except_osv(_("SMTP Test Connection Was Successful"), '') - except osv.except_osv, success_message: - raise success_message - except Exception, error: - raise osv.except_osv( - _("Out going connection test failed"), - _("Reason: %s") % error - ) - - def do_approval(self, cr, uid, ids, context=None): - #TODO: Check if user has rights - self.write(cr, uid, ids, {'state':'approved'}, context=context) -# wf_service = netsvc.LocalService("workflow") - - def smtp_connection(self, cursor, user, id, context=None): - """ - This method should now wrap smtp_connection - """ - #This function returns a SMTP server object - logger = netsvc.Logger() - core_obj = self.browse(cursor, user, id, context=context) - if core_obj.smtpserver and core_obj.smtpport and core_obj.state == 'approved': - try: - serv = self.get_outgoing_server(cursor, user, id, context) - except Exception, error: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed on login. Probable Reason:Could not login to server\nError: %s") % (id, error)) - return False - #Everything is complete, now return the connection - return serv - else: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id) - return False - -#**************************** MAIL SENDING FEATURES ***********************# - - - - - def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, message_id=None, context=None): - #TODO: Replace all this with a single email object - if body is None: - body = {} - if payload is None: - payload = {} - if context is None: - context = {} - logger = netsvc.Logger() - for id in ids: - core_obj = self.browse(cr, uid, id, context) - serv = self.smtp_connection(cr, uid, id) - if serv: - try: - # Prepare multipart containers depending on data - text_subtype = (core_obj.send_pref == 'alternative') and 'alternative' or 'mixed' - # Need a multipart/mixed wrapper for attachments if content is alternative - if payload and text_subtype == 'alternative': - payload_part = MIMEMultipart(_subtype='mixed') - text_part = MIMEMultipart(_subtype=text_subtype) - payload_part.attach(text_part) - else: - # otherwise a single multipart/mixed will do the whole job - payload_part = text_part = MIMEMultipart(_subtype=text_subtype) - - if subject: - payload_part['Subject'] = subject - from_email = core_obj.email_id - if '<' in from_email: - # We have a structured email address, keep it untouched - payload_part['From'] = Header(core_obj.email_id, 'utf-8').encode() - else: - # Plain email address, construct a structured one based on the name: - sender_name = Header(core_obj.name, 'utf-8').encode() - payload_part['From'] = sender_name + " <" + core_obj.email_id + ">" - payload_part['Organization'] = tools.ustr(core_obj.user.company_id.name) - payload_part['Date'] = formatdate() - addresses_l = extract_emails_from_dict(addresses) - if addresses_l['To']: - payload_part['To'] = u','.join(addresses_l['To']) - if addresses_l['CC']: - payload_part['CC'] = u','.join(addresses_l['CC']) - if addresses_l['Reply-To']: - payload_part['Reply-To'] = addresses_l['Reply-To'][0] - if message_id: - payload_part['Message-ID'] = message_id - if body.get('text', False): - temp_body_text = body.get('text', '') - l = len(temp_body_text.replace(' ', '').replace('\r', '').replace('\n', '')) - if l == 0: - body['text'] = u'No Mail Message' - # Attach parts into message container. - # According to RFC 2046, the last part of a multipart message, in this case - # the HTML message, is best and preferred. - if core_obj.send_pref in ('text', 'mixed', 'alternative'): - body_text = body.get('text', u'') - body_text = tools.ustr(body_text) - text_part.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8')) - if core_obj.send_pref in ('html', 'mixed', 'alternative'): - html_body = body.get('html', u'') - if len(html_body) == 0 or html_body == u'': - html_body = body.get('text', u'

<Empty Message>

').replace('\n', '
').replace('\r', '
') - html_body = tools.ustr(html_body) - text_part.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8')) - - #Now add attachments if any, wrapping into a container multipart/mixed if needed - if payload: - for file in payload: - part = MIMEBase('application', "octet-stream") - part.set_payload(base64.decodestring(payload[file])) - part.add_header('Content-Disposition', 'attachment; filename="%s"' % file) - Encoders.encode_base64(part) - payload_part.attach(part) - except Exception, error: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, error)) - return {'error_msg': _("Server Send Error\nDescription: %s")%error} - try: - serv.sendmail(payload_part['From'], addresses_l['all-recipients'], payload_part.as_string()) - except Exception, error: - logging.getLogger('email_template').error(_("Mail from Account %s failed. Probable Reason: Server Send Error\n Description: %s"), id, error, exc_info=True) - return {'error_msg': _("Server Send Error\nDescription: %s")%error} - #The mail sending is complete - serv.close() - logger.notifyChannel(_("Email Template"), netsvc.LOG_INFO, _("Mail from Account %s successfully Sent.") % (id)) - return True - else: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id) - return {'nodestroy':True,'error_msg': _("Mail from Account %s failed. Probable Reason:Account not approved")% id} - - def extracttime(self, time_as_string): - """ - TODO: DOC THis - """ - logger = netsvc.Logger() - #The standard email dates are of format similar to: - #Thu, 8 Oct 2009 09:35:42 +0200 - date_as_date = False - convertor = {'+':1, '-':-1} - try: - time_as_string = time_as_string.replace(',', '') - date_list = time_as_string.split(' ') - date_temp_str = ' '.join(date_list[1:5]) - if len(date_list) >= 6: - sign = convertor.get(date_list[5][0], False) - else: - sign = False - try: - dt = datetime.datetime.strptime( - date_temp_str, - "%d %b %Y %H:%M:%S") - except: - try: - dt = datetime.datetime.strptime( - date_temp_str, - "%d %b %Y %H:%M") - except: - return False - if sign: - try: - offset = datetime.timedelta( - hours=sign * int( - date_list[5][1:3] - ), - minutes=sign * int( - date_list[5][3:5] - ) - ) - except Exception, e2: - """Looks like UT or GMT, just forget decoding""" - return False - else: - offset = datetime.timedelta(hours=0) - dt = dt + offset - date_as_date = dt.strftime('%Y-%m-%d %H:%M:%S') - except Exception, e: - logger.notifyChannel( - _("Email Template"), - netsvc.LOG_WARNING, - _( - "Datetime Extraction failed.Date:%s \ - \tError:%s") % ( - time_as_string, - e) - ) - return date_as_date - - def send_receive(self, cr, uid, ids, context=None): - for id in ids: - ctx = context.copy() - ctx['filters'] = [('account_id', '=', id)] - self.pool.get('email_template.mailbox').send_all_mail(cr, uid, [], context=ctx) - return True - - def decode_header_text(self, text): - """ Decode internationalized headers RFC2822. - To, CC, BCC, Subject fields can contain - text slices with different encodes, like: - =?iso-8859-1?Q?Enric_Mart=ED?= , - =?Windows-1252?Q?David_G=F3mez?= - Sometimes they include extra " character at the beginning/ - end of the contact name, like: - "=?iso-8859-1?Q?Enric_Mart=ED?=" - and decode_header() does not work well, so we use regular - expressions (?= ? ? ?=) to split the text slices - """ - if not text: - return text - p = re.compile("(=\?.*?\?.\?.*?\?=)") - text2 = '' - try: - for t2 in p.split(text): - text2 += ''.join( - [s.decode( - t or 'ascii' - ) for (s, t) in decode_header(t2)] - ).encode('utf-8') - except: - return text - return text2 - -email_template_account() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_account_view.xml b/addons/email_template/email_template_account_view.xml deleted file mode 100644 index 00429efc16c..00000000000 --- a/addons/email_template/email_template_account_view.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - email_template.account.form - email_template.account - form - -
- - - - - - - - -