[NEW] email_template (extracted from poweremail) just to send emails -specially for the marketing_campaign

bzr revid: dsh@tinyerp.com-20100602143627-n0v33g88homb8p70
This commit is contained in:
DSH (Open ERP) 2010-06-02 20:06:27 +05:30
parent fbf74a6937
commit 394e10020d
16 changed files with 2763 additions and 0 deletions

View File

@ -0,0 +1,5 @@
import email_template_account
import email_template
import email_template_mailbox
import wizard

View File

@ -0,0 +1,24 @@
{
"name" : "Email Template for Open ERP",
"version" : "0.7 RC",
"author" : "Open ERP",
"website" : "http://openerp.com",
"category" : "Added functionality",
"depends" : ['base'],
"description": """
Email Template is extraction of Power Email basically just to send the emails.
""",
"init_xml": ['email_template_scheduler_data.xml'],
"update_xml": [
'security/email_template_security.xml',
'email_template_workflow.xml',
'email_template_account_view.xml',
'email_template_view.xml',
'email_template_mailbox_view.xml',
'wizard/email_template_send_wizard_view.xml',
],
"installable": True,
"active": False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,698 @@
import base64
import random
import time
import types
import netsvc
LOGGER = netsvc.Logger()
TEMPLATE_ENGINES = []
from osv import osv, fields
from tools.translate import _
from mako.template import Template #For backward combatibility
try:
from mako.template import Template as MakoTemplate
from mako import exceptions
TEMPLATE_ENGINES.append(('mako', 'Mako Templates'))
except:
LOGGER.notifyChannel(
_("Email Template"),
netsvc.LOG_ERROR,
_("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:
LOGGER.notifyChannel(
_("Email Template"),
netsvc.LOG_ERROR,
_("Django templates not installed")
)
import email_template_engines
import tools
import report
import pooler
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: Open ERP 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)
env = {
'user':pool.get('res.users').browse(cursor, user, user, 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:
return u""
else:
return message
class email_template(osv.osv):
"Templates for sending Email"
_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}
}
_columns = {
'name' : fields.char('Name of Template', size=100, required=True),
'object_name':fields.many2one('ir.model', 'Model'),
'model_int_name':fields.char('Model Internal Name', size=200,),
'def_to':fields.char(
'Recepient (To)',
size=250,
help="The default recepient of email."
"Placeholders can be used here."),
'def_cc':fields.char(
'Default CC',
size=250,
help="The default CC for the email."
" Placeholders can be used here."),
'def_bcc':fields.char(
'Default BCC',
size=250,
help="The default BCC for the email."
" Placeholders can be used here."),
'lang':fields.char(
'Language',
size=250,
help="The default language for the email."
" Placeholders can be used here. "
"eg. ${object.partner_id.lang}"),
'def_subject':fields.char(
'Default Subject',
size=200,
help="The default 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(
'Use Signature',
help="the signature from the User details"
"will be appened to the mail"),
'file_name':fields.char(
'File Name Pattern',
size=200,
help="File name pattern can be specified with placeholders."
"eg. 2009_SO003.pdf",
translate=True),
'report_template':fields.many2one(
'ir.actions.report.xml',
'Report to send'),
'ref_ir_act_window':fields.many2one(
'ir.actions.act_window',
'Window Action',
readonly=True),
'ref_ir_value':fields.many2one(
'ir.values',
'Wizard Button',
readonly=True),
'allowed_groups':fields.many2many(
'res.groups',
'template_group_rel',
'templ_id', 'group_id',
string="Allowed User Groups",
help="Only users from these groups will be"
" allowed to send mails from this Template"),
'enforce_from_account':fields.many2one(
'email_template.account',
string="Enforce From Account",
help="Emails will be sent only from this account.",
domain="[('company','=','yes')]"),
'model_object_field':fields.many2one(
'ir.model.fields',
string="Field",
help="Select the field from the model you want to use."
"\nIf it is a relationship field you will be able to "
"choose the nested values in the box below\n(Note:If "
"there are no values make sure you have selected the"
" correct model)",
store=False),
'sub_object':fields.many2one(
'ir.model',
'Sub-model',
help='When a relation field is used this field'
' will show you the type of field you have selected',
store=False),
'sub_model_object_field':fields.many2one(
'ir.model.fields',
'Sub Field',
help="When you choose relationship fields "
"this field will specify the sub value you can use.",
store=False),
'null_value':fields.char(
'Null Value',
help="This Value is used if the field is empty",
size=50, store=False),
'copyvalue':fields.char(
'Expression',
size=100,
help="Copy and paste the value in the "
"location you want to use a system value.",
store=False),
'table_html':fields.text(
'HTML code',
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 = {
}
_sql_constraints = [
('name', 'unique (name)', _('The template name must be unique !'))
]
def create(self, cr, uid, vals, context=None):
id = super(email_template, self).create(cr, uid, vals, context)
src_obj = self.pool.get('ir.model').read(cr, uid, vals['object_name'], ['model'], context)['model']
vals['ref_ir_act_window'] = self.pool.get('ir.actions.act_window').create(cr, uid, {
'name': _("%s Mail Form") % vals['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, 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)') % vals['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, id, {
'ref_ir_act_window': vals['ref_ir_act_window'],
'ref_ir_value': vals['ref_ir_value'],
}, context)
return id
def unlink(self, cr, uid, ids, context=None):
for template in self.browse(cr, uid, ids, context):
obj = self.pool.get(template.object_name.model)
try:
if template.ref_ir_act_window:
self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
if template.ref_ir_value:
self.pool.get('ir.values').unlink(cr, uid, template.ref_ir_value.id, context)
except:
raise osv.except_osv(_("Warning"), _("Deletion of Record failed"))
return super(email_template, self).unlink(cr, uid, ids, context)
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
default = default.copy()
old = self.read(cr, uid, id, ['name'], context=context)
new_name = _("Copy of template ") + old.get('name', 'No Name')
check = self.search(cr, uid, [('name', '=', new_name)], context=context)
if check:
new_name = new_name + '_' + random.choice('abcdefghij') + random.choice('lmnopqrs') + random.choice('tuvwzyz')
default.update({'name':new_name})
return super(email_template, self).copy(cr, uid, id, default, context)
def compute_pl(self,
model_object_field,
sub_model_object_field,
null_value, template_language='mako'):
"""
Returns the expression based on data provided
@param model_object_field: First level field
@param sub_model_object_field: Second level drilled down field (M2O)
@param null_value: What has to be returned if the value is empty
@param template_language: The language used for templating
@return: computed expression
"""
#Configure for MAKO
copy_val = ''
if template_language == 'mako':
if model_object_field:
copy_val = "${object." + model_object_field
if sub_model_object_field:
copy_val += "." + sub_model_object_field
if null_value:
copy_val += " or '" + null_value + "'"
if model_object_field:
copy_val += "}"
elif template_language == 'django':
if model_object_field:
copy_val = "{{object." + model_object_field
if sub_model_object_field:
copy_val += "." + sub_model_object_field
if null_value:
copy_val = copy_val + '|default:"' + null_value + '"'
copy_val = copy_val + "}}"
return copy_val
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.compute_pl(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.compute_pl(field_obj.name,
False,
False,
template_language
)
result['sub_model_object_field'] = False
result['null_value'] = False
return {'value':result}
def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, template_language, context=None):
if not model_object_field or not sub_model_object_field:
return {}
result = {}
field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
if res_ids:
result['sub_object'] = res_ids[0]
result['copyvalue'] = self.compute_pl(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.compute_pl(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.compute_pl(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.compute_pl(field_obj.name,
False,
null_value,
template_language
)
result['sub_model_object_field'] = False
result['null_value'] = null_value
return {'value':result}
def generate_attach_reports(self,
cursor,
user,
template,
record_id,
mail,
context=None):
"""
Generate report to be attached and attach it
to the email
@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
"""
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)
attachment_obj = self.pool.get('ir.attachment')
new_att_vals = {
'name':mail.subject + ' (Email Attachment)',
'datas':base64.b64encode(result),
'datas_fname':tools.ustr(
get_value(
cursor,
user,
record_id,
template.file_name,
template,
context
) or 'Report') + "." + format,
'description':mail.subject or "No Description",
'res_model':'email_template.mailbox',
'res_id':mail.id
}
attachment_id = attachment_obj.create(cursor,
user,
new_att_vals,
context)
if attachment_id:
self.pool.get('email_template.mailbox').write(
cursor,
user,
mail.id,
{
'attachments_ids':[
[6, 0, [attachment_id]]
],
'mail_type':'multipart/mixed'
},
context)
return True
def generate_mailbox_item_from_template(self,
cursor,
user,
template,
record_id,
context=None):
"""
Generates an email from the template for
record record_id of target object
@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
)
else:
from_account = {
'id':template.enforce_from_account.id,
'name':template.enforce_from_account.name,
'email_id':template.enforce_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)
mailbox_values = {
'email_from': tools.ustr(from_account['name']) + \
"<" + tools.ustr(from_account['email_id']) + ">",
'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),
'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'
}
#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)
return mailbox_id
def generate_mail(self,
cursor,
user,
template_id,
record_ids,
context=None):
if context is None:
context = {}
template = self.browse(cursor, user, template_id, context=context)
if not template:
raise Exception("The requested template could not be loaded")
for record_id in record_ids:
mailbox_id = self._generate_mailbox_item_from_template(
cursor,
user,
template,
record_id,
context)
mail = self.pool.get('email_template.mailbox').browse(
cursor,
user,
mailbox_id,
context=context
)
if template.report_template:
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
)
return True
email_template()
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 'active_id' in context.keys():
# context['active_id'] = 5
ref_obj_id = self.pool.get('email.template').read(cr, uid, context['active_id'], ['object_name'], context)
ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id[0], ['model'], context)['model']
ref_obj_ids = self.pool.get(ref_obj_name).search(cr, uid, [], context=context)
ref_obj_recs = self.pool.get(ref_obj_name).name_get(cr, uid, ref_obj_ids, context)
return ref_obj_recs
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: Open ERP Context
"""
return self.pool.get('email.template').read(
cursor,
user,
context['active_id'],
['object_name'],
context)['object_name']
_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),
'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['active_id'],
'rel_model': _default_model
}
def on_change_ref(self, cr, uid, ids, rel_model_ref, context=None):
if context is None:
context = {}
if not rel_model_ref:
return {}
vals = {}
if context == {}:
context = self.context
template = self.pool.get('email.template').browse(cr, uid, context['active_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['active_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['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context)
vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context)
vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context)
vals['report'] = get_value(cr, uid, rel_model_ref, template.file_name, template, context)
return {'value':vals}
email_template_preview()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,440 @@
from osv import osv, fields
from html2text import html2text
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 re
import netsvc
import string
import email
import time, datetime
import email_template_engines
from tools.translate import _
import tools
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('Email Account Desc',
size=64, required=True,
readonly=True, select=True,
states={'draft':[('readonly', False)]}),
'user':fields.many2one('res.users',
'Related User', required=True,
readonly=True, states={'draft':[('readonly', False)]}),
'email_id': fields.char('Email ID',
size=120, required=True,
readonly=True, states={'draft':[('readonly', False)]} ,
help=" eg:yourname@yourdomain.com "),
'smtpserver': fields.char('Server',
size=120, required=True,
readonly=True, states={'draft':[('readonly', False)]},
help="Enter name of outgoing server,eg:smtp.gmail.com "),
'smtpport': fields.integer('SMTP Port ',
size=64, required=True,
readonly=True, states={'draft':[('readonly', False)]},
help="Enter port number,eg:SMTP-587 "),
'smtpuname': fields.char('User Name',
size=120, required=False,
readonly=True, states={'draft':[('readonly', False)]}),
'smtppass': fields.char('Password',
size=120, invisible=True,
required=False, readonly=True,
states={'draft':[('readonly', False)]}),
'smtptls':fields.boolean('Use TLS',
states={'draft':[('readonly', False)]}, readonly=True),
'smtpssl':fields.boolean('Use 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'),
('both', 'Both HTML & Text')
], 'Mail Format', required=True),
'allowed_groups':fields.many2many(
'res.groups',
'account_group_rel', 'templ_id', 'group_id',
string="Allowed User Groups",
help="Only users from these groups will be" \
"allowed to send mails from this ID"),
'company':fields.selection([
('yes', 'Yes'),
('no', 'No')
], 'Company Mail A/c',
readonly=True,
help="Select if this mail account does not belong" \
"to specific user but the organisation as a whole." \
"eg:info@somedomain.com",
required=True, states={
'draft':[('readonly', False)]
}),
'state':fields.selection([
('draft', 'Initiated'),
('suspended', 'Suspended'),
('approved', 'Approved')
],
'Account Status', required=True, readonly=True),
}
_defaults = {
'name':lambda self, cursor, user, context:self.pool.get(
'res.users'
).read(
cursor,
user,
user,
['name'],
context
)['name'],
'smtpssl':lambda * a:True,
'state':lambda * a:'draft',
'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 _constraint_unique(self, cursor, user, ids):
"""
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 on_change_emailid(self, cursor, user, ids, name=None, email_id=None, context=None):
"""
Called when the email ID field changes.
UI enhancement
Writes the same email value to the smtpusername
and incoming username
"""
#TODO: Check and remove the write. Is it needed?
self.write(cursor, user, ids, {'state':'draft'}, context=context)
return {
'value': {
'state': 'draft',
'smtpuname':email_id,
'isuser':email_id
}
}
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)
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(this_object.smtpuname, 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={}):
#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)
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 split_to_ids(self, ids_as_str):
"""
Identifies email IDs separated by separators
and returns a list
TODO: Doc this
"a@b.com,c@bcom; d@b.com;e@b.com->['a@b.com',...]"
"""
email_sep_by_commas = ids_as_str \
.replace('; ', ',') \
.replace(';', ',') \
.replace(', ', ',')
return email_sep_by_commas.split(',')
def get_ids_from_dict(self, addresses={}):
"""
TODO: Doc this
"""
result = {'all':[]}
keys = ['To', 'CC', 'BCC']
for each in keys:
ids_as_list = self.split_to_ids(addresses.get(each, u''))
while u'' in ids_as_list:
ids_as_list.remove(u'')
result[each] = ids_as_list
result['all'].extend(ids_as_list)
return result
def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, context=None):
#TODO: Replace all this crap 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:
msg = MIMEMultipart()
if subject:
msg['Subject'] = subject
sender_name = Header(core_obj.name, 'utf-8').encode()
msg['From'] = sender_name + " <" + core_obj.email_id + ">"
msg['Organization'] = tools.ustr(core_obj.user.company_id.name)
msg['Date'] = formatdate()
addresses_l = self.get_ids_from_dict(addresses)
if addresses_l['To']:
msg['To'] = u','.join(addresses_l['To'])
if addresses_l['CC']:
msg['CC'] = u','.join(addresses_l['CC'])
# if addresses_l['BCC']:
# msg['BCC'] = u','.join(addresses_l['BCC'])
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 == 'text' or core_obj.send_pref == 'both':
body_text = body.get('text', u'No Mail Message')
body_text = tools.ustr(body_text)
msg.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8'))
if core_obj.send_pref == 'html' or core_obj.send_pref == 'both':
html_body = body.get('html', u'')
if len(html_body) == 0 or html_body == u'':
html_body = body.get('text', u'<p>No Mail Message</p>').replace('\n', '<br/>').replace('\r', '<br/>')
html_body = tools.ustr(html_body)
msg.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8'))
#Now add attachments if any
for file in payload.keys():
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)
msg.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
try:
#print msg['From'],toadds
serv.sendmail(msg['From'], addresses_l['all'], msg.as_string())
except Exception, error:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Server Send Error\nDescription: %s") % (id, error))
return 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)
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
#print time_as_string
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')
#print date_as_date
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):
self.get_mails(cr, uid, ids, context)
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:

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem name="Tools" id="base.menu_tools" icon="STOCK_PREFERENCES" sequence="15"/>
<menuitem name="Email Template" id="menu_email_template" parent="base.menu_tools"/>
<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">
<field name="smtpserver" select="1" colspan="2" />
<field name="smtpport" select="2" colspan="2" />
<field name="smtpssl" select="2" colspan="2" />
<field name="smtptls" select="2" colspan="2" />
</group>
<button name="check_outgoing_connection" type="object" string="Check Outgoing Connection" />
<separator string="User Information" colspan="4" />
<group col="2" colspan="2">
<field name="email_id" select="1" on_change="on_change_emailid(name,email_id)" 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>
<page string="security" attrs="{'invisible':[('company','!=','yes')]}">
<field name="allowed_groups" attrs="{'required':[('company','=','yes')]}" nolabel="1"/>
</page>
</notebook>
<group colspan="4" col="10">
<field name="state" select="1"/>
<button string="Approve Account" name="button_approval" states="draft" type="workflow"/>
<button string="Suspend Account" name="button_suspended" states="approved" type="workflow" />
<button string="Request Re-activation" name="get_reapprove" states="suspended" type="workflow" />
<button string="Send/Receive" name="send_receive" states="approved" type="object" />
</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 string="SMTP Server">
<field name="name" select="2" />
<field name="email_id" select="2" />
<field name="smtpuname" select="2" />
<field name="user" select="2" />
<field name="smtpserver" select="2" />
<field name="smtpport" select="2" />
<field name="state" select="2" />
</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="Accounts">
<filter icon="terp-crm" string="My Accounts" name="my" domain="[('user','=',uid)]"/>
<filter icon="terp-crm" string="Personal Accounts" domain="[('company','=','no')]"/>
<filter icon="terp-crm" string="Company Accounts" domain="[('company','=','yes')]"/>
<separator orientation="vertical"/>
<filter icon="terp-crm" string="Draft" name="draft" domain="[('state','=','draft')]"/>
<filter icon="terp-crm" string="Suspended" domain="[('state','=','suspended')]"/>
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="user" 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">{'group_by': [], 'search_default_draft': 1, 'search_default_my': 1}</field>
<field name="search_view_id" ref="view_email_template_account_search"/>
</record>
<menuitem name="Configuration" id="menu_email_template_configuration" parent="menu_email_template" />
<menuitem name="All Accounts" id="menu_email_template_account_all" parent="menu_email_template_configuration" action="action_email_template_account_tree_all" groups="res_groups_email_template_manager" />
</data>
</openerp>

View File

@ -0,0 +1,84 @@
# To change this template, choose Tools | Templates
# and open the template in the editor.
from osv import fields,osv
import pooler
import netsvc
import re
class email_template_engines(osv.osv):
_name = "email_template.engines"
_description = "Email Template Engine"
# def __init__(self):
# print "Started Engine"
def check(self):
print "Start self check"
def strip_html(self,text):
#Removes HTML, Have to check if still relevent
if text:
def fixup(m):
text = m.group(0)
if text[:1] == "<":
return "" # ignore tags
if text[:2] == "&#":
try:
if text[:3] == "&#x":
return unichr(int(text[3:-1], 16))
else:
return unichr(int(text[2:-1]))
except ValueError:
pass
elif text[:1] == "&":
import htmlentitydefs
entity = htmlentitydefs.entitydefs.get(text[1:-1])
if entity:
if entity[:2] == "&#":
try:
return unichr(int(entity[2:-1]))
except ValueError:
pass
else:
return unicode(entity, "iso-8859-1")
return text # leave as is
return re.sub("(?s)<[^>]*>|&#?\w+;", fixup, text)
def parsevalue(self,cr,uid,id,message,templateid,context):
#id: ID of the template's model's record to be used
#message: the complete text including placeholders
#templateid: the template id of the template
#context: TODO
#print cr,uid,id,message,templateid,context
if message:
logger = netsvc.Logger()
def merge(match):
template = self.pool.get("email.template").browse(cr,uid,templateid,context)
obj_pool = self.pool.get(template.object_name.model)
obj = obj_pool.browse(cr, uid, id, context)
exp = str(match.group()[2:-2]).strip()
#print "level 1:",exp
exp_spl = exp.split('/')
#print "level 2:",exp_spl
try:
result = eval(exp_spl[0], {'object':obj,})
except:
result = "Rendering Error"
#print "result:",result
try:
if result in (None, False):
if len(exp_spl)>1:
return exp_spl[1]
else:
return 'Not Available'
return str(result)
except:
return "Rendering Error"
if message:
com = re.compile('(\[\[.+?\]\])')
retmessage = com.sub(merge, message)
else:
retmessage=""
return retmessage
email_template_engines()

View File

@ -0,0 +1,182 @@
from osv import osv, fields
import time
import email_template_engines
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 Open ERP Scheduler
to periodically send emails
"""
try:
self.send_all_mail(cursor, user, context)
except Exception, e:
LOGGER.notifyChannel(
_("Email Template"),
netsvc.LOG_ERROR,
_("Error sending mail: %s" % str(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):
if ids is None:
ids = []
for id in ids:
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 = self.pool.get('ir.attachment').browse(cr, uid, attid, context)#,['datas_fname','datas'])
payload[attachment.datas_fname] = attachment.datas
print "233333333333333"
result = account_obj.send_mail(cr, uid,
[values['account_id'][0]],
{'To':values.get('email_to', u'') or u'', 'CC':values.get('email_cc', u'') or u'', 'BCC':values.get('email_bcc', u'') or u''},
values['subject'] or u'',
{'text':values.get('body_text', u'') or u'', 'html':values.get('body_html', u'') or u''},
payload=payload, context=context)
if result == True:
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:
self.historise(cr, uid, [id], result, context)
except Exception, error:
logger = netsvc.Logger()
logger.notifyChannel(_("Power Email"), 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 True
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(
'Recepient (To)',
size=250,),
'email_cc':fields.char(
' CC',
size=250),
'email_bcc':fields.char(
' BCC',
size=250),
'subject':fields.char(
' Subject',
size=200,),
'body_text':fields.text(
'Standard Body (Text)'),
'body_html':fields.text(
'Body (Text-Web Client 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'),
'history':fields.text(
'History',
readonly=True,
store=True)
}
_defaults = {
'state': lambda * a: 'na',
'folder': lambda * a: 'outbox',
}
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
context = {}
if context.get('company', False):
users_groups = self.pool.get('res.users').browse(cr, uid, uid, context).groups_id
group_acc_rel = {}
#get all accounts and get a table of {group1:[account1,account2],group2:[account1]}
for each_account_id in self.pool.get('email_template.account').search(cr, uid, [('state', '=', 'approved'), ('company', '=', 'yes')], context=context):
account = self.pool.get('email_template.account').browse(cr, uid, each_account_id, context)
for each_group in account.allowed_groups:
if not account.id in group_acc_rel.get(each_group, []):
groups = group_acc_rel.get(each_group, [])
groups.append(account.id)
group_acc_rel[each_group] = groups
users_company_accounts = []
for each_group in group_acc_rel.keys():
if each_group in users_groups:
for each_account in group_acc_rel[each_group]:
if not each_account in users_company_accounts:
users_company_accounts.append(each_account)
args.append(('account_id', 'in', users_company_accounts))
return super(osv.osv, self).search(cr, uid, args, offset, limit,
order, context=context, count=count)
email_template_mailbox()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,170 @@
<?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="Outbox">
<group col="4" colspan="2">
<field name="email_from" colspan="4" select="1"/>
<field name="email_cc" colspan="4" select="1"/>
<field name="date_mail" colspan="4" select="2"/>
</group>
<group col="4" colspan="2">
<field name="email_to" colspan="4" required="1" select="1" />
<field name="email_bcc" colspan="4" 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" />
<!--<label string="Note: HTML body can't be edited with GTK desktop client." colspan="4"/>
--></page>
</notebook>
</page>
<page string="Attachments">
<group col="4">
<separator colspan="4" string="Attachments" />
<field name="attachments_ids" colspan="4" nolabel="1" />
</group>
</page>
<page string="Advanced">
<group col="4">
<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"/>
<separator string="History" colspan="4" />
<field name="history" nolabel="1" colspan="4"/>
</group>
</page>
</notebook>
<separator colspan="4" string="" />
<group col="4" colspan="4">
<field name="state" colspan="2" readonly="1" />
<button name="complete_mail" type="object" string="Download Full Mail" colspan="2" states="read,unread" />
<button name="send_this_mail" type="object" string="Send Mail" />
</group>
</form>
</field>
</record>
<!--============================================= TREE VIEWS =============================================-->
<!--DRAFTS-->
<record model="ir.ui.view" id="email_template_drafts_tree">
<field name="name">email_template.mailbox.draftstree</field>
<field name="model">email_template.mailbox</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Drafts">
<field name="user" />
<field name="email_from" select="1" />
<field name="subject" select="1" />
<field name="attachments_ids" select="2" />
<field name="date_mail" select="2" />
</tree>
</field>
</record>
<!--OUTBOX-->
<record model="ir.ui.view" id="email_template_outbox_tree">
<field name="name">email_template.mailbox.outboxtree</field>
<field name="model">email_template.mailbox</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Outbox">
<field name="user" />
<field name="email_from" select="1" />
<field name="subject" select="1" />
<field name="attachments_ids" select="2" />
<field name="date_mail" select="2" />
</tree>
</field>
</record>
<!--SENT-->
<record model="ir.ui.view" id="email_template_sentbox_tree">
<field name="name">email_template.mailbox.sentboxtree</field>
<field name="model">email_template.mailbox</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Sent">
<field name="user" />
<field name="email_from" select="1" />
<field name="subject" select="1" />
<field name="attachments_ids" select="2" />
<field name="date_mail" select="2" />
</tree>
</field>
</record>
<!--TRASH-->
<record model="ir.ui.view" id="email_template_trashbox_tree">
<field name="name">email_template.mailbox.trashboxtree</field>
<field name="model">email_template.mailbox</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Trash">
<field name="user" />
<field name="email_from" select="1" />
<field name="subject" select="1" />
<field name="attachments_ids" select="2" />
<field name="date_mail" select="2" />
</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="Mailboxes">
<filter icon="terp-crm" string="Drafts" name="draft" domain="[('folder','=','drafts']"/>
<filter icon="terp-crm" string="Outbox" name="outbox" domain="[('folder','=','outbox')]"/>
<separator orientation="vertical"/>
<filter icon="terp-crm" string="Sent" domain="[('folder','=','sent')]"/>
<filter icon="terp-crm" string="Trash" domain="[('folder','=','trash')]"/>
<separator orientation="vertical"/>
<filter icon="terp-crm" string="Not Applicable" domain="[('state','=','na')]"/>
<filter icon="terp-crm" string="Sending" domain="[('state','=','sending')]"/>
<separator orientation="vertical"/>
<field name="email_from" select="1"/>
<field name="email_to" select="1"/>
<field name="subject" select="1"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_template_mailbox">
<field name="name">Mailbox</field>
<field name="res_model">email_template.mailbox</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="email_template_outbox_tree" />
<field name="context">{'group_by': [], 'search_default_draft': 1, 'search_default_outbox': 1}</field>
<field name="search_view_id" ref="view_email_template_mailbox_search"/>
</record>
<!--======================================== MENUS ========================================-->
<menuitem name="MailBox" id="menu_email_template_mailbox_all_main2" parent="menu_email_template" />
<menuitem name="Personal" id="menu_email_template_personal" parent="menu_email_template_mailbox_all_main2" />
<menuitem name="Mails" id="menu_email_template_personal_mails" parent="menu_email_template_personal" action="action_email_template_mailbox"/>
<menuitem name="Company" id="menu_email_template_company" parent="menu_email_template_mailbox_all_main2" />
<menuitem name="Mails" id="menu_email_template_company_mails" parent="menu_email_template_company" action="action_email_template_mailbox"/>
</data>
</openerp>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_mail_scheduler_action" model="ir.cron">
<field name="name">Email Template 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">12</field>
<field eval="False" name="doall"/>
<field eval="'email_template.mailbox'" name="model"/>
<field eval="'run_mail_scheduler'" name="function"/>
<field eval="'()'" name="args"/>
</record>
</data>
</openerp>

View File

@ -0,0 +1,188 @@
<?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">
<field name="rel_model" />
<newline />
<field name="rel_model_ref" on_change="on_change_ref(rel_model_ref, context)" />
<newline />
<field name="to" />
<newline />
<field name="cc" />
<newline />
<field name="bcc" />
<newline />
<field name="subject" />
<newline />
<field name="body_text" />
<newline />
<!-- <field name="body_html" widget="text_html" />-->
<!--
Removed text_html because it doesnt work in GTK
And TinyMCE messes up the HTML in Web Client
-->
<field name="body_html"/>
<newline />
<field name="report" />
<group>
<button icon="gtk-ok" special="cancel" string="OK" />
</group>
</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">{'ids':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" />
<notebook colspan="4">
<page string="Mail Details">
<group col="4" colspan="2">
<field name="def_to" colspan="4" required="1" />
<field name="def_cc" colspan="4" />
<field name="def_bcc" colspan="4" />
</group>
<group col="2" colspan="2">
<field name="def_subject" colspan="4" required="1" />
<field name="use_sign" colspan="4" />
<field name="lang" colspan="4" />
</group>
<separator colspan="3" string="Standard Body" />
<separator colspan="1" string="Expression Builder" />
<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>
</notebook>
<group col="4">
<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" />
<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)" />
<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" />
</page>
</notebook>
<button name="%(wizard_email_template_preview)d" string="Preview Template"
type="action" colspan="4" target="new" />
</group>
</page>
<page string="Security">
<separator colspan="4" string="Allowed User Groups" />
<field name="allowed_groups" string="Allowed User Groups"
nolabel="1" />
</page>
<page string="Advanced">
<separator string="Automatic Email" colspan="4" />
<field name="enforce_from_account" attrs="{'required':[('auto_email','=',True)]}" />
<newline/>
<field name="ref_ir_act_window" />
<field name="ref_ir_value" />
<separator string="Attachments (Report to attach)" colspan="4"/>
<field name="file_name" colspan="2" />
<field name="report_template" colspan="2"
domain="[('model','=',model_int_name)]" />
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.ui.view" id="email_template_tree">
<field name="name">email.template.tree</field>
<field name="model">email.template</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Email Templates">
<field name="name" select="1" />
<field name="object_name" required="1" select="1" />
<field name="def_to" colspan="4" select="2" />
<field name="def_cc" colspan="4" select="2" />
<field name="def_bcc" colspan="4" select="2" />
<field name="def_subject" colspan="4" select="2" />
<field name="use_sign" colspan="4" select="2" />
<field name="file_name" colspan="4" />
<field name="enforce_from_account" />
</tree>
</field>
</record>
<record id="view_email_template_search" model="ir.ui.view">
<field name="name">email.template.search</field>
<field name="model">email.template</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Templates">
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="object_name" select="1"/>
<field name="def_to" select="1"/>
<separator orientation="vertical"/>
<field name="lang" select="1"/>
<field name="def_subject" select="1"/>
<field name="file_name" select="1"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_email_template_tree_all">
<field name="name">Email Templates</field>
<field name="res_model">email.template</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="email_template_tree" />
<field name="search_view_id" ref="view_email_template_search"/>
</record>
<menuitem name="E-MAIL Templates" id="menu_email_template_all"
parent="menu_email_template_configuration" action="action_email_template_tree_all" />
</data>
</openerp>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="PE_ADMIN" model="res.roles">
<field name="name">Email Administrator</field>
</record>
<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>
<field name="role_id" ref="PE_ADMIN"/>
</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>
<field name="role_id" ref="PE_ADMIN"/>
</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>
<field name="role_id" ref="PE_ADMIN"/>
</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>
<field name="role_id" ref="PE_ADMIN"/>
</record>
</data>
</openerp>

View File

@ -0,0 +1,455 @@
#!/usr/bin/env python
"""html2text: Turn HTML into equivalent Markdown-structured text."""
__version__ = "2.36"
__author__ = "Aaron Swartz (me@aaronsw.com)"
__copyright__ = "(C) 2004-2008 Aaron Swartz. GNU GPL 3."
__contributors__ = ["Martin 'Joey' Schulze", "Ricardo Reyes", "Kevin Jay North"]
# TODO:
# Support decoded entities with unifiable.
if not hasattr(__builtins__, 'True'): True, False = 1, 0
import re, sys, urllib, htmlentitydefs, codecs, StringIO, types
import sgmllib
import urlparse
sgmllib.charref = re.compile('&#([xX]?[0-9a-fA-F]+)[^0-9a-fA-F]')
try: from textwrap import wrap
except: pass
# Use Unicode characters instead of their ascii psuedo-replacements
UNICODE_SNOB = 0
# Put the links after each paragraph instead of at the end.
LINKS_EACH_PARAGRAPH = 0
# Wrap long lines at position. 0 for no wrapping. (Requires Python 2.3.)
BODY_WIDTH = 78
# Don't show internal links (href="#local-anchor") -- corresponding link targets
# won't be visible in the plain text file anyway.
SKIP_INTERNAL_LINKS = False
### Entity Nonsense ###
def name2cp(k):
if k == 'apos': return ord("'")
if hasattr(htmlentitydefs, "name2codepoint"): # requires Python 2.3
return htmlentitydefs.name2codepoint[k]
else:
k = htmlentitydefs.entitydefs[k]
if k.startswith("&#") and k.endswith(";"): return int(k[2:-1]) # not in latin-1
return ord(codecs.latin_1_decode(k)[0])
unifiable = {'rsquo':"'", 'lsquo':"'", 'rdquo':'"', 'ldquo':'"',
'copy':'(C)', 'mdash':'--', 'nbsp':' ', 'rarr':'->', 'larr':'<-', 'middot':'*',
'ndash':'-', 'oelig':'oe', 'aelig':'ae',
'agrave':'a', 'aacute':'a', 'acirc':'a', 'atilde':'a', 'auml':'a', 'aring':'a',
'egrave':'e', 'eacute':'e', 'ecirc':'e', 'euml':'e',
'igrave':'i', 'iacute':'i', 'icirc':'i', 'iuml':'i',
'ograve':'o', 'oacute':'o', 'ocirc':'o', 'otilde':'o', 'ouml':'o',
'ugrave':'u', 'uacute':'u', 'ucirc':'u', 'uuml':'u'}
unifiable_n = {}
for k in unifiable.keys():
unifiable_n[name2cp(k)] = unifiable[k]
def charref(name):
if name[0] in ['x','X']:
c = int(name[1:], 16)
else:
c = int(name)
if not UNICODE_SNOB and c in unifiable_n.keys():
return unifiable_n[c]
else:
return unichr(c)
def entityref(c):
if not UNICODE_SNOB and c in unifiable.keys():
return unifiable[c]
else:
try: name2cp(c)
except KeyError: return "&" + c
else: return unichr(name2cp(c))
def replaceEntities(s):
s = s.group(1)
if s[0] == "#":
return charref(s[1:])
else: return entityref(s)
r_unescape = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
def unescape(s):
return r_unescape.sub(replaceEntities, s)
def fixattrs(attrs):
# Fix bug in sgmllib.py
if not attrs: return attrs
newattrs = []
for attr in attrs:
newattrs.append((attr[0], unescape(attr[1])))
return newattrs
### End Entity Nonsense ###
def onlywhite(line):
"""Return true if the line does only consist of whitespace characters."""
for c in line:
if c is not ' ' and c is not ' ':
return c is ' '
return line
def optwrap(text):
"""Wrap all paragraphs in the provided text."""
if not BODY_WIDTH:
return text
assert wrap, "Requires Python 2.3."
result = ''
newlines = 0
for para in text.split("\n"):
if len(para) > 0:
if para[0] is not ' ' and para[0] is not '-' and para[0] is not '*':
for line in wrap(para, BODY_WIDTH):
result += line + "\n"
result += "\n"
newlines = 2
else:
if not onlywhite(para):
result += para + "\n"
newlines = 1
else:
if newlines < 2:
result += "\n"
newlines += 1
return result
def hn(tag):
if tag[0] == 'h' and len(tag) == 2:
try:
n = int(tag[1])
if n in range(1, 10): return n
except ValueError: return 0
class _html2text(sgmllib.SGMLParser):
def __init__(self, out=sys.stdout.write, baseurl=''):
sgmllib.SGMLParser.__init__(self)
if out is None: self.out = self.outtextf
else: self.out = out
self.outtext = u''
self.quiet = 0
self.p_p = 0
self.outcount = 0
self.start = 1
self.space = 0
self.a = []
self.astack = []
self.acount = 0
self.list = []
self.blockquote = 0
self.pre = 0
self.startpre = 0
self.lastWasNL = 0
self.abbr_title = None # current abbreviation definition
self.abbr_data = None # last inner HTML (for abbr being defined)
self.abbr_list = {} # stack of abbreviations to write later
self.baseurl = baseurl
def outtextf(self, s):
self.outtext += s
def close(self):
sgmllib.SGMLParser.close(self)
self.pbr()
self.o('', 0, 'end')
return self.outtext
def handle_charref(self, c):
self.o(charref(c))
def handle_entityref(self, c):
self.o(entityref(c))
def unknown_starttag(self, tag, attrs):
self.handle_tag(tag, attrs, 1)
def unknown_endtag(self, tag):
self.handle_tag(tag, None, 0)
def previousIndex(self, attrs):
""" returns the index of certain set of attributes (of a link) in the
self.a list
If the set of attributes is not found, returns None
"""
if not attrs.has_key('href'): return None
i = -1
for a in self.a:
i += 1
match = 0
if a.has_key('href') and a['href'] == attrs['href']:
if a.has_key('title') or attrs.has_key('title'):
if (a.has_key('title') and attrs.has_key('title') and
a['title'] == attrs['title']):
match = True
else:
match = True
if match: return i
def handle_tag(self, tag, attrs, start):
attrs = fixattrs(attrs)
if hn(tag):
self.p()
if start: self.o(hn(tag)*"#" + ' ')
if tag in ['p', 'div']: self.p()
if tag == "br" and start: self.o(" \n")
if tag == "hr" and start:
self.p()
self.o("* * *")
self.p()
if tag in ["head", "style", 'script']:
if start: self.quiet += 1
else: self.quiet -= 1
if tag in ["body"]:
self.quiet = 0 # sites like 9rules.com never close <head>
if tag == "blockquote":
if start:
self.p(); self.o('> ', 0, 1); self.start = 1
self.blockquote += 1
else:
self.blockquote -= 1
self.p()
if tag in ['em', 'i', 'u']: self.o("_")
if tag in ['strong', 'b']: self.o("**")
if tag == "code" and not self.pre: self.o('`') #TODO: `` `this` ``
if tag == "abbr":
if start:
attrsD = {}
for (x, y) in attrs: attrsD[x] = y
attrs = attrsD
self.abbr_title = None
self.abbr_data = ''
if attrs.has_key('title'):
self.abbr_title = attrs['title']
else:
if self.abbr_title != None:
self.abbr_list[self.abbr_data] = self.abbr_title
self.abbr_title = None
self.abbr_data = ''
if tag == "a":
if start:
attrsD = {}
for (x, y) in attrs: attrsD[x] = y
attrs = attrsD
if attrs.has_key('href') and not (SKIP_INTERNAL_LINKS and attrs['href'].startswith('#')):
self.astack.append(attrs)
self.o("[")
else:
self.astack.append(None)
else:
if self.astack:
a = self.astack.pop()
if a:
i = self.previousIndex(a)
if i is not None:
a = self.a[i]
else:
self.acount += 1
a['count'] = self.acount
a['outcount'] = self.outcount
self.a.append(a)
self.o("][" + `a['count']` + "]")
if tag == "img" and start:
attrsD = {}
for (x, y) in attrs: attrsD[x] = y
attrs = attrsD
if attrs.has_key('src'):
attrs['href'] = attrs['src']
alt = attrs.get('alt', '')
i = self.previousIndex(attrs)
if i is not None:
attrs = self.a[i]
else:
self.acount += 1
attrs['count'] = self.acount
attrs['outcount'] = self.outcount
self.a.append(attrs)
self.o("![")
self.o(alt)
self.o("]["+`attrs['count']`+"]")
if tag == 'dl' and start: self.p()
if tag == 'dt' and not start: self.pbr()
if tag == 'dd' and start: self.o(' ')
if tag == 'dd' and not start: self.pbr()
if tag in ["ol", "ul"]:
if start:
self.list.append({'name':tag, 'num':0})
else:
if self.list: self.list.pop()
self.p()
if tag == 'li':
if start:
self.pbr()
if self.list: li = self.list[-1]
else: li = {'name':'ul', 'num':0}
self.o(" "*len(self.list)) #TODO: line up <ol><li>s > 9 correctly.
if li['name'] == "ul": self.o("* ")
elif li['name'] == "ol":
li['num'] += 1
self.o(`li['num']`+". ")
self.start = 1
else:
self.pbr()
if tag in ["table", "tr"] and start: self.p()
if tag == 'td': self.pbr()
if tag == "pre":
if start:
self.startpre = 1
self.pre = 1
else:
self.pre = 0
self.p()
def pbr(self):
if self.p_p == 0: self.p_p = 1
def p(self): self.p_p = 2
def o(self, data, puredata=0, force=0):
if self.abbr_data is not None: self.abbr_data += data
if not self.quiet:
if puredata and not self.pre:
data = re.sub('\s+', ' ', data)
if data and data[0] == ' ':
self.space = 1
data = data[1:]
if not data and not force: return
if self.startpre:
#self.out(" :") #TODO: not output when already one there
self.startpre = 0
bq = (">" * self.blockquote)
if not (force and data and data[0] == ">") and self.blockquote: bq += " "
if self.pre:
bq += " "
data = data.replace("\n", "\n"+bq)
if self.start:
self.space = 0
self.p_p = 0
self.start = 0
if force == 'end':
# It's the end.
self.p_p = 0
self.out("\n")
self.space = 0
if self.p_p:
self.out(('\n'+bq)*self.p_p)
self.space = 0
if self.space:
if not self.lastWasNL: self.out(' ')
self.space = 0
if self.a and ((self.p_p == 2 and LINKS_EACH_PARAGRAPH) or force == "end"):
if force == "end": self.out("\n")
newa = []
for link in self.a:
if self.outcount > link['outcount']:
self.out(" ["+`link['count']`+"]: " + urlparse.urljoin(self.baseurl, link['href']))
if link.has_key('title'): self.out(" ("+link['title']+")")
self.out("\n")
else:
newa.append(link)
if self.a != newa: self.out("\n") # Don't need an extra line when nothing was done.
self.a = newa
if self.abbr_list and force == "end":
for abbr, definition in self.abbr_list.items():
self.out(" *[" + abbr + "]: " + definition + "\n")
self.p_p = 0
self.out(data)
self.lastWasNL = data and data[-1] == '\n'
self.outcount += 1
def handle_data(self, data):
if r'\/script>' in data: self.quiet -= 1
self.o(data, 1)
def unknown_decl(self, data): pass
def wrapwrite(text): sys.stdout.write(text.encode('utf8'))
def html2text_file(html, out=wrapwrite, baseurl=''):
h = _html2text(out, baseurl)
h.feed(html)
h.feed("")
return h.close()
def html2text(html, baseurl=''):
return optwrap(html2text_file(html, None, baseurl))
if __name__ == "__main__":
baseurl = ''
if sys.argv[1:]:
arg = sys.argv[1]
if arg.startswith('http://'):
baseurl = arg
j = urllib.urlopen(baseurl)
try:
from feedparser import _getCharacterEncoding as enc
except ImportError:
enc = lambda x, y: ('utf-8', 1)
text = j.read()
encoding = enc(j.headers, text)[0]
if encoding == 'us-ascii': encoding = 'utf-8'
data = text.decode(encoding)
else:
encoding = 'utf8'
if len(sys.argv) > 2:
encoding = sys.argv[2]
f = open(arg, 'r')
try:
data = f.read().decode(encoding)
finally:
f.close()
else:
data = sys.stdin.read().decode('utf8')
wrapwrite(html2text(data, baseurl))

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="res_groups_email_template_manager" model="res.groups">
<field name="name">Email Template / Settings_Manager</field>
</record>
<record id="res_groups_email_template_userse" model="res.groups">
<field name="name">Email Template / External_users</field>
</record>
<record id="res_groups_email_template_usersi" model="res.groups">
<field name="name">Email Template / Internal_users</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1 @@
import email_template_send_wizard

View File

@ -0,0 +1,235 @@
from osv import osv, fields
from mako.template import Template
from mako import exceptions
import netsvc
import base64
import time
from tools.translate import _
import tools
from email_template.email_template import get_value
class email_template_send_wizard(osv.osv_memory):
_name = 'email_template.send.wizard'
_description = 'This is the wizard for sending mail'
_rec_name = "subject"
def _get_accounts(self, cr, uid, context=None):
if context is None:
context = {}
template = self._get_template(cr, uid, context)
if not template:
return []
logger = netsvc.Logger()
if template.enforce_from_account:
return [(template.enforce_from_account.id, '%s (%s)' % (template.enforce_from_account.name, template.enforce_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(_("Power Email"), 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 power email account."))
raise osv.except_osv(_("Power Email"),_("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal power email account."))
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
template = self.pool.get('email.template').browse(cr, uid, template_ids[0], context)
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
def _get_template_value(self, cr, uid, field, context=None):
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)
_columns = {
'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,),
'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'),
}
_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,
'requested':lambda self,cr,uid,ctx: len(ctx['src_rec_ids']),
'full_success': lambda *a: False
}
def fields_get(self, cr, uid, fields=None, context=None, read_access=True):
result = super(email_template_send_wizard, self).fields_get(cr, uid, fields, context, read_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):
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':'drafts'}, 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' }
def get_generated(self, cr, uid, ids=None, context=None):
if ids is None:
ids = []
if context is None:
context = {}
logger = netsvc.Logger()
if context['src_rec_ids'] and len(context['src_rec_ids'])>1:
#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(_("Power Email"), netsvc.LOG_INFO, _("Emails for multiple items saved in outbox."))
self.write(cr, uid, ids, {
'generated':len(mail_ids),
'state':'done'
}, context)
else:
raise osv.except_osv(_("Power Email"),_("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
mail_ids = []
template = self._get_template(cr, uid, context)
for id in context['src_rec_ids']:
print "@22222222222222222222222",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
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:

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<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" />
<!--<label string="Note: HTML body can't be edited with GTK desktop client." colspan="4"/>
--></page>
<page string="Attachments">
<label string="Add here all attachments of the current document you want to include in the e-mail." 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>
</data>
</openerp>