[Merge]: Merge with lp:~openerp-dev/openobject-addons/emails-framework-addons

bzr revid: rha@tinyerp.com-20110317100217-n6p21hw4q0y1c83k
This commit is contained in:
Rifakat Haradwala (Open ERP)
2011-03-17 15:32:17 +05:30
136 changed files with 3387 additions and 3098 deletions
+1 -1
View File
@@ -43,7 +43,7 @@
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['account'],
'depends': ['account', 'email'],
'init_xml': [],
'update_xml': [
'security/ir.model.access.csv',
@@ -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 = {}
@@ -277,7 +278,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 )
+4 -4
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@@ -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 <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@@ -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', 'email'],
'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': [],
+65 -64
View File
@@ -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)
+3 -3
View File
@@ -20,9 +20,9 @@
##############################################################################
{
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : ["base"],
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : ["base", "email"],
'description': """Full featured calendar system that supports:
- Calendar of events
- Alerts (create requests)
+35 -31
View File
@@ -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 = '<br>'.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)
+2 -2
View File
@@ -56,7 +56,7 @@ Create dashboard for CRM that includes:
'base',
'base_action_rule',
'process',
'mail_gateway',
'email',
'base_calendar',
'resource',
'board'
@@ -83,7 +83,7 @@ Create dashboard for CRM that includes:
'wizard/crm_opportunity_to_phonecall_view.xml',
'wizard/crm_partner_to_opportunity_view.xml',
'wizard/crm_send_email_view.xml',
# 'wizard/crm_send_email_view.xml',
'wizard/crm_add_note_view.xml',
'wizard/crm_merge_opportunities_view.xml',
+13 -12
View File
@@ -452,6 +452,7 @@ class crm_case(object):
@param ids: List of case's IDs to remind
@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 destination and not case.email_from:
raise osv.except_osv(_('Error!'), ("Partner Email is not specified in Case"))
@@ -486,18 +487,18 @@ class crm_case(object):
attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
# Send an email
subject = "Reminder: [%s] %s" % (str(case.id), case.name,)
tools.email_send(
src,
[dest],
subject,
body,
reply_to=case.section_id.reply_to or '',
openobject_id=str(case.id),
attach=attach_to_send
)
self._history(cr, uid, [case], _('Send'), history=True, subject=subject, email=dest, details=body, email_from=src)
subject = "Reminder: [%s] %s" % (str(case.id), case.name, )
email_message_obj.email_send(cr, uid,
src,
[dest],
subject,
body,
model='crm.case',
reply_to=case.section_id.reply_to,
openobject_id=str(case.id),
attach=attach_to_send
)
self._history(cr, uid, [case], _('Send'), history=True, subject=subject, email=dest, details=body, email_from=src)
return True
def _check(self, cr, uid, ids=False, context=None):
+16 -15
View File
@@ -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
+1 -1
View File
@@ -148,7 +148,7 @@ class crm_lead(crm_case, osv.osv):
\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)]),
'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
+14 -14
View File
@@ -105,10 +105,11 @@
</group>
<group colspan="2" col="3">
<separator string="Communication" colspan="4" col="3"/>
<field name="email_from" widget="email"/><button string="Send Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.lead'}"
icon="terp-mail-message-new" type="action" colspan="1"/>
<field name="email_from" widget="email"/>
<button string="Send Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'crm.lead', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action" colspan="1"/>
<newline/>
<field name="phone"/>
<newline/>
@@ -188,8 +189,8 @@
<field name="history" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="Communication history">
@@ -205,11 +206,10 @@
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button colspan="4" string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
@@ -226,9 +226,9 @@
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.lead'}"
icon="terp-mail-message-new" type="action" />
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'crm.lead', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
</notebook>
</form>
+7 -7
View File
@@ -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','<new'))
return user_id
res_users()
+15 -17
View File
@@ -60,11 +60,11 @@
<group col="3" colspan="2">
<field name="email_from" string="Email" />
<button string="Send Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.lead'}"
icon="terp-mail-message-new" type="action" />
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model': 'crm.lead', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</group>
<field name="phone"/>
<field name="phone"/>
</group>
<group col="2" colspan="2">
@@ -144,11 +144,10 @@
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="history" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</tree>
<form string="History">
<group col="4" colspan="4">
@@ -162,13 +161,12 @@
</group>
<notebook colspan="4">
<page string="Details">
<field name="description" colspan="4" nolabel="1"/>
<field name="description" colspan="4" nolabel="1"/>
<group attrs="{'invisible': [('history', '!=', True)]}">
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</group>
@@ -183,10 +181,10 @@
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.lead'}"
icon="terp-mail-message-new" type="action" />
<button string="Send New Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model': 'crm.lead', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
<page string="Extra Info" groups="base.group_extended">
<group col="2" colspan="2">
+44 -44
View File
@@ -37,60 +37,60 @@ class crm_phonecall(crm_case, osv.osv):
# From crm.case
'id': fields.integer('ID'),
'name': fields.char('Call Summary', size=64),
'active': fields.boolean('Active', required=False),
'active': fields.boolean('Active', required=False),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'create_date': fields.datetime('Creation Date' , readonly=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
select=True, help='Sales team to which Case belongs to.'),
'user_id': fields.many2one('res.users', 'Responsible'),
'partner_id': fields.many2one('res.partner', 'Partner'),
select=True, help='Sales team to which Case belongs to.'),
'user_id': fields.many2one('res.users', 'Responsible'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
domain="[('partner_id','=',partner_id)]"),
'company_id': fields.many2one('res.company', 'Company'),
'description': fields.text('Description'),
domain="[('partner_id','=',partner_id)]"),
'company_id': fields.many2one('res.company', 'Company'),
'description': fields.text('Description'),
'state': fields.selection([
('draft', 'Draft'),
('open', 'Todo'),
('cancel', 'Cancelled'),
('done', 'Done'),
('draft', 'Draft'),
('open', 'Todo'),
('cancel', 'Cancelled'),
('done', 'Done'),
('pending', 'Pending'),
], 'State', size=16, readonly=True,
], 'State', size=16, readonly=True,
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\'.'),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date_open': fields.datetime('Opened', readonly=True),
# phonecall fields
'duration': fields.float('Duration', help="Duration in Minutes"),
'duration': fields.float('Duration', help="Duration in Minutes"),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="['|',('section_id','=',section_id),('section_id','=',False),\
('object_id.model', '=', 'crm.phonecall')]"),
'partner_phone': fields.char('Phone', size=32),
('object_id.model', '=', 'crm.phonecall')]"),
'partner_phone': fields.char('Phone', size=32),
'partner_contact': fields.related('partner_address_id', 'name', \
type="char", string="Contact", size=128),
'partner_mobile': fields.char('Mobile', size=32),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
type="char", string="Contact", size=128),
'partner_mobile': fields.char('Mobile', size=32),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'canal_id': fields.many2one('res.partner.canal', 'Channel', \
help="The channels represent the different communication\
modes available with the customer." \
" With each commercial opportunity, you can indicate\
the canall which is this opportunity source."),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Date'),
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity'),
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
the canall which is this opportunity source."),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Date'),
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity'),
'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
_defaults = {
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'priority': crm.AVAILABLE_PRIORITIES[2][0],
'state': 'open',
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'priority': crm.AVAILABLE_PRIORITIES[2][0],
'state': 'open',
'user_id': lambda self,cr,uid,ctx: uid,
'active': 1,
'active': 1,
}
# From crm.case
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
@@ -174,23 +174,23 @@ class crm_phonecall(crm_case, osv.osv):
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
context = {
'default_phonecall_id': phonecall.id,
'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
'default_email': phonecall.email_from ,
'default_phonecall_id': phonecall.id,
'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
'default_email': phonecall.email_from ,
'default_name': phonecall.name
}
value = {
'name': _('Meetings'),
'domain' : "[('user_id','=',%s)]" % (uid),
'context': context,
'view_type': 'form',
'view_mode': 'calendar,form,tree',
'res_model': 'crm.meeting',
'view_id': False,
'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
'type': 'ir.actions.act_window',
'search_view_id': res['res_id'],
'name': _('Meetings'),
'domain' : "[('user_id','=',%s)]" % (uid),
'context': context,
'view_type': 'form',
'view_mode': 'calendar,form,tree',
'res_model': 'crm.meeting',
'view_id': False,
'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
'type': 'ir.actions.act_window',
'search_view_id': res['res_id'],
'nodestroy': True
}
+1 -1
View File
@@ -109,7 +109,7 @@ class crm_lead_report(osv.osv):
c.planned_revenue,
c.planned_revenue*(c.probability/100) as probable_revenue,
1 as nbr,
(SELECT count(id) FROM mailgate_message WHERE model='crm.lead' AND res_id=c.id AND history=True) AS email,
(SELECT count(id) FROM email_message WHERE model='crm.lead' AND res_id=c.id AND history=True) AS email,
date_trunc('day',c.create_date) as create_date,
extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close,
abs(extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24)) as delay_expected,
+4 -4
View File
@@ -27,15 +27,15 @@
"access_res_partner_manager","res.partner.crm.manager","base.model_res_partner","base.group_sale_manager",1,0,0,0
"access_res_partner_address_manager","res.partner.address.crm.user.manager","base.model_res_partner_address","base.group_sale_manager",1,0,0,0
"access_res_partner_category_manager","res.partner.category.crm.manager","base.model_res_partner_category","base.group_sale_manager",1,0,0,0
"mail_gateway_mailgate_message_manager","mail_gateway.mailgate.message.manager","mail_gateway.model_mailgate_message","base.group_sale_manager",1,0,0,0
"mail_gateway_mailgate_thread_manager","mail_gateway.mailgate.thread.manager","mail_gateway.model_mailgate_thread","base.group_sale_manager",1,1,1,1
"email_email_message_manager","email.email.message.manager","email.model_email_message","base.group_sale_manager",1,0,0,0
"email_thread_manager","email.mailgate.thread.manager","email.model_mailgate_thread","base.group_sale_manager",1,1,1,1
"access_calendar_attendee_crm_user","calendar.attendee.crm.user","model_calendar_attendee","base.group_sale_salesman",1,1,1,0
"access_calendar_attendee_crm_manager","calendar.attendee.crm.manager","model_calendar_attendee","base.group_sale_manager",1,1,1,1
"access_res_partner","res.partner.crm.user","base.model_res_partner","base.group_sale_salesman",1,1,1,0
"access_res_partner_address","res.partner.address.crm.user","base.model_res_partner_address","base.group_sale_salesman",1,1,1,0
"access_res_partner_category","res.partner.category.crm.user","base.model_res_partner_category","base.group_sale_salesman",1,1,1,0
"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","mail_gateway.model_mailgate_thread","base.group_sale_salesman",1,1,1,1
"mail_gateway_mailgate_message_user","mail_gateway.mailgate.message.user","mail_gateway.model_mailgate_message","base.group_sale_salesman",1,1,1,1
"email_mailgate_thread","email.mailgate.thread","email.model_mailgate_thread","base.group_sale_salesman",1,1,1,1
"email_gateway_email_message_user","email.email.message.user","email.model_email_message","base.group_sale_salesman",1,1,1,1
"access_crm_case_categ_manager","crm.case.categ manager","model_crm_case_categ","base.group_sale_manager",1,1,1,1
"access_base_action_rule_manager","base.action.rule manager","model_base_action_rule","base.group_sale_manager",1,1,1,1
"access_crm_lead_report_user","crm.lead.report user","model_crm_lead_report","base.group_sale_salesman",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
27 access_res_partner_manager res.partner.crm.manager base.model_res_partner base.group_sale_manager 1 0 0 0
28 access_res_partner_address_manager res.partner.address.crm.user.manager base.model_res_partner_address base.group_sale_manager 1 0 0 0
29 access_res_partner_category_manager res.partner.category.crm.manager base.model_res_partner_category base.group_sale_manager 1 0 0 0
30 mail_gateway_mailgate_message_manager email_email_message_manager mail_gateway.mailgate.message.manager email.email.message.manager mail_gateway.model_mailgate_message email.model_email_message base.group_sale_manager 1 0 0 0
31 mail_gateway_mailgate_thread_manager email_thread_manager mail_gateway.mailgate.thread.manager email.mailgate.thread.manager mail_gateway.model_mailgate_thread email.model_mailgate_thread base.group_sale_manager 1 1 1 1
32 access_calendar_attendee_crm_user calendar.attendee.crm.user model_calendar_attendee base.group_sale_salesman 1 1 1 0
33 access_calendar_attendee_crm_manager calendar.attendee.crm.manager model_calendar_attendee base.group_sale_manager 1 1 1 1
34 access_res_partner res.partner.crm.user base.model_res_partner base.group_sale_salesman 1 1 1 0
35 access_res_partner_address res.partner.address.crm.user base.model_res_partner_address base.group_sale_salesman 1 1 1 0
36 access_res_partner_category res.partner.category.crm.user base.model_res_partner_category base.group_sale_salesman 1 1 1 0
37 mail_gateway_mailgate_thread email_mailgate_thread mail_gateway.mailgate.thread email.mailgate.thread mail_gateway.model_mailgate_thread email.model_mailgate_thread base.group_sale_salesman 1 1 1 1
38 mail_gateway_mailgate_message_user email_gateway_email_message_user mail_gateway.mailgate.message.user email.email.message.user mail_gateway.model_mailgate_message email.model_email_message base.group_sale_salesman 1 1 1 1
39 access_crm_case_categ_manager crm.case.categ manager model_crm_case_categ base.group_sale_manager 1 1 1 1
40 access_base_action_rule_manager base.action.rule manager model_base_action_rule base.group_sale_manager 1 1 1 1
41 access_crm_lead_report_user crm.lead.report user model_crm_lead_report base.group_sale_salesman 1 1 1 1
+2 -1
View File
@@ -19,7 +19,8 @@
#
##############################################################################
import crm_send_email
#import crm_send_email
import email_compose_message
import crm_add_note
import crm_lead_to_partner
+13 -3
View File
@@ -5,6 +5,16 @@ import base64
AVAILABLE_STATES = crm.AVAILABLE_STATES + [('unchanged', 'Unchanged')]
class crm_add_note_email_attachment(osv.osv_memory):
_name = 'crm.add.note.email.attachment'
_columns = {
'binary' : fields.binary('Attachment', required=True),
'name' : fields.char('Name', size=128, required=True),
'wizard_id' : fields.many2one('crm.add.note', 'Wizard', required=True),
}
crm_add_note_email_attachment()
class crm_add_note(osv.osv_memory):
"""Adds a new note to the case."""
@@ -14,8 +24,8 @@ class crm_add_note(osv.osv_memory):
_columns = {
'body': fields.text('Note Body', required=True),
'state': fields.selection(AVAILABLE_STATES, string='Set New State To',
required=True),
'attachment_ids' : fields.one2many('crm.send.mail.attachment', 'wizard_id'),
required=True),
'attachment_ids' : fields.one2many('crm.add.note.email.attachment', 'wizard_id'),
}
def action_add(self, cr, uid, ids, context=None):
@@ -37,7 +47,7 @@ class crm_add_note(osv.osv_memory):
attach = [
(x.name, base64.decodestring(x.binary)) for x in obj.attachment_ids
]
case_pool.history(cr, uid, [case], self.pool.get('mailgate.message').truncate_data(cr, uid, obj.body, context=context), history=False,
case_pool.history(cr, uid, [case], self.pool.get('email.message').truncate_data(cr, uid, obj.body, context=context), history=False,
details=obj.body, email_from=user_name, attach=attach)
if obj.state == 'unchanged':
+1 -1
View File
@@ -117,7 +117,7 @@ Leads Could not convert into Opportunity"))
leads.history(cr, uid, [lead], _('Converted to opportunity'), details='Converted to Opportunity', context=context)
if lead.partner_id:
msg_ids = [ x.id for x in lead.message_ids]
self.pool.get('mailgate.message').write(cr, uid, msg_ids, {
self.pool.get('email.message').write(cr, uid, msg_ids, {
'partner_id': lead.partner_id.id
}, context=context)
leads.log(cr, uid, lead.id, _("Lead '%s' has been converted to an opportunity.") % lead.name)
+1 -2
View File
@@ -60,7 +60,7 @@ class crm_merge_opportunity(osv.osv_memory):
@param opp_ids : list of opportunities ids to merge
"""
opp_obj = self.pool.get('crm.lead')
message_obj = self.pool.get('mailgate.message')
message_obj = self.pool.get('email.message')
lead_ids = context and context.pop('lead_ids', []) or []
@@ -194,7 +194,6 @@ class crm_merge_opportunity(osv.osv_memory):
context['lead_ids'] = [op_ids[0].id]
return self.merge(cr, uid, op_ids, context)
_columns = {
'opportunity_ids' : fields.many2many('crm.lead', 'merge_opportunity_rel', 'merge_id', 'opportunity_id', 'Opportunities', domain=[('type', '=', 'opportunity')]),
}
+5 -17
View File
@@ -31,18 +31,6 @@ import re
AVAILABLE_STATES = crm.AVAILABLE_STATES + [('unchanged', 'Unchanged')]
class crm_send_new_email_attachment(osv.osv_memory):
_name = 'crm.send.mail.attachment'
_columns = {
'binary' : fields.binary('Attachment', required=True),
'name' : fields.char('Name', size=128, required=True),
'wizard_id' : fields.many2one('crm.send.mail', 'Wizard', required=True),
}
crm_send_new_email_attachment()
class crm_send_new_email(osv.osv_memory):
""" Sends new email for the case"""
_name = "crm.send.mail"
@@ -63,8 +51,7 @@ class crm_send_new_email(osv.osv_memory):
def action_send(self, cr, uid, ids, context=None):
""" This sends an email to ALL the addresses of the selected partners.
"""
hist_obj = self.pool.get('mailgate.message')
email_message_obj = self.pool.get('email.message')
if context is None:
context = {}
@@ -103,7 +90,7 @@ class crm_send_new_email(osv.osv_memory):
attach += attach_all
else:
hist = hist_obj.browse(cr, uid, res_id, context=context)
hist = email_message_obj.browse(cr, uid, res_id, context=context)
message_id = hist.message_id
model = hist.model
case_pool = self.pool.get(model)
@@ -124,11 +111,12 @@ class crm_send_new_email(osv.osv_memory):
if obj.html:
subtype = 'html'
flag = tools.email_send(
flag = email_message_obj.email_send(cr, uid,
email_from,
emails,
obj.subject,
body,
model='crm.send.mail',
email_cc=email_cc,
attach=attach,
subtype=subtype,
@@ -204,7 +192,7 @@ class crm_send_new_email(osv.osv_memory):
"""
This function gets default values for reply mail
"""
hist_obj = self.pool.get('mailgate.message')
hist_obj = self.pool.get('email.message')
res_ids = context and context.get('active_ids', []) or []
user_obj = self.pool.get('res.users')
@@ -0,0 +1,55 @@
# -*- 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
email_model = [
'crm.lead',
]
class email_compose_message(osv.osv_memory):
_inherit = 'email.compose.message'
def get_value(self, cr, uid, model, resource_id, context=None):
if context is None:
context = {}
result = super(email_compose_message, self).get_value(cr, uid, model, resource_id, context=context)
if model in email_model and resource_id:
model_obj = self.pool.get(model)
data = model_obj.browse(cr, uid , resource_id, context)
result.update({
'name' : data.name or False,
'email_to' : data.email_from or False,
'email_from' : data.user_id and data.user_id.address_id and data.user_id.address_id.email or False,
'description' : '\n' + (tools.ustr(data.user_id.signature or '')),
'email_cc' : tools.ustr(data.email_cc or ''),
'model': model or False,
'res_id': resource_id or False,
})
if hasattr(data, 'section_id'):
result.update({'reply_to' : data.section_id and data.section_id.reply_to or False})
return result
email_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+46 -44
View File
@@ -22,9 +22,11 @@
from osv import fields, osv
from crm import crm
import time
from crm import wizard
import binascii
import tools
wizard.email_compose_message.email_model.append('crm.claim')
CRM_CLAIM_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
crm.AVAILABLE_STATES[3][0], # Done
@@ -40,47 +42,47 @@ class crm_claim(crm.crm_case, osv.osv):
_order = "priority,date desc"
_inherit = ['mailgate.thread']
_columns = {
'id': fields.integer('ID', readonly=True),
'name': fields.char('Claim Subject', size=128, required=True),
'id': fields.integer('ID', readonly=True),
'name': fields.char('Claim Subject', size=128, required=True),
'action_next': fields.char('Next Action', size=200),
'date_action_next': fields.datetime('Next Action Date'),
'description': fields.text('Description'),
'resolution': fields.text('Resolution'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Claim Date'),
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
'description': fields.text('Description'),
'resolution': fields.text('Resolution'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Claim Date'),
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.claim')]"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
('object_id.model', '=', 'crm.claim')]"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'type_action': fields.selection([('correction','Corrective Action'),('prevention','Preventive Action')], 'Action Type'),
'user_id': fields.many2one('res.users', 'Responsible'),
'user_fault': fields.char('Trouble Responsible', size=64),
'user_id': fields.many2one('res.users', 'Responsible'),
'user_fault': fields.char('Trouble Responsible', size=64),
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
select=True, help="Sales team to which Case belongs to."\
"Define Responsible user and Email account for"\
" mail gateway."),
'company_id': fields.many2one('res.company', 'Company'),
'partner_id': fields.many2one('res.partner', 'Partner'),
" mail gateway."),
'company_id': fields.many2one('res.company', 'Company'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
# domain="[('partner_id','=',partner_id)]"
),
'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'partner_phone': fields.char('Phone', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('type','=','claim')]"),
'cause': fields.text('Root Cause'),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
),
'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'partner_phone': fields.char('Phone', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('type','=','claim')]"),
'cause': fields.text('Root Cause'),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
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)]),
}
def _get_stage_id(self, cr, uid, context=None):
"""Finds type of stage according to object.
@param self: The object pointer
@@ -95,18 +97,18 @@ class crm_claim(crm.crm_case, osv.osv):
return stage_ids and stage_ids[0] or False
_defaults = {
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from':crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'section_id':crm.crm_case. _get_section,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from':crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'section_id':crm.crm_case. _get_section,
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
#'stage_id': _get_stage_id,
#'stage_id': _get_stage_id,
}
def onchange_partner_id(self, cr, uid, ids, part, email=False):
"""This function returns value of partner address based on partner
@param self: The object pointer
@@ -118,7 +120,7 @@ class crm_claim(crm.crm_case, osv.osv):
"""
if not part:
return {'value': {'partner_address_id': False,
'email_from': False,
'email_from': False,
'partner_phone': False,
'partner_mobile': False
}}
@@ -140,19 +142,19 @@ class crm_claim(crm.crm_case, osv.osv):
return {'value': {'email_from': False}}
address = self.pool.get('res.partner.address').browse(cr, uid, add)
return {'value': {'email_from': address.email, 'partner_phone': address.phone, 'partner_mobile': address.mobile}}
def case_open(self, cr, uid, ids, *args):
"""
Opens Claim
"""
res = super(crm_claim, self).case_open(cr, uid, ids, *args)
claims = self.browse(cr, uid, ids)
for i in xrange(0, len(ids)):
if not claims[i].stage_id :
stage_id = self._find_first_stage(cr, uid, 'claim', claims[i].section_id.id or False)
self.write(cr, uid, [ids[i]], {'stage_id' : stage_id})
return res
def message_new(self, cr, uid, msg, context=None):
@@ -252,18 +254,18 @@ crm_claim()
class crm_stage_claim(osv.osv):
def _get_type_value(self, cr, user, context):
list = super(crm_stage_claim, self)._get_type_value(cr, user, context)
list.append(('claim','Claim'))
return list
_inherit = "crm.case.stage"
_columns = {
'type': fields.selection(_get_type_value, 'Type'),
}
crm_stage_claim()
+13 -19
View File
@@ -91,15 +91,10 @@
<group>
<field name="name" />
<field name="date"/>
</group>
<group colspan="4" col="6">
<field name="user_id"/>
<field name="section_id" widget="selection" />
<group colspan="2" col="4">
<field name="stage_id" domain="[('type','=','claim')]"/>
<button name="stage_previous" string="" type="object" icon="gtk-go-back" />
@@ -180,11 +175,11 @@
<field name="message_ids" colspan="4" nolabel="1" mode="tree,form">
<tree string="History">
<field name="display_text" string="History Information"/>
<button
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.claim', 'include_original' : True}"
icon="terp-mail-replied" type="action" attrs="{'invisible': [('history', '!=', True)]}" />
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="Communication &amp; History">
<group col="4" colspan="4">
@@ -199,11 +194,10 @@
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.claim', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button colspan="4" string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
@@ -219,10 +213,10 @@
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.claim'}"
icon="terp-mail-message-new" type="action" />
<button string="Send New Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'crm.claim', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
</notebook>
</group>
+4 -4
View File
@@ -60,7 +60,7 @@ class crm_claim_report(osv.osv):
('11', 'November'), ('12', 'December')], 'Month', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'create_date': fields.datetime('Create Date', readonly=True, select=True),
'day': fields.char('Day', size=128, readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', readonly=True, domain="[('type','=','claim')]"),
'categ_id': fields.many2one('crm.case.categ', 'Category',\
@@ -71,8 +71,8 @@ class crm_claim_report(osv.osv):
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'priority': fields.selection(AVAILABLE_PRIORITIES, 'Priority'),
'type_action': fields.selection([('correction','Corrective Action'),('prevention','Preventive Action')], 'Action Type'),
'date_closed': fields.date('Close Date', readonly=True, select=True),
'date_deadline': fields.date('Deadline', readonly=True, select=True),
'date_closed': fields.date('Close Date', readonly=True, select=True),
'date_deadline': fields.date('Deadline', readonly=True, select=True),
'delay_expected': fields.float('Overpassed Deadline',digits=(16,2),readonly=True, group_operator="avg"),
'email': fields.integer('# Emails', size=128, readonly=True),
'probability': fields.float('Probability',digits=(16,2),readonly=True, group_operator="avg")
@@ -106,7 +106,7 @@ class crm_claim_report(osv.osv):
c.type_action as type_action,
date_trunc('day',c.create_date) as create_date,
avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close,
(SELECT count(id) FROM mailgate_message WHERE model='crm.claim' AND res_id=c.id AND history=True) AS email,
(SELECT count(id) FROM email_message WHERE model='crm.claim' AND res_id=c.id AND history=True) AS email,
(SELECT avg(probability) FROM crm_case_stage WHERE type='claim' AND id=c.stage_id) AS probability,
extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24) as delay_expected
from
+53 -50
View File
@@ -21,6 +21,9 @@
from osv import fields, osv
from crm import crm
from crm import wizard
wizard.email_compose_message.email_model.append('crm.fundraising')
class crm_fundraising(crm.crm_case, osv.osv):
""" Fund Raising Cases """
@@ -30,83 +33,83 @@ class crm_fundraising(crm.crm_case, osv.osv):
_order = "id desc"
_inherit = ['mailgate.thread']
_columns = {
'id': fields.integer('ID'),
'id': fields.integer('ID'),
'name': fields.char('Name', size=128, required=True),
'active': fields.boolean('Active', required=False),
'active': fields.boolean('Active', required=False),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'description': fields.text('Description'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'user_id': fields.many2one('res.users', 'Responsible'),
'date_action_next': fields.datetime('Next Action', readonly=1),
'description': fields.text('Description'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'user_id': fields.many2one('res.users', 'Responsible'),
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
select=True, help='Sales team to which Case belongs to. Define Responsible user and Email account for mail gateway.'),
'company_id': fields.many2one('res.company', 'Company'),
'partner_id': fields.many2one('res.partner', 'Partner'),
select=True, help='Sales team to which Case belongs to. Define Responsible user and Email account for mail gateway.'),
'company_id': fields.many2one('res.company', 'Company'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
domain="[('partner_id','=',partner_id)]"),
'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Date'),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
domain="[('partner_id','=',partner_id)]"),
'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date_closed': fields.datetime('Closed', readonly=True),
'date': fields.datetime('Date'),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.fundraising')]"),
'planned_revenue': fields.float('Planned Revenue'),
'planned_cost': fields.float('Planned Costs'),
'probability': fields.float('Probability (%)'),
'partner_name': fields.char("Employee's Name", size=64),
'partner_name2': fields.char('Employee Email', size=64),
'partner_phone': fields.char('Phone', size=32),
'partner_mobile': fields.char('Mobile', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('type', '=', 'fundraising')]"),
('object_id.model', '=', 'crm.fundraising')]"),
'planned_revenue': fields.float('Planned Revenue'),
'planned_cost': fields.float('Planned Costs'),
'probability': fields.float('Probability (%)'),
'partner_name': fields.char("Employee's Name", size=64),
'partner_name2': fields.char('Employee Email', size=64),
'partner_phone': fields.char('Phone', size=32),
'partner_mobile': fields.char('Mobile', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('type', '=', 'fundraising')]"),
'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \
domain="[('section_id','=',section_id)]"),
'duration': fields.float('Duration'),
'ref': fields.reference('Reference', selection=crm._links_get, size=128),
'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
domain="[('section_id','=',section_id)]"),
'duration': fields.float('Duration'),
'ref': fields.reference('Reference', selection=crm._links_get, size=128),
'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
'canal_id': fields.many2one('res.partner.canal', 'Channel', \
help="The channels represent the different communication \
modes available with the customer."),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
modes available with the customer."),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
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,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'section_id': crm.crm_case. _get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'probability': lambda *a:0.0,
'planned_cost': lambda *a:0.0,
'planned_revenue': lambda *a:0.0,
'active': lambda *a: 1,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'section_id': crm.crm_case. _get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'probability': lambda *a:0.0,
'planned_cost': lambda *a:0.0,
'planned_revenue': lambda *a:0.0,
}
crm_fundraising()
class crm_stage_fundraising(osv.osv):
def _get_type_value(self, cr, user, context):
list = super(crm_stage_fundraising, self)._get_type_value(cr, user, context)
list.append(('fundraising','Fundraising'))
return list
_inherit = "crm.case.stage"
_columns = {
'type': fields.selection(_get_type_value, 'Type'),
}
crm_stage_fundraising()
+13 -14
View File
@@ -166,11 +166,11 @@
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="history" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.fundraising', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="History">
<group col="4" colspan="4">
@@ -185,11 +185,10 @@
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.fundraising', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button colspan="4" string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
@@ -205,10 +204,10 @@
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.fundraising'}"
icon="terp-mail-message-new" type="action" />
<button string="Send New Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'crm.fundraising', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
<page string="Extra Info" groups="base.group_extended">
<group col="2" colspan="2">
+40 -38
View File
@@ -22,9 +22,11 @@
from crm import crm
from osv import fields, osv
import time
from crm import wizard
import binascii
import tools
wizard.email_compose_message.email_model.append('crm.helpdesk')
CRM_HELPDESK_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
crm.AVAILABLE_STATES[3][0], # Done
@@ -39,59 +41,59 @@ class crm_helpdesk(crm.crm_case, osv.osv):
_order = "id desc"
_inherit = ['mailgate.thread']
_columns = {
'id': fields.integer('ID', readonly=True),
'name': fields.char('Name', size=128, required=True),
'active': fields.boolean('Active', required=False),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'description': fields.text('Description'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'user_id': fields.many2one('res.users', 'Responsible'),
'id': fields.integer('ID', readonly=True),
'name': fields.char('Name', size=128, required=True),
'active': fields.boolean('Active', required=False),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'description': fields.text('Description'),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'user_id': fields.many2one('res.users', 'Responsible'),
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
select=True, help='Sales team to which Case belongs to.\
Define Responsible user and Email account for mail gateway.'),
'company_id': fields.many2one('res.company', 'Company'),
'date_closed': fields.datetime('Closed', readonly=True),
'partner_id': fields.many2one('res.partner', 'Partner'),
Define Responsible user and Email account for mail gateway.'),
'company_id': fields.many2one('res.company', 'Company'),
'date_closed': fields.datetime('Closed', readonly=True),
'partner_id': fields.many2one('res.partner', 'Partner'),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
domain="[('partner_id','=',partner_id)]"),
'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date': fields.datetime('Date'),
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128),
domain="[('partner_id','=',partner_id)]"),
'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date': fields.datetime('Date'),
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128),
'canal_id': fields.many2one('res.partner.canal', 'Channel', \
help="The channels represent the different communication \
modes available with the customer."),
'planned_revenue': fields.float('Planned Revenue'),
'planned_cost': fields.float('Planned Costs'),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'probability': fields.float('Probability (%)'),
modes available with the customer."),
'planned_revenue': fields.float('Planned Revenue'),
'planned_cost': fields.float('Planned Costs'),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'probability': fields.float('Probability (%)'),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.helpdesk')]"),
'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
('object_id.model', '=', 'crm.helpdesk')]"),
'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
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)]),
'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
_defaults = {
'active': lambda *a: 1,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'active': lambda *a: 1,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'section_id': crm.crm_case. _get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'section_id': crm.crm_case. _get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
}
def message_new(self, cr, uid, msg, context=None):
+13 -14
View File
@@ -100,11 +100,11 @@
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="history" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.helpdesk', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="History">
<group col="4" colspan="4">
@@ -119,11 +119,10 @@
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.helpdesk', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button colspan="4" string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
@@ -139,10 +138,10 @@
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'crm.helpdesk'}"
icon="terp-mail-message-new" type="action" />
<button string="Send New Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'crm.helpdesk', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
<page string="Extra Info" groups="base.group_extended">
<group colspan="2" col="2">
@@ -56,7 +56,7 @@ class crm_helpdesk_report(osv.osv):
'date_deadline': fields.date('Deadline', select=True),
'priority': fields.selection([('5', 'Lowest'), ('4', 'Low'), \
('3', 'Normal'), ('2', 'High'), ('1', 'Highest')], 'Priority'),
'canal_id': fields.many2one('res.partner.canal', 'Channel'),
'canal_id': fields.many2one('res.partner.canal', 'Channel'),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.helpdesk')]"),
@@ -97,7 +97,7 @@ class crm_helpdesk_report(osv.osv):
c.planned_cost,
count(*) as nbr,
extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close,
(SELECT count(id) FROM mailgate_message WHERE model='crm.helpdesk' AND res_id=c.id AND history=True) AS email,
(SELECT count(id) FROM email_message WHERE model='crm.helpdesk' AND res_id=c.id AND history=True) AS email,
abs(avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24)) as delay_expected
from
crm_helpdesk c
@@ -30,10 +30,10 @@ from tools.translate import _
class crm_lead_forward_to_partner(osv.osv_memory):
"""Forwards lead history"""
_name = 'crm.lead.forward.to.partner'
_inherit = "crm.send.mail"
_inherit = "email.compose.message"
_columns = {
'name': fields.selection([('user', 'User'), ('partner', 'Partner'), \
'send_to': fields.selection([('user', 'User'), ('partner', 'Partner'), \
('email', 'Email Address')], 'Send to', required=True),
'user_id': fields.many2one('res.users', "User"),
'partner_id' : fields.many2one('res.partner', 'Partner'),
@@ -42,7 +42,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
}
_defaults = {
'name' : 'email',
'send_to' : 'email',
'history': 'latest',
'email_from': lambda self, cr, uid, *a: self.pool.get('res.users')._get_email_from(cr, uid, uid)[uid]
}
@@ -69,7 +69,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
@param hist_id: Id of latest history
@param context: A standard dictionary for contextual values
"""
log_pool = self.pool.get('mailgate.message')
log_pool = self.pool.get('email.message')
hist = log_pool.browse(cr, uid, hist_id, context=context)
header = '-------- Original Message --------'
sender = 'From: %s' %(hist.email_from or '')
@@ -87,7 +87,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
@param uid: the current user’s ID for security checks,
@param ids: List of Mail’s IDs
@param user: Changed User id
@param partner: Changed Partner id
@param partner: Changed Partner id
"""
if not user:
return {'value': {'email_to': False}}
@@ -110,7 +110,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
res_id = context.get('active_id')
msg_val = self._get_case_history(cr, uid, history_type, res_id, context=context)
if msg_val:
res = {'value': {'body' : '\n\n' + msg_val}}
res = {'value': {'description' : '\n\n' + msg_val}}
return res
def _get_case_history(self, cr, uid, history_type, res_id, context=None):
@@ -146,7 +146,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
@param uid: the current user’s ID for security checks,
@param ids: List of Mail’s IDs
@param user: Changed User id
@param partner: Changed Partner id
@param partner: Changed Partner id
"""
if not partner_id:
return {'value' : {'email_to' : False, 'address_id': False}}
@@ -161,7 +161,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
email = user_id and user_id.user_email or ''
data.update({'email_cc' : email})
return {
'value' : data,
'value' : data,
'domain' : {'address_id' : partner_id and "[('partner_id', '=', partner_id)]" or "[]"}
}
@@ -171,6 +171,20 @@ class crm_lead_forward_to_partner(osv.osv_memory):
email = self.pool.get('res.partner.address').browse(cr, uid, address_id).email
return {'value': {'email_to' : email}}
def save_to_drafts(self, cr, uid, ids, context=None):
if context is None:
context = {}
super(crm_lead_forward_to_partner, self).save_to_drafts(cr, uid, ids, context=context)
self.action_forward(cr, uid, ids, context)
return {'type': 'ir.actions.act_window_close'}
def send_mail(self, cr, uid, ids, context=None):
if context is None:
context = {}
super(crm_lead_forward_to_partner, self).send_mail(cr, uid, ids, context=context)
self.action_forward(cr, uid, ids, context)
return {'type': 'ir.actions.act_window_close'}
def action_forward(self, cr, uid, ids, context=None):
"""
Forward the lead to a partner
@@ -181,15 +195,13 @@ class crm_lead_forward_to_partner(osv.osv_memory):
case_pool = self.pool.get(context.get('active_model'))
res_id = context and context.get('active_id', False) or False
case = case_pool.browse(cr, uid, res_id, context=context)
context.update({'mail': 'forward'})
super(crm_lead_forward_to_partner, self).action_send(cr, uid, ids, context=context)
to_write = {'date_assign': time.strftime('%Y-%m-%d')}
if (this.name == 'partner' and this.partner_id):
if (this.send_to == 'partner' and this.partner_id):
to_write['partner_assigned_id'] = this.partner_id.id
if this.name == 'user':
if this.send_to == 'user':
to_write.update({'user_id' : this.user_id.id})
email_re = r'([^ ,<@]+@[^> ,]+)'
email_cc = re.findall(email_re, case.email_cc or '')
@@ -203,7 +215,6 @@ class crm_lead_forward_to_partner(osv.osv_memory):
new_cc.append(to)
to_write.update({'email_cc' : ', '.join(new_cc) })
case_pool.write(cr, uid, case.id, to_write, context=context)
return {'type': 'ir.actions.act_window_close'}
def get_lead_details(self, cr, uid, lead_id, context=None):
@@ -237,22 +248,22 @@ class crm_lead_forward_to_partner(osv.osv_memory):
elif lead.type == 'opportunity':
pa = lead.partner_address_id
body = [
"Partner: %s" % (lead.partner_id and lead.partner_id.name_get()[0][1]),
"Contact: %s" % (pa.name or ''),
"Title: %s" % (pa.title or ''),
"Function: %s" % (pa.function or ''),
"Street: %s" % (pa.street or ''),
"Street2: %s" % (pa.street2 or ''),
"Zip: %s" % (pa.zip or ''),
"City: %s" % (pa.city or ''),
"Country: %s" % (pa.country_id and pa.country_id.name_get()[0][1] or ''),
"State: %s" % (pa.state_id and pa.state_id.name_get()[0][1] or ''),
"Email: %s" % (pa.email or ''),
"Phone: %s" % (pa.phone or ''),
"Fax: %s" % (pa.fax or ''),
"Mobile: %s" % (pa.mobile or ''),
"Lead Category: %s" % (lead.categ_id and lead.categ_id.name or ''),
"Details: %s" % (lead.description or ''),
"Partner: %s" % (lead.partner_id and lead.partner_id.name_get()[0][1]),
"Contact: %s" % (pa.name or ''),
"Title: %s" % (pa.title or ''),
"Function: %s" % (pa.function or ''),
"Street: %s" % (pa.street or ''),
"Street2: %s" % (pa.street2 or ''),
"Zip: %s" % (pa.zip or ''),
"City: %s" % (pa.city or ''),
"Country: %s" % (pa.country_id and pa.country_id.name_get()[0][1] or ''),
"State: %s" % (pa.state_id and pa.state_id.name_get()[0][1] or ''),
"Email: %s" % (pa.email or ''),
"Phone: %s" % (pa.phone or ''),
"Fax: %s" % (pa.fax or ''),
"Mobile: %s" % (pa.mobile or ''),
"Lead Category: %s" % (lead.categ_id and lead.categ_id.name or ''),
"Details: %s" % (lead.description or ''),
]
return "\n".join(body + ['---'])
@@ -274,8 +285,8 @@ class crm_lead_forward_to_partner(osv.osv_memory):
body = self._get_case_history(cr, uid, defaults.get('history', 'latest'), lead.id, context=context)
defaults.update({
'subject' : '%s: %s' % (_('Fwd'), lead.name),
'body' : body,
'name' : '%s: %s' % (_('Fwd'), lead.name),
'description' : body,
'email_cc' : ''
})
return defaults
@@ -1,49 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="crm_lead_forward_to_partner_form">
<field name="name">crm_lead_forward_to_partner</field>
<field name="model">crm.lead.forward.to.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="crm.crm_send_new_mail_view"/>
<field name="arch" type="xml">
<field name="email_from" position="before">
<form string="Send Mail">
<separator string="Forward to Partner" colspan="4" />
<group col="4" colspan="6">
<field name="history" colspan="2" on_change="on_change_history(history, context)"/>
<field name="name" colspan="2" />
<group col="2" colspan="2" attrs="{ 'invisible' : [('name','!=','user')]}">
<field name="send_to" colspan="2" />
<group col="2" colspan="2" attrs="{ 'invisible' : [('send_to','!=','user')]}">
<field name="user_id"
attrs="{ 'required' : [('name','=','user')]}"
attrs="{ 'required' : [('send_to','=','user')]}"
on_change="on_change_email(user_id)" />
</group>
<group col="4" colspan="4" attrs="{'invisible' : [('name','!=','partner')]}">
<field name="partner_id" attrs="{'required' : [('name','=','partner')]}" on_change="on_change_partner(partner_id)" colspan="2" />
<group col="4" colspan="4" attrs="{'invisible' : [('send_to','!=','partner')]}">
<field name="partner_id" attrs="{'required' : [('send_to','=','partner')]}" on_change="on_change_partner(partner_id)" colspan="2" />
<field name="address_id" string="Contact" on_change="on_change_address(address_id)" colspan="2" />
</group>
</group>
<separator string="" colspan="4" />
</field>
<group col="6" colspan="4">
<field name="smtp_server_id" widget="selection" colspan="4"/>
<field name="email_from" colspan="4" required="1"/>
<field name="email_to" colspan="4" required="1"/>
<field name="email_cc" colspan="4"/>
<field name="email_bcc" colspan="4"/>
<field name="reply_to" colspan="4"/>
<field name="name" colspan="4" widget="char" size="512"/>
</group>
<separator string="" colspan="4"/>
<notebook colspan="4">
<page string="Body">
<field name="description" 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"/>
<field name="attachment_ids" colspan="4" nolabel="1"/>
</page>
</notebook>
<group col="4" colspan="4">
<label string="" colspan="1"/>
<button icon="gtk-close" special="cancel" string="Close"/>
<button icon="gtk-apply" name="save_to_drafts" string="Save in Drafts" type="object"/>
<button icon="gtk-ok" name="send_mail" string="Send now" type="object"/>
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="crm_lead_forward_to_partner_form1">
<field name="name">crm_lead_forward_to_partner1</field>
<field name="model">crm.lead.forward.to.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="crm.crm_send_new_mail_view"/>
<field name="arch" type="xml">
<button name="action_send" position="replace">
<button name="action_forward" string="Forward" icon="gtk-go-forward" type="object" />
</button>
</field>
</record>
<record model="ir.actions.act_window" id="crm_lead_forward_to_partner_act">
<field name="name">Forward to Partner</field>
<field name="res_model">crm.lead.forward.to.partner</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="crm_lead_forward_to_partner_form1"/>
<field name="view_id" ref="crm_lead_forward_to_partner_form"/>
<field name="target">new</field>
</record>
@@ -19,8 +19,11 @@
#
##############################################################################
import mail_gateway
import email_smtp_server
import email_message
import email_gateway
import res_partner
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -20,25 +20,28 @@
##############################################################################
{
'name': 'Email Gateway System',
'name': 'Email System',
'version': '1.0',
'category': 'Generic Modules/Mail Service',
'description': """The generic email gateway system allows to send and receive emails
* History for Emails
* Easy Integration with any Module""",
'description': """The generic email system allows to send and receive emails
* SMTP Server Configuration
* Provide API for Sending Messages
* Store all emails releated messages """,
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base'],
'depends': ['base', 'base_tools'],
'init_xml': [],
'update_xml': [
"mail_gateway_view.xml",
"wizard/email_compose_message_view.xml",
"email_view.xml",
"email_gateway_view.xml",
"res_partner_view.xml",
'security/ir.model.access.csv'
'security/ir.model.access.csv',
'email_data.xml',
],
'demo_xml': [],
'installable': True,
'active': False,
'certificate': '001056784984222247309',
'certificate': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,14 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="email_smtp_server_administrator0" model="email.smtp_server">
<field name="name">Administrator</field>
<field eval="1" name="default"/>
<field eval="1" name="smtptls"/>
<field name="email_id">example@test.com</field>
<field name="state">draft</field>
<field eval="25" name="smtpport"/>
<field name="smtpserver">localhost</field>
<field eval="0" name="smtpssl"/>
</record>
<record forcecreate="True" id="ir_cron_mail_scheduler_action" model="ir.cron">
<field name="name">Email Template scheduler</field>
<field name="name">Email scheduler</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'email_template.mailbox'" name="model"/>
<field eval="'email.message'" name="model"/>
<field eval="'run_mail_scheduler'" name="function"/>
<field eval="'()'" name="args"/>
</record>
@@ -42,7 +42,7 @@ class mailgate_thread(osv.osv):
_description = 'Mailgateway Thread'
_columns = {
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', readonly=True),
'message_ids': fields.one2many('email.message', 'res_id', 'Messages', readonly=True),
}
def copy(self, cr, uid, id, default=None, context=None):
@@ -97,8 +97,8 @@ class mailgate_thread(osv.osv):
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param cases: a browse record list
@param keyword: Subject of the history item
@param history: Value True/False, If True it makes entry in case History otherwise in Case Log
@param keyword: Case action keyword e.g.: If case is closed "Close" keyword is used
@param history: Value True/False, If True it makes entry as a Emails Messages otherwise Log Messages
@param email: Email-To / Recipient address
@param email_from: Email From / Sender address if any
@param email_cc: Comma-Separated list of Carbon Copy Emails To addresse if any
@@ -123,15 +123,11 @@ class mailgate_thread(osv.osv):
cases = self.browse(cr, uid, cases, context=context)
att_obj = self.pool.get('ir.attachment')
obj = self.pool.get('mailgate.message')
obj = self.pool.get('email.message')
for case in cases:
attachments = []
for att in attach:
att_ids = att_obj.search(cr, uid, [('name','=',att[0]), ('res_id', '=', case.id)])
if att_ids:
attachments.append(att_ids[0])
else:
attachments.append(att_obj.create(cr, uid, {'res_model':case._name,'res_id':case.id, 'name': att[0], 'datas': base64.encodestring(att[1])}))
partner_id = hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False
@@ -145,7 +141,7 @@ class mailgate_thread(osv.osv):
'res_id': case.id,
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'message_id': message_id,
'description': details,
'description': details or (hasattr(case, 'description') and case.description or False),
'attachment_ids': [(6, 0, attachments)]
}
@@ -161,7 +157,7 @@ class mailgate_thread(osv.osv):
'model' : case._name,
'res_id': case.id,
'date': email_date or time.strftime('%Y-%m-%d %H:%M:%S'),
'description': details or (hasattr(case, 'description') and case.description or False),
'description': details,
'email_to': email,
'email_from': email_from or \
(hasattr(case, 'user_id') and case.user_id and case.user_id.address_id and \
@@ -171,9 +167,9 @@ class mailgate_thread(osv.osv):
'partner_id': partner_id,
'references': references,
'message_id': message_id,
'folder': 'inbox',
'attachment_ids': [(6, 0, attachments)]
}
obj.create(cr, uid, data, context=context)
return True
mailgate_thread()
@@ -184,116 +180,6 @@ def format_date_tz(date, tz=None):
format = tools.DEFAULT_SERVER_DATETIME_FORMAT
return tools.server_to_local_timestamp(date, format, format, tz)
class mailgate_message(osv.osv):
'''
Mailgateway Message
'''
def open_document(self, cr, uid, ids, context=None):
""" To Open Document
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID of messages
@param context: A standard dictionary
"""
action_data = False
if ids:
message_id = ids[0]
mailgate_data = self.browse(cr, uid, message_id, context=context)
model = mailgate_data.model
res_id = mailgate_data.res_id
action_pool = self.pool.get('ir.actions.act_window')
action_ids = action_pool.search(cr, uid, [('res_model', '=', model)])
if action_ids:
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
action_data.update({
'domain' : "[('id','=',%d)]"%(res_id),
'nodestroy': True,
'context': {}
})
return action_data
def open_attachment(self, cr, uid, ids, context=None):
""" To Open attachments
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID of messages
@param context: A standard dictionary
"""
action_data = False
action_pool = self.pool.get('ir.actions.act_window')
message_pool = self.browse(cr ,uid, ids, context=context)[0]
att_ids = [x.id for x in message_pool.attachment_ids]
action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')])
if action_ids:
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
action_data.update({
'domain': [('id','in',att_ids)],
'nodestroy': True
})
return action_data
def truncate_data(self, cr, uid, data, context=None):
data_list = data and data.split('\n') or []
if len(data_list) > 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'
msg_txt += 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'
@@ -321,7 +207,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
@@ -341,6 +227,7 @@ class mailgate_tool(osv.osv_memory):
'references': msg.get('references') or msg.get('in-reply-to'),
'res_id': res_id,
'user_id': uid,
'folder': 'inbox',
'attachment_ids': [(6, 0, attach)]
}
msg_pool.create(cr, uid, msg_data, context=context)
+90
View File
@@ -0,0 +1,90 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_mailgate_thread_form">
<field name="name">mailgate.thread.form</field>
<field name="model">mailgate.thread</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Mailgateway Thread">
<separator string="History" colspan="4"/>
<field name="message_ids" nolabel="1" colspan="4" mode="tree,form">
<tree string="Mailgateway History">
<field name="display_text"/>
</tree>
<form string="Mailgate History">
<field name="name" widget="char"/>
<field name="date"/>
<field name="user_id"/>
<field name="message_id"/>
<field name="history"/>
<notebook colspan="4">
<page string="Email Details">
<group col="4" colspan="4">
<separator string="Email Details" colspan="4"/>
<field name="email_from"/>
<field name="email_to"/>
<field name="email_cc"/>
<field name="email_bcc"/>
</group>
</page>
<page string="Attachments">
<separator string="Attachments" colspan="4"/>
<field name="attachment_ids" nolabel="1" colspan="4" />
</page>
</notebook>
</form>
</field>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_mailgate_thread_tree">
<field name="name">mailgate.thread.tree</field>
<field name="model">mailgate.thread</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Mailgateway Thread">
<field name="message_ids" />
</tree>
</field>
</record>
<!-- Emails action-->
<record model="ir.actions.act_window" id="action_view_mailgate_thread">
<field name="name">Mailgateway Threads</field>
<field name="res_model">mailgate.thread</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_mailgate_thread_tree"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_mailgate_thread_tree"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_mailgate_thread_form"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_mailgate_thread_tree"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_mailgate_thread_form"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
</data>
</openerp>
+402
View File
@@ -0,0 +1,402 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 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 <http://www.gnu.org/licenses/>
#
##############################################################################
from osv import osv
from osv import fields
from tools.translate import _
import tools
import netsvc
import base64
import time
#import binascii
#import email
#from email.header import decode_header
#from email.utils import parsedate
#import base64
#import re
#import logging
#import xmlrpclib
#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
#import tools
#import logging
#email_content_types = [
# 'multipart/mixed',
# 'multipart/alternative',
# 'multipart/related',
# 'text/plain',
# 'text/html'
#]
LOGGER = netsvc.Logger()
def format_date_tz(date, tz=None):
if not date:
return 'n/a'
format = tools.DEFAULT_SERVER_DATETIME_FORMAT
return tools.server_to_local_timestamp(date, format, format, tz)
class email_message_template(osv.osv_memory):
_name = 'email.message.template'
_columns = {
'name':fields.text('Subject', translate=True),
'model': fields.char('Object Name', size=128, select=1),
'res_id': fields.integer('Resource ID', select=1),
'date': fields.datetime('Date'),
'user_id': fields.many2one('res.users', 'User Responsible'),
'email_from': fields.char('From', size=128, help="Email From"),
'email_to': fields.char('To', help="Email Recipients", size=256),
'email_cc': fields.char('Cc', help="Carbon Copy Email Recipients", size=256),
'email_bcc': fields.char('Bcc', help='Blind Carbon Copy Email Recipients', size=256),
'message_id': fields.char('Message Id', size=1024, help="Message Id on Email.", select=1),
'references': fields.text('References', help="References emails."),
'reply_to':fields.char('Reply-To', size=250),
'sub_type': fields.char('Sub Type', size=32),
'headers': fields.char('x_headers',size=256),
'priority':fields.integer('Priority'),
'description': fields.text('Description', translate=True),
'smtp_server_id':fields.many2one('email.smtp_server', 'SMTP Server'),
}
_sql_constraints = []
email_message_template()
class email_message(osv.osv):
'''
Email Message
'''
_inherit = 'email.message.template'
_name = 'email.message'
_description = 'Email Message'
_order = 'date desc'
def open_document(self, cr, uid, ids, context=None):
""" To Open Document
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID of messages
@param context: A standard dictionary
"""
action_data = False
if ids:
message_id = ids[0]
mailgate_data = self.browse(cr, uid, message_id, context=context)
model = mailgate_data.model
res_id = mailgate_data.res_id
action_pool = self.pool.get('ir.actions.act_window')
action_ids = action_pool.search(cr, uid, [('res_model', '=', model)])
if action_ids:
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
action_data.update({
'domain' : "[('id','=',%d)]"%(res_id),
'nodestroy': True,
'context': {}
})
return action_data
def open_attachment(self, cr, uid, ids, context=None):
""" To Open attachments
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID of messages
@param context: A standard dictionary
"""
action_data = False
action_pool = self.pool.get('ir.actions.act_window')
message_pool = self.browse(cr ,uid, ids, context=context)[0]
att_ids = [x.id for x in message_pool.attachment_ids]
action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')])
if action_ids:
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
action_data.update({
'domain': [('id','in',att_ids)],
'nodestroy': True
})
return action_data
def truncate_data(self, cr, uid, data, context=None):
data_list = data and data.split('\n') or []
if len(data_list) > 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'
msg_txt += message.name
result[message.id] = msg_txt
return result
_columns = {
'message': fields.text('Description'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'),
'display_text': fields.function(_get_display_text, method=True, type='text', size="512", string='Display Text'),
'debug':fields.boolean('Debug', readonly=True),
'history': fields.boolean('History', readonly=True),
'folder':fields.selection([
('drafts', 'Drafts'),
('inbox', 'Inbox'),
('outbox', 'Outbox'),
('trash', 'Trash'),
('sent', 'Sent Items'),
], 'Folder'),
'state':fields.selection([
('draft', 'Draft'),
('sending', 'Sending'),
('waiting', 'Waiting'),
('sent', 'Sent'),
('exception', 'Exception'),
], 'State', readonly=True),
}
_defaults = {
'state': lambda * a: 'draft',
'folder': lambda * a: 'outbox',
}
def unlink(self, cr, uid, ids, context=None):
"""
It just changes the folder of the item to "Trash", if it is no in Trash folder yet,
or completely deletes it if it is already in Trash.
"""
to_update = []
to_remove = []
for mail in self.browse(cr, uid, ids, context=context):
if mail.folder == 'trash':
to_remove.append(mail.id)
else:
to_update.append(mail.id)
# Changes the folder to trash
self.write(cr, uid, to_update, {'folder': 'trash'}, context=context)
return super(email_message, self).unlink(cr, uid, to_remove, context=context)
def init(self, cr):
cr.execute("""SELECT indexname
FROM pg_indexes
WHERE indexname = 'email_message_res_id_model_idx'""")
if not cr.fetchone():
cr.execute("""CREATE INDEX email_message_res_id_model_idx
ON email_message (model, res_id)""")
def process_queue(self, cr, uid, ids, arg):
self.process_email_queue(cr, uid, ids=ids)
return True
def run_mail_scheduler(self, cursor, user, context=None):
"""
This method is called by OpenERP Scheduler
to periodically send emails
"""
try:
self.process_email_queue(cursor, user, context=context)
except Exception, e:
LOGGER.notifyChannel(
"Email Template",
netsvc.LOG_ERROR,
_("Error sending mail: %s") % e)
def email_send(self, cr, uid, email_from, email_to, subject, body, model=False, email_cc=None, email_bcc=None, reply_to=False, attach=None,
message_id=False, references=False, openobject_id=False, debug=False, subtype='plain', x_headers={}, priority='3', smtp_server_id=False, context=None):
attachment_obj = self.pool.get('ir.attachment')
if email_to and type(email_to) != list:
email_to = [email_to]
if email_cc and type(email_cc) != list:
email_cc = [email_cc]
if email_bcc and type(email_bcc) != list:
email_bcc = [email_bcc]
msg_vals = {
'name': subject,
'model': model or '',
'date': time.strftime('%Y-%m-%d'),
'user_id': uid,
'description': body,
'email_from': email_from,
'email_to': email_to and ','.join(email_to) or '',
'email_cc': email_cc and ','.join(email_cc) or '',
'email_bcc': email_bcc and ','.join(email_bcc) or '',
'reply_to': reply_to,
'res_id': openobject_id,
'message_id': message_id,
'references': references or '',
'sub_type': subtype or '',
'headers': x_headers or False,
'priority': priority,
'debug': debug,
'folder': 'outbox',
'history': True,
'smtp_server_id': smtp_server_id,
'state': 'waiting',
}
email_msg_id = self.create(cr, uid, msg_vals, context)
if attach:
attachment_ids = []
for attachment in attach:
attachment_data = {
'name': (subject or '') + _(' (Email Attachment)'),
'datas': attachment[1],
'datas_fname': attachment[0],
'description': subject or _('No Description'),
'res_model':'email.message',
'res_id': email_msg_id,
}
attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context))
self.write(cr, uid, email_msg_id,
{ 'attachment_ids': [[6, 0, attachment_ids]] }, context)
return email_msg_id
def process_retry(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state':'waiting'}, context)
def process_email_queue(self, cr, uid, ids=None, context=None):
if ids is None:
ids = []
if context is None:
context = {}
attachment_obj = self.pool.get('ir.attachment')
smtp_server_obj = self.pool.get('email.smtp_server')
if not ids:
filters = [('folder', '=', 'outbox'), ('state', '=', 'waiting')]
if 'filters' in context:
filters.extend(context['filters'])
ids = self.search(cr, uid, filters, context=context)
self.write(cr, uid, ids, {'state':'sending', 'folder':'sent'}, context)
for message in self.browse(cr, uid, ids, context):
try:
attachments = []
for attach in message.attachment_ids:
attachments.append((attach.datas_fname ,base64.b64decode(attach.datas)))
smtp_server = message.smtp_server_id
if not smtp_server:
smtp_ids = smtp_server_obj.search(cr, uid, [('default','=',True)])
if smtp_ids:
smtp_server = smtp_server_obj.browse(cr, uid, smtp_ids, context)[0]
res = tools.email_send(message.email_from,
message.email_to and message.email_to.split(',') or [],
message.name, message.description,
email_cc=message.email_cc and message.email_cc.split(',') or [],
email_bcc=message.email_bcc and message.email_bcc.split(',') or [],
reply_to=message.reply_to,
attach=attachments, message_id=message.message_id, references = message.references,
openobject_id=message.res_id,
subtype=message.sub_type,
x_headers=message.headers and eval(message.headers) or {},
priority=message.priority, debug=message.debug,
smtp_server=smtp_server and smtp_server.smtpserver or None,
smtp_port=smtp_server and smtp_server.smtpport or None,
ssl=smtp_server and smtp_server.smtpssl or False,
smtp_user=smtp_server and smtp_server.smtpuname or None,
smtp_password=smtp_server and smtp_server.smtppass or None)
if res:
self.write(cr, uid, [message.id], {'state':'sent', 'message_id': res}, context)
else:
self.write(cr, uid, [message.id], {'state':'exception'}, context)
except Exception, error:
logger = netsvc.Logger()
logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (message.id, error))
self.write(cr, uid, [message.id], {'state':'exception'}, context)
return ids
# OLD Code.
# def send_all_mail(self, cr, uid, ids=None, context=None):
# if ids is None:
# ids = []
# if context is None:
# context = {}
# filters = [('folder', '=', 'outbox'), ('state', '!=', 'sending')]
# if 'filters' in context.keys():
# for each_filter in context['filters']:
# filters.append(each_filter)
# ids = self.search(cr, uid, filters, context=context)
# self.write(cr, uid, ids, {'state':'sending'}, context)
# self.send_this_mail(cr, uid, ids, context)
# return True
#
# def send_this_mail(self, cr, uid, ids=None, context=None):
# #previous method to send email (link with email account can be found at the revision 4172 and below
# result = True
# attachment_pool = self.pool.get('ir.attachment')
# for id in (ids or []):
# try:
# account_obj = self.pool.get('email.smtp_server')
# values = self.read(cr, uid, id, [], context)
# payload = {}
# if values['attachments_ids']:
# for attid in values['attachments_ids']:
# attachment = attachment_pool.browse(cr, uid, attid, context)#,['datas_fname','datas'])
# payload[attachment.datas_fname] = attachment.datas
# result = account_obj.send_email(cr, uid,
# [values['account_id'][0]],
# {'To':values.get('email_to') or u'',
# 'CC':values.get('email_cc') or u'',
# 'BCC':values.get('email_bcc') or u'',
# 'Reply-To':values.get('reply_to') or u''},
# values['subject'] or u'',
# {'text':values.get('body_text') or u'', 'html':values.get('body_html') or u''},
# payload=payload,
# message_id=values['message_id'],
# context=context)
# if result == True:
# account = account_obj.browse(cr, uid, values['account_id'][0], context=context)
# if account.auto_delete:
# self.write(cr, uid, id, {'folder': 'trash'}, context=context)
# self.unlink(cr, uid, [id], context=context)
# # Remove attachments for this mail
# attachment_pool.unlink(cr, uid, values['attachments_ids'], context=context)
# else:
# self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
# else:
# error = result['error_msg']
#
# except Exception, error:
# logger = netsvc.Logger()
# logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error))
# self.write(cr, uid, id, {'state':'na'}, context)
# return result
email_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+376
View File
@@ -0,0 +1,376 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 Tiny SPRL (<http://tiny.be>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 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 <http://www.gnu.org/licenses/>
#
##############################################################################
from osv import osv
from osv import fields
from tools.translate import _
import tools
#import time
#import binascii
#import email
#from email.header import decode_header
#from email.utils import parsedate
#import base64
#import re
#import logging
#import xmlrpclib
#_logger = logging.getLogger('mailgate')
#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
#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" <d@b.com> , 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_smtp_server(osv.osv):
"""
SMTP Server
"""
_name = "email.smtp_server"
_columns = {
'name': fields.char('Name',
size=64, required=True,
select=True,
help="The Name 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 <john@doe.com>",
),
'email_id': fields.char('From Email',
size=120, required=True,
help="eg: 'john@doe.com' or 'John Doe <john@doe.com>'"),
'smtpserver': fields.char('Server',
size=120, required=True,
help="Enter name of outgoing server, eg: smtp.yourdomain.com"),
'smtpport': fields.integer('SMTP Port',
size=64, required=True,
help="Enter port number, eg: 25 or 587"),
'smtpuname': fields.char('User Name',
size=120, required=False,
help="Specify the username if your SMTP server requires authentication, "
"otherwise leave it empty."),
'smtppass': fields.char('Password',
size=120,
required=False),
'smtptls':fields.boolean('TLS'),
'smtpssl':fields.boolean('SSL/TLS'),
'default': fields.boolean('Default', help="Only one account can be default at a time"),
}
_defaults = {
'name':lambda self, cursor, user, context:self.pool.get( 'res.users'
).read(cursor, user, user, ['name'], context)['name'],
'smtpport': tools.config.get('smtp_port',25),
'smtpserver': tools.config.get('smtp_server','localhost'),
'smtpssl': tools.config.get('smtp_ssl',False),
'smtptls': True,
}
_sql_constraints = [
(
'email_uniq',
'unique (email_id)',
'Another setting already exists with this email ID !')
]
def _constraint_unique(self, cr, uid, ids, context=None):
default_ids = self.search(cr, uid, [('default','=',True)])
if len(default_ids) > 1:
return False
return True
_constraints = [
(_constraint_unique,
'Error: You must be define one default smtp server account !.',
[])
]
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 test_smtp_connection(self, cr, uid, ids, context=None):
"""
Test SMTP connection works
"""
try:
for smtp_server in self.browse(cr, uid, ids, context=context):
smtp = tools.connect_smtp_server(smtp_server.smtpserver, smtp_server.smtpport, user_name=smtp_server.smtpuname,
user_password=smtp_server.smtppass, ssl=smtp_server.smtpssl, tls=smtp_server.smtptls)
try:
smtp.quit()
except Exception:
# ignored, just a consequence of the previous exception
pass
except Exception, error:
raise osv.except_osv(
_("SMTP Connection: Test failed"),
_("Reason: %s") % error
)
raise osv.except_osv(_("SMTP Connection: Test Successfully!"), '')
# 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_email(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:
# # Need a multipart/mixed wrapper for attachments if content is alternative
# if payload:
# payload_part = MIMEMultipart(_subtype='mixed')
# text_part = MIMEMultipart(_subtype='mixed')
# payload_part.attach(text_part)
# else:
# # otherwise a single multipart/mixed will do the whole job
# payload_part = text_part = MIMEMultipart(_subtype='mixed')
#
# 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'<Empty Message>')
## 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'<p>&lt;Empty Message&gt;</p>').replace('\n', '<br/>').replace('\r', '<br/>')
# 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.message').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?= <enricmarti@company.com>,
# =?Windows-1252?Q?David_G=F3mez?= <david@company.com>
# Sometimes they include extra " character at the beginning/
# end of the contact name, like:
# "=?iso-8859-1?Q?Enric_Mart=ED?=" <enricmarti@company.com>
# 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_smtp_server()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+231
View File
@@ -0,0 +1,231 @@
<?xml version="1.0"?>
<openerp>
<data>
<menuitem name="Configuration" parent="base.menu_tools"
id="base.menu_lunch_survey_root" sequence="20"/>
<record model="ir.ui.view" id="view_email_message_form">
<field name="name">email.message.form</field>
<field name="model">email.message</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email message">
<group colspan="4" col="6">
<field name="name" widget="char" size="512"/>
<field name="date"/>
<field name="user_id" string="User"/>
<field name="partner_id" readonly="1" />
</group>
<notebook colspan="4">
<page string="Details">
<group col="2" colspan="2">
<separator string="Email Followers" colspan="4"/>
<field name="email_from"/>
<field name="email_to"/>
<field name="email_cc"/>
<field name="email_bcc" groups="base.group_extended"/>
<field name="reply_to"/>
</group>
<group col="2" colspan="2">
<separator string="Message Details" colspan="4"/>
<field name="model" readonly="1"/>
<group col="3" colspan="2">
<field name="res_id" readonly="1"/>
<button name="open_document" string="Open Document" type="object" icon="gtk-jump-to"/>
<field name="message_id"/>
<button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied"
context="{'mail':'reply', 'message_id':active_id}"/>
</group>
<field name="references" widget="char" size="4096"/>
</group>
<separator string="Description" colspan="4"/>
<field name="description" nolabel="1" colspan="4"/>
<group col="4" colspan="4" attrs="{'invisible':[('folder','not in',['drafts','outbox'])]}">
<separator string="" colspan="4"/>
<field name="state"/>
<button name="process_queue" string="Send" type="object" icon="gtk-execute" states='draft,waiting,sending'/>
<button name="process_retry" string="Re-Try" type="object" icon="gtk-execute" states='exception'/>
</group>
</page>
<page string="Attachments">
<separator string="Attachments" colspan="4"/>
<field name="attachment_ids" nolabel="1" colspan="4" readonly="1"/>
</page>
<page string="Advanced">
<group col="4" colspan="4">
<field name="smtp_server_id"/>
<field name="folder"/>
<field name="sub_type"/>
<field name="headers"/>
<field name="priority"/>
<field name="debug"/>
<field name="history"/>
</group>
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_email_message_tree">
<field name="name">email.message.tree</field>
<field name="model">email.message</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree colors="gray:folder in ('trash')" string="Emails">
<field name="date"/>
<field name="name"/>
<field name="email_from"/>
<field name="user_id" string="User"/>
<field name="message_id" string="Message" invisible="1"/>
<field name="partner_id" invisible="1"/>
<field name="folder"/>
<field name="state"/>
<button name="open_document" string="Open Document" type="object" icon="gtk-jump-to"/>
<button name="open_attachment" string="Open Attachments" type="object" icon="gtk-jump-to"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_email_message_search">
<field name="name">email.message.search</field>
<field name="model">email.message</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Email Search">
<filter icon="terp-document-new" string="Draft" domain="[('folder','=','drafts')]"/>
<filter icon="terp-personal" string="Inbox" domain="[('folder','=','inbox')]"/>
<filter icon="terp-check" string="Outbox" domain="[('folder','=','outbox')]"/>
<filter icon="terp-partner" string="Sent Items" domain="[('folder','=','sent')]"/>
<filter icon="terp-mail_delete" string="Trash" domain="[('folder','=','trash')]"/>
<separator orientation="vertical"/>
<filter icon="terp-call-start" string="Waiting" domain="[('state','=','waiting')]"/>
<filter icon="terp-gtk-stop" string="Exception" domain="[('state','=','exception')]"/>
<field name="email_from"/>
<field name="name"/>
<field name="date"/>
<field name="user_id" string="User"/>
<field name="partner_id" string="Partner Name"/>
<newline/>
<group expand="0" string="Group By..." groups="base.group_extended">
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="User" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/>
<separator orientation="vertical"/>
<filter string="Thread" icon="terp-mail-" domain="[]" context="{'group_by':'message_id'}"/>
<separator orientation="vertical"/>
<filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>
<record id="action_view_mail_message" model="ir.actions.act_window">
<field name="name">Messages</field>
<field name="res_model">email.message</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('history', '=', True)]</field>
<field name="search_view_id" ref="view_email_message_search"/>
</record>
<act_window domain="[('partner_id', '=', active_id), ('history', '=', True)]"
id="act_res_partner_emails" name="Emails"
res_model="email.message"
src_model="res.partner"
view_id="view_email_message_tree"/>
<act_window
id="act_res_partner_open_email" name="Attachments"
res_model="ir.attachment"
src_model="email.message"
domain="[('res_id', '=', res_id),('res_model','=',model)]"/>
<menuitem name="Email" id="menu_email_message_tools"
parent="base.menu_tools" />
<menuitem name="Messages"
id="menu_email_message"
parent="menu_email_message_tools"
action="action_view_mail_message" />
<record model="ir.ui.view" id="email_smtp_server_form">
<field name="name">email.smtp_server.form</field>
<field name="model">email.smtp_server</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Smtp Server Configuration">
<group colspan="4">
<field name="name"/>
<field name="default"/>
</group>
<notebook colspan="4">
<page string="Configuration">
<separator string="Server Information" colspan="4"/>
<group colspan="4" col="8">
<field name="smtpserver"/>
<field name="smtpport"/>
<field name="smtpssl"/>
<field name="smtptls"/>
</group>
<separator string="User Information" colspan="4"/>
<group col="6" colspan="4">
<field name="email_id"/>
<field name="smtpuname"/>
<field name="smtppass" password="True"/>
</group>
<separator string="" colspan="4"/>
<label string="" colspan="2"/>
<button name="test_smtp_connection" type="object" string="Test Connection" icon="gtk-network" colspan="2"/>
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.ui.view" id="email_smtp_server_tree">
<field name="name">email.smtp_server.tree</field>
<field name="model">email.smtp_server</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="SMTP Server">
<field name="name"/>
<field name="email_id"/>
<field name="smtpserver"/>
<field name="default"/>
<button name="test_smtp_connection" type="object" string="Test Connection" icon="gtk-network"/>
</tree>
</field>
</record>
<record id="view_email_smtp_server_search" model="ir.ui.view">
<field name="name">email_smtp.server.search</field>
<field name="model">email.smtp_server</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Smtp Server">
<field name="name"/>
<field name="email_id"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_smtp_server_tree_all">
<field name="name">Outgoing Servers</field>
<field name="res_model">email.smtp_server</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="email_smtp_server_tree" />
<field name="context">{'group_by': [], 'search_default_draft': 1, 'search_default_my': 1}</field>
<field name="search_view_id" ref="view_email_smtp_server_search"/>
</record>
<menuitem name="Email" id="menu_config_email" parent="base.menu_lunch_survey_root" sequence="20"/>
<menuitem name="Outgoing Servers" id="menu_email_smtp_server_all" parent="menu_config_email" action="action_email_smtp_server_tree_all"/>
</data>
</openerp>
@@ -19,17 +19,16 @@
#
##############################################################################
from osv import fields,osv
from osv import osv
from osv import fields
class res_partner(osv.osv):
""" Inherits partner and adds CRM information in the partner form """
_inherit = 'res.partner'
_columns = {
'emails': fields.one2many('mailgate.message', 'partner_id',\
'Emails', readonly=True, domain=[('history','=',True)]),
'emails': fields.one2many('email.message', 'partner_id', 'Emails', readonly=True, domain=[('history','=',True)]),
}
res_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Partners inherited form -->
<record id="base.view_crm_partner_info_History" model="ir.ui.view">
<field name="name">res.partner.crm.history.inherit1</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='History']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
</field>
</record>
<record id="view_emails_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.emails.info.inherit</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<page string="History" position="inside">
<field name="emails" colspan="4" nolabel="1"/>
</page>
</field>
</record>
</data>
</openerp>
@@ -0,0 +1,4 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_email_smtp_server","email.smtp_server","model_email_smtp_server",,1,0,0,0
"access_email_message","email.message","model_email_message",,1,0,0,0
"access_mailgate_thread","mailgate.thread","model_mailgate_thread",,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_email_smtp_server email.smtp_server model_email_smtp_server 1 0 0 0
3 access_email_message email.message model_email_message 1 0 0 0
4 access_mailgate_thread mailgate.thread model_mailgate_thread 1 0 0 0
+24
View File
@@ -0,0 +1,24 @@
# -*- 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 email_compose_message
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,235 @@
# -*- 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
class email_compose_message(osv.osv_memory):
_name = 'email.compose.message'
_inherit = 'email.message.template'
_description = 'This is the wizard for Compose E-mail'
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
result = super(email_compose_message, self).default_get(cr, uid, fields, context=context)
vals = {}
if context.get('email_model') and context.get('email_res_id'):
vals = self.get_value(cr, uid, context.get('email_model'), context.get('email_res_id'), context)
elif context.get('message_id', False):
vals = self.get_message_data(cr, uid, int(context.get('message_id', False)), context)
else:
result['model'] = context.get('email_model', False)
if not vals:
return result
if 'name' in fields:
result.update({'name' : vals.get('name','')})
if 'email_to' in fields:
result.update({'email_to' : vals.get('email_to','')})
if 'email_from' in fields:
result.update({'email_from' : vals.get('email_from','')})
if 'description' in fields:
result.update({'description' : vals.get('description','')})
if 'model' in fields:
result.update({'model' : vals.get('model','')})
if 'email_cc' in fields:
result.update({'email_cc' : vals.get('email_cc','')})
if 'email_bcc' in fields:
result.update({'email_bcc' : vals.get('email_bcc','')})
if 'res_id' in fields:
result.update({'res_id' : vals.get('res_id',0)})
if 'reply_to' in fields:
result['reply_to'] = vals.get('reply_to','')
if 'message_id' in fields:
result['message_id'] = vals.get('message_id','')
if 'attachment_ids' in fields:
result['attachment_ids'] = vals.get('attachment_ids',[])
if 'user_id' in fields:
result['user_id'] = vals.get('user_id',False)
if 'references' in fields:
result['references'] = vals.get('references',False)
if 'sub_type' in fields:
result['sub_type'] = vals.get('sub_type',False)
if 'headers' in fields:
result['headers'] = vals.get('headers',False)
if 'priority' in fields:
result['priority'] = vals.get('priority',False)
if 'debug' in fields:
result['debug'] = vals.get('debug',False)
return result
def _get_records(self, cr, uid, context=None):
"""
Return Records of particular Model
"""
if context is None:
context = {}
record_ids = []
model_pool = False
if context.get('message_id'):
message_pool = self.pool.get('email.message')
message_data = message_pool.browse(cr, uid, int(context.get('message_id')), context)
model_pool = self.pool.get(message_data.model)
record_ids = [message_data.res_id]
elif context.get('email_model',False):
model = context.get('email_model')
model_pool = self.pool.get(model)
record_ids = context.get('email_res_id') and [context.get('email_res_id')] or []
if not record_ids:
record_ids = model_pool.search(cr, uid, [])
if model_pool:
return model_pool.name_get(cr, uid, record_ids, context)
return []
_columns = {
'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'),
'debug':fields.boolean('Debug', readonly=True),
'res_id':fields.selection(_get_records, 'Referred Document'),
}
def get_value(self, cr, uid, model, res_id, context=None):
return {}
def get_message_data(self, cr, uid, message_id, context=None):
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.name or '')
if context.get('mail','') == 'reply':
subject = "Re :- " + subject
description = message_data and message_data.description and message_data.description or ''
message_body = False
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])
result.update({
'description' : description,
'name' : subject,
'message_id' : message_data and message_data.message_id or False,
'attachment_ids' : message_data and message_pool.read(cr, uid, message_id, ['attachment_ids'])['attachment_ids'] or [],
'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 on_change_referred_doc(self, cr, uid, ids, model, resource_id, context=None):
if context is None:
context = {}
if context.get('mail') == 'reply':
return {'value':{}}
result = {}
if resource_id and model:
vals = self.get_value(cr, uid, model, resource_id, context)
if vals:
result.update({
'email_from': vals.get('email_from',''),
'email_to': vals.get('email_to',''),
'name': vals.get('name',''),
'description': vals.get('description',''),
'email_cc': vals.get('email_cc',''),
'email_bcc': vals.get('email_bcc',''),
'reply_to': vals.get('reply_to',''),
})
return {'value': result}
def on_change_smtp_server(self, cr, uid, ids, smtp_server_id, email_from, context=None):
if not email_from and smtp_server_id:
email_smtp_server_pool = self.pool.get("email.smtp_server")
email_from = email_smtp_server_pool.browse(cr, uid, smtp_server_id, context).email_id or False
return {'value':{'email_from': email_from}}
def save_to_drafts(self, cr, uid, ids, context=None):
if context is None:
context = {}
email_id = self.save_to_mailbox(cr, uid, ids, context=context)
self.pool.get('email.message').write(cr, uid, email_id, {'folder':'drafts', 'state': 'draft'}, context)
return {'type': 'ir.actions.act_window_close'}
def send_mail(self, cr, uid, ids, context=None):
if context is None:
context = {}
email_id = self.save_to_mailbox(cr, uid, ids, context)
return {'type': 'ir.actions.act_window_close'}
def save_to_mailbox(self, cr, uid, ids, context=None):
email_ids = []
email_message_pool = self.pool.get('email.message')
attachment = []
for mail in self.browse(cr, uid, ids, context=context):
for attach in mail.attachment_ids:
attachment.append((attach.datas_fname, attach.datas))
references = False
message_id = False
if context.get('mail',False) == 'reply' and mail.message_id:
references = mail.references and mail.references + "," + mail.message_id or mail.message_id
else:
message_id = mail.message_id
email_id = email_message_pool.email_send(cr, uid, mail.email_from, mail.email_to, mail.name, mail.description,
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), debug=mail.debug,
subtype=mail.sub_type, x_headers=mail.headers, priority=mail.priority, smtp_server_id=mail.smtp_server_id and mail.smtp_server_id.id, context=context)
email_ids.append(email_id)
return email_ids
email_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<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="type">form</field>
<field name="arch" type="xml">
<form string="Reply Email">
<group col="6" colspan="4">
<field name="model" invisible="1"/>
<field name="res_id" invisible="context.get('active_model','') != 'ir.ui.menu'"
colspan="4" on_change="on_change_referred_doc(model, res_id, context)"/>
<field name="smtp_server_id" widget="selection" colspan="4"
on_change="on_change_smtp_server(smtp_server_id, email_from)"/>
<field name="email_from" colspan="4" required="1"/>
<field name="email_to" colspan="4" required="1"/>
<field name="email_cc" colspan="4"/>
<field name="email_bcc" colspan="4"/>
<field name="reply_to" colspan="4"/>
<field name="name" colspan="4" widget="char" size="512"/>
</group>
<separator string="" colspan="4"/>
<notebook colspan="4">
<page string="Body">
<field name="description" 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"/>
<field name="attachment_ids" colspan="4" nolabel="1"/>
</page>
</notebook>
<group col="4" colspan="4">
<label string="" colspan="1"/>
<button icon="gtk-close" special="cancel" string="Close"/>
<button icon="gtk-apply" name="save_to_drafts" string="Save in Drafts" type="object"/>
<button icon="gtk-ok" name="send_mail" string="Send now" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_email_compose_message_wizard" model="ir.actions.act_window">
<field name="name">Send E-mail</field>
<field name="res_model">email.compose.message</field>
<field name="src_model">email.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>
</data>
</openerp>
+3 -4
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>)
# 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
@@ -19,9 +19,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import email_template_account
import mako_template
import email_template
import email_template_mailbox
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+4 -6
View File
@@ -26,18 +26,16 @@
"author" : "Openlabs",
"website" : "http://openerp.com",
"category" : "Added functionality",
"depends" : ['marketing', 'base_tools'],
"depends" : [ 'email'],
"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',
'wizard/email_compose_message_view.xml',
'security/ir.model.access.csv'
],
"installable": True,
+279 -605
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>)
# 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
@@ -20,133 +20,55 @@
#
##############################################################################
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 _
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):
"""
Evaluates an expression and returns its value
@param cursor: Database Cursor
@param user: ID of current user
@param recid: ID of the target record under evaluation
@param message: The expression to be evaluated
@param template: BrowseRecord object of the current template
@param context: OpenERP Context
@return: Computed message (unicode) or u""
"""
pool = pooler.get_pool(cursor.dbname)
if message is None:
message = {}
#Returns the computed expression
if message:
try:
message = tools.ustr(message)
object = pool.get(template.model_int_name).browse(cursor, user, recid, context=context)
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))
return reply or False
except Exception:
logging.exception("can't render %r", message)
return u""
else:
return message
class email_template(osv.osv):
"Templates for sending Email"
_inherit = 'email.message.template'
_name = "email.template"
_description = 'Email Templates for Models'
def change_model(self, cursor, user, ids, object_name, context=None):
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}
}
def get_template_value(self, cr, uid, message=None, model=None, record_id=None, context=None):
import mako_template
return mako_template.get_value(cr, uid, message=message, model=model, record_id=record_id, context=context)
def get_email_template(self, cr, uid, template_id=False, record_id=None, context=None):
"Return Template Object"
if context is None:
context = {}
if not template_id:
template_id = context.get('template_id', False)
if not template_id:
return False
template = self.browse(cr, uid, int(template_id), context)
lang = self.get_template_value(cr, uid, template.lang, template.model, record_id, context)
if lang:
# Use translated template if necessary
ctx = context.copy()
ctx['lang'] = lang
template = self.browse(cr, uid, template.id, ctx)
return template
def onchange_model_id(self, cr, uid, ids, model_id, context=None):
mod_name = False
if model_id:
mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
return {'value':{'model':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',
string="Email Account",
help="Emails will be sent from this approved account."),
'def_to':fields.char(
'Recipient (To)',
size=250,
help="The Recipient of email. "
"Placeholders can be used here. "
"e.g. ${object.email_to}"),
'def_cc':fields.char(
'CC',
size=250,
help="Carbon Copy address(es), comma-separated."
" Placeholders can be used here. "
"e.g. ${object.email_cc}"),
'def_bcc':fields.char(
'BCC',
size=250,
help="Blind Carbon Copy address(es), comma-separated."
" Placeholders can be used here. "
"e.g. ${object.email_bcc}"),
'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. "
"e.g. ${object.email_reply_to}"),
'message_id':fields.char('Message-ID',
size=250,
help="Specify the Message-ID SMTP header to use in outgoing emails. Please note that this overrides the Resource tracking option! Placeholders can be used here."),
'name': fields.char('Name', size=250),
'model_id':fields.many2one('ir.model', 'Resource'),
'model': fields.related('model_id', 'model', string='Model', type="char", size=128, store=True, readonly=True),
'track_campaign_item':fields.boolean('Resource Tracking',
help="Enable this is you wish to include a special \
tracking marker in outgoing emails so you can identify replies and link \
@@ -158,25 +80,25 @@ This is useful for CRM leads for example"),
help="The default language for the email."
" Placeholders can be used here. "
"eg. ${object.partner_id.lang}"),
'def_subject':fields.char(
'subject':fields.char(
'Subject',
size=200,
help="The subject of email."
" Placeholders can be used here.",
translate=True),
'def_body_text':fields.text(
'Standard Body (Text)',
help="The text version of the mail",
translate=True),
'def_body_html':fields.text(
'Body (Text-Web Client Only)',
help="The text version of the mail",
translate=True),
'use_sign':fields.boolean(
# 'description':fields.text(
# 'Standard Body (Text)',
# help="The text version of the mail",
# translate=True),
# 'body_html':fields.text(
# 'Body (Text-Web Client Only)',
# help="The text version of the mail",
# translate=True),
'user_signature':fields.boolean(
'Signature',
help="the signature from the User details"
" will be appended to the mail"),
'file_name':fields.char(
'report_name':fields.char(
'Report Filename',
size=200,
help="Name of the generated report file. Placeholders can be used in the filename. eg: 2009_SO003.pdf",
@@ -245,17 +167,8 @@ 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"),
'model': fields.related('model_id','model', type='char', size=128, string='Object'),
}
_sql_constraints = [
@@ -266,31 +179,35 @@ 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')
data_obj = self.pool.get('ir.model.data')
for template in self.browse(cr, uid, ids, context=context):
src_obj = template.model_id.model
model_data_id = data_obj._get_id(cr, uid, 'email', 'email_compose_message_wizard_form')
res_id = data_obj.browse(cr, uid, model_data_id, context=context).res_id
vals['ref_ir_act_window'] = action_obj.create(cr, uid, {
'name': template.name,
'type': 'ir.actions.act_window',
'res_model': 'email.compose.message',
'src_model': src_obj,
'view_type': 'form',
'context': "{'email_model':'%s', 'email_res_id': active_id,'template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template.id),
'view_mode':'form,tree',
'view_id': res_id,
'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,500 +240,257 @@ 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
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 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
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')
attachment_data = {
'name': (name or '') + _(' (Email Attachment)'),
'datas': data,
'datas_fname': filename,
'description': name or _('No Description'),
'res_model':'email_template.mailbox',
'res_id': mailbox_id,
}
attachment_id = attachment_obj.create(cursor,
user,
attachment_data,
context)
if attachment_id:
self.pool.get('email_template.mailbox').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):
"""
Generate report to be attached and attach it
to the email, and add any directly attached files as well.
@param cursor: Database Cursor
@param user: ID of User
@param template: Browse record of
template
@param record_id: ID of the target model
for which this mail has
to be generated
@param mail: Browse record of email object
@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']
service = netsvc.LocalService(reportname)
data = {}
data['model'] = template.model_int_name
(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')
ext = '.' + format
if not fname.endswith(ext):
fname += ext
self._add_attachment(cursor, user, mail.id, mail.subject, base64.b64encode(result), fname, context)
if template.attachment_ids:
for attachment in template.attachment_ids:
self._add_attachment(cursor, user, mail.id, attachment.name, attachment.datas, attachment.datas_fname, context)
return True
def _generate_mailbox_item_from_template(self,
cursor,
user,
template,
record_id,
context=None):
def _generate_email(self, cr, uid, template_id, record_id, context=None):
"""
Generates an email from the template for
record record_id of target object
@param cursor: Database Cursor
@param user: ID of User
@param template: Browse record of
template
@param record_id: ID of the target model
for which this mail has
to be generated
@return: ID of created object
"""
if context is None:
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
)
smtp_pool = self.pool.get('email.smtp_server')
email_message_pool = self.pool.get('email.message')
report_xml_pool = self.pool.get('ir.actions.report.xml')
template = self.get_email_template(cr, uid, template_id, record_id, context)
smtp_server_id = context.get('smtp_server_id', False)
if not smtp_server_id and template.smtp_server_id:
smtp_server_id = template.smtp_server_id.id
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)
if lang:
ctx = context.copy()
ctx.update({'lang':lang})
template = self.browse(cursor, user, template.id, context=ctx)
smtp_ids = smtp_pool.search(cr, uid, [('default','=',True)])
smtp_server_id = smtp_ids and smtp_ids[0]
smtp_server = smtp_pool.browse(cr, uid, smtp_server_id, context=context)
# determine name of sender, either it is specified in email_id
# determine name of sender, either it is specified in email_id or we
# use the account name
email_id = from_account['email_id'].strip()
email_id = smtp_server.email_id.strip()
email_from = re.findall(r'([^ ,<@]+@[^> ,]+)', email_id)[0]
if email_from != email_id:
# we should keep it all, name is probably specified in the address
email_from = from_account['email_id']
email_from = smtp_server.email_id
else:
email_from = tools.ustr(from_account['name']) + "<" + tools.ustr(email_id) + ">"
email_from = tools.ustr(smtp_server.name) + "<" + tools.ustr(email_id) + ">"
# FIXME: should do this in a loop and rename template fields to the corresponding
# mailbox fields. (makes no sense to have different names I think.
mailbox_values = {
model = template.model_id.model
values = {
'email_from': email_from,
'email_to':get_value(cursor,
user,
record_id,
template.def_to,
template,
context),
'email_cc':get_value(cursor,
user,
record_id,
template.def_cc,
template,
context),
'email_bcc':get_value(cursor,
user,
record_id,
template.def_bcc,
template,
context),
'reply_to':get_value(cursor,
user,
record_id,
template.reply_to,
template,
context),
'subject':get_value(cursor,
user,
record_id,
template.def_subject,
template,
context),
'body_text':get_value(cursor,
user,
record_id,
template.def_body_text,
template,
context),
'body_html':get_value(cursor,
user,
record_id,
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',
'email_to': self.get_template_value(cr, uid, template.email_to, model, record_id, context),
'email_cc': self.get_template_value(cr, uid, template.email_cc, model, record_id, context),
'email_bcc': self.get_template_value(cr, uid, template.email_bcc, model, record_id, context),
'reply_to': self.get_template_value(cr, uid, template.reply_to, model, record_id, context),
'name': self.get_template_value(cr, uid, template.subject, model, record_id, context),
'description': self.get_template_value(cr, uid, template.description, model, record_id, context),
#'body_html': self.get_template_value(cr, uid, template.body_html, model, record_id, context),
}
if template['message_id']:
if template.message_id:
# use provided message_id with placeholders
mailbox_values.update({'message_id': get_value(cursor, user, record_id, template['message_id'], template, context)})
values.update({'message_id': self.get_template_value(cr, uid, template.message_id, model, record_id, context)})
elif template['track_campaign_item']:
# get appropriate message-id
mailbox_values.update({'message_id': tools.misc.generate_tracking_message_id(record_id)})
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.")
#Use signatures if allowed
if template.use_sign:
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)
if template.user_signature:
sign = self.pool.get('res.users').read(cr, uid, uid, ['signature'], context)['signature']
if values['description']:
values['description'] += '\n\n' + sign
#if values['body_html']:
# values['body_html'] += sign
return mailbox_id
attachment = []
# Add report as a Document
if template.report_template:
report_name = template.report_name
reportname = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
data = {}
data['model'] = template.model
# Ensure report is rendered using template's language
ctx = context.copy()
if template.lang:
ctx['lang'] = self.get_template_value(cr, uid, template.lang, template.model, record_id, context)
service = netsvc.LocalService(reportname)
(result, format) = service.create(cr, uid, [record_id], data, ctx)
result = base64.b64encode(result)
if not report_name:
report_name = reportname
report_name = report_name + "." + format
attachment.append((report_name, result))
def generate_mail(self,
cursor,
user,
template_id,
record_ids,
context=None):
# Add document attachments
for attach in template.attachment_ids:
#attach = attahcment_obj.browse(cr, uid, attachment_id, context)
attachment.append((attach.datas_fname, attach.datas))
#Send emails
context.update({'notemplate':True})
email_id = email_message_pool.email_send(cr, uid, values.get('email_from'), values.get('email_to'), values.get('name'), values.get('description'),
model=model, email_cc=values.get('email_cc'), email_bcc=values.get('email_bcc'), reply_to=values.get('reply_to'),
attach=attachment, message_id=values.get('message_id'), openobject_id=record_id, debug=True, subtype='plain', x_headers={}, priority='3', smtp_server_id=smtp_server.id, context=context)
email_message_pool.write(cr, uid, email_id, {'template_id': context.get('template_id',template.id)})
return email_id
def generate_email(self, cr, uid, template_id, record_id, 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')
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
)
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)
return result
email_id = self._generate_email(cr, uid, template_id, record_id, context)
return email_id
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):
def email_send(self, cr, uid, email_from, email_to, subject, body, model=False, email_cc=None, email_bcc=None, reply_to=False, attach=None,
message_id=False, references=False, openobject_id=False, debug=False, subtype='plain', x_headers={}, priority='3', smtp_server_id=False, 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}
notemplate = context.get('notemplate', True)
if (not notemplate) and model and openobject_id:
template_pool = self.pool.get('email.template')
template_ids = template_pool.search(cr, uid, [('model','=',model)])
if template_ids and len(template_ids):
template_id = template_ids[0]
return template_pool.generate_email(cr, uid, template_id, openobject_id, context=context)
email_template_preview()
return super(email_message, self).email_send(cr, uid, email_from, email_to, subject, body, model=model, email_cc=email_cc, email_bcc=email_bcc, reply_to=reply_to, attach=attach,
message_id=message_id, references=references, openobject_id=openobject_id, debug=debug, subtype=subtype, x_headers=x_headers, priority=priority, smtp_server_id=smtp_server_id, context=context)
email_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,470 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 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, 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" <d@b.com> , 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 <john@doe.com>",
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 <john@doe.com>'"),
'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'<Empty Message>')
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'<p>&lt;Empty Message&gt;</p>').replace('\n', '<br/>').replace('\r', '<br/>')
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?= <enricmarti@company.com>,
=?Windows-1252?Q?David_G=F3mez?= <david@company.com>
Sometimes they include extra " character at the beginning/
end of the contact name, like:
"=?iso-8859-1?Q?Enric_Mart=ED?=" <enricmarti@company.com>
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:
@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem name="Marketing" icon="terp-crm" id="base.marketing_menu" sequence="17"
groups="marketing.group_marketing_user,marketing.group_marketing_manager"/>
<menuitem name="Emails" id="base.menu_emails" parent="base.marketing_menu" sequence="5"/>
<menuitem name="Email Template" id="menu_email_template" parent="base.menu_emails"/>
<record model="ir.ui.view" id="email_template_account_form">
<field name="name">email_template.account.form</field>
<field name="model">email_template.account</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email Account Configuration">
<group colspan="2">
<field name="name" select="1" />
</group>
<notebook colspan="4">
<page string="Outgoing">
<separator string="Server Information" colspan="4" />
<group colspan="4" col="4">
<field name="smtpserver" select="1"/>
<button name="check_outgoing_connection" type="object" string="Test Outgoing Connection" icon="gtk-network" colspan="2"/>
<field name="smtpport" select="2" />
<field name="smtpssl" select="2" />
<field name="smtptls" select="2" />
<field name="auto_delete" />
</group>
<separator string="User Information" colspan="4" />
<group col="2" colspan="2">
<field name="email_id" select="1" colspan="2" />
<field name="smtppass" password="True" colspan="2" />
<field name="company" select="2" colspan="2" />
</group>
<group col="2" colspan="2">
<field name="smtpuname" select="1" colspan="2" />
<field name="user" select="2" colspan="2" />
<field name="send_pref" colspan="2" />
</group>
</page>
</notebook>
<group colspan="4" col="10">
<field name="state" select="1"/>
<button string="Approve Account" name="button_approval" states="draft" type="workflow" icon="terp-camera_test"/>
<button string="Suspend Account" name="button_suspended" states="approved" type="workflow" icon="gtk-cancel"/>
<button string="Request Re-activation" name="get_reapprove" states="suspended" type="workflow" icon="gtk-convert"/>
<button string="Send/Receive" name="send_receive" states="approved" type="object" icon="terp-check"/>
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="email_template_account_tree">
<field name="name">email_template.account.tree</field>
<field name="model">email_template.account</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree colors="blue:state in ('draft');black:state in ('suspended','approved')" string="Email Accounts">
<field name="name" />
<field name="email_id" />
<field name="smtpuname" />
<field name="user" />
<field name="smtpserver" />
<field name="smtpport" />
<field name="auto_delete" />
<field name="state" />
</tree>
</field>
</record>
<record id="view_email_template_account_search" model="ir.ui.view">
<field name="name">email_template.account.search</field>
<field name="model">email_template.account</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Email Accounts">
<filter icon="terp-document-new" string="Draft" name="draft" domain="[('state','=','draft')]"/>
<filter icon="terp-camera_test" string="Approved" domain="[('state','=','approved')]"/>
<filter icon="terp-emblem-important" string="Suspended" domain="[('state','=','suspended')]"/>
<separator orientation="vertical"/>
<filter icon="terp-go-home" string="Company Accounts" domain="[('company','=','yes')]"/>
<separator orientation="vertical"/>
<field name="user" select="1">
<filter icon="terp-personal" help="My Accounts" name="my" domain="[('user','=',uid)]"/>
</field>
<field name="name" select="1"/>
<field name="email_id" select="1"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_template_account_tree_all">
<field name="name">Accounts</field>
<field name="res_model">email_template.account</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="email_template_account_tree" />
<field name="context">{'search_default_draft': 1, 'search_default_my': 1}</field>
<field name="search_view_id" ref="view_email_template_account_search"/>
</record>
<menuitem name="Configuration" parent="base.marketing_menu"
id="base.menu_marketing_config_root" sequence="20" groups="base.group_system"/>
<menuitem name="Email Template" id="menu_email_template_configuration" parent="base.menu_marketing_config_root" />
<menuitem name="Email Accounts" id="menu_email_template_account_all" parent="menu_email_template_configuration" action="action_email_template_account_tree_all"/>
<menuitem name="Configuration" parent="base.menu_tools"
id="base.menu_lunch_survey_root" sequence="20" />
<menuitem name="Email Template" id="menu_email_template_config_tools"
parent="base.menu_lunch_survey_root" />
<menuitem name="Email Accounts" id="menu_email_account_all_tools"
parent="menu_email_template_config_tools" action="action_email_template_account_tree_all" />
</data>
</openerp>
@@ -1,213 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 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, fields
import time
import netsvc
from tools.translate import _
import tools
LOGGER = netsvc.Logger()
class email_template_mailbox(osv.osv):
_name = "email_template.mailbox"
_description = 'Email Mailbox'
_rec_name = "subject"
_order = "date_mail desc"
def run_mail_scheduler(self, cursor, user, context=None):
"""
This method is called by OpenERP Scheduler
to periodically send emails
"""
try:
self.send_all_mail(cursor, user, context=context)
except Exception, e:
LOGGER.notifyChannel(
"Email Template",
netsvc.LOG_ERROR,
_("Error sending mail: %s") % e)
def send_all_mail(self, cr, uid, ids=None, context=None):
if ids is None:
ids = []
if context is None:
context = {}
filters = [('folder', '=', 'outbox'), ('state', '!=', 'sending')]
if 'filters' in context.keys():
for each_filter in context['filters']:
filters.append(each_filter)
ids = self.search(cr, uid, filters, context=context)
self.write(cr, uid, ids, {'state':'sending'}, context)
self.send_this_mail(cr, uid, ids, context)
return True
def send_this_mail(self, cr, uid, ids=None, context=None):
#previous method to send email (link with email account can be found at the revision 4172 and below
result = True
attachment_pool = self.pool.get('ir.attachment')
for id in (ids or []):
try:
account_obj = self.pool.get('email_template.account')
values = self.read(cr, uid, id, [], context)
payload = {}
if values['attachments_ids']:
for attid in values['attachments_ids']:
attachment = attachment_pool.browse(cr, uid, attid, context)#,['datas_fname','datas'])
payload[attachment.datas_fname] = attachment.datas
result = account_obj.send_mail(cr, uid,
[values['account_id'][0]],
{'To':values.get('email_to') or u'',
'CC':values.get('email_cc') or u'',
'BCC':values.get('email_bcc') or u'',
'Reply-To':values.get('reply_to') or u''},
values['subject'] or u'',
{'text':values.get('body_text') or u'', 'html':values.get('body_html') or u''},
payload=payload,
message_id=values['message_id'],
context=context)
if result == True:
account = account_obj.browse(cr, uid, values['account_id'][0], context=context)
if account.auto_delete:
self.write(cr, uid, id, {'folder': 'trash'}, context=context)
self.unlink(cr, uid, [id], context=context)
# Remove attachments for this mail
attachment_pool.unlink(cr, uid, values['attachments_ids'], context=context)
else:
self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
self.historise(cr, uid, [id], "Email sent successfully", context)
else:
error = result['error_msg']
self.historise(cr, uid, [id], error, context)
except Exception, error:
logger = netsvc.Logger()
logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error))
self.historise(cr, uid, [id], error, context)
self.write(cr, uid, id, {'state':'na'}, context)
return result
def historise(self, cr, uid, ids, message='', context=None):
for id in ids:
history = self.read(cr, uid, id, ['history'], context).get('history', '')
self.write(cr, uid, id, {'history': (history or '' )+ "\n" + time.strftime("%Y-%m-%d %H:%M:%S") + ": " + tools.ustr(message)}, context)
_columns = {
'email_from':fields.char(
'From',
size=64),
'email_to':fields.char(
'Recipient (To)',
size=250,),
'email_cc':fields.char(
'CC',
size=250),
'email_bcc':fields.char(
'BCC',
size=250),
'reply_to':fields.char(
'Reply-To',
size=250),
'message_id':fields.char(
'Message-ID',
size=250),
'subject':fields.char(
'Subject',
size=200,),
'body_text':fields.text(
'Standard Body (Text)'),
'body_html':fields.text(
'Body (Rich Text Clients Only)'),
'attachments_ids':fields.many2many(
'ir.attachment',
'mail_attachments_rel',
'mail_id',
'att_id',
'Attachments'),
'account_id' :fields.many2one(
'email_template.account',
'User account',
required=True),
'user':fields.related(
'account_id',
'user',
type="many2one",
relation="res.users",
string="User"),
'server_ref':fields.integer(
'Server Reference of mail',
help="Applicable for inward items only"),
'mail_type':fields.selection([
('multipart/mixed',
'Has Attachments'),
('multipart/alternative',
'Plain Text & HTML with no attachments'),
('multipart/related',
'Intermixed content'),
('text/plain',
'Plain Text'),
('text/html',
'HTML Body'),
], 'Mail Contents'),
#I like GMAIL which allows putting same mail in many folders
#Lets plan it for 0.9
'folder':fields.selection([
('drafts', 'Drafts'),
('outbox', 'Outbox'),
('trash', 'Trash'),
('sent', 'Sent Items'),
], 'Folder', required=True),
'state':fields.selection([
('na', 'Not Applicable'),
('sending', 'Sending'),
], 'Status', required=True),
'date_mail':fields.datetime('Rec/Sent Date', help="Date on which Email Sent or Received"),
'history':fields.text(
'History',
readonly=True,
store=True)
}
_defaults = {
'state': lambda * a: 'na',
'folder': lambda * a: 'outbox',
}
def unlink(self, cr, uid, ids, context=None):
"""
It just changes the folder of the item to "Trash", if it is no in Trash folder yet,
or completely deletes it if it is already in Trash.
"""
to_update = []
to_remove = []
for mail in self.browse(cr, uid, ids, context=context):
if mail.folder == 'trash':
to_remove.append(mail.id)
else:
to_update.append(mail.id)
# Changes the folder to trash
self.write(cr, uid, to_update, {'folder': 'trash'}, context=context)
return super(email_template_mailbox, self).unlink(cr, uid, to_remove, context=context)
email_template_mailbox()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,134 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<!-- Email Template-->
<record model="ir.ui.view" id="email_template_mailbox_form">
<field name="name">email_template.mailbox.form</field>
<field name="model">email_template.mailbox</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Emails">
<group col="4" colspan="4" name="headers">
<field name="email_from" select="1"/>
<field name="email_to" required="1" select="1" />
<field name="reply_to" select="2"/>
<field name="email_cc" select="1"/>
<field name="email_bcc" select="2"/>
<field name="date_mail" select="2"/>
<field name="subject" colspan="4" select="1"/>
</group>
<notebook colspan="4">
<page string="Standard Body">
<separator colspan="4" string="Standard Body" />
<notebook colspan="4">
<page string="Standard Body (Text)">
<field name="body_text" nolabel="1" colspan="4" select="1"/>
</page>
<page string="Body (HTML-Web Client Only)">
<field name="body_html" nolabel="1" colspan="4" />
</page>
</notebook>
<separator colspan="4" string="" />
<group col="4" colspan="4">
<field name="state" readonly="1" string="State"/>
<button name="send_this_mail" type="object" string="Send Mail" icon="terp-mail-message-new"/>
</group>
</page>
<page string="Attachments">
<separator colspan="4" string="Attachments" />
<field name="attachments_ids" colspan="4" nolabel="1" />
</page>
<page string="Advanced">
<field name="account_id" colspan="2" />
<field name="server_ref" colspan="2" />
<field name="mail_type" colspan="2" />
<field name="folder" colspan="2" select="2"/>
<field name="message_id" select="2"/>
<separator string="History" colspan="4" />
<field name="history" nolabel="1" colspan="4"/>
</page>
</notebook>
</form>
</field>
</record>
<!--============================================= TREE VIEWS =============================================-->
<record id="view_email_template_mailbox_tree" model="ir.ui.view">
<field name="name">email_template.mailbox.tree</field>
<field name="model">email_template.mailbox</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Emails" colors="blue:folder=='drafts';grey:folder=='trash'">
<field name="subject" select="1" />
<field name="user" />
<field name="email_from" select="1" />
<field name="email_to"/>
<field name="date_mail" select="2" />
<field name="attachments_ids" select="2" />
<field name="folder" invisible="1"/>
</tree>
</field>
</record>
<record id="view_email_template_mailbox_search" model="ir.ui.view">
<field name="name">email_template.mailbox.search</field>
<field name="model">email_template.mailbox</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Emails">
<filter icon="terp-document-new" string="Drafts" name="draft" domain="[('folder','=','drafts')]"/>
<filter icon="terp-mail-" string="Outbox" name="outbox" domain="[('folder','=','outbox')]"/>
<filter icon="terp-camera_test" string="Sent" domain="[('folder','=','sent')]"/>
<filter icon="terp-mail_delete" string="Trash" domain="[('folder','=','trash')]"/>
<separator orientation="vertical"/>
<filter icon="terp-personal+" string="Personal Emails" name="personal" domain="[('account_id.company','=','no')]"/>
<filter icon="terp-go-home" string="Company Emails" name="company" domain="[('account_id.company','=','yes')]"/>
<separator orientation="vertical"/>
<field name="subject"/>
<field name="email_from"/>
<field name="user">
<filter icon="terp-personal"
string="My Emails" help="My Emails" name="myemails"
domain="[('account_id.user','=', uid)]" />
</field>
<newline/>
<field name="email_to"/>
<field name="date_mail"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_template_mailbox">
<field name="name">Emails</field>
<field name="res_model">email_template.mailbox</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_email_template_mailbox_tree" />
<field name="context">{'search_default_outbox': 1}</field>
<field name="help">An email template is an email document that will be sent as part of a marketing campaign. You can personalize it according to specific customer profile fields, so that a partner name or other partner related information may be inserted automatically.</field>
<field name="search_view_id" ref="view_email_template_mailbox_search"/>
</record>
<!--======================================== MENUS ========================================-->
<menuitem name="Emails"
id="menu_email_template_personal_mails"
parent="menu_email_template"
action="action_email_template_mailbox" />
<!-- Mailbox menu in Tools -->
<menuitem name="Email Template" id="menu_email_template_tools"
parent="base.menu_tools" />
<menuitem name="Emails"
id="menu_email_template_mails_tools"
parent="menu_email_template_tools"
action="action_email_template_mailbox" />
</data>
</openerp>
+64 -106
View File
@@ -1,115 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<!-- Email Template Preview -->
<record model="ir.ui.view" id="email_template_preview_form">
<field name="name">email_template.preview.form</field>
<field name="model">email_template.preview</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email Preview">
<group col="4" colspan="4">
<field name="rel_model" />
<field name="rel_model_ref" on_change="on_change_ref(rel_model_ref, context)"/>
</group>
<group col="8" colspan="4">
<field name="to" />
<field name="cc" />
<field name="bcc" />
<field name="reply_to" />
<field name="message_id" attrs="{'invisible':[('message_id','=',False)]}" groups="base.group_extended"/>
<field name="subject" colspan="8"/>
</group>
<group col="4" colspan="4">
<separator string= "Body(Text)" colspan="2"/>
<separator string= "Body(Html)" colspan="2"/>
<newline/>
<field name="body_text" nolabel="1" colspan="2"/>
<field name="body_html" nolabel="1" colspan="2"/>
</group>
<field name="report" colspan="2"/>
<button icon="gtk-ok" special="cancel" string="OK" colspan="1"/>
</form>
</field>
</record>
<record id="wizard_email_template_preview" model="ir.actions.act_window">
<field name="name">Template Preview</field>
<field name="res_model">email_template.preview</field>
<field name="src_model">email_template.preview</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="auto_refresh" eval="1" />
<field name="target">new</field>
<field name="context">{'template_id':active_id}</field>
</record>
<!--EMail client Form view -->
<record model="ir.ui.view" id="email_template_form">
<field name="name">email.template.form</field>
<field name="model">email.template</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email Templates">
<field name="name" />
<field name="object_name" required="1"
on_change="change_model(object_name)" />
<field name="model_int_name" invisible="1" />
<form string="Templates">
<field name="name" required="1"/>
<field name="model_id" required="1"
on_change="onchange_model_id(model_id)"/>
<field name="model" invisible="1"/>
<notebook colspan="4">
<page string="Mail Details">
<page string="Email Details">
<group col="2" colspan="2">
<separator string="Addresses" colspan="2"/>
<field name="from_account" required="1"/>
<field name="def_to" required="1"/>
<field name="def_cc"/>
<field name="def_bcc"/>
<field name="smtp_server_id"/>
<field name="email_to" required="1"/>
<field name="email_cc"/>
<field name="email_bcc"/>
<field name="reply_to"/>
</group>
<group col="2" colspan="2">
<separator string="Options" colspan="2"/>
<field name="lang" colspan="4" />
<field name="use_sign" colspan="4" />
<field name="user_signature" colspan="4" />
<field name="track_campaign_item" colspan="4"/>
</group>
<group col="2" colspan="2">
<separator colspan="2" string="Email Content " />
<field name="def_subject" colspan="4" required="1" />
<separator colspan="2" string="Email Content"/>
<field name="subject" colspan="4" required="1"/>
<notebook>
<page string="Body (Text)">
<field name="def_body_text" colspan="4" nolabel="1" />
</page>
<page string="Body (Raw HTML)">
<field name="def_body_html" colspan="4" nolabel="1" />
<label string="Note: This is Raw HTML." colspan="4" />
<page string="Body">
<field name="description" colspan="4" nolabel="1"/>
</page>
<!-- <page string="Body (Raw HTML)">
<field name="def_body_html" colspan="4" nolabel="1"/>
<label string="Note: This is Raw HTML." colspan="4"/>
</page> -->
</notebook>
</group>
<group col="4" colspan="2">
<separator colspan="4" string="Expression Builder" />
<field name="template_language"
on_change="onchange_null_value(model_object_field,sub_model_object_field,null_value,template_language,context)" />
<notebook>
<page string="Insert Simple Field">
<field name="model_object_field"
domain="[('model_id','=',object_name),('ttype','!=','one2many'),('ttype','!=','many2many')]"
on_change="onchange_model_object_field(model_object_field, template_language,context)"
colspan="4" />
<field name="sub_object" readonly="1" colspan="4" />
domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]"
on_change="onchange_sub_model_object_value_field(model_object_field)"
colspan="4"/>
<field name="sub_object" readonly="1" colspan="4"/>
<field name="sub_model_object_field"
domain="[('model_id','=',sub_object),('ttype','!=','one2many'),('ttype','!=','many2many')]"
colspan="4"
attrs="{'readonly':[('sub_object','=',False)],'required':[('sub_object','!=',False)]}"
on_change="onchange_sub_model_object_field(model_object_field,sub_model_object_field,template_language,context)" />
on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field)"/>
<field name="null_value" colspan="4"
on_change="onchange_null_value(model_object_field,sub_model_object_field,null_value,template_language,context)" />
<field name="copyvalue" colspan="4" />
on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field,null_value)" />
<field name="copyvalue" colspan="4"/>
</page>
</notebook>
<button name="%(wizard_email_template_preview)d" string="Preview Template"
type="action" colspan="4" target="new" icon="gtk-zoom-fit"/>
type="action" colspan="4" target="new" icon="gtk-zoom-fit" context="{'template_id':active_id}"/>
</group>
</page>
<page string="Advanced">
@@ -124,6 +75,7 @@
<group colspan="2" col="2">
<separator string="Advanced Options" colspan="2"/>
<field name="message_id"/>
<field name="auto_delete"/>
</group>
</group>
<group colspan="2" col="2">
@@ -134,8 +86,8 @@
</page>
<page string="Report">
<field name="report_template" colspan="4"
domain="[('model','=',model_int_name)]" />
<field name="file_name" colspan="4" />
domain="[('model','=',model)]"/>
<field name="report_name" colspan="4" />
</page>
</notebook>
</group>
@@ -150,16 +102,16 @@
<field name="model">email.template</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Email Templates">
<tree string="Templates">
<field name="name"/>
<field name="from_account"/>
<field name="object_name"/>
<field name="def_to"/>
<field name="def_cc"/>
<field name="def_bcc"/>
<field name="def_subject"/>
<field name="use_sign"/>
<field name="file_name"/>
<field name="smtp_server_id"/>
<field name="model_id"/>
<field name="email_to"/>
<field name="email_cc"/>
<field name="email_bcc"/>
<field name="subject"/>
<field name="user_signature"/>
<field name="report_name"/>
<button name="%(wizard_email_template_preview)d" string="Preview Template"
type="action" target="new" icon="gtk-zoom-fit"/>
</tree>
@@ -173,26 +125,26 @@
<field name="arch" type="xml">
<search string="Templates">
<group col="13" colspan="4">
<field name="name" select="1"/>
<field name="object_name" select="1"/>
<field name="def_to" select="1"/>
<field name="name"/>
<field name="model_id"/>
<field name="email_to"/>
<separator orientation="vertical"/>
<field name="lang" select="1"/>
<field name="def_subject" select="1"/>
<field name="file_name" select="1"/>
<field name="lang"/>
<field name="subject"/>
<field name="report_name"/>
</group>
<newline/>
<group expand="0" string="Group by..." colspan="4" col="10">
<filter string="Account" domain="[]" context="{'group_by':'from_account'}" icon="terp-folder-orange"/>
<filter string="SMTP Server" domain="[]" context="{'group_by':'smtp_server_id'}" icon="terp-folder-orange"/>
<separator orientation="vertical"/>
<filter string="Resource" domain="[]" context="{'group_by':'object_name'}" icon="terp-accessories-archiver"/>
<filter string="Model" domain="[]" context="{'group_by':'model_id'}" icon="terp-accessories-archiver"/>
</group>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_template_tree_all">
<field name="name">Email Templates</field>
<field name="name">Templates</field>
<field name="res_model">email.template</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
@@ -200,16 +152,22 @@
<field name="search_view_id" ref="view_email_template_search"/>
</record>
<menuitem name="Email Templates" id="menu_email_template_all"
parent="menu_email_template_configuration" action="action_email_template_tree_all" />
<menuitem name="Templates" id="menu_email_template_all_tools"
parent="email.menu_email_message_tools" action="action_email_template_tree_all" />
<!-- Inherit email.message view -->
<record model="ir.ui.view" id="view_email_message_template_form">
<field name="name">email.message.template.form</field>
<field name="model">email.message</field>
<field name="inherit_id" ref="email.view_email_message_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<field name="debug" position="after">
<field name="template_id"/>
</field>
</field>
</record>
<!-- Email Template menu in Tools -->
<menuitem name="Email Templates" id="menu_email_template_all_tools"
parent="menu_email_template_config_tools" action="action_email_template_tree_all" />
</data>
</openerp>
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="wkf_email_template_setting" model="workflow">
<field name="name">Email Template Workflow</field>
<field name="osv">email_template.account</field>
<field name="on_create">True</field>
</record>
<!--Activity -->
<record id="act_draft" model="workflow.activity">
<field name="wkf_id" ref="wkf_email_template_setting"/>
<field name="flow_start">True</field>
<field name="name">draft</field>
<field name="kind">function</field>
<field name="action">write({'state':'draft'})</field>
</record>
<record id="act_approved" model="workflow.activity">
<field name="name">approval</field>
<field name="wkf_id" ref="wkf_email_template_setting"/>
<field name="kind">function</field>
<field name="action">do_approval()</field>
</record>
<record id="act_suspended" model="workflow.activity">
<field name="name">suspended</field>
<field name="wkf_id" ref="wkf_email_template_setting"/>
<field name="kind">function</field>
<field name="action">write({'state':'suspended'})</field>
</record>
<record id="act_dummy" model="workflow.activity">
<field name="name">dummy</field>
<field name="wkf_id" ref="wkf_email_template_setting"/>
<field name="flow_stop">True</field>
</record>
<!-- Transition -->
<record id="trans_awaiting_approved" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_approved"/>
<field name="signal">button_approval</field>
</record>
<record id="trans_approved_suspended" model="workflow.transition">
<field name="act_from" ref="act_approved"/>
<field name="act_to" ref="act_suspended"/>
<field name="signal">button_suspended</field>
</record>
<record id="trans_suspended_reapproved" model="workflow.transition">
<field name="act_from" ref="act_suspended"/>
<field name="act_to" ref="act_draft"/>
<field name="signal">get_reapprove</field>
</record>
<record id="trans_suspended_dummy" model="workflow.transition">
<field name="act_from" ref="act_suspended"/>
<field name="act_to" ref="act_dummy"/>
<field name="signal">get_never</field>
</record>
</data>
</openerp>
+59
View File
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# 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 tools
from tools.translate import _
import pooler
import logging
try:
from mako.template import Template as MakoTemplate
except ImportError:
logging.getLogger('init').warning("module email_template: Mako templates not installed")
def get_value(cr, uid, message=None, model=None, record_id=False, context=None):
"""
returns Messages in Mako Template
"""
pool = pooler.get_pool(cr.dbname)
if message is None:
message = {}
#Returns the computed expression
if message:
try:
message = tools.ustr(message)
record = pool.get(model).browse(cr, uid, record_id, context=context)
env = {
'user': pool.get('res.users').browse(cr, uid, uid, context=context),
'db': cr.dbname
}
templ = MakoTemplate(message, input_encoding='utf-8')
reply = MakoTemplate(message).render_unicode(object=record, peobject=record, env=env, format_exceptions=True)
return reply or False
except Exception:
logging.exception("can't render %r", message)
return u""
else:
return message
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
</data>
</openerp>
@@ -1,10 +1,4 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_email_template_account","email_template.account","model_email_template_account","marketing.group_marketing_user",1,0,0,0
"access_email_template","email.template","model_email_template","marketing.group_marketing_user",1,0,0,0
"access_email_template_mailbox","email_template.mailbox","model_email_template_mailbox","marketing.group_marketing_user",1,1,1,1
"access_email_template_account_system","email_template.account system","model_email_template_account","base.group_system",1,1,1,1
"access_email_template","email.template","model_email_template",,1,0,0,0
"access_email_template_system","email.template system","model_email_template","base.group_system",1,1,1,1
"access_email_template_mailbox_system","email_template.mailbox system","model_email_template_mailbox","base.group_system",1,0,0,0
"access_email_template_account_manager","email_template.account","model_email_template_account","marketing.group_marketing_manager",1,1,1,1
"access_email_template_manager","email.template","model_email_template","marketing.group_marketing_manager",1,1,1,1
"access_email_template_mailbox_manager","email_template.mailbox","model_email_template_mailbox","marketing.group_marketing_manager",1,1,1,1
"access_email_template_manager","email.template","model_email_template",,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_email_template_account access_email_template email_template.account email.template model_email_template_account model_email_template marketing.group_marketing_user 1 0 0 0
access_email_template email.template model_email_template marketing.group_marketing_user 1 0 0 0
access_email_template_mailbox email_template.mailbox model_email_template_mailbox marketing.group_marketing_user 1 1 1 1
access_email_template_account_system email_template.account system model_email_template_account base.group_system 1 1 1 1
3 access_email_template_system email.template system model_email_template base.group_system 1 1 1 1
4 access_email_template_mailbox_system access_email_template_manager email_template.mailbox system email.template model_email_template_mailbox model_email_template base.group_system 1 0 1 0 1 0 1
access_email_template_account_manager email_template.account model_email_template_account marketing.group_marketing_manager 1 1 1 1
access_email_template_manager email.template model_email_template marketing.group_marketing_manager 1 1 1 1
access_email_template_mailbox_manager email_template.mailbox model_email_template_mailbox marketing.group_marketing_manager 1 1 1 1
+4 -2
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>)
# 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
@@ -19,5 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import email_template_preview
import email_template_send_wizard
import email_compose_message
@@ -0,0 +1,145 @@
# -*- 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
class email_compose_message(osv.osv_memory):
_name = 'email.compose.message'
_inherit = 'email.compose.message'
def get_template_data(self, cr, uid, res_id, template_id, context=None):
if context is None:
context = {}
result = {}
template_pool = self.pool.get('email.template')
if template_id:
template = template_pool.get_email_template(cr, uid, template_id=template_id, context=context)
def _get_template_value(field):
if not template:
return False
if len(context.get('src_rec_ids',[])) > 1: # Multiple Mail: Gets original template values for multiple email change
return getattr(template, field)
else: # Simple Mail: Gets computed template values
return template_pool.get_template_value(cr, uid, getattr(template, field), template.model, context.get('email_res_id'), context)
result.update({
'template_id' : template.id,
'smtp_server_id' : template.smtp_server_id.id,
'description' : _get_template_value('description') or False,
'name' : _get_template_value('subject') or False,
'attachment_ids' : template_pool.read(cr, uid, template.id, ['attachment_ids'])['attachment_ids'] or [],
'res_id' : res_id or False,
'email_to' : _get_template_value('email_to') or False,
'email_cc' : _get_template_value('email_cc') or False,
'email_bcc' : _get_template_value('email_bcc') or False,
'reply_to' : _get_template_value('reply_to') or False,
'model' : template.model or False,
})
return result
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
result = super(email_compose_message, self).default_get(cr, uid, fields, context=context)
template_id = context.get('template_id', False)
vals = {}
if template_id and context.get('email_model') and context.get('email_res_id'):
vals = self.get_template_data(cr, uid, context.get('email_res_id'), template_id, context)
if not vals:
return result
if 'template_id' in fields:
result.update({'template_id' : vals.get('template_id', False)})
if 'smtp_server_id' in fields:
result.update({'smtp_server_id' : vals.get('smtp_server_id', False)})
if 'attachment_ids' in fields:
result.update({'attachment_ids' : vals.get('attachment_ids', False)})
if 'model' in fields:
result.update({'model' : vals.get('model', False)})
if 'res_id' in fields:
result.update({'res_id' : vals.get('res_id', False)})
if 'email_to' in fields:
result.update({'email_to' : vals.get('email_to', False)})
if 'email_cc' in fields:
result.update({'email_cc' : vals.get('email_cc', False)})
if 'email_bcc' in fields:
result.update({'email_bcc' : vals.get('email_bcc', False)})
if 'name' in fields:
result.update({'name' : vals.get('name', False)})
if 'description' in fields:
result.update({'description' : vals.get('description', False)})
if 'reply_to' in fields:
result.update({'reply_to' : vals.get('reply_to', False)})
return result
def _get_templates(self, cr, uid, context=None):
"""
Return Email Template of particular Model.
"""
if context is None:
context = {}
record_ids = []
email_temp_pool = self.pool.get('email.template')
model = False
if context.get('message_id'):
message_pool = self.pool.get('email.message')
message_data = message_pool.browse(cr, uid, int(context.get('message_id')), context)
model = message_data.model
elif context.get('email_model',False):
model = context.get('email_model')
if model:
record_ids = email_temp_pool.search(cr, uid, [('model','=',model)])
return email_temp_pool.name_get(cr, uid, record_ids, context) + [(False,'')]
return []
_columns = {
'template_id': fields.selection(_get_templates, 'Template'),
}
def on_change_template(self, cr, uid, ids, model, resource_id, template_id, context=None):
if context is None:
context = {}
if context.get('mail') == 'reply':
return {'value':{}}
result = self.on_change_referred_doc(cr, uid, [], model, resource_id, context=context)
vals = result.get('value',{})
if template_id and resource_id:
vals.update(self.get_template_data(cr, uid, resource_id, template_id, context))
else:
vals.update({'attachment_ids' : []})
return {'value': vals}
email_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form">
<field name="name">email.compose.message.form</field>
<field name="model">email.compose.message</field>
<field name="type">form</field>
<field name="inherit_id" ref="email.email_compose_message_wizard_form"/>
<field name="arch" type="xml">
<field name="res_id" position="replace">
<field name="template_id" colspan="4"
on_change="on_change_template(model, res_id, template_id, context)"/>
<field name="res_id"
invisible="context.get('active_model','') != 'ir.ui.menu'"
colspan="4" on_change="on_change_template(model, res_id, template_id, context)"/>
</field>
</field>
</record>
</data>
</openerp>
@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# 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, fields
from tools.translate import _
import tools
class email_template_preview(osv.osv_memory):
_inherit = "email.template"
_name = "email_template.preview"
_description = "Email Template Preview"
_rec_name = "subject"
def _get_records(self, cr, uid, context=None):
"""
Return Records of particular Email Template's Model
"""
if context is None:
context = {}
template_id = context.get('template_id', False)
if not template_id:
return []
template_pool = self.pool.get('email.template')
model_pool = self.pool.get('ir.model')
template = template_pool.browse(cr, uid, int(template_id), context=context)
template_object = template.model_id
model = self.pool.get(template_object.model)
record_ids = model.search(cr, uid, [], 0, 20, 'id', context=context)
default_id = context.get('default_res_id')
if default_id and default_id not in record_ids:
record_ids.insert(0, default_id)
return model.name_get(cr, uid, record_ids, context)
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)
template_pool = self.pool.get('email.template')
template_id = context.get('template_id',False)
if 'res_id' in fields:
records = self._get_records(cr, uid, context=context)
result['res_id'] = records and records[0][0] or False # select first record as a Default
if template_id and 'model_id' in fields:
result['model_id'] = template_pool.read(cr, uid, int(template_id), ['model_id'], context).get('model_id', False)
return result
_columns = {
'res_id':fields.selection(_get_records, 'Referred Document'),
}
_defaults = {
}
def on_change_ref(self, cr, uid, ids, res_id, context=None):
if context is None:
context = {}
if not res_id:
return {}
vals = {}
if context == {}:
context = self.context
template_pool = self.pool.get('email.template')
template_id = context.get('template_id', False)
template = template_pool.get_email_template(cr, uid, template_id=template_id, record_id=res_id, context=context)
model = template.model
vals['email_to'] = self.get_template_value(cr, uid, template.email_to, model, res_id, context)
vals['email_cc'] = self.get_template_value(cr, uid, template.email_cc, model, res_id, context)
vals['email_bcc'] = self.get_template_value(cr, uid, template.email_bcc, model, res_id, context)
vals['reply_to'] = self.get_template_value(cr, uid, template.reply_to, model, res_id, context)
if template.message_id:
vals['message_id'] = self.get_template_value(cr, uid, message_id, model, res_id, context)
elif template.track_campaign_item:
vals['message_id'] = tools.misc.generate_tracking_message_id(res_id)
vals['subject'] = self.get_template_value(cr, uid, template.subject, model, res_id, context)
description = self.get_template_value(cr, uid, template.description, model, res_id, context) or ''
if template.user_signature:
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
description += '\n' + signature
vals['description'] = description
vals['report_name'] = self.get_template_value(cr, uid, template.report_name, model, res_id, context)
return {'value':vals}
email_template_preview()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Email Template Preview -->
<record model="ir.ui.view" id="email_template_preview_form">
<field name="name">email_template.preview.form</field>
<field name="model">email_template.preview</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email Preview">
<group col="4" colspan="4">
<field name="model_id" readonly="1"/>
<field name="res_id" on_change="on_change_ref(res_id, context)"/>
</group>
<group col="8" colspan="4">
<field name="email_to" readonly="1"/>
<field name="email_cc" readonly="1"/>
<field name="email_bcc" readonly="1"/>
<field name="reply_to" readonly="1"/>
<field name="message_id" readonly="1" attrs="{'invisible':[('message_id','=',False)]}" groups="base.group_extended"/>
<field name="subject" colspan="8" readonly="1"/>
</group>
<group col="4" colspan="4">
<separator string= "Body" colspan="2" />
<!--<separator string= "Body(Html)" colspan="2" />-->
<newline/>
<field name="description" nolabel="1" colspan="2" readonly="1"/>
<!-- <field name="body_html" nolabel="1" colspan="2" readonly="1"/>-->
</group>
<field name="report_name" colspan="2" readonly="1"/>
<button icon="gtk-ok" special="cancel" string="OK" colspan="1"/>
</form>
</field>
</record>
<record id="wizard_email_template_preview" model="ir.actions.act_window">
<field name="name">Template Preview</field>
<field name="res_model">email_template.preview</field>
<field name="src_model">email_template.preview</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="auto_refresh" eval="1" />
<field name="target">new</field>
<field name="context">{'template_id':active_id}</field>
</record>
</data>
</openerp>
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>)
# 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
@@ -21,165 +21,125 @@
##############################################################################
from osv import osv, fields
from mako.template import Template
from mako import exceptions
import netsvc
import base64
from tools.translate import _
import tools
from email_template.email_template import get_value
## FIXME: this wizard duplicates a lot of features of the email template preview,
## one of the 2 should inherit from the other!
class email_template_send_wizard(osv.osv_memory):
_name = 'email_template.send.wizard'
_inherit = 'email.template'
_description = 'This is the wizard for sending mail'
_rec_name = "subject"
def _get_accounts(self, cr, uid, context=None):
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
result = super(email_template_send_wizard, self).default_get(cr, uid, fields, context=context)
template = self._get_template(cr, uid, context)
if not template:
return []
template_pool = self.pool.get('email.template')
model_pool = self.pool.get('ir.model')
template_id=context.get('template_id', False)
template = template_pool.get_email_template(cr, uid, template_id=template_id, context=context)
def _get_template_value(field):
if not template:
return False
if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets original template values for multiple email change
return getattr(template, field)
else: # Simple Mail: Gets computed template values
return self.get_template_value(cr, uid, getattr(template, field), template.model, context.get('active_id'), context)
logger = netsvc.Logger()
if 'user_signature' in fields:
result['user_signature'] = template.user_signature
if template.from_account:
return [(template.from_account.id, '%s (%s)' % (template.from_account.name, template.from_account.email_id))]
else:
account_id = self.pool.get('email_template.account').search(cr,uid,[('company','=','no'),('user','=',uid)], context=context)
if account_id:
account = self.pool.get('email_template.account').browse(cr,uid,account_id, context)
return [(r.id,r.name + " (" + r.email_id + ")") for r in account]
else:
logger.notifyChannel(_("email-template"), netsvc.LOG_ERROR, _("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account."))
raise osv.except_osv(_("Missing mail account"),_("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account."))
if 'report_template' in fields:
result['report_template'] = template.report_template and template.report_template.id or False
def get_value(self, cursor, user, template, message, context=None, id=None):
"""Gets the value of the message parsed with the content of object id (or the first 'src_rec_ids' if id is not given)"""
if not message:
return ''
if not id:
id = context['src_rec_ids'][0]
return get_value(cursor, user, id, message, template, context)
def _get_template(self, cr, uid, context=None):
if context is None:
context = {}
if not 'template' in context and not 'template_id' in context:
return None
if 'template_id' in context.keys():
template_ids = self.pool.get('email.template').search(cr, uid, [('id','=',context['template_id'])], context=context)
elif 'template' in context.keys():
# Old versions of email_template used the name of the template. This caused
# problems when the user changed the name of the template, but we keep the code
# for compatibility with those versions.
template_ids = self.pool.get('email.template').search(cr, uid, [('name','=',context['template'])], context=context)
if not template_ids:
return None
if 'template_id' in fields:
result['template_id'] = template.id
template = self.pool.get('email.template').browse(cr, uid, template_ids[0], context)
if 'smtp_server_id' in fields:
result['smtp_server_id'] = template.smtp_server_id.id
lang = self.get_value(cr, uid, template, template.lang, context)
if lang:
# Use translated template if necessary
ctx = context.copy()
ctx['lang'] = lang
template = self.pool.get('email.template').browse(cr, uid, template.id, ctx)
return template
if 'message_id' in fields:
result['message_id'] = template.message_id
def _get_template_value(self, cr, uid, field, context=None):
if context is None:
context = {}
template = self._get_template(cr, uid, context)
if not template:
return False
if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets original template values for multiple email change
return getattr(template, field)
else: # Simple Mail: Gets computed template values
return self.get_value(cr, uid, template, getattr(template, field), context)
if 'track_campaign_item' in fields:
result['track_campaign_item'] = template.track_campaign_item
if 'attachment_ids' in fields:
result['attachment_ids'] = template_pool.read(cr, uid, template.id, ['attachment_ids'])['attachment_ids']
if 'requested' in fields:
result['requested'] = len(context.get('src_rec_ids',''))
if 'state' in fields:
result['state'] = len(context.get('src_rec_ids','')) > 1 and 'multi' or 'single'
if 'model_id' in fields:
result['model_id'] = model_pool.search(cr, uid, [('model','=',context.get('src_model'))],context=context)[0]
if 'res_id' in fields:
result['res_id'] = context['active_id']
if 'email_to' in fields:
result['email_to'] = _get_template_value('email_to')
if 'email_cc' in fields:
result['email_cc'] = _get_template_value('email_cc')
if 'email_bcc' in fields:
result['email_bcc'] = _get_template_value('email_bcc')
if 'subject' in fields:
result['subject'] = _get_template_value('subject')
if 'description' in fields:
result['description'] = _get_template_value('description')
#if 'body_html' in fields:
# result['body_html'] = _get_template_value('body_html')
if 'reply_to' in fields:
result['reply_to'] = _get_template_value('reply_to')
if 'report_name' in fields:
result['report_name'] = _get_template_value('report_name')
return result
_columns = {
'requested':fields.integer('No of requested Mails',readonly=True),
'generated':fields.integer('No of generated Mails',readonly=True),
'full_success':fields.boolean('Complete Success',readonly=True),
'attachment_ids': fields.many2many('ir.attachment','send_wizard_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'),
'state':fields.selection([
('single','Simple Mail Wizard Step 1'),
('multi','Multiple Mail Wizard Step 1'),
('done','Wizard Complete')
],'Status',readonly=True),
'ref_template':fields.many2one('email.template','Template',readonly=True),
'rel_model':fields.many2one('ir.model','Model',readonly=True),
'rel_model_ref':fields.integer('Referred Document',readonly=True),
'from':fields.selection(_get_accounts,'From Account',select=True),
'to':fields.char('To',size=250,required=True),
'cc':fields.char('CC',size=250,),
'bcc':fields.char('BCC',size=250,),
'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),
'body_text':fields.text('Body',),
'body_html':fields.text('Body',),
'report':fields.char('Report File Name',size=100,),
'signature':fields.boolean('Attach my signature to mail'),
#'filename':fields.text('File Name'),
'requested':fields.integer('No of requested Mails',readonly=True),
'generated':fields.integer('No of generated Mails',readonly=True),
'full_success':fields.boolean('Complete Success',readonly=True),
'attachment_ids': fields.many2many('ir.attachment','send_wizard_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'),
],'State',readonly=True),
}
#FIXME: probably better by overriding default_get directly
_defaults = {
'state': lambda self,cr,uid,ctx: len(ctx['src_rec_ids']) > 1 and 'multi' or 'single',
'rel_model': lambda self,cr,uid,ctx: self.pool.get('ir.model').search(cr,uid,[('model','=',ctx['src_model'])],context=ctx)[0],
'rel_model_ref': lambda self,cr,uid,ctx: ctx['active_id'],
'to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_to', ctx),
'cc': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_cc', ctx),
'bcc': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_bcc', ctx),
'subject':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_subject', ctx),
'body_text':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_body_text', ctx),
'body_html':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_body_html', ctx),
'report': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'file_name', ctx),
'signature': lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).use_sign,
'ref_template':lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).id,
'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx),
'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx),
'requested':lambda self,cr,uid,ctx: len(ctx['src_rec_ids']),
'full_success': False,
'attachment_ids': [],
}
def fields_get(self, cr, uid, fields=None, context=None, write_access=True):
if context is None:
context = {}
result = super(email_template_send_wizard, self).fields_get(cr, uid, fields, context, write_access)
if 'attachment_ids' in result and 'src_model' in context:
result['attachment_ids']['domain'] = [('res_model','=',context['src_model']),('res_id','=',context['active_id'])]
return result
#def fields_get(self, cr, uid, fields=None, context=None, write_access=True):
# if context is None:
# context = {}
# result = super(email_template_send_wizard, self).fields_get(cr, uid, fields, context, write_access)
# if 'attachment_ids' in result and 'src_model' in context:
# result['attachment_ids']['domain'] = [('res_model','=',context['src_model']),('res_id','=',context['active_id'])]
# return result
def sav_to_drafts(self, cr, uid, ids, context=None):
def save_to_drafts(self, cr, uid, ids, context=None):
if context is None:
context = {}
mailid = self.save_to_mailbox(cr, uid, ids, context=context)
if self.pool.get('email_template.mailbox').write(cr, uid, mailid, {'folder':'drafts'}, context):
return {'type':'ir.actions.act_window_close' }
self.pool.get('email.message').write(cr, uid, mailid, {'folder':'drafts', 'state': 'draft'}, context)
return {'type': 'ir.actions.act_window_close'}
def send_mail(self, cr, uid, ids, context=None):
if context is None:
context = {}
mailid = self.save_to_mailbox(cr, uid, ids, context)
if self.pool.get('email_template.mailbox').write(cr, uid, mailid, {'folder':'outbox'}, context):
return {'type':'ir.actions.act_window_close' }
return {'type': 'ir.actions.act_window_close'}
def get_generated(self, cr, uid, ids=None, context=None):
if ids is None:
@@ -187,11 +147,10 @@ class email_template_send_wizard(osv.osv_memory):
if context is None:
context = {}
logger = netsvc.Logger()
if context['src_rec_ids'] and len(context['src_rec_ids'])>1:
if context.get('src_rec_ids'):
#Means there are multiple items selected for email.
mail_ids = self.save_to_mailbox(cr, uid, ids, context)
if mail_ids:
self.pool.get('email_template.mailbox').write(cr, uid, mail_ids, {'folder':'outbox'}, context)
logger.notifyChannel("email-template", netsvc.LOG_INFO, _("Emails for multiple items saved in outbox."))
self.write(cr, uid, ids, {
'generated':len(mail_ids),
@@ -200,80 +159,21 @@ class email_template_send_wizard(osv.osv_memory):
else:
raise osv.except_osv(_("Email Template"),_("Email sending failed for one or more objects."))
return True
def save_to_mailbox(self, cr, uid, ids, context=None):
def get_end_value(id, value):
if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets value from the template
return self.get_value(cr, uid, template, value, context, id)
else:
return value
#def get_end_value(id, value):
# if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets value from the template
# return self.get_value(cr, uid, template, value, context, id)
# else:
# return value
if context is None:
context = {}
mail_ids = []
template = self._get_template(cr, uid, context)
for id in context['src_rec_ids']:
screen_vals = self.read(cr, uid, ids[0], [],context)
account = self.pool.get('email_template.account').read(cr, uid, screen_vals['from'], context=context)
vals = {
'email_from': tools.ustr(account['name']) + "<" + tools.ustr(account['email_id']) + ">",
'email_to': get_end_value(id, screen_vals['to']),
'email_cc': get_end_value(id, screen_vals['cc']),
'email_bcc': get_end_value(id, screen_vals['bcc']),
'subject': get_end_value(id, screen_vals['subject']),
'body_text': get_end_value(id, screen_vals['body_text']),
'body_html': get_end_value(id, screen_vals['body_html']),
'account_id': screen_vals['from'],
'state':'na',
'mail_type':'multipart/alternative' #Options:'multipart/mixed','multipart/alternative','text/plain','text/html'
}
if screen_vals['signature']:
signature = self.pool.get('res.users').read(cr, uid, uid, ['signature'], context)['signature']
if signature:
vals['body_text'] = tools.ustr(vals['body_text'] or '') + signature
vals['body_html'] = tools.ustr(vals['body_html'] or '') + signature
email_ids = []
for template in self.browse(cr, uid, ids, context=context):
for record_id in context.get('src_rec_ids',[]):
email_id = self._generate_email(cr, uid, template.id, record_id, context)
email_ids.append(email_id)
return email_ids
attachment_ids = []
#Create partly the mail and later update attachments
mail_id = self.pool.get('email_template.mailbox').create(cr, uid, vals, context)
mail_ids.append(mail_id)
if template.report_template:
reportname = 'report.' + self.pool.get('ir.actions.report.xml').read(cr, uid, template.report_template.id, ['report_name'], context)['report_name']
data = {}
data['model'] = self.pool.get('ir.model').browse(cr, uid, screen_vals['rel_model'], context).model
# Ensure report is rendered using template's language
ctx = context.copy()
if template.lang:
ctx['lang'] = self.get_value(cr, uid, template, template.lang, context, id)
service = netsvc.LocalService(reportname)
(result, format) = service.create(cr, uid, [id], data, ctx)
attachment_id = self.pool.get('ir.attachment').create(cr, uid, {
'name': _('%s (Email Attachment)') % tools.ustr(vals['subject']),
'datas': base64.b64encode(result),
'datas_fname': tools.ustr(get_end_value(id, screen_vals['report']) or _('Report')) + "." + format,
'description': vals['body_text'] or _("No Description"),
'res_model': 'email_template.mailbox',
'res_id': mail_id
}, context)
attachment_ids.append( attachment_id )
# Add document attachments
for attachment_id in screen_vals.get('attachment_ids',[]):
new_id = self.pool.get('ir.attachment').copy(cr, uid, attachment_id, {
'res_model': 'email_template.mailbox',
'res_id': mail_id,
}, context)
attachment_ids.append( new_id )
if attachment_ids:
self.pool.get('email_template.mailbox').write(cr, uid, mail_id, {
'attachments_ids': [[6, 0, attachment_ids]],
'mail_type': 'multipart/mixed'
}, context)
return mail_ids
email_template_send_wizard()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -2,62 +2,65 @@
<openerp>
<data>
<record model="ir.ui.view" id="email_template_send_wizard_form">
<field name="name">email_template.send.wizard.form</field>
<field name="model">email_template.send.wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Send mail Wizard">
<group col="4" colspan="4">
<field name="rel_model" colspan="2" />
<field name="from" required="1" colspan="2" />
</group>
<group col="4" colspan="4">
<group col="6" colspan="4">
<field name="to" select="1" colspan="4" />
<newline />
<field name="cc" select="2" colspan="4" />
<newline />
<field name="bcc" select="2" colspan="4" />
<newline />
<field name="subject" select="2" colspan="4" attrs="{'required':[('state','=','single')]}" />
<newline />
<field name="report" colspan="4" />
</group>
<separator string="" colspan="4" />
<notebook colspan="4">
<page string="Body (Plain Text)">
<field name="body_text" select="2" colspan="4" nolabel="1" />
</page>
<page string="Body (HTML)">
<field name="body_html" select="2" 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"/>
<field name="attachment_ids" colspan="4" nolabel="1"/>
</page>
</notebook>
<field name="signature" colspan="4" />
</group>
<group col="4" colspan="4" attrs="{'invisible':[('state','!=','single')]}">
<button icon="gtk-apply" name="sav_to_drafts" string="Save in Drafts" type="object" />
<button icon="gtk-ok" name="send_mail" string="Send now" type="object" />
<button icon="gtk-cancel" special="cancel" string="Discard Mail" />
</group>
<group col="4" colspan="4" attrs="{'invisible':[('state','=','single')]}">
<label string="Tip: Multiple emails are sent in the same language (the first one is proposed). We suggest you send emails in groups according to language." colspan="4"/>
<field name="requested" />
<field name="generated" />
<button icon="gtk-ok" name="get_generated" string="Send all mails" type="object" states="multi" colspan="2" />
<button icon="gtk-cancel" special="cancel" string="Discard Mail" colspan="2" states="multi" />
</group>
<button icon="gtk-ok" special="cancel" string="Close" colspan="4" states="done" />
<field name="state" />
<newline />
<label string="After clicking send all mails, mails will be sent to outbox and cleared in next Send/Recieve" colspan="4"/>
</form>
</field>
</record>
<record model="ir.ui.view" id="email_template_send_wizard_form">
<field name="name">email_template.send.wizard.form</field>
<field name="model">email_template.send.wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Send Email Wizard">
<group col="4" colspan="4">
<field name="smtp_server_id" widget="selection" required="1" colspan="4"/>
</group>
<group col="4" colspan="4">
<group col="6" colspan="4">
<field name="email_to" colspan="4"/>
<field name="email_cc" colspan="4"/>
<field name="email_bcc" colspan="4"/>
<field name="subject" colspan="4" attrs="{'required':[('state','=','single')]}"/>
<field name="report_name" colspan="4"/>
<field name="user_signature" colspan="4"/>
</group>
<separator string="" colspan="4"/>
<notebook colspan="4">
<page string="Body">
<field name="description" colspan="4" nolabel="1"/>
</page>
<!-- <page string="Body (HTML)">
<field name="body_html" 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"/>
<field name="attachment_ids" colspan="4" nolabel="1"/>
</page>
</notebook>
</group>
<field name="state" invisible="1"/>
<group col="4" colspan="4" attrs="{'invisible':[('state','=','single')]}">
<field name="requested"/>
<field name="generated"/>
<newline/>
<label string="After clicking Send All, emails will be sent to outbox and cleared in next Send/Recieve" colspan="4"/>
<label string="Tip: Multiple emails are sent in the same language (the first one is proposed). We suggest you send emails in groups according to language." colspan="4"/>
<newline/>
</group>
<label string="" colspan="2"/>
<group col="4" colspan="2" attrs="{'invisible':[('state','=','single')]}">
<button icon="gtk-cancel" special="cancel" string="Discard" states="multi"/>
<button icon="gtk-ok" name="get_generated" string="Send All" type="object" states="multi"/>
<button icon="gtk-ok" special="cancel" string="Close" states="done"/>
</group>
<group col="4" colspan="2" attrs="{'invisible':[('state','!=','single')]}">
<button icon="gtk-cancel" special="cancel" string="Discard"/>
<button icon="gtk-apply" name="save_to_drafts" string="Save in Drafts" type="object"/>
<button icon="gtk-ok" name="send_mail" string="Send now" type="object"/>
</group>
</form>
</field>
</record>
</data>
</openerp>
+1 -1
View File
@@ -39,7 +39,7 @@
Events / Reporting
""",
'author': 'OpenERP SA',
'depends': ['crm', 'base_contact', 'account', 'marketing'],
'depends': ['crm', 'base_contact', 'account', 'marketing', 'email'],
'init_xml': [],
'update_xml': [
'security/event_security.xml',
+7 -5
View File
@@ -24,8 +24,10 @@ import time
from crm import crm
from osv import fields, osv
from tools.translate import _
import tools
import decimal_precision as dp
from crm import wizard
wizard.email_compose_message.email_model.append('event.registration')
class event_type(osv.osv):
""" Event Type """
@@ -313,8 +315,8 @@ class event_registration(osv.osv):
'create_date': fields.datetime('Creation Date', readonly=True),
'write_date': fields.datetime('Write Date', readonly=True),
'description': fields.text('Description', states={'done': [('readonly', True)]}),
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
'message_ids': fields.one2many('email.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'log_ids': fields.one2many('email.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
'date_deadline': fields.related('event_id','date_end', type='datetime', string="End Date", readonly=True),
'date': fields.related('event_id', 'date_begin', type='datetime', string="Start Date", readonly=True),
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
@@ -521,7 +523,7 @@ class event_registration(osv.osv):
"""
Send email to user
"""
email_message_obj = self.pool.get('email.message')
for regestration in self.browse(cr, uid, ids, context=context):
src = regestration.event_id.reply_to or False
email_to = []
@@ -545,7 +547,7 @@ class event_registration(osv.osv):
subject = _('Auto Confirmation: [%s] %s') %(regestration.id, regestration.name)
body = regestration.event_id.mail_confirm
if subject or body:
tools.email_send(src, email_to, subject, body, email_cc=email_cc, openobject_id=regestration.id)
email_message_obj.email_send(cr, uid, src, email_to, subject, body, model='event.registration', email_cc=email_cc, openobject_id=regestration.id)
self.history(cr, uid, [regestration], subject, history = True, \
email=email_to, details=body, \
subject=subject, email_from=src, \
+14 -15
View File
@@ -391,11 +391,11 @@
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="history" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'event.registration', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button
string="Reply" attrs="{'invisible': [('history', '!=', True)]}"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="History">
<group col="4" colspan="4">
@@ -410,11 +410,10 @@
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'event.registration', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
<button colspan="4" string="Reply"
name="%(email.action_email_compose_message_wizard)d"
context="{'mail':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action"/>
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
@@ -426,14 +425,14 @@
</notebook>
</form>
</field>
<button string="Add Internal Note"
<button string="Add Internal Note"
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'new', 'model': 'event.registration'}"
icon="terp-mail-message-new" type="action" />
<button string="Send New Email"
name="%(email.action_email_compose_message_wizard)d"
context="{'email_model':'event.registration', 'email_res_id': active_id}"
icon="terp-mail-message-new" type="action"/>
</page>
</notebook>
</form>
+3 -3
View File
@@ -2,7 +2,7 @@
#-*- coding:utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# mga@tinyerp.com
#
@@ -24,9 +24,9 @@
{
"name" : "Fetchmail Server",
"version" : "1.0",
"depends" : ["base", 'mail_gateway'],
"depends" : ["base", 'email'],
"author" : "OpenERP SA",
"description": """Fetchmail:
"description": """Fetchmail:
* Fetch email from Pop / IMAP server
* Support SSL
* Integrated with all Modules
+29 -37
View File
@@ -62,7 +62,7 @@ class email_server(osv.osv):
'object_id': fields.many2one('ir.model', "Model", required=True, help="OpenObject Model. Generates a record of this model.\nSelect Object with message_new attrbutes."),
'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Priority between 0 to 10, select define the order of Processing"),
'user_id':fields.many2one('res.users', 'User', required=False),
'message_ids': fields.one2many('mailgate.message', 'server_id', 'Messages', readonly=True),
'message_ids': fields.one2many('email.message', 'server_id', 'Messages', readonly=True),
}
_defaults = {
'state': lambda *a: "draft",
@@ -111,13 +111,13 @@ class email_server(osv.osv):
def set_draft(self, cr, uid, ids, context=None):
self.write(cr, uid, ids , {'state':'draft'})
return True
def button_confirm_login(self, cr, uid, ids, context=None):
if context is None:
context = {}
for server in self.browse(cr, uid, ids, context=context):
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail start checking for new emails on %s' % (server.name))
context.update({'server_id': server.id, 'server_type': server.type})
logger.notifyChannel(server.type, netsvc.LOG_INFO, 'fetchmail start checking for new emails on %s' % (server.name))
context.update({'server_id': server.id})
try:
if server.type == 'imap':
imap_server = None
@@ -128,7 +128,7 @@ class email_server(osv.osv):
imap_server.login(server.user, server.password)
ret_server = imap_server
elif server.type == 'pop':
pop_server = None
if server.is_ssl:
@@ -141,7 +141,7 @@ class email_server(osv.osv):
pop_server.user(server.user)
pop_server.pass_(server.password)
ret_server = pop_server
self.write(cr, uid, [server.id], {'state':'done'})
if context.get('get_server',False):
return ret_server
@@ -167,8 +167,8 @@ class email_server(osv.osv):
for server in self.browse(cr, uid, ids, context=context):
count = 0
user = server.user_id.id or uid
try:
if server.type == 'imap':
if server.type == 'imap':
try:
imap_server = self.button_confirm_login(cr, uid, [server.id], context=context)
imap_server.select()
result, data = imap_server.search(None, '(UNSEEN)')
@@ -180,14 +180,19 @@ class email_server(osv.osv):
imap_server.store(num, '+FLAGS', '\\Seen')
count += 1
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch/process %s email(s) from %s' % (count, server.name))
logger.notifyChannel(server.type, netsvc.LOG_INFO, 'fetchmail fetch/process %s email(s) from %s' % (count, server.name))
imap_server.close()
imap_server.logout()
elif server.type == 'pop':
except Exception, e:
logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (tools.ustr(e)))
finally:
if imap_server:
imap_server.close()
imap_server.logout()
elif server.type == 'pop':
try:
pop_server = self.button_confirm_login(cr, uid, [server.id], context=context)
pop_server.list()
(numMsgs, totalSize) = pop_server.stat()
pop_server.list()
for num in range(1, numMsgs + 1):
(header, msges, octets) = pop_server.retr(num)
msg = '\n'.join(msges)
@@ -196,55 +201,42 @@ class email_server(osv.osv):
action_pool.run(cr, user, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
pop_server.dele(num)
pop_server.quit()
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch %s email(s) from %s' % (numMsgs, server.name))
except Exception, e:
logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (tools.ustr(e)))
logger.notifyChannel(server.type, netsvc.LOG_INFO, 'fetchmail fetch %s email(s) from %s' % (numMsgs, server.name))
except Exception, e:
logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (tools.ustr(e)))
finally:
if pop_server:
pop_server.quit()
return True
email_server()
class mailgate_message(osv.osv):
class email_message(osv.osv):
_inherit = "mailgate.message"
_inherit = "email.message"
_columns = {
'server_id': fields.many2one('email.server', "Mail Server", readonly=True, select=True),
'server_type':fields.selection([
('pop', 'POP Server'),
('imap', 'IMAP Server'),
], 'Server Type', select=True, readonly=True),
}
_order = 'id desc'
def create(self, cr, uid, values, context=None):
if context is None:
context={}
server_id = context.get('server_id',False)
server_type = context.get('server_type',False)
if server_id:
values['server_id'] = server_id
if server_type:
values['server_type'] = server_type
res = super(mailgate_message,self).create(cr, uid, values, context=context)
res = super(email_message,self).create(cr, uid, values, context=context)
return res
def write(self, cr, uid, ids, values, context=None):
if context is None:
context={}
server_id = context.get('server_id',False)
server_type = context.get('server_type',False)
if server_id:
values['server_id'] = server_id
if server_type:
values['server_type'] = server_type
res = super(mailgate_message,self).write(cr, uid, ids, values, context=context)
res = super(email_message,self).write(cr, uid, ids, values, context=context)
return res
mailgate_message()
email_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+27 -34
View File
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_email_server_tree">
<field name="name">email.server.tree</field>
<field name="model">email.server</field>
@@ -18,7 +18,7 @@
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_email_server_form">
<field name="name">email.server.form</field>
<field name="model">email.server</field>
@@ -68,7 +68,7 @@
</form>
</field>
</record>
<record model="ir.ui.view" id="view_email_server_search">
<field name="name">email.server.search</field>
<field name="model">email.server</field>
@@ -91,9 +91,9 @@
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_server_tree">
<field name="name">Email Servers</field>
<field name="name">Incomming Servers</field>
<field name="res_model">email.server</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
@@ -101,58 +101,51 @@
<field name="search_view_id" ref="view_email_server_search"/>
</record>
<menuitem
parent="base.menu_mail_gateway"
<menuitem
parent="email.menu_config_email"
id="menu_action_fetchmail_server_tree"
action="action_email_server_tree"
name="Email Servers"
action="action_email_server_tree"
name="Incomming Servers"
/>
<record model="ir.ui.view" id="mailgate_message_tree_view">
<field name="name">mailgate.message.tree</field>
<field name="model">mailgate.message</field>
<record model="ir.ui.view" id="email_message_tree_view">
<field name="name">email.message.tree</field>
<field name="model">email.message</field>
<field name="type">tree</field>
<field name="inherit_id" ref="mail_gateway.view_mailgate_message_tree"/>
<field name="inherit_id" ref="email.view_email_message_tree"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="server_id" select="1"/>
<field name="ref_id"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="mailgate_message_search_view">
<field name="name">mailgate.message.inherit.search</field>
<field name="model">mailgate.message</field>
<record model="ir.ui.view" id="email_message_search_view">
<field name="name">email.message.inherit.search</field>
<field name="model">email.message</field>
<field name="type">search</field>
<field name="inherit_id" ref="mail_gateway.view_mailgate_message_search"/>
<field name="inherit_id" ref="email.view_email_message_search"/>
<field name="arch" type="xml">
<xpath expr="/search/field[@name='name']" position="before">
<filter string="Emails" name="emails" domain="[('server_id','!=',False)]" icon="terp-mail-message-new"/>
<separator orientation="vertical"/>
</xpath>
<xpath expr="/search/group/filter[@string='Thread']" position="before">
<filter string="Mail Server" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'server_id'}"/>
</xpath>
</field>
</record>
<record id="action_view_mail_message_emails" model="ir.actions.act_window">
<field name="name">Messages</field>
<field name="res_model">mailgate.message</field>
<field name="res_model">email.message</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_emails': 1}</field>
<field name="search_view_id" ref="mailgate_message_search_view"/>
<field name="search_view_id" ref="email_message_search_view"/>
</record>
<menuitem id="base.menu_email_gateway_form"
parent="base.menu_mail_gateway" action="action_view_mail_message_emails" />
<act_window
context="{'search_default_server_id': [active_id], 'default_server_id': active_id}"
<act_window
context="{'server_id': active_id}"
domain="[('server_id', '=', active_id)]"
id="act_server_history" name="Emails"
res_model="mailgate.message" src_model="email.server"/>
res_model="email.message" src_model="email.server"/>
</data>
</openerp>
+2 -2
View File
@@ -25,7 +25,7 @@
"author": "OpenERP SA",
"category": "Generic Modules/Human Resources",
"website": "http://www.openerp.com",
"depends": ["hr",'hr_recruitment','survey'],
"depends": ["hr","hr_recruitment","survey"],
"description": """
Ability to create employees evaluation.
An evaluation can be created by employee for subordinates,
@@ -41,7 +41,7 @@
"update_xml": [
"security/ir.model.access.csv",
"security/hr_evaluation_security.xml",
"wizard/hr_evaluation_mail_view.xml",
# "wizard/hr_evaluation_mail_view.xml",
"hr_evaluation_view.xml",
"report/hr_evaluation_report_view.xml",
"board_hr_evaluation_view.xml"
+2 -2
View File
@@ -24,7 +24,6 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta
from dateutil import parser
from osv import fields, osv
import tools
from tools.translate import _
class hr_evaluation_plan(osv.osv):
@@ -193,6 +192,7 @@ class hr_evaluation(osv.osv):
return {'value': {'plan_id':evaluation_plan_id}}
def button_plan_in_progress(self, cr, uid, ids, context=None):
email_message_obj = self.pool.get('email.message')
hr_eval_inter_obj = self.pool.get('hr.evaluation.interview')
if context is None:
context = {}
@@ -229,7 +229,7 @@ class hr_evaluation(osv.osv):
sub = phase.email_subject
dest = [child.work_email]
if dest:
tools.email_send(evaluation.employee_id.work_email, dest, sub, body)
email_message_obj.email_send(cr, uid, evaluation.employee_id.work_email, dest, sub, body, model='hr_evaluation.evaluation')
self.write(cr, uid, ids, {'state':'wait'}, context=context)
return True
+14 -1
View File
@@ -401,10 +401,23 @@
<field name="act_window_id" ref="action_hr_evaluation_interview_tree"/>
</record>
<!-- Email Compose message Action-->
<record id="action_email_compose_message_menu_wizard" model="ir.actions.act_window">
<field name="name">Send E-mail</field>
<field name="res_model">email.compose.message</field>
<field name="src_model">email.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="context">{'email_model':'hr.evaluation.interview'}</field>
<field name="target">new</field>
</record>
<menuitem name="Interview Requests" parent="menu_eval_hr" id="menu_open_hr_evaluation_interview_requests"
action="action_hr_evaluation_interview_tree"/>
<menuitem name="Evaluation Reminders" parent="menu_eval_hr" id="menu_eval_send_mail"
action="action_hr_evaluation_send_mail" sequence="45" groups="base.group_hr_manager"/>
action="action_email_compose_message_menu_wizard" sequence="45" groups="base.group_hr_manager"/>
<!-- Evaluation Interviews Button on Employee Form -->
<act_window
+2 -1
View File
@@ -19,6 +19,7 @@
#
##############################################################################
import hr_evaluation_mail
#import hr_evaluation_mail
import email_compose_message
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,71 @@
# -*- 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.translate import _
class email_compose_message(osv.osv_memory):
_inherit = 'email.compose.message'
def _get_records(self, cr, uid, context=None):
"""
Return Records of particular Model
"""
if context is None:
context = {}
record_ids = []
if context.get('email_model',False) and context.get('email_model') == 'hr.evaluation.interview':
model_pool = self.pool.get(context.get('email_model'))
record_ids = model_pool.search(cr, uid, [('state','=','waiting_answer')])
return model_pool.name_get(cr, uid, record_ids, context)
else:
return super(email_compose_message, self)._get_records(cr, uid, context=context)
_columns = {
'res_id':fields.selection(_get_records, 'Referred Document'),
}
def get_value(self, cr, uid, model, resource_id, context=None):
if context is None:
context = {}
result = super(email_compose_message, self).get_value(cr, uid, model, resource_id, context=context)
if model == 'hr.evaluation.interview' and resource_id:
model_pool = self.pool.get(model)
record_data = model_pool.browse(cr, uid, resource_id, context)
if record_data.state == "waiting_answer":
msg = _("Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks,") %(record_data.user_to_review_id.name, record_data.survey_id.title)
result.update({
'email_from': tools.config.get('email_from',''),
'email_to': record_data.user_to_review_id.work_email or False,
'name': _("Reminder to fill up Survey"),
'description': msg,
'res_id': resource_id,
'email_cc': False,
'email_bcc': False,
'reply_to': False,
})
return result
email_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -29,13 +29,14 @@ class hr_evaluation_reminder(osv.osv_memory):
}
def send_mail(self, cr, uid, ids, context=None):
email_message_obj = self.pool.get('email.message')
hr_evaluation_interview_obj = self.pool.get('hr.evaluation.interview')
evaluation_data = self.read(cr, uid, ids, context=context)[0]
current_interview = hr_evaluation_interview_obj.browse(cr, uid, evaluation_data.get('evaluation_id'))
if current_interview.state == "waiting_answer" and current_interview.user_to_review_id.work_email :
msg = " Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks," %(current_interview.user_to_review_id.name, current_interview.survey_id.title)
tools.email_send(tools.config['email_from'], [current_interview.user_to_review_id.work_email],\
'Reminder to fill up Survey', msg)
email_message_obj.email_send(cr, uid, tools.config['email_from'], [current_interview.user_to_review_id.work_email],\
'Reminder to fill up Survey', msg, model='hr.evaluation.reminder')
return {'type': 'ir.actions.act_window_close'}
hr_evaluation_reminder()
+4 -1
View File
@@ -29,6 +29,9 @@ import collections
import binascii
import tools
from tools.translate import _
from crm import wizard
wizard.email_compose_message.email_model.append('hr.applicant')
AVAILABLE_STATES = [
('draft', 'New'),
@@ -117,7 +120,7 @@ class hr_applicant(crm.crm_case, osv.osv):
_columns = {
'name': fields.char('Name', size=128, required=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)]),
'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."),
'description': fields.text('Description'),
'email_from': fields.char('Email', size=128, help="These people will receive email."),

Some files were not shown because too many files have changed in this diff Show More