[MERGE] Implementation of mail.alias by Jigar Amin

bzr revid: odo@openerp.com-20120703160155-gw8po01vmm0q30ow
This commit is contained in:
Olivier Dony
2012-07-03 18:01:55 +02:00
30 changed files with 667 additions and 77 deletions
+30
View File
@@ -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 """
+8
View File
@@ -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>
+26 -17
View File
@@ -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 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 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>
+1
View File
@@ -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"/>
+6 -6
View File
@@ -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()
+28 -1
View File
@@ -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 = {}
@@ -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>
@@ -1,3 +1,99 @@
-
!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')
@@ -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>
+1 -1
View File
@@ -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
+6
View File
@@ -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,
+55 -17
View File
@@ -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
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'])})
+9
View File
@@ -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>
+73
View File
@@ -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>
+13
View File
@@ -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>
+27 -1
View File
@@ -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
+3
View File
@@ -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>
+50 -4
View File
@@ -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
+31 -1
View File
@@ -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 :
+3
View File
@@ -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>
+2
View File
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_mail_subscription mail.subscription model_mail_subscription 1 1 1 1
6 access_mail_notification mail.notification model_mail_notification 1 1 1 1
7 access_mail_group mail.group model_mail_group base.group_user 1 1 1 1
8 access_mail_alias_user mail.alias model_mail_alias base.group_user 1 1 1 0
9 access_mail_alias_system mail.alias model_mail_alias base.group_system 1 1 1 1
+22 -22
View File
@@ -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',
+17
View File
@@ -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>
+1 -1
View File
@@ -20,5 +20,5 @@
##############################################################################
import mail_compose_message
import update_mail_alias
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+36
View File
@@ -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'}
@@ -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>
+47 -5
View File
@@ -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"
+5
View File
@@ -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 -->
+6 -1
View File
@@ -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>
+4
View File
@@ -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):