[MERGE] Next batch of improvements/fixes for mail.alias
bzr revid: odo@openerp.com-20120814123050-xw3ltthoc9nld76r
This commit is contained in:
commit
11bdbfd183
|
@ -6,9 +6,6 @@
|
|||
<record id="base.menu_res_company_global" model="ir.ui.menu">
|
||||
<field name="groups_id" eval="[(3, ref('base.group_no_one'))]"/>
|
||||
</record>
|
||||
<record id="base.menu_users" model="ir.ui.menu">
|
||||
<field name="groups_id" eval="[(3, ref('base.group_no_one'))]"/>
|
||||
</record>
|
||||
<record id="base.menu_publisher_warranty" model="ir.ui.menu">
|
||||
<field name="groups_id" eval="[(3, ref('base.group_no_one'))]"/>
|
||||
</record>
|
||||
|
|
|
@ -97,9 +97,10 @@
|
|||
<separator string="Emails Integration" attrs="{'invisible': [('module_crm','=',False)]}"/>
|
||||
<p attrs="{'invisible': [('module_crm','=',False)]}">
|
||||
OpenERP allows to automatically create leads (or others documents)
|
||||
from incoming emails. You can synchronize emails with OpenERP
|
||||
by connecting the mail gateway to your mail server or by manually
|
||||
pressing buttons in our mail clients.
|
||||
from incoming emails. You can automatically synchronize emails with OpenERP
|
||||
using regular POP/IMAP accounts, using a direct email integration script for your
|
||||
email server, or by manually pushing emails to OpenERP using specific
|
||||
plugins for your preferred email application.
|
||||
</p>
|
||||
<group name="On Mail Client" attrs="{'invisible': [('module_crm','=',False)]}">
|
||||
<label for="id" string="On Mail Client"/>
|
||||
|
|
|
@ -132,9 +132,10 @@ class crm_case_section(osv.osv):
|
|||
return ids
|
||||
|
||||
_defaults = {
|
||||
'active': lambda *a: 1,
|
||||
'allow_unlink': lambda *a: 1,
|
||||
'stage_ids': _get_stage_common
|
||||
'active': 1,
|
||||
'allow_unlink': 1,
|
||||
'stage_ids': _get_stage_common,
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
|
@ -162,16 +163,16 @@ class crm_case_section(osv.osv):
|
|||
return res
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
alias_pool = self.pool.get('mail.alias')
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
if not vals.get('alias_id'):
|
||||
name = vals.pop('alias_name', None) or vals['name']
|
||||
alias_id = alias_pool.create_unique_alias(cr, uid,
|
||||
{'alias_name': name},
|
||||
vals.pop('alias_name', None) # prevent errors during copy()
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid,
|
||||
{'alias_name': vals['name']},
|
||||
model_name="crm.lead",
|
||||
context=context)
|
||||
vals['alias_id'] = alias_id
|
||||
res = super(crm_case_section, self).create(cr, uid, vals, context)
|
||||
alias_pool.write(cr, uid, [vals['alias_id']],{'alias_defaults':{'section_id': res,'type':'lead'}},context)
|
||||
mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'section_id': res, 'type':'lead'}}, context)
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -26,17 +26,10 @@
|
|||
<record model="crm.case.channel" id="crm_case_channel_email">
|
||||
<field name="name">Email</field>
|
||||
</record>
|
||||
<record id="mail_alias_sales_department" model="mail.alias">
|
||||
<field name="alias_name">sales</field>
|
||||
<field name="alias_model_id" ref="model_crm_lead"/>
|
||||
<field name="alias_user_id" ref="base.user_root"/>
|
||||
<field name="alias_defaults">{'type':'lead'}</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="crm.case.section" id="section_sales_department">
|
||||
<field name="name">Sales Department</field>
|
||||
<field name="code">Sales</field>
|
||||
<field name="alias_id" ref="mail_alias_sales_department"/>
|
||||
</record>
|
||||
|
||||
<!-- Payment Mode -->
|
||||
|
@ -70,5 +63,13 @@
|
|||
|
||||
To manage quotations and sale orders, install the module "Sales Management".</value>
|
||||
</function>
|
||||
|
||||
<record model="mail.alias" id="default_sales_alias">
|
||||
<field name="alias_name">sales</field>
|
||||
<field name="alias_model_id" ref="model_crm_lead"/>
|
||||
<field name="alias_user_id" ref="base.user_root"/>
|
||||
<field name="alias_defaults">{'type':'lead'}</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="mail_alias_marketing_department" model="mail.alias">
|
||||
<field name="alias_name">info</field>
|
||||
<field name="alias_model_id" ref="model_crm_lead"/>
|
||||
<field name="alias_user_id" ref="base.user_root"/>
|
||||
<field name="alias_defaults">{'type':'lead'}</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data>
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="groups_id" eval="[(4,ref('base.group_sale_salesman'))]"/>
|
||||
|
|
|
@ -72,8 +72,6 @@
|
|||
</record>
|
||||
|
||||
<record model="crm.case.section" id="section_sales_department">
|
||||
<field name="name">Sales Department</field>
|
||||
<field name="code">Sales</field>
|
||||
<field name="stage_ids" eval="[ (4, ref('stage_lead1')), (4, ref('stage_lead2')),
|
||||
(4, ref('stage_lead3')), (4, ref('stage_lead4')),
|
||||
(4, ref('stage_lead5')), (4, ref('stage_lead6')),
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
<notebook colspan="4">
|
||||
<page string="Sales Team">
|
||||
<group>
|
||||
<field name="alias_id" invisible="1"/>
|
||||
<field name="alias_id" invisible="1" required="0"/>
|
||||
<label for="alias_name" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
|
||||
<div attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<field name="alias_name" class="oe_inline" attrs="{'required': [('alias_id', '!=', False)]}"/>@<field name="alias_domain" class="oe_inline"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</group>
|
||||
</group>
|
||||
<div name="config_other" version="7.0" position="inside">
|
||||
<separator string="Import and Synchronize Data from an Other Application"/>
|
||||
<separator string="Importing and Synchronizing with External Systems"/>
|
||||
<group>
|
||||
<label for="id" string="Synchronize with"/>
|
||||
<div>
|
||||
|
|
|
@ -189,14 +189,11 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS
|
|||
result, data = imap_server.search(None, '(UNSEEN)')
|
||||
for num in data[0].split():
|
||||
result, data = imap_server.fetch(num, '(RFC822)')
|
||||
if server.object_id:
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
data[0][1],
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
else:
|
||||
res_id = mail_thread.message_catchall(cr, uid, data[0][1])
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
data[0][1],
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
if res_id and server.action_id:
|
||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
|
||||
imap_server.store(num, '+FLAGS', '\\Seen')
|
||||
|
@ -217,14 +214,11 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS
|
|||
for num in range(1, numMsgs + 1):
|
||||
(header, msges, octets) = pop_server.retr(num)
|
||||
msg = '\n'.join(msges)
|
||||
if server.object_id:
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
msg,
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
else:
|
||||
res_id = mail_thread.message_catchall(cr, uid, data[0][1])
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
msg,
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
if res_id and server.action_id:
|
||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
|
||||
pop_server.dele(num)
|
||||
|
|
|
@ -518,37 +518,55 @@ class hr_job(osv.osv):
|
|||
"create new applicants for this job position."),
|
||||
}
|
||||
|
||||
def init(self, cr):
|
||||
# Installation hook to create aliases for all jobs, right after _auto_init
|
||||
_defaults = {
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
"""Installation hook to create aliases for all jobs and avoid constraint errors."""
|
||||
|
||||
# disable the unique alias_id not null constraint, to avoid spurious warning during
|
||||
# super.auto_init. We'll reinstall it afterwards.
|
||||
self._columns['alias_id'].required = False
|
||||
|
||||
super(hr_job,self)._auto_init(cr, context=context)
|
||||
|
||||
registry = RegistryManager.get(cr.dbname)
|
||||
mail_alias = registry.get('mail.alias')
|
||||
hr_job = registry.get('hr.job')
|
||||
jobs_no_alias = hr_job.search(cr, SUPERUSER_ID, [('alias_id', '=', False)])
|
||||
hr_jobs = registry.get('hr.job')
|
||||
jobs_no_alias = hr_jobs.search(cr, SUPERUSER_ID, [('alias_id', '=', False)])
|
||||
# Use read() not browse(), to avoid prefetching uninitialized inherited fields
|
||||
for job_data in hr_job.read(cr, SUPERUSER_ID, jobs_no_alias, ['name']):
|
||||
alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, {'alias_name': 'job_'+job_data['name'],
|
||||
'alias_force_id': job_data['id']},
|
||||
model_name=self._name)
|
||||
hr_job.write(cr, SUPERUSER_ID, job_data['id'], {'alias_id': alias_id})
|
||||
for job_data in hr_jobs.read(cr, SUPERUSER_ID, jobs_no_alias, ['name']):
|
||||
alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, {'alias_name': 'job+'+job_data['name'],
|
||||
'alias_defaults': {'job_id': job_data['id']}},
|
||||
model_name='hr.applicant')
|
||||
hr_jobs.write(cr, SUPERUSER_ID, job_data['id'], {'alias_id': alias_id})
|
||||
_logger.info('Mail alias created for hr.job %s (uid %s)', job_data['name'], job_data['id'])
|
||||
|
||||
# Finally attempt to reinstate the missing constraint
|
||||
try:
|
||||
cr.execute('ALTER TABLE hr_job ALTER COLUMN alias_id SET NOT NULL')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_logger.warning("Table '%s': unable to set a NOT NULL constraint on column '%s' !\n"\
|
||||
"If you want to have it, you should update the records and execute manually:\n"\
|
||||
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",
|
||||
self._table, 'alias_id', self._table, 'alias_id')
|
||||
|
||||
self._columns['alias_id'].required = True
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
alias_pool = self.pool.get('mail.alias')
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
if not vals.get('alias_id'):
|
||||
name = vals.pop('alias_name', None) or vals['name']
|
||||
alias_id = alias_pool.create_unique_alias(cr, uid,
|
||||
{'alias_name': "job_"+name},
|
||||
vals.pop('alias_name', None) # prevent errors during copy()
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid,
|
||||
# Using '+' allows using subaddressing for those who don't
|
||||
# have a catchall domain setup.
|
||||
{'alias_name': 'jobs+'+vals['name']},
|
||||
model_name="hr.applicant",
|
||||
context=context)
|
||||
vals['alias_id'] = alias_id
|
||||
res = super( hr_job, self).create(cr, uid, vals, context)
|
||||
alias_pool.write(cr, uid, [vals['alias_id']], {"alias_defaults": {'job_id': res}}, context)
|
||||
res = super(hr_job, self).create(cr, uid, vals, context)
|
||||
mail_alias.write(cr, uid, [vals['alias_id']], {"alias_defaults": {'job_id': res}}, context)
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -458,7 +458,6 @@ You can automatically create application records from an email gateway, that you
|
|||
<field name="alias_name">jobs</field>
|
||||
<field name="alias_model_id" ref="model_hr_applicant"/>
|
||||
<field name="alias_user_id" ref="base.user_root"/>
|
||||
<field name="alias_defaults">{}</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -334,7 +334,7 @@
|
|||
</field>
|
||||
<xpath expr="/form/sheet/h1" version="7.0" position="after">
|
||||
<div colspan="4" attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<field name="alias_id" invisible="1"/>
|
||||
<field name="alias_id" invisible="1" required="0"/>
|
||||
<label for="alias_name" class="oe_edit_only"/>
|
||||
<field name="alias_name" nolabel="1" class="oe_inline" attrs="{'required': [('alias_id', '!=', False)]}"/>@<field name="alias_domain" nolabel="1" class="oe_inline"/>
|
||||
</div>
|
||||
|
|
|
@ -20,9 +20,19 @@
|
|||
##############################################################################
|
||||
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import ustr
|
||||
|
||||
# Inspired by http://stackoverflow.com/questions/517923
|
||||
def remove_accents(input_str):
|
||||
"""Suboptimal-but-better-than-nothing way to replace accented
|
||||
latin letters by an ASCII equivalent. Will obviously change the
|
||||
meaning of input_str and work only for some cases"""
|
||||
input_str = ustr(input_str)
|
||||
nkfd_form = unicodedata.normalize('NFKD', input_str)
|
||||
return u''.join([c for c in nkfd_form if not unicodedata.combining(c)])
|
||||
|
||||
class mail_alias(osv.Model):
|
||||
"""A Mail Alias is a mapping of an email address with a given OpenERP Document
|
||||
|
@ -40,6 +50,7 @@ class mail_alias(osv.Model):
|
|||
_name = 'mail.alias'
|
||||
_description = "Email Aliases"
|
||||
_rec_name = 'alias_name'
|
||||
_order = 'alias_model_id, alias_name'
|
||||
|
||||
def _get_alias_domain(self, cr, uid, ids, name, args, context=None):
|
||||
ir_config_parameter = self.pool.get("ir.config_parameter")
|
||||
|
@ -55,10 +66,9 @@ class mail_alias(osv.Model):
|
|||
"corresponds. Any incoming email that does not reply to an "
|
||||
"existing record will cause the creation of a new record "
|
||||
"of this model (e.g. a Project Task)",
|
||||
# only allow selecting mail_thread models!
|
||||
#TODO kept doamin temporarily in comment, need to redefine domain
|
||||
#domain="[('field_id', 'in', 'message_ids')]"
|
||||
),
|
||||
# hack to only allow selecting mail_thread models (we might
|
||||
# (have a few false positives, though)
|
||||
domain="[('field_id.name', '=', 'message_ids')]"),
|
||||
'alias_user_id': fields.many2one('res.users', 'Owner',
|
||||
help="The owner of records created upon receiving emails on this alias. "
|
||||
"If this field is not set the system will attempt to find the right owner "
|
||||
|
@ -76,7 +86,10 @@ class mail_alias(osv.Model):
|
|||
|
||||
_defaults = {
|
||||
'alias_defaults': '{}',
|
||||
'alias_user_id': lambda self,cr,uid, context: uid
|
||||
'alias_user_id': lambda self,cr,uid,context: uid,
|
||||
|
||||
# looks better when creating new aliases - even if the field is informative only
|
||||
'alias_domain': lambda self,cr,uid,context: self._get_alias_domain(cr,1,[1],None,None)[1]
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
|
@ -122,12 +135,10 @@ class mail_alias(osv.Model):
|
|||
make it unique, and the ``alias_model_id`` value will set to the
|
||||
model ID of the ``model_name`` value, if provided,
|
||||
"""
|
||||
alias_name = re.sub(r'\W+', '_', vals['alias_name']).lower()
|
||||
alias_name = re.sub(r'[^\w+]', '-', remove_accents(vals['alias_name'])).lower()
|
||||
alias_name = self._find_unique(cr, uid, alias_name, context=context)
|
||||
vals['alias_name'] = alias_name
|
||||
if model_name:
|
||||
model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', model_name)], context=context)[0]
|
||||
vals['alias_model_id'] = model_id
|
||||
return self.create(cr, uid, vals, context=context)
|
||||
|
||||
|
||||
|
|
|
@ -10,14 +10,12 @@
|
|||
<form string="Alias" version="7.0">
|
||||
<sheet>
|
||||
<label for="alias_name" class="oe_edit_only"/>
|
||||
<h2><field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline"/></h2>
|
||||
<group colspan="4" col="4">
|
||||
<field name="alias_model_id" readonly="1"/>
|
||||
<field name="alias_user_id" readonly="1"/>
|
||||
<field name="alias_force_thread_id" readonly="1"/>
|
||||
<newline/>
|
||||
<separator string="Default Values" colspan="4"/>
|
||||
<field name="alias_defaults" colspan="4" nolabel="1" readonly="1"/>
|
||||
<h2><field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline"/></h2>
|
||||
<group>
|
||||
<field name="alias_model_id"/>
|
||||
<field name="alias_user_id"/>
|
||||
<field name="alias_force_thread_id"/>
|
||||
<field name="alias_defaults"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
|
|
|
@ -25,9 +25,7 @@ import openerp.tools as tools
|
|||
from operator import itemgetter
|
||||
from osv import osv
|
||||
from osv import fields
|
||||
import tools
|
||||
from tools.translate import _
|
||||
from lxml import etree
|
||||
|
||||
class mail_group(osv.osv):
|
||||
"""
|
||||
|
@ -78,7 +76,6 @@ class mail_group(osv.osv):
|
|||
|
||||
def get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None):
|
||||
result = {}
|
||||
message_obj = self.pool.get('mail.message')
|
||||
for id in ids:
|
||||
lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
|
||||
result[id] = self.message_search(cr, uid, [id], limit=None, domain=[('date', '>=', lower_date)], count=True, context=context)
|
||||
|
@ -153,6 +150,7 @@ class mail_group(osv.osv):
|
|||
'responsible_id': (lambda s, cr, uid, ctx: uid),
|
||||
'image': _get_default_image,
|
||||
'parent_id': _get_menu_parent,
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
}
|
||||
|
||||
def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None):
|
||||
|
@ -168,12 +166,14 @@ class mail_group(osv.osv):
|
|||
return self.message_subscribe(cr, uid, ids, user_ids, context=context)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
alias_pool = self.pool.get('mail.alias')
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
if not vals.get('alias_id'):
|
||||
name = vals.get('alias_name') or vals['name']
|
||||
alias_id = alias_pool.create_unique_alias(cr, uid,
|
||||
{'alias_name': "group_"+name},
|
||||
model_name=self._name, context=context)
|
||||
vals.pop('alias_name', None) # prevent errors during copy()
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid,
|
||||
# Using '+' allows using subaddressing for those who don't
|
||||
# have a catchall domain setup.
|
||||
{'alias_name': "group+"+vals['name']},
|
||||
model_name=self._name, context=context)
|
||||
vals['alias_id'] = alias_id
|
||||
|
||||
mail_group_id = super(mail_group, self).create(cr, uid, vals, context)
|
||||
|
@ -193,7 +193,8 @@ class mail_group(osv.osv):
|
|||
newref = cobj.copy(cr, uid, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
|
||||
self.write(cr, uid, [mail_group_id], {'action': 'ir.actions.client,'+str(newref), 'mail_group_id': mail_group_id}, context=context)
|
||||
|
||||
alias_pool.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context)
|
||||
mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context)
|
||||
|
||||
if vals.get('group_ids'):
|
||||
self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context)
|
||||
|
||||
|
|
|
@ -60,6 +60,11 @@
|
|||
<label for="name" string="Group Name"/>
|
||||
</div>
|
||||
<h1><field name="name"/></h1>
|
||||
<div name="alias_box" colspan="4" attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<field name="alias_id" invisible="1" required="0"/>
|
||||
<label for="alias_name" class="oe_edit_only"/>
|
||||
<field name="alias_name" nolabel="1" class="oe_inline" attrs="{'required': [('alias_id', '!=', False)]}"/>@<field name="alias_domain" nolabel="1" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="description" placeholder="Topics discussed in this group..."/>
|
||||
</div>
|
||||
<div class="oe_clear"/>
|
||||
|
|
|
@ -21,19 +21,26 @@
|
|||
|
||||
import base64
|
||||
import email
|
||||
from email.utils import parsedate
|
||||
import logging
|
||||
from mail_message import decode, to_email
|
||||
from operator import itemgetter
|
||||
from osv import osv, fields
|
||||
import re
|
||||
import time
|
||||
import xmlrpclib
|
||||
from email.utils import parsedate
|
||||
from email.message import Message
|
||||
|
||||
from osv import osv, fields
|
||||
from mail_message import decode, to_email
|
||||
import tools
|
||||
from tools.translate import _
|
||||
import xmlrpclib
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def decode_header(message, header, separator=' '):
|
||||
return separator.join(map(decode,message.get_all(header, [])))
|
||||
|
||||
|
||||
class mail_thread(osv.Model):
|
||||
'''Mixin model, meant to be inherited by any model that needs to
|
||||
act as a discussion topic on which messages can be attached.
|
||||
|
@ -151,10 +158,9 @@ class mail_thread(osv.Model):
|
|||
|
||||
message_obj = self.pool.get('mail.message')
|
||||
notification_obj = self.pool.get('mail.notification')
|
||||
body = vals.get('body_html', '') if vals.get('content_subtype') == 'html' else vals.get('body_text', '')
|
||||
|
||||
# automatically subscribe the writer of the message
|
||||
if vals['user_id']:
|
||||
if vals.get('user_id'):
|
||||
self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context)
|
||||
|
||||
# create message
|
||||
|
@ -173,12 +179,11 @@ class mail_thread(osv.Model):
|
|||
notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context)
|
||||
|
||||
# create the email to send
|
||||
email_id = self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context)
|
||||
self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context)
|
||||
|
||||
return msg_id
|
||||
|
||||
def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None):
|
||||
subscription_obj = self.pool.get('mail.subscription')
|
||||
# get body
|
||||
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '')
|
||||
|
||||
|
@ -301,7 +306,6 @@ class mail_thread(osv.Model):
|
|||
threads = model_pool.browse(cr, uid, threads, context=context)
|
||||
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
|
||||
new_msg_ids = []
|
||||
for thread in threads:
|
||||
|
@ -501,10 +505,6 @@ class mail_thread(osv.Model):
|
|||
|
||||
""" Retrieve all attachments names """
|
||||
map_id_to_name = dict((attachment_id, '') for message in messages for attachment_id in message['attachment_ids'])
|
||||
map_id_to_name = {}
|
||||
for msg in messages:
|
||||
for attach_id in msg["attachment_ids"]:
|
||||
map_id_to_name[attach_id] = '' # use empty string as a placeholder
|
||||
|
||||
ids = map_id_to_name.keys()
|
||||
names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context)
|
||||
|
@ -560,79 +560,127 @@ class mail_thread(osv.Model):
|
|||
msgs = msg_obj.read(cr, uid, msg_ids, context=context)
|
||||
return msgs
|
||||
|
||||
|
||||
def _get_user(self, cr, uid, alias, context):
|
||||
"""
|
||||
param alias: browse record of alias.
|
||||
return: int user_id.
|
||||
"""
|
||||
|
||||
user_obj = self.pool.get('res.user')
|
||||
user_id = 1
|
||||
if alias.alias_user_id:
|
||||
user_id = alias.alias_user_id.id
|
||||
#if user_id not defined in the alias then search related user using name of Email sender
|
||||
else:
|
||||
from_email = msg.get('from')
|
||||
user_ids = user_obj.search(cr, uid, [('name','=',from_email)], context)
|
||||
if user_ids:
|
||||
user_id = user_obj.browse(cr, uid, user_ids[0], context).id
|
||||
return user_id
|
||||
|
||||
def message_catchall(self, cr, uid, message, context=None):
|
||||
"""
|
||||
Process incoming mail and call messsage_process using details of the mail.alias model
|
||||
else raise Exception so that mailgate script will reject the mail and
|
||||
send notification mail sender that this mailbox does not exist so your mail have been rejected.
|
||||
"""
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
if isinstance(message, xmlrpclib.Binary):
|
||||
message = str(message.data)
|
||||
if isinstance(message, unicode):
|
||||
message = message.encode('utf-8')
|
||||
msg_txt = email.message_from_string(message)
|
||||
msg = mail_message.parse_message(msg_txt)
|
||||
alias_name = msg.get('to').split("@")[0] # @@@@
|
||||
alias_ids = mail_alias.search(cr, uid, [('alias_name','=',alias_name)])
|
||||
#if alias found then call message_process method. # @@@@
|
||||
if alias_ids:
|
||||
alias_id = mail_alias.browse(cr, uid, alias_ids[0], context)
|
||||
user_id = self._get_user( cr, uid, alias_id, context)
|
||||
alias_defaults = dict(eval(alias_id.alias_defaults or {}))
|
||||
self.message_process(cr, user_id, alias_id.alias_model_id.model, message,
|
||||
custom_values=alias_defaults,
|
||||
thread_id=alias_id.alias_force_thread_id or False,
|
||||
context=context)
|
||||
else:
|
||||
#if Mail box for the intended Mail Alias then give logger warning
|
||||
_logger.warning("No Mail Alias Found for the name '%s'."%(alias_name))
|
||||
raise # @@@@
|
||||
return True
|
||||
def _message_find_user_id(self, cr, uid, message, context=None):
|
||||
from_local_part = to_email(decode(message.get('From')))[0]
|
||||
user_ids = self.pool.get('res.users').search(cr, uid, [('login', '=', from_local_part)], context=context)
|
||||
return user_ids[0] if user_ids else uid
|
||||
|
||||
#------------------------------------------------------
|
||||
# Mail gateway
|
||||
#------------------------------------------------------
|
||||
# message_process will call either message_new or message_update.
|
||||
|
||||
def message_route(self, cr, uid, message, model=None, thread_id=None,
|
||||
custom_values=None, context=None):
|
||||
"""Attempt to figure out the correct target model, thread_id,
|
||||
custom_values and user_id to use for an incoming message.
|
||||
Multiple values may be returned, if a message had multiple
|
||||
recipients matching existing mail.aliases, for example.
|
||||
|
||||
The following heuristics are used, in this order:
|
||||
1. If the message replies to an existing thread_id, and
|
||||
properly contains the thread model in the 'In-Reply-To'
|
||||
header, use this model/thread_id pair, and ignore
|
||||
custom_value (not needed as no creation will take place)
|
||||
2. Look for a mail.alias entry matching the message
|
||||
recipient, and use the corresponding model, thread_id,
|
||||
custom_values and user_id.
|
||||
3. Fallback to the ``model``, ``thread_id`` and ``custom_values``
|
||||
provided.
|
||||
4. If all the above fails, raise an exception.
|
||||
|
||||
:param string message: an email.message instance
|
||||
:param string model: the fallback model to use if the message
|
||||
does not match any of the currently configured mail aliases
|
||||
(may be None if a matching alias is supposed to be present)
|
||||
:type dict custom_values: optional dictionary of default field values
|
||||
to pass to ``message_new`` if a new record needs to be created.
|
||||
Ignored if the thread record already exists, and also if a
|
||||
matching mail.alias was found (aliases define their own defaults)
|
||||
:param int thread_id: optional ID of the record/thread from ``model``
|
||||
to which this mail should be attached. Only used if the message
|
||||
does not reply to an existing thread and does not match any mail alias.
|
||||
:return: list of [model, thread_id, custom_values, user_id]
|
||||
"""
|
||||
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
|
||||
message_id = message.get('Message-Id')
|
||||
|
||||
# 1. Verify if this is a reply to an existing thread
|
||||
references = decode_header(message, 'References') or decode_header(message, 'In-Reply-To')
|
||||
ref_match = references and tools.reference_re.search(references)
|
||||
if ref_match:
|
||||
thread_id = int(ref_match.group(1))
|
||||
model = ref_match.group(2) or model
|
||||
model_pool = self.pool.get(model)
|
||||
if thread_id and model and model_pool and model_pool.exists(cr, uid, thread_id) \
|
||||
and hasattr(model_pool, 'message_update'):
|
||||
_logger.debug('Routing mail with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s',
|
||||
message_id, model, thread_id, custom_values, uid)
|
||||
return [(model, thread_id, custom_values, uid)]
|
||||
|
||||
# 2. Look for a matching mail.alias entry
|
||||
# Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values
|
||||
# for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value.
|
||||
rcpt_tos = decode_header(message, 'Delivered-To') or \
|
||||
','.join([decode_header(message, 'To'),
|
||||
decode_header(message, 'Cc'),
|
||||
decode_header(message, 'Resent-To'),
|
||||
decode_header(message, 'Resent-Cc')])
|
||||
local_parts = [e.split('@')[0] for e in to_email(rcpt_tos)]
|
||||
if local_parts:
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
alias_ids = mail_alias.search(cr, uid, [('alias_name', 'in', local_parts)])
|
||||
if alias_ids:
|
||||
routes = []
|
||||
for alias in mail_alias.browse(cr, uid, alias_ids, context=context):
|
||||
user_id = alias.alias_user_id.id
|
||||
if not user_id:
|
||||
user_id = self._message_find_user_id(cr, uid, message, context=context)
|
||||
routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, \
|
||||
eval(alias.alias_defaults), user_id))
|
||||
_logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes)
|
||||
return routes
|
||||
|
||||
# 3. Fallback to the provided parameters, if they work
|
||||
model_pool = self.pool.get(model)
|
||||
if not thread_id:
|
||||
# Legacy: fallback to matching [ID] in the Subject
|
||||
match = tools.res_re.search(decode_header(message, 'Subject'))
|
||||
thread_id = match and match.group(1)
|
||||
assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
|
||||
"No possible route found for incoming message with Message-Id %s. " \
|
||||
"Create an appropriate mail.alias or force the destination model." % message_id
|
||||
if thread_id and not model_pool.exists(cr, uid, thread_id):
|
||||
_logger.warning('Received mail reply to missing document %s! Ignoring and creating new document instead for Message-Id %s',
|
||||
thread_id, message_id)
|
||||
thread_id = None
|
||||
_logger.debug('Routing mail with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s',
|
||||
message_id, model, thread_id, custom_values, uid)
|
||||
return [(model, thread_id, custom_values, uid)]
|
||||
|
||||
|
||||
def message_process(self, cr, uid, model, message, custom_values=None,
|
||||
save_original=False, strip_attachments=False,
|
||||
thread_id=None, context=None):
|
||||
"""Process an incoming RFC2822 email message related to the
|
||||
given thread model, relying on ``mail.message.parse()``
|
||||
for the parsing operation, and then calling ``message_new``
|
||||
(if the thread record did not exist) or ``message_update``
|
||||
(if it did), then calling ``message_forward`` to automatically
|
||||
notify other people that should receive this message.
|
||||
"""Process an incoming RFC2822 email message, relying on
|
||||
``mail.message.parse()`` for the parsing operation,
|
||||
and ``message_route()`` to figure out the target model.
|
||||
|
||||
Once the target model is known, its ``message_new`` method
|
||||
is called with the new message (if the thread record did not exist)
|
||||
or its ``message_update`` method (if it did). Finally,
|
||||
``message_forward`` is called to automatically notify other
|
||||
people that should receive this message.
|
||||
|
||||
:param string model: the thread model for which a new message
|
||||
must be processed
|
||||
:param message: source of the RFC2822 mail
|
||||
:param string model: the fallback model to use if the message
|
||||
does not match any of the currently configured mail aliases
|
||||
(may be None if a matching alias is supposed to be present)
|
||||
:param message: source of the RFC2822 message
|
||||
:type message: string or xmlrpclib.Binary
|
||||
:type dict custom_values: optional dictionary of field values
|
||||
to pass to ``message_new`` if a new
|
||||
record needs to be created. Ignored
|
||||
if the thread record already exists.
|
||||
to pass to ``message_new`` if a new record needs to be created.
|
||||
Ignored if the thread record already exists, and also if a
|
||||
matching mail.alias was found (aliases define their own defaults)
|
||||
:param bool save_original: whether to keep a copy of the original
|
||||
email source attached to the message after it is imported.
|
||||
:param bool strip_attachments: whether to strip all attachments
|
||||
|
@ -642,66 +690,41 @@ class mail_thread(osv.Model):
|
|||
overrides the automatic detection based on the message
|
||||
headers.
|
||||
"""
|
||||
if context is None: context = {}
|
||||
|
||||
# extract message bytes - we are forced to pass the message as binary because
|
||||
# we don't know its encoding until we parse its headers and hence can't
|
||||
# convert it to utf-8 for transport between the mailgate script and here.
|
||||
if isinstance(message, xmlrpclib.Binary):
|
||||
message = str(message.data)
|
||||
|
||||
if context is None: context = {}
|
||||
|
||||
mail_message = self.pool.get('mail.message')
|
||||
model_pool = self.pool.get(model)
|
||||
if self._name != model:
|
||||
context.update({'thread_model': model})
|
||||
|
||||
# Parse Message
|
||||
# Warning: message_from_string doesn't always work correctly on unicode,
|
||||
# we must use utf-8 strings here :-(
|
||||
if isinstance(message, unicode):
|
||||
message = message.encode('utf-8')
|
||||
msg_txt = email.message_from_string(message)
|
||||
msg = mail_message.parse_message(msg_txt, save_original=save_original, context=context)
|
||||
|
||||
# update state
|
||||
msg['state'] = 'received'
|
||||
|
||||
routes = self.message_route(cr, uid, msg_txt, model,
|
||||
thread_id, custom_values,
|
||||
context=context)
|
||||
msg = self.pool.get('mail.message').parse_message(msg_txt, save_original=save_original, context=context)
|
||||
msg['state'] = 'received'
|
||||
if strip_attachments and 'attachments' in msg:
|
||||
del msg['attachments']
|
||||
|
||||
# Create New Record into particular model
|
||||
def create_record(msg):
|
||||
if hasattr(model_pool, 'message_new'):
|
||||
return model_pool.message_new(cr, uid, msg,
|
||||
custom_values,
|
||||
context=context)
|
||||
if not thread_id and (msg.get('references') or msg.get('in-reply-to')):
|
||||
references = msg.get('references') or msg.get('in-reply-to')
|
||||
if '\r\n' in references:
|
||||
references = references.split('\r\n')
|
||||
for model, thread_id, custom_values, user_id in routes:
|
||||
if self._name != model:
|
||||
context.update({'thread_model': model})
|
||||
model_pool = self.pool.get(model)
|
||||
assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
|
||||
"Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
|
||||
(msg['message-id'], model)
|
||||
if thread_id and hasattr(model_pool, 'message_update'):
|
||||
model_pool.message_update(cr, user_id, [thread_id], msg, context=context)
|
||||
else:
|
||||
references = references.split(' ')
|
||||
for ref in references:
|
||||
ref = ref.strip()
|
||||
thread_id = tools.reference_re.search(ref)
|
||||
if not thread_id:
|
||||
thread_id = tools.res_re.search(msg['subject'])
|
||||
if thread_id:
|
||||
thread_id = int(thread_id.group(1))
|
||||
if not model_pool.exists(cr, uid, thread_id) or \
|
||||
not hasattr(model_pool, 'message_update'):
|
||||
# referenced thread not found or not updatable,
|
||||
# -> create a new one
|
||||
thread_id = False
|
||||
if not thread_id:
|
||||
thread_id = create_record(msg)
|
||||
else:
|
||||
model_pool.message_update(cr, uid, [thread_id], msg, {}, context=context)
|
||||
# To forward the email to other followers
|
||||
self.message_forward(cr, uid, model, [thread_id], msg_txt, context=context)
|
||||
# Set as Unread
|
||||
model_pool.message_mark_as_unread(cr, uid, [thread_id], context=context)
|
||||
return thread_id
|
||||
thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context)
|
||||
|
||||
# Forward the email to other followers
|
||||
self.message_forward(cr, uid, model, [thread_id], msg_txt, context=context)
|
||||
model_pool.message_mark_as_unread(cr, uid, [thread_id], context=context)
|
||||
return True
|
||||
|
||||
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
|
||||
"""Called by ``message_process`` when a new message is received
|
||||
|
@ -735,7 +758,7 @@ class mail_thread(osv.Model):
|
|||
fields = model_pool.fields_get(cr, uid, context=context)
|
||||
data = model_pool.default_get(cr, uid, fields, context=context)
|
||||
if 'name' in fields and not data.get('name'):
|
||||
data['name'] = msg_dict.get('from', '')
|
||||
data['name'] = msg_dict.get('subject', '')
|
||||
if custom_values and isinstance(custom_values, dict):
|
||||
data.update(custom_values)
|
||||
res_id = model_pool.create(cr, uid, data, context=context)
|
||||
|
@ -791,7 +814,6 @@ class mail_thread(osv.Model):
|
|||
"""
|
||||
model_pool = self.pool.get(model)
|
||||
smtp_server_obj = self.pool.get('ir.mail_server')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
for res in model_pool.browse(cr, uid, thread_ids, context=context):
|
||||
if hasattr(model_pool, 'message_thread_followers'):
|
||||
followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id]
|
||||
|
|
|
@ -54,6 +54,7 @@ class res_users(osv.osv):
|
|||
|
||||
_defaults = {
|
||||
'notification_email_pref': 'to_me',
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
}
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
|
@ -67,32 +68,45 @@ class res_users(osv.osv):
|
|||
self.SELF_WRITEABLE_FIELDS.append('notification_email_pref')
|
||||
return init_res
|
||||
|
||||
def init(self, cr):
|
||||
# Installation hook to create aliases for all users, right after _auto_init
|
||||
def _auto_init(self, cr, context=None):
|
||||
"""Installation hook to create aliases for all users and avoid constraint errors."""
|
||||
|
||||
# disable the unique alias_id not null constraint, to avoid spurious warning during
|
||||
# super.auto_init. We'll reinstall it afterwards.
|
||||
self._columns['alias_id'].required = False
|
||||
|
||||
super(res_users,self)._auto_init(cr, context=context)
|
||||
|
||||
registry = RegistryManager.get(cr.dbname)
|
||||
mail_alias = registry.get('mail.alias')
|
||||
res_users = registry.get('res.users')
|
||||
users_no_alias = res_users.search(cr, SUPERUSER_ID, [('alias_id', '=', False)])
|
||||
res_users_model = registry.get('res.users')
|
||||
users_no_alias = res_users_model.search(cr, SUPERUSER_ID, [('alias_id', '=', False)])
|
||||
# Use read() not browse(), to avoid prefetching uninitialized inherited fields
|
||||
for user_data in res_users.read(cr, SUPERUSER_ID, users_no_alias, ['login']):
|
||||
for user_data in res_users_model.read(cr, SUPERUSER_ID, users_no_alias, ['login']):
|
||||
alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, {'alias_name': user_data['login'],
|
||||
'alias_force_id': user_data['id']},
|
||||
model_name=self._name)
|
||||
res_users.write(cr, SUPERUSER_ID, user_data['id'], {'alias_id': alias_id})
|
||||
res_users_model.write(cr, SUPERUSER_ID, user_data['id'], {'alias_id': alias_id})
|
||||
_logger.info('Mail alias created for user %s (uid %s)', user_data['login'], user_data['id'])
|
||||
|
||||
# Finally attempt to reinstate the missing constraint
|
||||
try:
|
||||
cr.execute('ALTER TABLE res_users ALTER COLUMN alias_id SET NOT NULL')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
_logger.warning("Table '%s': unable to set a NOT NULL constraint on column '%s' !\n"\
|
||||
"If you want to have it, you should update the records and execute manually:\n"\
|
||||
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",
|
||||
self._table, 'alias_id', self._table, 'alias_id')
|
||||
|
||||
self._columns['alias_id'].required = True
|
||||
|
||||
|
||||
def create(self, cr, uid, data, context=None):
|
||||
# create default alias same as the login
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context)
|
||||
data['alias_id'] = alias_id
|
||||
data.pop('alias_name', None) # prevent errors during copy()
|
||||
user_id = super(res_users, self).create(cr, uid, data, context=context)
|
||||
mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context)
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</xpath>
|
||||
<field name="user_email" position="after">
|
||||
<field name="alias_domain" invisible="1"/>
|
||||
<field name="alias_id" readonly="1" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
|
||||
<field name="alias_id" readonly="1" required="0" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
|
|
|
@ -106,15 +106,15 @@ class EmailParser(object):
|
|||
self.email_default = email_default
|
||||
|
||||
|
||||
def parse(self, method, message, custom_values=None, save_original=None):
|
||||
def parse(self, message, custom_values=None, save_original=None):
|
||||
# pass message as bytes because we don't know its encoding until we parse its headers
|
||||
# and hence can't convert it to utf-8 for transport
|
||||
res_id = self.rpc('mail.thread',
|
||||
method,
|
||||
self.model,
|
||||
xmlrpclib.Binary(message),
|
||||
custom_values or {},
|
||||
save_original or False)
|
||||
return self.rpc('mail.thread',
|
||||
'message_process',
|
||||
self.model,
|
||||
xmlrpclib.Binary(message),
|
||||
custom_values or {},
|
||||
save_original or False)
|
||||
|
||||
def configure_parser():
|
||||
parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.1')
|
||||
|
@ -123,32 +123,32 @@ def configure_parser():
|
|||
"with the OpenERP server for case management in the CRM module.")
|
||||
parser.add_option_group(group)
|
||||
parser.add_option("-u", "--user", dest="userid",
|
||||
help="ID of the user in OpenERP",
|
||||
help="OpenERP user id to connect with",
|
||||
default=config.OPENERP_DEFAULT_USER_ID, type='int')
|
||||
parser.add_option("-p", "--password", dest="password",
|
||||
help="Password of the user in OpenERP",
|
||||
help="OpenERP user password",
|
||||
default=config.OPENERP_DEFAULT_PASSWORD)
|
||||
parser.add_option("-o", "--model", dest="model",
|
||||
help="Name or ID of crm model",
|
||||
help="Name or ID of destination model",
|
||||
default="crm.lead")
|
||||
parser.add_option("-m", "--default", dest="default",
|
||||
help="Default eMail in case of any trouble.",
|
||||
help="Admin email for error notifications.",
|
||||
default=None)
|
||||
parser.add_option("-d", "--dbname", dest="dbname",
|
||||
help="Database name (default: %default)",
|
||||
help="OpenERP database name (default: %default)",
|
||||
default=config.OPENERP_DEFAULT_DATABASE)
|
||||
parser.add_option("--host", dest="host",
|
||||
help="Hostname of the OpenERP Server",
|
||||
help="OpenERP Server hostname",
|
||||
default=config.OPENERP_HOSTNAME)
|
||||
parser.add_option("--port", dest="port",
|
||||
help="Port of the OpenERP Server",
|
||||
help="OpenERP Server XML-RPC port number",
|
||||
default=config.OPENERP_PORT)
|
||||
parser.add_option("--custom-values", dest="custom_values",
|
||||
help="Add Custom Values to the object",
|
||||
help="Dictionary of extra values to pass when creating records",
|
||||
default=None)
|
||||
parser.add_option("-s", dest="save_original",
|
||||
action="store_true",
|
||||
help="Attach a copy of original email to the message entry",
|
||||
help="Keep a full copy of the email source attached to each message",
|
||||
default=False)
|
||||
|
||||
return parser
|
||||
|
@ -160,7 +160,6 @@ def main():
|
|||
|
||||
parser = configure_parser()
|
||||
(options, args) = parser.parse_args()
|
||||
method = "message_process"
|
||||
email_parser = EmailParser(options.userid,
|
||||
options.password,
|
||||
options.dbname,
|
||||
|
@ -170,8 +169,6 @@ def main():
|
|||
email_default= options.default)
|
||||
msg_txt = sys.stdin.read()
|
||||
custom_values = {}
|
||||
if not options.model:
|
||||
method = "message_catchall"
|
||||
try:
|
||||
custom_values = dict(eval(options.custom_values or "{}" ))
|
||||
except:
|
||||
|
@ -179,7 +176,7 @@ def main():
|
|||
traceback.print_exc()
|
||||
|
||||
try:
|
||||
email_parser.parse(method, msg_txt, custom_values, options.save_original or False)
|
||||
email_parser.parse(msg_txt, custom_values, options.save_original or False)
|
||||
except Exception:
|
||||
msg = '\n'.join([
|
||||
'parameters',
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Business Applications
|
||||
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
|
||||
#
|
||||
# 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 . import test_mail
|
||||
|
||||
checks = [
|
||||
test_mail,
|
||||
]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,112 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Business Applications
|
||||
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
|
||||
#
|
||||
# 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 openerp.tests import common
|
||||
|
||||
MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
|
||||
To: {to}
|
||||
Received: by mail1.openerp.com (Postfix, from userid 10002)
|
||||
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
|
||||
From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
|
||||
Subject: {subject}
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="----=_Part_4200734_24778174.1344608186754"
|
||||
Date: Fri, 10 Aug 2012 14:16:26 +0000
|
||||
Message-ID: <1198923581.41972151344608186760.JavaMail@agrolait.com>
|
||||
{extra}
|
||||
------=_Part_4200734_24778174.1344608186754
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
Please call me as soon as possible this afternoon!
|
||||
|
||||
--
|
||||
Sylvie
|
||||
------=_Part_4200734_24778174.1344608186754
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>=20
|
||||
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
|
||||
</head>=20
|
||||
<body style=3D"margin: 0; padding: 0; background: #ffffff;-webkit-text-size-adjust: 100%;">=20
|
||||
|
||||
<p>Please call me as soon as possible this afternoon!</p>
|
||||
|
||||
<p>--<br/>
|
||||
Sylvie
|
||||
<p>
|
||||
</body>
|
||||
</html>
|
||||
------=_Part_4200734_24778174.1344608186754--
|
||||
"""
|
||||
|
||||
|
||||
class test_mail(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(test_mail, self).setUp()
|
||||
self.ir_model = self.registry('ir.model')
|
||||
self.mail_alias = self.registry('mail.alias')
|
||||
self.mail_thread = self.registry('mail.thread')
|
||||
self.mail_group = self.registry('mail.group')
|
||||
self.res_users = self.registry('res.users')
|
||||
|
||||
# groups@.. will cause the creation of new mail groups
|
||||
self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model','=', 'mail.group')])[0]
|
||||
self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups',
|
||||
'alias_model_id': self.mail_group_model_id})
|
||||
|
||||
# tech@... will append new messages to the 'tech' group
|
||||
self.group_tech_id = self.mail_group.create(self.cr, self.uid, {'name': 'tech'})
|
||||
|
||||
def test_message_process(self):
|
||||
# Incoming mail creates a new mail_group "frogs"
|
||||
self.assertEqual(self.mail_group.search(self.cr, self.uid, [('name','=','frogs')]), [])
|
||||
mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='')
|
||||
self.mail_thread.message_process(self.cr, self.uid, None, mail_frogs)
|
||||
frog_groups = self.mail_group.search(self.cr, self.uid, [('name','=','frogs')])
|
||||
self.assertTrue(len(frog_groups) == 1)
|
||||
|
||||
# Previously-created group can be emailed now - it should have an implicit alias group+frogs@...
|
||||
frog_group = self.mail_group.browse(self.cr, self.uid, frog_groups[0])
|
||||
group_messages = frog_group.message_ids
|
||||
self.assertTrue(len(group_messages) == 1, 'New group should only have the original message')
|
||||
mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs <group+frogs@example.com>', subject='news', extra='')
|
||||
self.mail_thread.message_process(self.cr, self.uid, None, mail_frog_news)
|
||||
frog_group.refresh()
|
||||
self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now')
|
||||
|
||||
# Even with a wrong destination, a reply should end up in the correct thread
|
||||
mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news',
|
||||
extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n'%frog_group.id)
|
||||
self.mail_thread.message_process(self.cr, self.uid, None, mail_reply)
|
||||
frog_group.refresh()
|
||||
self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now')
|
||||
|
||||
# No model passed and no matching alias must raise
|
||||
mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='')
|
||||
self.assertRaises(Exception,
|
||||
self.mail_thread.message_process,
|
||||
self.cr, self.uid, None, mail_spam)
|
|
@ -267,6 +267,7 @@ class project(osv.osv):
|
|||
'type_ids': _get_type_common,
|
||||
'alias_model': 'project.task',
|
||||
'privacy_visibility': 'public',
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
}
|
||||
|
||||
# TODO: Why not using a SQL contraints ?
|
||||
|
@ -337,11 +338,11 @@ class project(osv.osv):
|
|||
context['active_test'] = False
|
||||
default['state'] = 'open'
|
||||
default['tasks'] = []
|
||||
default['alias_id'] = False
|
||||
default.pop('alias_name', None)
|
||||
default.pop('alias_id', None)
|
||||
proj = self.browse(cr, uid, id, context=context)
|
||||
if not default.get('name', False):
|
||||
default['name'] = proj.name + _(' (copy)')
|
||||
default['alias_name'] = default['name']
|
||||
res = super(project, self).copy(cr, uid, id, default, context)
|
||||
self.map_tasks(cr,uid,id,res,context)
|
||||
return res
|
||||
|
@ -527,11 +528,13 @@ def Project():
|
|||
context = dict(context, project_creation_in_progress=True)
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
if not vals.get('alias_id'):
|
||||
name = vals.pop('alias_name', None) or vals['name']
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid,
|
||||
{'alias_name': "project_"+short_name(name)},
|
||||
model_name=vals.get('alias_model', 'project.task'),
|
||||
context=context)
|
||||
vals.pop('alias_name', None) # prevent errors during copy()
|
||||
alias_id = mail_alias.create_unique_alias(cr, uid,
|
||||
# Using '+' allows using subaddressing for those who don't
|
||||
# have a catchall domain setup.
|
||||
{'alias_name': "project+"+short_name(vals['name'])},
|
||||
model_name=vals.get('alias_model', 'project.task'),
|
||||
context=context)
|
||||
vals['alias_id'] = alias_id
|
||||
project_id = super(project, self).create(cr, uid, vals, context)
|
||||
mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'project_id': project_id} }, context)
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
<field name="name" string="Project Name"/>
|
||||
</h1>
|
||||
<div attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<field name="alias_id" invisible="1"/>
|
||||
<field name="alias_id" invisible="1" required="0"/>
|
||||
<label for="alias_name" class="oe_edit_only "/>
|
||||
<field name="alias_name" class="oe_inline" attrs="{'required': [('alias_id', '!=', False)]}"/>@<field name="alias_domain" class="oe_inline"/>
|
||||
</div>
|
||||
|
|
|
@ -267,9 +267,6 @@ class project_issue(base_stage, osv.osv):
|
|||
}),
|
||||
}
|
||||
|
||||
def on_change_project(self, cr, uid, ids, project_id, context=None):
|
||||
return {}
|
||||
|
||||
_defaults = {
|
||||
'active': 1,
|
||||
'partner_id': lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
|
||||
|
@ -496,7 +493,7 @@ class project_issue(base_stage, osv.osv):
|
|||
key = maps.get(res.group(1).lower())
|
||||
update_vals[key] = res.group(2).lower()
|
||||
|
||||
return super(project_issue, self).message_update(cr, uid, ids, update_vals=update_vals, context=context)
|
||||
return super(project_issue, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
|
|
Loading…
Reference in New Issue