diff options
Diffstat (limited to 'addons')
30 files changed, 667 insertions, 77 deletions
diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 65af3f9e530..238029a3f58 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -21,6 +21,7 @@ import base64 import time +from lxml import etree from osv import fields from osv import osv import tools @@ -97,6 +98,7 @@ class crm_case_stage(osv.osv): class crm_case_section(osv.osv): """ Model for sales teams. """ _name = "crm.case.section" + _inherits = {'mail.alias': 'alias_id'} _description = "Sales Teams" _order = "complete_name" @@ -120,6 +122,7 @@ class crm_case_section(osv.osv): 'note': fields.text('Description'), 'working_hours': fields.float('Working Hours', digits=(16,2 )), 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), + 'alias_id': fields.many2one('mail.alias', 'Mail Alias', ondelete="cascade", required=True), } def _get_stage_common(self, cr, uid, context): @@ -155,6 +158,33 @@ class crm_case_section(osv.osv): name = record['parent_id'][1] + ' / ' + name res.append((record['id'], name)) return res + + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + res = super(crm_case_section,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form': + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not domain: + doc = etree.XML(res['arch']) + alias_node = doc.xpath("//field[@name='alias_id']")[0] + parent = alias_node.getparent() + parent.remove(alias_node) + res['arch'] = etree.tostring(doc) + return res + + def create(self, cr, uid, vals, context=None): + model_pool = self.pool.get('ir.model.data') + alias_pool = self.pool.get('mail.alias') + if not vals.get('alias_id'): + model, res_id = model_pool.get_object_reference( cr, uid, "crm", "model_crm_lead") + vals.update({'alias_name': "sales", + 'alias_model_id': res_id}) + alias_pool.create_unique_alias(cr, uid, vals, context=context) + res = super(crm_case_section, self).create(cr, uid, vals, context) + record = self.read(cr, uid, res, context) + alias_pool.write(cr, uid, [record['alias_id']],{'alias_defaults':{'section_id':record['id'],'type':'lead'}},context) + return res + return super(crm_case_section, self).create(cr, uid, vals, context) + class crm_case_categ(osv.osv): """ Category of Case """ diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index 4ca40a691f5..9192b52e7b8 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -13,9 +13,17 @@ <record model="crm.case.channel"> <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> </data> </openerp> diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml index f345d86d030..c53b05df811 100644 --- a/addons/crm/crm_demo.xml +++ b/addons/crm/crm_demo.xml @@ -1,28 +1,37 @@ <?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'))]"/> </record> - <record id="ir_ui_view_sc_calendar_demo" model="ir.ui.view_sc"> - <field name="name">Meetings</field> - <field name="resource">ir.ui.menu</field> - <field name="user_id" ref="base.user_demo"/> - <field name="res_id" ref="crm.menu_crm_case_categ_meet"/> - </record> - - <record model="crm.case.section" id="section_sales_marketing_department"> - <field name="name">Sales Marketing Department</field> - <field name="code">Sales Marketing</field> - <field name="parent_id" ref="crm.section_sales_department"></field> - </record> + <record id="ir_ui_view_sc_calendar_demo" model="ir.ui.view_sc"> + <field name="name">Meetings</field> + <field name="resource">ir.ui.menu</field> + <field name="user_id" ref="base.user_demo"/> + <field name="res_id" ref="crm.menu_crm_case_categ_meet"/> + </record> - <record model="crm.segmentation" id="crm_segmentation0"> - <field name="name">OpenERP partners</field> - <field name="exclusif">True</field> - <field name="categ_id" ref="base.res_partner_category_2"/> - </record> + <record model="crm.case.section" id="section_sales_marketing_department"> + <field name="name">Sales Marketing Department</field> + <field name="code">Sales Marketing</field> + <field name="parent_id" ref="crm.section_sales_department"></field> + <field name="alias_id" ref="mail_alias_marketing_department"/> + </record> + <record model="crm.segmentation" id="crm_segmentation0"> + <field name="name">OpenERP partners</field> + <field name="exclusif">True</field> + <field name="categ_id" ref="base.res_partner_category_2"/> + </record> </data> </openerp> diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index 257d1cd93f7..7f41df641b3 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -71,6 +71,7 @@ <field name="name" colspan="2"/> <field name="parent_id"/> <field name="code"/> + <field name="alias_id" required="0" domain="[('alias_model_id', '=','crm.lead')]"/> </group> <group> <field name="user_id"/> diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py index cddcf642dc4..55923c3043a 100644 --- a/addons/fetchmail/fetchmail.py +++ b/addons/fetchmail/fetchmail.py @@ -151,7 +151,7 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS connection = server.connect() server.write({'state':'done'}) except Exception, e: - logger.exception("Failed to connect to %s server %s", server.type, server.name) + _logger.exception("Failed to connect to %s server %s", server.type, server.name) raise osv.except_osv(_("Connection test failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e)) finally: try: @@ -177,7 +177,7 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS mail_thread = self.pool.get('mail.thread') action_pool = self.pool.get('ir.actions.server') for server in self.browse(cr, uid, ids, context=context): - logger.info('start checking for new emails on %s server %s', server.type, server.name) + _logger.info('start checking for new emails on %s server %s', server.type, server.name) context.update({'fetchmail_server_id': server.id, 'server_type': server.type}) count = 0 imap_server = False @@ -198,9 +198,9 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS imap_server.store(num, '+FLAGS', '\\Seen') cr.commit() count += 1 - logger.info("fetched/processed %s email(s) on %s server %s", count, server.type, server.name) + _logger.info("fetched/processed %s email(s) on %s server %s", count, server.type, server.name) except Exception, e: - logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) + _logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) finally: if imap_server: imap_server.close() @@ -222,9 +222,9 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]}) pop_server.dele(num) cr.commit() - logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name) + _logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name) except Exception, e: - logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) + _logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) finally: if pop_server: pop_server.quit() diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index c893755c7c0..d000c0c07ae 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -22,7 +22,7 @@ from base_status.base_stage import base_stage import time from datetime import datetime, timedelta - +from lxml import etree from osv import fields, osv from crm import crm import tools @@ -549,10 +549,37 @@ class hr_applicant(base_stage, osv.Model): class hr_job(osv.osv): _inherit = "hr.job" _name = "hr.job" + _inherits = {'mail.alias': 'alias_id'} _columns = { 'survey_id': fields.many2one('survey', 'Interview Form', help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"), + 'alias_id': fields.many2one('mail.alias', 'Mail Alias', ondelete="cascade", required=True), } + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + res = super(hr_job,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form': + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not domain: + doc = etree.XML(res['arch']) + alias_node = doc.xpath("//field[@name='alias_id']")[0] + parent = alias_node.getparent() + parent.remove(alias_node) + res['arch'] = etree.tostring(doc) + return res + + def create(self, cr, uid, vals, context=None): + model_pool = self.pool.get('ir.model.data') + alias_pool = self.pool.get('mail.alias') + res_id = model_pool.get_object( cr, uid, "hr_recruitment", "model_hr_applicant") + vals.update({'alias_name': "job", + 'alias_model_id': res_id.id}) + alias_pool.create_unique_alias(cr, uid, vals, context=context) + res = super( hr_job, self).create(cr, uid, vals, context) + record = self.read(cr, uid, res, context) + alias_pool.write(cr, uid, [record['alias_id']], {"alias_defaults": {'job_id': record['id']}}, context) + return res + + def action_print_survey(self, cr, uid, ids, context=None): if context is None: context = {} diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index 13436bef45a..aed3fe1667e 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -449,5 +449,13 @@ <field name="question_id" ref="survey_question_job_1"/> <field eval="13" name="sequence"/> </record> + + <record id="mail_alias_jobs" model="mail.alias"> + <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> </openerp> diff --git a/addons/hr_recruitment/hr_recruitment_demo.yml b/addons/hr_recruitment/hr_recruitment_demo.yml index 59508b83c28..c616e9e7e95 100644 --- a/addons/hr_recruitment/hr_recruitment_demo.yml +++ b/addons/hr_recruitment/hr_recruitment_demo.yml @@ -1,4 +1,100 @@ - + !record {model: mail.alias, id: alias_job_devloper, view: False}: + alias_name: 'job-developer' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_devloper')], {"alias_defaults": {'job_id': ref('hr.job_developer')}}) +- + !record {model: hr.job, id: hr.job_developer, view: False}: + alias_id: alias_job_devloper +- + !record {model: mail.alias, id: alias_job_ceo, view: False}: + alias_name: 'job-ceo' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_ceo')], {'alias_defaults': {'job_id': ref('hr.job_ceo')}}) +- + !record {model: hr.job, id: hr.job_ceo, view: False}: + alias_id: alias_job_ceo +- + !record {model: mail.alias, id: alias_job_cto, view: False}: + alias_name: 'job-cto' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_cto')], {"alias_defaults": {'job_id': ref('hr.job_cto')}}) +- + !record {model: hr.job, id: hr.job_cto, view: False}: + alias_id: alias_job_cto +- + !record {model: mail.alias, id: alias_job_hr_manager, view: False}: + alias_name: 'job-hr-manager' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_hr_manager')], {"alias_defaults": {'job_id': ref('hr.job_hr_manager')}}) +- + !record {model: hr.job, id: hr.job_hr_manager, view: False}: + alias_id: alias_job_hr_manager +- + !record {model: mail.alias, id: alias_job_consultant, view: False}: + alias_name: 'job-consultant' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_consultant')], {"alias_defaults": {'job_id': ref('hr.job_consultant')}}) +- + !record {model: hr.job, id: hr.job_consultant, view: False}: + alias_id: alias_job_consultant +- + !record {model: mail.alias, id: alias_job_hrm, view: False}: + alias_name: 'job-hrm' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_hrm')], {"alias_defaults": {'job_id': ref('hr.job_hrm')}}) +- + !record {model: hr.job, id: hr.job_hrm, view: False}: + alias_id: alias_job_hrm +- + !record {model: mail.alias, id: alias_job_marketing, view: False}: + alias_name: 'job-marketing' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_marketing')], {"alias_defaults": {'job_id': ref('hr.job_marketing')}}) +- + !record {model: hr.job, id: hr.job_marketing, view: False}: + alias_id: alias_job_marketing +- + !record {model: mail.alias, id: alias_job_trainee, view: False}: + alias_name: 'job-trainees' + alias_model_id: model_hr_applicant + alias_user_id: base.user_root + alias_defaults: '{}' +- + !python {model: mail.alias}: | + self.write(cr, uid, [ref('alias_job_trainee')], {"alias_defaults": {'job_id': ref('hr.job_trainee')}}) +- + !record {model: hr.job, id: hr.job_trainee, view: False}: + alias_id: alias_job_trainee +- !record {model: hr.applicant, id: hr_case_salesman0}: date: !eval time.strftime('%Y-%m-01 10:35:50') type_id: degree_graduate diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index f31778b7874..56d0da979ee 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -348,6 +348,9 @@ attrs="{'invisible':[('survey_id','=',False)]}"/> </div> </field> + <field name="company_id" version="7.0" position="after"> + <field name="alias_id" required="0" domain="[('alias_model_id','=','hr.applicant')]"/> + </field> </field> </record> diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 4c313338ceb..f06b4536040 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -19,11 +19,11 @@ # ############################################################################## +import mail_alias import mail_message import mail_thread import mail_group import mail_subscription -import mail_alias import res_users import res_partner import report diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 95ce7d5619d..5c0afc27ec1 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -56,7 +56,9 @@ The main features are: 'depends': ['base', 'base_tools'], 'data': [ 'wizard/mail_compose_message_view.xml', + 'wizard/update_mail_alias_wizard.xml', 'mail_message_view.xml', + 'mail_alias_data.xml', 'mail_subscription_view.xml', 'mail_thread_view.xml', 'mail_group_view.xml', @@ -66,6 +68,10 @@ The main features are: 'mail_data.xml', 'mail_group_data.xml', 'res_users_view.xml', + 'mail_alias_view.xml', + ], + 'demo_xml': [ + 'user_mail_alias_demo.xml', ], 'installable': True, 'auto_install': False, diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index b5aa6f6d76a..292b2f742cd 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.osv import fields, osv +from tools.translate import _ class mail_alias(osv.Model): """A Mail Alias is a mapping of an email address with a given OpenERP Document @@ -39,8 +40,8 @@ class mail_alias(osv.Model): _rec_name = 'alias_name' _columns = { - 'alias_name': fields.char('Mailbox Alias', required=True, - help="The name of the mailbox alias, e.g. `jobs' " + 'alias_name': fields.char('Mailbox Alias', size=255, required=True, + help="The name of the mailbox alias, e.g. 'jobs' " "if you want to catch emails for <jobs@example.my.openerp.com>",), 'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True, help="The model (OpenERP Document Kind) to which this alias " @@ -48,7 +49,9 @@ class mail_alias(osv.Model): "existing record will cause the creation of a new record " "of this model (e.g. a Project Task)", # only allow selecting mail_thread models! - domain="[('field_ids', 'in', 'message_ids')]"), + #TODO kept doamin temporarily in comment, need to redefine domain + #domain="[('field_id', 'in', '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 kept empty the system will attempt to find the right owner " @@ -64,19 +67,54 @@ class mail_alias(osv.Model): } _defaults = { 'alias_defaults': '{}', - 'alias_user_id': lambda s,c,u,ctx: u + 'alias_user_id': lambda self,cr,uid, context: uid } - _sql_constraint = [ - ('mailbox_uniq', 'unique (alias_name)', 'Unfortunately this mail alias is already used, please choose a unique one') + _sql_constraints = [ + ('mailbox_uniq', 'UNIQUE(alias_name)', 'Unfortunately this mail alias is already used, please choose a unique one') ] - - def create_unique_alias(self, cr, uid, values, context=None): - # TODO: call create() with `values` after checking that `alias_name` - # is unique. If not unique, append a sequential number after it until - # a unique one if found. - # E.g if create_unique_alias is called with {'alias_name': 'abc'} - # and 'abc', 'abc1', 'abc2' alias exist, replace alias_name with 'abc3'. - return - - -
\ No newline at end of file + + 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: + dict(eval(record.alias_defaults)) + return True + except: + return False + + _constraints = [ + (_check_alias_defaults, "Default Values are in wrong format.\nIt must be in python dictionary format e.g.{'field_name': 'value' or {}}", ['alias_defaults']), + ] + + def name_get(self, cr, uid, ids, context=None): + """ + 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` + """ + if context is None: + context = {} + res = [] + config_parameter_pool = self.pool.get("ir.config_parameter") + domain = config_parameter_pool.get_param(cr, uid, "mail.catchall.domain", context=context) + for record in self.read(cr, uid, ids, ['alias_name'], context=context): + domain_alias = "%s@%s"%(record['alias_name'], domain) + res.append((record['id'], domain_alias)) + return res + + def create_unique_alias(self, cr, uid, values, sequence=1 ,context=None): + if sequence: + prob_alias = "%s%s"%(values['alias_name'], sequence) + search_alias = self.search(cr, uid, [('alias_name', '=', prob_alias)]) + if search_alias: + values = self.create_unique_alias(cr, uid, values, sequence+1, context) + else: + values.update({'alias_name': prob_alias}) + return values + else: + return values.update({'alias_name': "%s"%(values['alias_name'])}) + diff --git a/addons/mail/mail_alias_data.xml b/addons/mail/mail_alias_data.xml new file mode 100644 index 00000000000..041c1dd3316 --- /dev/null +++ b/addons/mail/mail_alias_data.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record model="ir.config_parameter" id="user_mail_alias"> + <field name="key">mail.catchall.domain</field> + <field name="value">openerp.my.openerp.com</field> + </record> + </data> +</openerp> diff --git a/addons/mail/mail_alias_view.xml b/addons/mail/mail_alias_view.xml new file mode 100644 index 00000000000..e72143bdb9a --- /dev/null +++ b/addons/mail/mail_alias_view.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <!-- Alias Form View !--> + <record model="ir.ui.view" id="view_mail_alias_form"> + <field name="name">mail.alias.form</field> + <field name="model">mail.alias</field> + <field name="type">form</field> + <field name="priority" eval="10"/> + <field name="arch" type="xml"> + <form string="Alias" version="7.0"> + <sheet> + <group colspan="4" col="4"> + <field name="alias_name"/> + <field name="alias_model_id" readonly="1"/> + <field name="alias_user_id"/> + <field name="alias_force_thread_id"/> + <field name="alias_defaults"/> + </group> + </sheet> + </form> + </field> + </record> + + <!-- Alias List View !--> + <record model="ir.ui.view" id="view_mail_alias_tree"> + <field name="name">mail.alias.tree</field> + <field name="model">mail.alias</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree string="Alias"> + <field name="alias_name"/> + <field name="alias_model_id"/> + <field name="alias_user_id"/> + <field name="alias_defaults"/> + </tree> + </field> + </record> + + <!-- Alias Search View !--> + <record model="ir.ui.view" id="view_mail_alias_search"> + <field name="name">mail.alias.search</field> + <field name="model">mail.alias</field> + <field name="type">search</field> + <field name="arch" type="xml"> + <search string="Search Alias"> + <field name="alias_name"/> + <field name="alias_model_id"/> + <group expand="0" string="Group By..."> + <filter string="User" name="User" icon="terp-personal" context="{'group_by':'alias_user_id'}"/> + <filter string="Model" name="Model" context="{'group_by':'alias_model_id'}"/> + </group> + </search> + </field> + </record> + + <record id="action_view_mail_alias" model="ir.actions.act_window"> + <field name="name">Mail Alias</field> + <field name="res_model">mail.alias</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="search_view_id" ref="view_mail_alias_search"/> + </record> + + <menuitem id="mail_alias_menu" + parent="base.menu_email" + action="action_view_mail_alias" + sequence="50" + groups="base.group_no_one"/> + + </data> +</openerp> diff --git a/addons/mail/mail_data.xml b/addons/mail/mail_data.xml index c8e8b1a488e..2448b89fcd6 100644 --- a/addons/mail/mail_data.xml +++ b/addons/mail/mail_data.xml @@ -12,5 +12,18 @@ <field eval="'process_email_queue'" name="function"/> <field eval="'()'" name="args"/> </record> + + <record id="mail_alias_root_user" model="mail.alias"> + <field name="alias_name">admin</field> + <field name="alias_model_id" ref="model_res_users"/> + <field name="alias_user_id" ref="base.user_root"/> + <field name="alias_force_thread_id" ref="base.user_root"/> + <field name="alias_defaults">{}</field> + </record> + + <record model="res.users" id="base.user_root"> + <field name="alias_id" ref="mail_alias_root_user"/> + </record> + </data> </openerp> diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 0a8677d7862..41d2d0a148e 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -30,6 +30,7 @@ from PIL import Image import StringIO import tools from tools.translate import _ +from lxml import etree class mail_group(osv.osv): """ @@ -48,7 +49,7 @@ class mail_group(osv.osv): _description = 'Discussion group' _name = 'mail.group' _inherit = ['mail.thread'] - + _inherits = {'mail.alias': 'alias_id'} def action_group_join(self, cr, uid, ids, context={}): return self.message_subscribe(cr, uid, ids, context=context); @@ -129,6 +130,7 @@ class mail_group(osv.osv): 'member_count': fields.function(get_member_ids, type='integer', string='Member count', multi='get_member_ids'), 'is_subscriber': fields.function(get_member_ids, type='boolean', string='Joined', multi='get_member_ids'), 'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer', string='Messages count for last month'), + 'alias_id': fields.many2one('mail.alias', 'Mail Alias', ondelete="cascade", required=True) } _defaults = { @@ -136,3 +138,27 @@ class mail_group(osv.osv): 'responsible_id': (lambda s, cr, uid, ctx: uid), 'photo': _get_default_photo, } + + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + res = super(mail_group,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form': + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not domain: + doc = etree.XML(res['arch']) + alias_node = doc.xpath("//field[@name='alias_id']")[0] + parent = alias_node.getparent() + parent.remove(alias_node) + res['arch'] = etree.tostring(doc) + return res + + def create(self, cr, uid, vals, context=None): + model_pool = self.pool.get('ir.model.data') + alias_pool = self.pool.get('mail.alias') + model, res_id = model_pool.get_object_reference( cr, uid, "mail", "model_mail_group") + vals.update({'alias_name': "mailing-group", + 'alias_model_id': res_id}) + alias_pool.create_unique_alias(cr, uid, vals, context=context) + res = super( mail_group, self).create(cr, uid, vals, context) + record = self.read(cr, uid, res, context) + alias_pool.write(cr, uid, [record['alias_id']], {"alias_force_thread_id":record['id']}, context) + return res diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 921c3b528c4..b02447576e9 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -69,6 +69,9 @@ <field name="public" class="oe_inline"/> </div> <field name="description" colspan="4" nolabel="1"/> + <div class="oe_edit_only"> + <field name="alias_id" required="0" domain="[('alias_model_id','=','mail.group')]" /> + </div> </div> </div> <footer> diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 03b6b5a02c6..2e1d653d6b7 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -475,11 +475,57 @@ class mail_thread(osv.osv): return msgs - def message_catchall(self, cr, uid, message, context=None): - """TODO proper docstring, inspired by messsage_process()""" - # TODO - pass + 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. + """ + + alias_pool = self.pool.get('mail.alias') + user_pool = self.pool.get('res.users') + mail_compose_pool = self.pool.get('mail.compose.message') + mail_message_pool = 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_pool.parse_message(msg_txt) + alias_name = msg.get('to').split("@")[0] + alias_ids = alias_pool.search(cr, uid, [('alias_name','=',alias_name)]) + #if alias found then call message_process method. + if alias_ids: + alias_id = alias_pool.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 #------------------------------------------------------ # Email specific diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index f62e29fb44d..e9a9d1222b7 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -21,6 +21,7 @@ from osv import osv, fields from tools.translate import _ +from lxml import etree class res_users(osv.osv): """ Update of res.users class @@ -29,6 +30,7 @@ class res_users(osv.osv): """ _name = 'res.users' _inherit = ['res.users', 'mail.thread'] + _inherits = {'mail.alias': 'alias_id'} _columns = { 'notification_email_pref': fields.selection([ @@ -38,6 +40,7 @@ class res_users(osv.osv): ('none', 'Never') ], 'Receive Feeds by Email', required=True, help="Choose in which case you want to receive an email when you receive new feeds."), + 'alias_id': fields.many2one('mail.alias', 'Mail Alias', ondelete="cascade", required=True), } _defaults = { @@ -55,9 +58,28 @@ class res_users(osv.osv): self.SELF_WRITEABLE_FIELDS.append('notification_email_pref') return init_res + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + res = super(res_users,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form': + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not domain: + doc = etree.XML(res['arch']) + alias_node = doc.xpath("//field[@name='alias_id']")[0] + parent = alias_node.getparent() + parent.remove(alias_node) + res['arch'] = etree.tostring(doc) + return res + def create(self, cr, uid, data, context=None): + # create default alias same as the login + model_pool = self.pool.get('ir.model.data') + alias_pool = self.pool.get('mail.alias') + res_id = model_pool.get_object( cr, uid, "mail", "model_res_users") + data.update({'alias_name': data.get('login'), + 'alias_model_id': res_id.id}) user_id = super(res_users, self).create(cr, uid, data, context=context) - user = self.browse(cr, uid, [user_id], context=context)[0] + user = self.browse(cr, uid, user_id, context=context) + alias_pool.write(cr, uid, [user.alias_id.id], {"alias_force_thread_id": user.id}, context) # make user follow itself self.message_subscribe(cr, uid, [user_id], [user_id], context=context) # create a welcome message to broadcast @@ -66,7 +88,15 @@ class res_users(osv.osv): # TODO: clean the broadcast feature. As this is not cleany specified, temporarily remove the message broadcasting that is not buggy but not very nice. #self.message_broadcast(cr, uid, [user.id], 'Welcome notification', message, 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 'login' in vals.keys(): + for user in self.browse(cr, uid, ids, context=context): + self.pool.get('mail.alias').write(cr, uid, [user.alias_id.id], {'alias_name': vals['login']}, context=context) + return super(res_users, self).write(cr, uid, ids, vals, context=context) + def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None): """ Override of message_load_ids User discussion page : diff --git a/addons/mail/res_users_view.xml b/addons/mail/res_users_view.xml index 428f53d092e..9af90efebae 100644 --- a/addons/mail/res_users_view.xml +++ b/addons/mail/res_users_view.xml @@ -31,6 +31,9 @@ <field name="message_ids" colspan="4" widget="ThreadView" nolabel="1"/> </div> </xpath> + <field name="active" position="after"> + <field name="alias_id" readonly="1" required="0"/> + </field> </data> </field> </record> diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 011e8b27dc5..d4146271a29 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -5,3 +5,5 @@ access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0 access_mail_subscription,mail.subscription,model_mail_subscription,,1,1,1,1
access_mail_notification,mail.notification,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
+access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
+access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
diff --git a/addons/mail/static/scripts/openerp_mailgate.py b/addons/mail/static/scripts/openerp_mailgate.py index 68ae20d35e3..64dafc56785 100755 --- a/addons/mail/static/scripts/openerp_mailgate.py +++ b/addons/mail/static/scripts/openerp_mailgate.py @@ -94,22 +94,23 @@ class RPCProxy(object): return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request, **kwargs) class EmailParser(object): - def __init__(self, uid, password, model, email_default, dbname, host, port): + def __init__(self, uid, password, dbname, host, port, model=False, email_default=False): self.rpc = RPCProxy(uid, password, host=host, port=port, dbname=dbname) - try: - self.model_id = int(model) - self.model = str(model) - except: - self.model_id = self.rpc('ir.model', 'search', [('model', '=', model)])[0] - self.model = str(model) - self.email_default = email_default + if model: + try: + self.model_id = int(model) + self.model = str(model) + except: + self.model_id = self.rpc('ir.model', 'search', [('model', '=', model)])[0] + self.model = str(model) + self.email_default = email_default - def parse(self, message, custom_values=None, save_original=None): + def parse(self, method, 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', - 'message_process', + method, self.model, xmlrpclib.Binary(message), custom_values or {}, @@ -156,30 +157,29 @@ def main(): """ Receive the email via the stdin and send it to the OpenERP Server """ + parser = configure_parser() (options, args) = parser.parse_args() - - + method = "message_process" email_parser = EmailParser(options.userid, options.password, - options.model, - options.default, - dbname=options.dbname, - host=options.host, - port=options.port) - - + options.dbname, + options.host, + options.port, + model=options.model, + 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 {} )) + custom_values = dict(eval(options.custom_values or "{}" )) except: import traceback traceback.print_exc() try: - email_parser.parse(msg_txt, custom_values, options.save_original or False) + email_parser.parse(method, msg_txt, custom_values, options.save_original or False) except Exception: msg = '\n'.join([ 'parameters', diff --git a/addons/mail/user_mail_alias_demo.xml b/addons/mail/user_mail_alias_demo.xml new file mode 100644 index 00000000000..eea7d521776 --- /dev/null +++ b/addons/mail/user_mail_alias_demo.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data noupdate="1"> + <record id="mail_alias_demo_user" model="mail.alias"> + <field name="alias_name">demo</field> + <field name="alias_model_id" ref="model_res_users"/> + <field name="alias_user_id" ref="base.user_root"/> + <field name="alias_force_thread_id" ref="base.user_demo"/> + <field name="alias_defaults">{}</field> + </record> + + <record model="res.users" id="base.user_demo"> + <field name="alias_id" ref="mail_alias_demo_user"/> + </record> + + </data> +</openerp> diff --git a/addons/mail/wizard/__init__.py b/addons/mail/wizard/__init__.py index 88b9581f68e..bf35381fc94 100644 --- a/addons/mail/wizard/__init__.py +++ b/addons/mail/wizard/__init__.py @@ -20,5 +20,5 @@ ############################################################################## import mail_compose_message - +import update_mail_alias # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/update_mail_alias.py b/addons/mail/wizard/update_mail_alias.py new file mode 100644 index 00000000000..b419050d48c --- /dev/null +++ b/addons/mail/wizard/update_mail_alias.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import orm +from osv import fields + +class transient_update_maildomain(orm.TransientModel): + + _name = "transient.update.maildomain" + _description = "Update Mail Domain" + _columns = { + 'name' : fields.text('Domain', required=True), + } + def update_domain(self, cr, uid, ids, context=None): + config_parameter_pool = self.pool.get("ir.config_parameter") + for record in self.browse(cr, uid, ids, context): + config_parameter_pool.set_param(cr, uid, "mail.catchall.domain", record.name, context) + return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mail/wizard/update_mail_alias_wizard.xml b/addons/mail/wizard/update_mail_alias_wizard.xml new file mode 100644 index 00000000000..4580266adde --- /dev/null +++ b/addons/mail/wizard/update_mail_alias_wizard.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record model="ir.ui.view" id="transient_update_maildomain_form"> + <field name="name">transient.update.maildomain.form</field> + <field name="model">transient.update.maildomain</field> + <field name="type">form</field> + <field name="arch" type="xml" > + <form string="Configure Mail Domain" version="7.0"> + <header> + <button name="update_domain" string="Update" + type="object" class="oe_highlight"/> + or + <button special="cancel" string="Cancel" class="oe_link"/> + </header> + <group colspan="4"> + <separator string="Email Domain" colspan="4"/> + <field name="name" nolabel="1" colspan="4" placeholder="openerp.my.openerp.com"/> + </group> + </form> + </field> + </record> + + <record id="action_transient_update_maildomain_form" model="ir.actions.act_window"> + <field name="name">Configure Mail Domain</field> + <field name="res_model">transient.update.maildomain</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <!-- View for general setting -->> + + <record id="view_general_configuration_mail_domain" model="ir.ui.view"> + <field name="name">General Settings</field> + <field name="model">base.config.settings</field> + <field name="inherit_id" ref="base_setup.view_general_configuration"/> + <field name="type">form</field> + <field name="arch" type="xml"> + <xpath expr="/form/sheet/group[@string='Others']" position='inside'> + <group colspan="4" col="4"> + <label string="Configure Mail Domain" for="id"/> + <button type="action" name="%(action_transient_update_maildomain_form)d" string="Configure" icon="gtk-execute"/> + </group> + </xpath> + </field> + </record> + + </data> +</openerp> diff --git a/addons/project/project.py b/addons/project/project.py index ab02cc870bb..aa32fbb16e2 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -55,7 +55,7 @@ class project_task_type(osv.osv): class project(osv.osv): _name = "project.project" _description = "Project" - _inherits = {'account.analytic.account': "analytic_account_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): @@ -164,6 +164,8 @@ class project(osv.osv): for task in self.pool.get('project.task').browse(cr, uid, task_ids, context): res[task.project_id.id] += 1 return res + def _get_alias_model(self, cr, uid, context=None): + return [('model_project_task', "Tasks")] def _get_followers(self, cr, uid, ids, name, arg, context=None): ''' @@ -215,12 +217,13 @@ class project(osv.osv): 'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}), 'task_count': fields.function(_task_count, type='integer', string="Open Tasks"), 'color': fields.integer('Color Index'), + 'alias_id': fields.many2one('mail.alias', 'Mail Alias', ondelete="cascade", required=True), + 'alias_model': fields.selection(_get_alias_model, "Alias Model",select="1", required=True), 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility'), '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, type='many2many', relation='res.users', string='Followers'), } - def dummy(self, cr, uid, ids, context): return True @@ -236,6 +239,7 @@ class project(osv.osv): 'priority': 1, 'sequence': 10, 'type_ids': _get_type_common, + 'alias_model':'model_project_task', } # TODO: Why not using a SQL contraints ? @@ -250,6 +254,26 @@ class project(osv.osv): (_check_dates, 'Error! project start-date must be lower then project end-date.', ['date_start', 'date']) ] + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + res = super(project,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form' or view_type == 'kanban': + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not domain: + doc = etree.XML(res['arch']) + alias_node = doc.xpath("//field[@name='alias_id']")[0] + parent = alias_node.getparent() + parent.remove(alias_node) + if view_type == "form": + model_node = doc.xpath("//field[@name='alias_model']")[0] + parent = model_node.getparent() + parent.remove(model_node) + else: + model_node = doc.xpath("//field[@name='alias_id']")[0] + parent = model_node.getparent() + parent.remove(model_node) + res['arch'] = etree.tostring(doc) + return res + def set_template(self, cr, uid, ids, context=None): res = self.setActive(cr, uid, ids, value=False, context=context) return res @@ -497,9 +521,17 @@ def Project(): return self.pool.get('res.users').read(cr, uid, sub_ids, context=context) def create(self, cr, uid, vals, context=None): - obj_id = super(project, self).create(cr, uid, vals, context=context) - self.create_send_note(cr, uid, [obj_id], context=context) - return obj_id + model_pool = self.pool.get('ir.model.data') + alias_pool = self.pool.get('mail.alias') + model, res_id = model_pool.get_object_reference( cr, uid, "project", vals.get('alias_model','model_project_task')) + vals.update({'alias_name':"project", + 'alias_model_id': res_id}) + alias_pool.create_unique_alias(cr, uid, vals, context=context) + res = super( project, self).create(cr, uid, vals, context) + record = self.read(cr, uid, res, context) + alias_pool.write(cr, uid, [record['alias_id']], {'alias_defaults':{'project_id': record['id']}}, context) + self.create_send_note(cr, uid, [res], context=context) + return res def create_send_note(self, cr, uid, ids, context=None): return self.message_append_note(cr, uid, ids, body=_("Project has been <b>created</b>."), context=context) @@ -520,6 +552,16 @@ def Project(): message = _("Project has been <b>closed</b>.") 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.data') + alias_pool = self.pool.get('mail.alias') + # if alias_model have been changed then change alias_model_id of alias also. + if vals.get('alias_model'): + model, res_id = model_pool.get_object_reference( cr, uid, "project", vals.get('alias_model','model_project_task')) + alias_id = self.browse(cr, uid, ids[0], context).alias_id + alias_pool.write(cr, uid, [alias_id.id], {'alias_model_id': res_id}, context=context) + return super(project, self).write(cr, uid, ids, vals, context=context) + class task(base_stage, osv.osv): _name = "project.task" diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml index 07951280dc7..889cc285af3 100644 --- a/addons/project/project_demo.xml +++ b/addons/project/project_demo.xml @@ -66,6 +66,7 @@ ref('base.user_vad')])]" name="members"/> <field name="user_id" ref="base.user_al"/> <field name="name">Integrate a Sale and Warehouse Management.</field> + <field name="alias_model">model_project_task</field> </record> <!-- Tasks --> @@ -290,6 +291,7 @@ <field name="parent_id" ref="all_projects_account"/> <field name="user_id" ref="base.user_root"/> <field eval="[(6, 0, [ref('project_tt_specification'),ref('project_tt_development'),ref('project_tt_testing'),ref('project_tt_merge')])]" name="type_ids"/> + <field name="alias_model">model_project_task</field> </record> <record id="project_project_21" model="project.project"> <field name="priority">10</field> @@ -298,6 +300,7 @@ <field name="user_id" ref="base.user_root"/> <field eval="[(6, 0, [ref('project_tt_specification'),ref('project_tt_development')])]" name="type_ids"/> <field eval="[(6, 0, [ref('base.user_root'),ref('base.user_demo')])]" name="members"/> + <field name="alias_model">model_project_task</field> </record> <record id="project_project_22" model="project.project"> <field name="priority">20</field> @@ -306,6 +309,7 @@ <field name="user_id" ref="base.user_root"/> <field eval="[(6, 0, [ref('project_tt_specification'), ref('project_tt_development')])]" name="type_ids"/> <field eval="[(6, 0, [ref('base.user_root'),ref('base.user_demo')])]" name="members"/> + <field name="alias_model">model_project_task</field> </record> <record id="project_project_23" model="project.project"> <field name="priority">30</field> @@ -313,6 +317,7 @@ <field name="name">Install, data import, configuration</field> <field eval="[(6, 0, [ref('project_tt_development')])]" name="type_ids"/> <field eval="[(6, 0, [ref('base.user_root'),ref('base.user_demo')])]" name="members"/> + <field name="alias_model">model_project_task</field> </record> <!-- Tasks --> diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 6662e46c79f..8024e4e8c63 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -55,6 +55,8 @@ <field name="analytic_account_id" invisible="1" required="0"/> <field name="parent_id" invisible="1" string="Parent" domain="[('id','!=',analytic_account_id)]" context="{'current_model': 'project.project'}"/> <field name="privacy_visibility"/> + <field name="alias_id" required="0" domain="[('alias_model_id','in',('project.task','project.issue'))]"/> + <field name="alias_model"/> </group> <group> <table> @@ -192,6 +194,7 @@ <field name="date"/> <field name="color"/> <field name="task_count"/> + <field name="alias_id"/> <templates> <t t-name="kanban-box"> <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_project oe_kanban_auto_height oe_kanban_global_click"> @@ -216,7 +219,9 @@ <div>Deadline</div> <div><field name="date"/></div> </div> - + <div t-if="record.alias_id.raw_value"> + <a t-attf-href="mailto:#{record.alias_id.raw_value}"><field name="alias_id"/></a> + </div> <div class="oe_kanban_project_fields oe_kanban_project_progress"> <div>Progress</div> <div><t t-esc="Math.round(record.effective_hours.raw_value)"/> / <t t-esc="Math.round(record.planned_hours.raw_value)"/> <field name="company_uom_id"/></div> diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 2aa6d4e1bcd..a7322959e70 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -581,6 +581,9 @@ project_issue() class project(osv.osv): _inherit = "project.project" + def _get_alias_model(self, cr, uid, context=None): + return [('model_project_task', "Tasks"),("model_project_issue", "Issue")] + def _issue_count(self, cr, uid, ids, field_name, arg, context=None): res = dict.fromkeys(ids, 0) issue_ids = self.pool.get('project.issue').search(cr, uid, [('project_id', 'in', ids)]) @@ -592,6 +595,7 @@ class project(osv.osv): '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'), + 'alias_model': fields.selection(_get_alias_model, "Alias Model"), } def _check_escalation(self, cr, uid, ids, context=None): |
