diff --git a/addons/crm/crm.py b/addons/crm/crm.py index b7a96957e94..81d2c632266 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -166,10 +166,10 @@ class crm_case_section(osv.osv): 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': "sales_team_"+name, - 'alias_model_id': "crm.lead"}, context=context) - alias = alias_pool.read(cr, uid, alias_id, ['alias_name'],context) - vals.update({'alias_id': alias_id, 'alias_name': alias['alias_name']}) + {'alias_name': 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) return res diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index db77719a85b..6cd21061e4f 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -54,8 +54,8 @@ class base_action_rule(osv.osv): name = '[%d] %s' % (obj.id, tools.ustr(obj.name)) emailfrom = tools.ustr(emailfrom) - if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.reply_to: - reply_to = obj.section_id.reply_to + if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.alias_id: + reply_to = obj.section_id.alias_id.name_get()[0][1] else: reply_to = emailfrom if not emailfrom: diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index 3d526b6c2e1..da8995fdd13 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -87,20 +87,13 @@ - - - - - - - - - - -
-
+
+
+ + + diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index bb41d905344..c2112c9ad84 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -520,10 +520,10 @@ class hr_job(osv.osv): 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': "job_"+name, - 'alias_model_id': "hr.applicant"}, context=context) - alias = alias_pool.read(cr, uid, alias_id, ['alias_name'],context) - vals.update({'alias_id': alias_id, 'alias_name': alias['alias_name']}) + {'alias_name': "job_"+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) return res diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 6514faf5d7c..372b9a2566c 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -64,17 +64,16 @@ The main features of the module are: 'wizard/mail_compose_message_view.xml', 'res_config_view.xml', 'mail_message_view.xml', - 'mail_alias_data.xml', 'mail_subscription_view.xml', 'mail_thread_view.xml', 'mail_group_view.xml', 'res_partner_view.xml', - 'res_users_view.xml', 'data/mail_data.xml', 'data/mail_group_data.xml', 'security/mail_security.xml', 'security/ir.model.access.csv', 'mail_alias_view.xml', + 'res_users_view.xml', ], 'demo': [ 'data/mail_demo.xml', diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 2448b89fcd6..c8e8b1a488e 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -12,18 +12,5 @@ - - - admin - - - - {} - - - - - - diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml index 4b42f8b10d6..8be63f22804 100644 --- a/addons/mail/data/mail_demo.xml +++ b/addons/mail/data/mail_demo.xml @@ -88,20 +88,10 @@ Check the file in attachment for more information ! (third comment)]]> + + mail.catchall.domain + demo.openerp.com + + - - - - demo - - - - {} - - - - - - - diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index 2108fc0ae46..9aefd5a94d4 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -20,8 +20,8 @@ ############################################################################## import re + from openerp.osv import fields, osv -from tools.translate import _ class mail_alias(osv.Model): @@ -38,20 +38,17 @@ class mail_alias(osv.Model): created, it becomes immediately usable and OpenERP will accept email for it. """ _name = 'mail.alias' - _description = "Mail Alias" + _description = "Email Aliases" _rec_name = 'alias_name' def _get_alias_domain(self, cr, uid, ids, name, args, context=None): - res = {} - config_parameter_pool = self.pool.get("ir.config_parameter") - domain = config_parameter_pool.get_param(cr, uid, "mail.catchall.domain", context=context) - for alias in self.browse(cr, uid, ids, context=context): - res[alias.id] = domain or "" - return res + ir_config_parameter = self.pool.get("ir.config_parameter") + domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context) + return dict.fromkeys(ids, domain or "") _columns = { - 'alias_name': fields.char('Mailbox Alias', size=255, required=True, - help="The name of the mailbox alias, e.g. 'jobs' " + 'alias_name': fields.char('Alias', required=True, + help="The name of the email alias, e.g. 'jobs' " "if you want to catch emails for ",), 'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True, help="The model (OpenERP Document Kind) to which this alias " @@ -64,17 +61,17 @@ class mail_alias(osv.Model): ), 'alias_user_id': fields.many2one('res.users', 'Owner', help="The owner of records created upon receiving emails on this alias. " - "If this field is kept empty the system will attempt to find the right owner " + "If this field is not set the system will attempt to find the right owner " "based on the sender (From) address, or will use the Administrator account " "if no system user is found for that address."), 'alias_defaults': fields.text('Default Values', required=True, - help="The representation of a Python dictionary that will be evaluated to provide " - "default values when creating new records."), + help="A Python dictionary that will be evaluated to provide " + "default values when creating new records for this alias."), 'alias_force_thread_id': fields.integer('Record Thread ID', - help="Optional ID of the thread (record) to which all " + help="Optional ID of a thread (record) to which all incoming " "messages will be attached, even if they did not reply to it. " "If set, this will disable the creation of new records completely."), - 'alias_domain': fields.function(_get_alias_domain, string="Alias Doamin", type='char', size=None), + 'alias_domain': fields.function(_get_alias_domain, string="Alias Domain", type='char', size=None), } _defaults = { @@ -87,16 +84,11 @@ class mail_alias(osv.Model): ] def _check_alias_defaults(self, cr, uid, ids, context=None): - """ - Strick constraints checking for the alias defaults values. - it must follow the python dict format. So message_catachall - will not face any issue. - """ - for record in self.browse(cr, uid, ids, context=context): - try: + try: + for record in self.browse(cr, uid, ids, context=context): dict(eval(record.alias_defaults)) - except Exception: - return False + except Exception: + return False return True _constraints = [ @@ -104,48 +96,38 @@ class mail_alias(osv.Model): ] def name_get(self, cr, uid, ids, context=None): + """Return the mail alias display alias_name, inclusing the implicit + mail catchall domain from config. + e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` """ - Return the mail alias display alias_name, inclusing the Implicit and dynamic - Email Alias Domain from config. - e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` - """ - res = [] - for record in self.read(cr, uid, ids, ['alias_name', 'alias_domain'], context=context): - domain_alias = "%s@%s"%(record['alias_name'], record['alias_domain']) - res.append((record['id'], domain_alias)) - return res + return [(record['id'], "%s@%s" % (record['alias_name'], record['alias_domain'])) + for record in self.read(cr, uid, ids, ['alias_name', 'alias_domain'], context=context)] - def _generate_alias(self, cr, uid, name, sequence=1 ,context=None): + def _find_unique(self, cr, uid, name, context=None): + """Find a unique alias name similar to ``name``. If ``name`` is + already taken, make a variant by adding an integer suffix until + an unused alias is found. """ - If alias is existing then this method will create a new unique alias name - by appending n sequence integer number. - """ - new_name = "%s%s"%(name, sequence) - search_alias = self.search(cr, uid, [('alias_name', '=', new_name)]) - if search_alias: - new_name = self._generate_alias(cr, uid, name, sequence+1 ,context=None) + sequence = None + while True: + new_name = "%s%s" % (name, sequence) if sequence is not None else name if not self.search(cr, uid, [('alias_name', '=', new_name)]): - return new_name - else: - return new_name + break + sequence = (sequence + 1) if sequence else 2 + return new_name - def create_unique_alias(self, cr, uid, vals, context=None): + def create_unique_alias(self, cr, uid, vals, model_name=None, context=None): + """Creates an email.alias record according to the values provided in ``vals``, + with 2 alterations: the ``alias_name`` value may be suffixed in order to + make it unique, and the ``alias_model_id`` value will set to the + model ID of the ``model_name`` value, if provided, """ - Methods accepts the vals param in dict format and create new alias record - @vals : dict accept {alias_name: '', alias_model_id: '',...} - @return int: New alias_id - """ - model_pool = self.pool.get('ir.model') alias_name = re.sub(r'\W+', '_', vals['alias_name']).lower() - values = {'alias_name': alias_name} - #Find for the mail alias exist or not if exit then get new mail address. - saids = self.search(cr, uid, [('alias_name', '=',alias_name)]) - if saids: - alias_name = self._generate_alias(cr, uid, alias_name, sequence=1, context=context) - values.update({'alias_name': alias_name}) - #Set the model fo rhte mail alias - model_sids = model_pool.search(cr, uid, [('model', '=', vals['alias_model_id'])]) - values.update({'alias_model_id': model_sids[0]}) - return self.create(cr, uid, values, context=context) + 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) diff --git a/addons/mail/mail_alias_data.xml b/addons/mail/mail_alias_data.xml deleted file mode 100644 index 041c1dd3316..00000000000 --- a/addons/mail/mail_alias_data.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - mail.catchall.domain - openerp.my.openerp.com - - - diff --git a/addons/mail/mail_alias_view.xml b/addons/mail/mail_alias_view.xml index 344a98d74db..61a89b774ca 100644 --- a/addons/mail/mail_alias_view.xml +++ b/addons/mail/mail_alias_view.xml @@ -2,12 +2,11 @@ - + mail.alias.form mail.alias form -
@@ -18,7 +17,7 @@ - + @@ -26,7 +25,7 @@ - + mail.alias.tree mail.alias @@ -41,7 +40,7 @@ - + mail.alias.search mail.alias @@ -59,11 +58,8 @@ - Mail Alias + Aliases mail.alias - form - tree,form - form - + diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 70df10a6bc8..cad2734d672 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -19,9 +19,14 @@ # ############################################################################## +import logging + from osv import osv, fields +from openerp.modules.registry import RegistryManager +from openerp import SUPERUSER_ID from tools.translate import _ -from lxml import etree + +_logger = logging.getLogger(__name__) class res_users(osv.osv): """ Update of res.users class @@ -43,14 +48,14 @@ class res_users(osv.osv): help="Choose in which case you want to receive an email when you "\ "receive new feeds."), 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, - help="The email address associated with this user. New emails received will automatically " - "appear in the user's notifications."), + help="Email address internally associated with this user. Incoming emails will appear " + "in the user's notifications."), } _defaults = { 'notification_email_pref': 'to_me', } - + def __init__(self, pool, cr): """ Override of __init__ to add access rights on notification_email_pref field. Access rights are disabled by default, but allowed on @@ -61,27 +66,48 @@ class res_users(osv.osv): self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS) 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 + 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)]) + # User read() not browse(), to avoid prefetching uninitialized inherited fields + for user_data in res_users.read(cr, SUPERUSER_ID, users_no_alias, ['login']): + alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, {'alias_name': user_data['login'], + 'alias_model_id': self._name, + 'alias_force_id': user_data['id']}) + res_users.write(cr, SUPERUSER_ID, user_data['id'], {'alias_id': alias_id}) + _logger.info('Mail alias created for user_data %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 + 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'], 'alias_model_id': self._name}, context=context) - data.update({'alias_id': alias_id}) + alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context) + data['alias_id'] = alias_id user_id = super(res_users, self).create(cr, uid, data, context=context) - mail_alias.write(cr, uid, [alias_id], {"alias_force_thread_id": user_id}, context) + mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context) + user = self.browse(cr, uid, user_id, context=context) # make user follow itself self.message_subscribe(cr, uid, [user_id], [user_id], context=context) # create a welcome message - company_name = user.company_id.name if user.company_id else 'the company' - message = _('%s has joined %s! Welcome in OpenERP !') % (user.name, company_name) - self.message_append_note(cr, uid, [user_id], subject='Welcom to OpenERP', body=message, type='comment', context=context) + company_name = user.company_id.name if user.company_id else _('the company') + message = _('%s has joined %s! Welcome to OpenERP !') % (user.name, company_name) + self.message_append_note(cr, uid, [user_id], subject='Welcome to OpenERP', body=message, type='comment', context=context) return user_id def write(self, cr, uid, ids, vals, context=None): - # if login of user have been changed then change alias of user also. - if vals.get('login'): - vals['alias_name'] = vals['login'] + # User alias is sync'ed with login + if vals.get('login'): vals['alias_name'] = vals['login'] return super(res_users, self).write(cr, uid, ids, vals, context=context) def unlink(self, cr, uid, ids, context=None): diff --git a/addons/mail/res_users_view.xml b/addons/mail/res_users_view.xml index 6ce2680f9bc..d739e56661e 100644 --- a/addons/mail/res_users_view.xml +++ b/addons/mail/res_users_view.xml @@ -31,10 +31,9 @@ - - - - + + + diff --git a/addons/project/project.py b/addons/project/project.py index 5304bd11c24..e2a9a49e9ed 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -52,10 +52,20 @@ class project_task_type(osv.osv): } _order = 'sequence' + +def short_name(name): + """Keep first word(s) of name to make it small enough + but distinctive""" + if not name: return name + # keep 7 chars + end of the last word + hardcut_tokens = name[:7].split() + return ' '.join(name.split()[:len(hardcut_tokens)]) + class project(osv.osv): _name = "project.project" _description = "Project" - _inherits = {'account.analytic.account': "analytic_account_id","mail.alias":"alias_id"} + _inherits = {'account.analytic.account': "analytic_account_id", + "mail.alias": "alias_id"} _inherit = ['ir.needaction_mixin', 'mail.thread'] def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): @@ -230,10 +240,10 @@ class project(osv.osv): 'task_count': fields.function(_task_count, type='integer', string="Open Tasks"), 'color': fields.integer('Color Index'), 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, - help="The email address associated with this project. New emails received will automatically " - "create project tasks (optionally project issues if the Project Issue module is installed)."), + help="Internal email associated with this project. Incoming emails are automatically synchronized" + "with Tasks (or optionally Issues if the Issue Tracker module is installed)."), 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, - help="The kind of document created when an email is received by this project's email alias"), + help="The kind of document created when an email is received on this project's email alias"), 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), 'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers, @@ -512,18 +522,18 @@ def Project(): return user_ids 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': "project_"+name, - 'alias_model_id': vals.get('alias_model', 'project.task')}, context=context) - alias = alias_pool.read(cr, uid, alias_id, ['alias_name'],context) - vals.update({'alias_id': alias_id, 'alias_name': alias['alias_name']}) - res = super( project, self).create(cr, uid, vals, context) - alias_pool.write(cr, uid, [vals['alias_id']], {'alias_defaults':{'project_id': res}}, context) - self.create_send_note(cr, uid, [res], context=context) - return res + 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['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) + self.create_send_note(cr, uid, [project_id], context=context) + return project_id def create_send_note(self, cr, uid, ids, context=None): return self.message_append_note(cr, uid, ids, body=_("Project has been created."), context=context) @@ -545,10 +555,9 @@ def Project(): return self.message_append_note(cr, uid, ids, body=message, context=context) def write(self, cr, uid, ids, vals, context=None): - model_pool = self.pool.get('ir.model') - # if alias_model has been changed, update alias_model_id of alias accordingly + # if alias_model has been changed, update alias_model_id accordingly if vals.get('alias_model'): - model_ids = model_pool.search(cr, uid, [('model', '=', vals.get('alias_model','project.task'))]) + model_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', vals.get('alias_model', 'project.task'))]) vals.update(alias_model_id=model_ids[0]) return super(project, self).write(cr, uid, ids, vals, context=context) diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 2e1f9c3034d..9f8891dbb3e 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -82,9 +82,7 @@
-
diff --git a/addons/project/res_config_view.xml b/addons/project/res_config_view.xml index 6248efde5d2..b8b4b82e2c6 100644 --- a/addons/project/res_config_view.xml +++ b/addons/project/res_config_view.xml @@ -24,7 +24,7 @@ - + diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 2673563ec90..5845c6d366e 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -543,7 +543,7 @@ class project(osv.osv): _inherit = "project.project" def _get_alias_models(self, cr, uid, context=None): - return [('project.task', "Tasks"), ("project.issue", "Issue")] + return [('project.task', "Tasks"), ("project.issue", "Issues")] def _issue_count(self, cr, uid, ids, field_name, arg, context=None): res = dict.fromkeys(ids, 0) @@ -554,7 +554,6 @@ class project(osv.osv): _columns = { 'project_escalation_id' : fields.many2one('project.project','Project Escalation', help='If any issue is escalated from the current Project, it will be listed under the project selected here.', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}), - 'reply_to' : fields.char('Reply-To Email Address', size=256), 'issue_count': fields.function(_issue_count, type='integer'), } diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 08bb4490ce5..e16a59dc97f 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -376,10 +376,9 @@ - - - + +