summaryrefslogtreecommitdiff
path: root/addons/mail
diff options
context:
space:
mode:
authorVidhin Mehta <vme@tinyerp.com>2013-08-23 10:36:02 +0530
committerVidhin Mehta <vme@tinyerp.com>2013-08-23 10:36:02 +0530
commit75ddd5ccbec73c4a671b6a74efaa3d41dc2ea2db (patch)
tree50ed3620df15e8533760f5d47e754a1d39c1e856 /addons/mail
parent42f27a15275effd9c43dfe99e32c395ca6b3fd8a (diff)
parenta7f4b6aaa0359d12046e81183417c24f093cd7c3 (diff)
[MERGE]Trunk.
bzr revid: vme@tinyerp.com-20130823050602-33bqv07e7g8dhv7x
Diffstat (limited to 'addons/mail')
-rw-r--r--addons/mail/__openerp__.py3
-rw-r--r--addons/mail/controllers/main.py18
-rw-r--r--addons/mail/doc/changelog.rst17
-rw-r--r--addons/mail/doc/index.rst.inc9
-rw-r--r--addons/mail/i18n/bs.po1687
-rw-r--r--addons/mail/i18n/th.po1702
-rw-r--r--addons/mail/i18n/vi.po1687
-rw-r--r--addons/mail/mail_alias.py101
-rw-r--r--addons/mail/mail_alias_view.xml26
-rw-r--r--addons/mail/mail_followers.py25
-rw-r--r--addons/mail/mail_followers_view.xml4
-rw-r--r--addons/mail/mail_group.py31
-rw-r--r--addons/mail/mail_group_view.xml24
-rw-r--r--addons/mail/mail_mail.py60
-rw-r--r--addons/mail/mail_mail_view.xml2
-rw-r--r--addons/mail/mail_message.py55
-rw-r--r--addons/mail/mail_message_view.xml3
-rw-r--r--addons/mail/mail_thread.py561
-rw-r--r--addons/mail/mail_thread_view.xml3
-rw-r--r--addons/mail/res_partner.py18
-rw-r--r--addons/mail/res_users.py78
-rw-r--r--addons/mail/res_users_view.xml28
-rw-r--r--addons/mail/security/mail_security.xml11
-rw-r--r--addons/mail/static/description/icon.png (renamed from addons/mail/static/src/img/icon.png)bin5754 -> 5754 bytes
-rw-r--r--addons/mail/static/description/index.html (renamed from addons/mail/html/index.html)2
-rw-r--r--addons/mail/static/description/mail_illustration.png (renamed from addons/mail/html/mail_illustration.png)bin34110 -> 34110 bytes
-rw-r--r--addons/mail/static/description/mail_sc_00.png (renamed from addons/mail/html/mail_sc_00.png)bin71427 -> 71427 bytes
-rw-r--r--addons/mail/static/description/mail_sc_01.png (renamed from addons/mail/html/mail_sc_01.png)bin23632 -> 23632 bytes
-rw-r--r--addons/mail/static/description/mail_sc_02.png (renamed from addons/mail/html/mail_sc_02.png)bin27486 -> 27486 bytes
-rw-r--r--addons/mail/static/description/mail_sc_03.png (renamed from addons/mail/html/mail_sc_03.png)bin21784 -> 21784 bytes
-rw-r--r--addons/mail/static/description/mail_sc_04.png (renamed from addons/mail/html/mail_sc_04.png)bin92888 -> 92888 bytes
-rw-r--r--addons/mail/static/description/mail_sc_05.png (renamed from addons/mail/html/mail_sc_05.png)bin27375 -> 27375 bytes
-rw-r--r--addons/mail/static/description/mail_sc_06.png (renamed from addons/mail/html/mail_sc_06.png)bin27375 -> 27375 bytes
-rw-r--r--addons/mail/static/description/mail_sc_07.png (renamed from addons/mail/html/mail_sc_07.png)bin100179 -> 100179 bytes
-rw-r--r--addons/mail/static/description/photo.png (renamed from addons/mail/html/photo.png)bin6289 -> 6289 bytes
-rw-r--r--addons/mail/static/lib/jquery.expander/jquery.expander.js385
-rw-r--r--addons/mail/static/src/css/mail.css95
-rw-r--r--addons/mail/static/src/css/mail_group.css1
-rw-r--r--addons/mail/static/src/js/mail.js94
-rw-r--r--addons/mail/static/src/js/mail_followers.js112
-rw-r--r--addons/mail/static/src/js/suggestions.js77
-rw-r--r--addons/mail/static/src/xml/mail.xml38
-rw-r--r--addons/mail/static/src/xml/mail_followers.xml1
-rw-r--r--addons/mail/static/src/xml/suggestions.xml38
-rw-r--r--addons/mail/tests/test_mail_base.py7
-rw-r--r--addons/mail/tests/test_mail_features.py76
-rw-r--r--addons/mail/tests/test_mail_gateway.py473
-rw-r--r--addons/mail/wizard/mail_compose_message.py37
-rw-r--r--addons/mail/wizard/mail_compose_message_view.xml3
49 files changed, 6580 insertions, 1012 deletions
diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py
index 63cd0888986..1b9111d3040 100644
--- a/addons/mail/__openerp__.py
+++ b/addons/mail/__openerp__.py
@@ -83,14 +83,15 @@ Main Features
'static/src/css/mail_group.css',
],
'js': [
- 'static/lib/jquery.expander/jquery.expander.js',
'static/src/js/mail.js',
'static/src/js/mail_followers.js',
'static/src/js/many2many_tags_email.js',
+ 'static/src/js/suggestions.js',
],
'qweb': [
'static/src/xml/mail.xml',
'static/src/xml/mail_followers.xml',
+ 'static/src/xml/suggestions.xml',
],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py
index 4c6f6e56064..ef3a60987d6 100644
--- a/addons/mail/controllers/main.py
+++ b/addons/mail/controllers/main.py
@@ -1,4 +1,7 @@
import base64
+
+import openerp
+from openerp import SUPERUSER_ID
import openerp.addons.web.http as oeweb
from openerp.addons.web.controllers.main import content_disposition
@@ -20,3 +23,18 @@ class MailController(oeweb.Controller):
headers=[('Content-Type', 'application/octet-stream'),
('Content-Disposition', content_disposition(filename, req))])
return req.not_found()
+
+ @oeweb.jsonrequest
+ def receive(self, req):
+ """ End-point to receive mail from an external SMTP server. """
+ dbs = req.jsonrequest.get('databases')
+ for db in dbs:
+ message = dbs[db].decode('base64')
+ try:
+ registry = openerp.registry(db)
+ with registry.cursor() as cr:
+ mail_thread = registry['mail.thread']
+ mail_thread.message_process(cr, SUPERUSER_ID, None, message)
+ except psycopg2.Error:
+ pass
+ return True
diff --git a/addons/mail/doc/changelog.rst b/addons/mail/doc/changelog.rst
new file mode 100644
index 00000000000..a7a2cfcf2b5
--- /dev/null
+++ b/addons/mail/doc/changelog.rst
@@ -0,0 +1,17 @@
+.. _changelog:
+
+Changelog
+=========
+
+`trunk (saas-2)`
+----------------
+
+ - added support of ``active_domain`` form context, coming from the list view.
+ When checking the header hook, the mass mailing will be done on all records
+ matching the ``active_domain``.
+ - added ``mail_server_id`` to mail_message, removing it from mail_mail. This allows
+ to set the preferred mail server to use for notifications emails, when using
+ templates.
+ - added ``_mail_post_access`` attribute that specifies the access right that
+ should have the user in order to post a new message on a given model. Values
+ are ``read`` (portal documents), ``write`` (default value), ``unlink`` or ``create``.
diff --git a/addons/mail/doc/index.rst.inc b/addons/mail/doc/index.rst.inc
index 4b01d3ec7bd..7f56666336b 100644
--- a/addons/mail/doc/index.rst.inc
+++ b/addons/mail/doc/index.rst.inc
@@ -12,3 +12,12 @@ Mail Module documentation topics
mail_partner
mail_state
mail_subtype
+
+
+Changelog
+'''''''''
+
+.. toctree::
+ :maxdepth: 1
+
+ changelog.rst
diff --git a/addons/mail/i18n/bs.po b/addons/mail/i18n/bs.po
new file mode 100644
index 00000000000..b50a043e2a7
--- /dev/null
+++ b/addons/mail/i18n/bs.po
@@ -0,0 +1,1687 @@
+# Bosnian translation for openobject-addons
+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2012-12-21 17:04+0000\n"
+"PO-Revision-Date: 2013-08-06 10:21+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Bosnian <bs@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2013-08-07 04:46+0000\n"
+"X-Generator: Launchpad (build 16721)\n"
+
+#. module: mail
+#: view:mail.followers:0
+msgid "Followers Form"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_publisher_warranty_contract
+msgid "publisher_warranty.contract"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,author_id:0
+#: view:mail.mail:0
+#: field:mail.message,author_id:0
+msgid "Author"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Message Details"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_to:0
+msgid "Message recipients"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,default:0
+msgid "Activated by default when subscribing."
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Comments"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "Group By..."
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,body:0
+#: help:mail.message,body:0
+msgid "Automatically sanitized HTML contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_name:0
+msgid ""
+"The name of the email alias, e.g. 'jobs' if you want to catch emails for "
+"<jobs@example.my.openerp.com>"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
+#: view:mail.compose.message:0
+msgid "Compose Email"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:132
+#, python-format
+msgid "Add them into recipients and followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Name"
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Public"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Body"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Show messages to read"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,email_from:0
+#: help:mail.message,email_from:0
+msgid ""
+"Email address of the sender. This field is set when no matching partner is "
+"found for incoming emails."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_compose_message
+msgid "Email composition wizard"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:23
+#, python-format
+msgid "Add others"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,parent_id:0
+msgid "Parent"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_unread:0
+#: field:mail.thread,message_unread:0
+#: field:res.partner,message_unread:0
+msgid "Unread Messages"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "show"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,group_ids:0
+msgid ""
+"Members of those groups will automatically added as followers. Note that "
+"they will be able to manage their subscription manually if necessary."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:1029
+#, python-format
+msgid "Do you really want to delete this message?"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+#: field:mail.notification,read:0
+msgid "Read"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Search Groups"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:157
+#, python-format
+msgid "followers"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:737
+#, python-format
+msgid "Access Denied"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image_medium:0
+msgid ""
+"Medium-sized photo of the group. It is automatically resized as a 128x128px "
+"image, with aspect ratio preserved. Use this field in form views or some "
+"kanban views."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:212
+#, python-format
+msgid "Uploading error"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_support
+msgid "Support"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:738
+#, python-format
+msgid ""
+"The requested operation cannot be completed due to security restrictions. "
+"Please contact your system administrator.\n"
+"\n"
+"(Document type: %s, Operation: %s)"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Received"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Thread"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "Open the full mail composer"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "&ograve"
+msgstr ""
+
+#. module: mail
+#: field:base.config.settings,alias_domain:0
+msgid "Alias Domain"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_ids:0
+msgid "Auto Subscription"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,references:0
+msgid "References"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:206
+#, python-format
+msgid "No messages."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_group
+msgid "Discussion group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:96
+#: code:addons/mail/static/src/xml/mail.xml:108
+#, python-format
+msgid "uploading"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "more."
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,type:0
+#: help:mail.message,type:0
+msgid ""
+"Message type: email for email message, notification for system message, "
+"comment for other messages such as user replies"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,relation_field:0
+msgid ""
+"Field used to link the related model to the subtype model when using "
+"automatic subscription on a related document. The field is used to compute "
+"getattr(related_document.relation_field)."
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Cancelled"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,reply_to:0
+msgid "Reply-To"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:37
+#, python-format
+msgid "<div>You have been invited to follow %s.</div>"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_unread:0
+#: help:mail.thread,message_unread:0
+#: help:res.partner,message_unread:0
+msgid "If checked new messages require your attention."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_medium:0
+msgid "Medium-sized photo"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_to_me_feeds
+#: model:ir.ui.menu,name:mail.mail_tomefeeds
+msgid "To: me"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,name:0
+msgid "Message Type"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,auto_delete:0
+msgid "Auto Delete"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:12
+#: view:mail.group:0
+#, python-format
+msgid "Unfollow"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:312
+#, python-format
+msgid "show one more message"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid "Invalid Action!"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:25
+#, python-format
+msgid "User img"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_mail
+#: model:ir.ui.menu,name:mail.menu_mail_mail
+#: view:mail.mail:0
+#: view:mail.message:0
+msgid "Emails"
+msgstr ""
+
+#. module: mail
+#: field:mail.followers,partner_id:0
+msgid "Related Partner"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_summary:0
+#: help:mail.thread,message_summary:0
+#: help:res.partner,message_summary:0
+msgid ""
+"Holds the Chatter summary (number of messages, ...). This summary is "
+"directly in html format in order to be inserted in kanban views."
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_model_id:0
+msgid ""
+"The model (OpenERP Document Kind) to which this alias corresponds. Any "
+"incoming email that does not reply to an existing record will cause the "
+"creation of a new record of this model (e.g. a Project Task)"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,relation_field:0
+msgid "Relation field"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: selection:mail.message,type:0
+msgid "System notification"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_partner
+msgid "Partner"
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_my_stuff
+msgid "Organizer"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,subject:0
+#: field:mail.message,subject:0
+msgid "Subject"
+msgstr ""
+
+#. module: mail
+#: field:mail.wizard.invite,partner_ids:0
+msgid "Partners"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Retry"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,email_from:0
+#: field:mail.mail,email_from:0
+#: field:mail.message,email_from:0
+msgid "From"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: view:mail.message.subtype:0
+msgid "Email message"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_base_config_settings
+msgid "base.config.settings"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:36
+#: view:mail.compose.message:0
+#, python-format
+msgid "Send"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:153
+#, python-format
+msgid "No followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Failed"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:22
+#: model:ir.actions.act_window,name:mail.action_view_followers
+#: model:ir.ui.menu,name:mail.menu_email_followers
+#: view:mail.followers:0
+#: field:mail.group,message_follower_ids:0
+#: field:mail.thread,message_follower_ids:0
+#: field:res.partner,message_follower_ids:0
+#, python-format
+msgid "Followers"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_archives_feeds
+#: model:ir.ui.menu,name:mail.mail_archivesfeeds
+msgid "Archives"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:95
+#: code:addons/mail/static/src/xml/mail.xml:107
+#, python-format
+msgid "Delete this attachment"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:245
+#: view:mail.mail:0
+#, python-format
+msgid "Reply"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:155
+#, python-format
+msgid "One follower"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,type:0
+#: field:mail.message,type:0
+msgid "Type"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Email"
+msgstr ""
+
+#. module: mail
+#: field:ir.ui.menu,mail_group_id:0
+msgid "Mail Group"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Comments and Emails"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_defaults:0
+msgid "Default Values"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/res_users.py:89
+#, python-format
+msgid "%s has joined the %s network."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image_small:0
+msgid ""
+"Small-sized photo of the group. It is automatically resized as a 64x64px "
+"image, with aspect ratio preserved. Use this field anywhere a small image is "
+"required."
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: field:mail.message,partner_ids:0
+msgid "Recipients"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:140
+#, python-format
+msgid "<<<"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:43
+#, python-format
+msgid "Write to the followers of this document..."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_public_id:0
+msgid "Authorized Group"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Join Group"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_from:0
+msgid "Message sender, taken from user preferences."
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:40
+#, python-format
+msgid "<div>You have been invited to follow a new document.</div>"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,parent_id:0
+#: field:mail.message,parent_id:0
+msgid "Parent Message"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,res_id:0
+#: field:mail.followers,res_id:0
+#: field:mail.message,res_id:0
+#: field:mail.wizard.invite,res_id:0
+msgid "Related Document ID"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_to_me_feeds
+msgid ""
+"<p>\n"
+" <b>No private message.</b>\n"
+" </p><p>\n"
+" This list contains messages sent to you.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_rd
+msgid "R&D"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:61
+#, python-format
+msgid "/web/binary/upload_attachment"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_thread
+msgid "Email Thread"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Advanced"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:244
+#, python-format
+msgid "Move to Inbox"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/mail_compose_message.py:193
+#, python-format
+msgid "Re:"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,to_read:0
+#: field:mail.message,to_read:0
+msgid "To read"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid ""
+"You may not create a user. To create new users, you should use the "
+"\"Settings > Users\" menu."
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,res_model:0
+#: help:mail.wizard.invite,res_model:0
+msgid "Model of the followed resource"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:337
+#, python-format
+msgid "like"
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+msgid "Cancel"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:47
+#, python-format
+msgid "Share with my followers..."
+msgstr ""
+
+#. module: mail
+#: field:mail.notification,partner_id:0
+msgid "Contact"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"Only the invited followers can read the\n"
+" discussions on this group."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_ir_ui_menu
+msgid "ir.ui.menu"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Has attachments"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "on"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:926
+#, python-format
+msgid ""
+"The following partners chosen as recipients for the email have no email "
+"address linked :"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_defaults:0
+msgid ""
+"A Python dictionary that will be evaluated to provide default values when "
+"creating new records for this alias."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message_subtype
+msgid "Message subtypes"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notified_partner_ids:0
+#: help:mail.message,notified_partner_ids:0
+msgid ""
+"Partners that have a notification pushing this message in their mailboxes"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Comment"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_inbox_feeds
+msgid ""
+"<p>\n"
+" <b>Good Job!</b> Your inbox is empty.\n"
+" </p><p>\n"
+" Your inbox contains private messages or emails sent to "
+"you\n"
+" as well as information related to documents or people "
+"you\n"
+" follow.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,notification:0
+msgid "Is Notification"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:188
+#, python-format
+msgid "Compose a new message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Send Now"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#, python-format
+msgid ""
+"Unable to send email, please configure the sender's email address or alias."
+msgstr ""
+
+#. module: mail
+#: help:res.users,alias_id:0
+msgid ""
+"Email address internally associated with this user. Incoming emails will "
+"appear in the user's notifications."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image:0
+msgid "Photo"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:54
+#: code:addons/mail/static/src/xml/mail.xml:191
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+#, python-format
+msgid "or"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,vote_user_ids:0
+#: help:mail.message,vote_user_ids:0
+msgid "Users that voted for this message"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,alias_id:0
+msgid ""
+"The email address associated with this group. New emails received will "
+"automatically create new topics."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Month"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Email Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,child_ids:0
+#: field:mail.message,child_ids:0
+msgid "Child Messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_user_id:0
+msgid "Owner"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_users
+msgid "Users"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message
+#: field:mail.mail,mail_message_id:0
+#: view:mail.message:0
+#: field:mail.notification,message_id:0
+#: field:mail.wizard.invite,message:0
+msgid "Message"
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,res_id:0
+#: help:mail.wizard.invite,res_id:0
+msgid "Id of the followed resource"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,body:0
+#: field:mail.message,body:0
+msgid "Contents"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_alias
+#: model:ir.ui.menu,name:mail.mail_alias_menu
+msgid "Aliases"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,description:0
+msgid ""
+"Description that will be added in the message posted for this subtype. If "
+"void, the name will be added instead."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,vote_user_ids:0
+#: field:mail.message,vote_user_ids:0
+msgid "Votes"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,starred:0
+#: help:mail.message,starred:0
+msgid "Current user has a starred notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,public:0
+msgid "Privacy"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Notification"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:654
+#, python-format
+msgid "Please complete partner's informations"
+msgstr ""
+
+#. module: mail
+#: view:mail.wizard.invite:0
+msgid "Add Followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+msgid "Followers of selected items and"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_force_thread_id:0
+msgid "Record Thread ID"
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_group_root
+msgid "My Groups"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_archives_feeds
+msgid ""
+"<p>\n"
+" No message found and no message sent yet.\n"
+" </p><p>\n"
+" Click on the top-right icon to compose a message. This\n"
+" message will be sent by email if it's an internal "
+"contact.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: field:mail.mail,state:0
+msgid "Status"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Outgoing"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "All feeds"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,record_name:0
+#: help:mail.message,record_name:0
+msgid "Name get of the related document."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_notifications
+#: model:ir.model,name:mail.model_mail_notification
+#: model:ir.ui.menu,name:mail.menu_email_notifications
+#: field:mail.compose.message,notification_ids:0
+#: view:mail.message:0
+#: field:mail.message,notification_ids:0
+#: view:mail.notification:0
+msgid "Notifications"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+msgid "Search Alias"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_force_thread_id:0
+msgid ""
+"Optional ID of a thread (record) to which all incoming messages will be "
+"attached, even if they did not reply to it. If set, this will disable the "
+"creation of new records completely."
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,name:0
+msgid ""
+"Message subtype gives a more precise type on the message, especially for "
+"system notifications. For example, it can be a notification related to a new "
+"record (New), or to a stage change in a process (Stage change). Message "
+"subtypes allow to precisely tune the notifications the user want to receive "
+"on its wall."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "by"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_best_sales_practices
+msgid "Best Sales Practices"
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Selected Group Only"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_is_follower:0
+#: field:mail.thread,message_is_follower:0
+#: field:res.partner,message_is_follower:0
+msgid "Is a Follower"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "User"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Groups"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Messages Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,date:0
+#: field:mail.message,date:0
+msgid "Date"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:34
+#, python-format
+msgid "Post"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Extended Filters..."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:120
+#, python-format
+msgid "To:"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:193
+#, python-format
+msgid "Write to my followers"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_groups
+msgid "Access Groups"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,default:0
+msgid "Default"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:311
+#, python-format
+msgid "show more message"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:246
+#, python-format
+msgid "Mark as Todo"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,parent_id:0
+msgid "Parent subtype, used for automatic subscription."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_wizard_invite
+msgid "Invite wizard"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_summary:0
+#: field:mail.thread,message_summary:0
+#: field:res.partner,message_summary:0
+msgid "Summary"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,res_model:0
+msgid ""
+"Model the subtype applies to. If False, this subtype applies to all models."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,subtype_id:0
+#: field:mail.followers,subtype_ids:0
+#: field:mail.message,subtype_id:0
+#: view:mail.message.subtype:0
+msgid "Subtype"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Form"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,starred:0
+#: field:mail.message,starred:0
+#: field:mail.notification,starred:0
+msgid "Starred"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "more messages"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:13
+#, python-format
+msgid "Following"
+msgstr ""
+
+#. module: mail
+#: sql_constraint:mail.alias:0
+msgid ""
+"Unfortunately this email alias is already used, please choose a unique one"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_user_id:0
+msgid ""
+"The owner of records created upon receiving emails on this alias. If this "
+"field is not set the system will attempt to find the right owner based on "
+"the sender (From) address, or will use the Administrator account if no "
+"system user is found for that address."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "And"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,message_id:0
+#: field:mail.message,message_id:0
+msgid "Message-Id"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image:0
+msgid ""
+"This field holds the image used as photo for the group, limited to "
+"1024x1024px."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,attachment_ids:0
+#: view:mail.mail:0
+#: field:mail.message,attachment_ids:0
+msgid "Attachments"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,record_name:0
+#: field:mail.message,record_name:0
+msgid "Message Record Name"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_cc:0
+msgid "Cc"
+msgstr ""
+
+#. module: mail
+#: help:mail.notification,starred:0
+msgid "Starred message that goes into the todo mailbox"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:123
+#: view:mail.compose.message:0
+#, python-format
+msgid "Followers of"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,auto_delete:0
+msgid "Permanently delete this email after sending it, to save space"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_group_feeds
+msgid "Discussion Group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:242
+#, python-format
+msgid "Done"
+msgstr ""
+
+#. module: mail
+#: model:mail.message.subtype,name:mail.mt_comment
+msgid "Discussions"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:11
+#, python-format
+msgid "Follow"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,name:0
+msgid "Name"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_all_employees
+msgid "Whole Company"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:129
+#: code:addons/mail/static/src/xml/mail.xml:292
+#: view:mail.compose.message:0
+#, python-format
+msgid "and"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,body_html:0
+msgid "Rich-text/HTML message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Creation Month"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:323
+#, python-format
+msgid "Compose new Message"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,menu_id:0
+msgid "Related Menu"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Content"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_to:0
+msgid "To"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,notified_partner_ids:0
+#: field:mail.message,notified_partner_ids:0
+msgid "Notified partners"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,public:0
+msgid ""
+"This group is visible by non members. Invisible groups can add "
+"members through the invite button."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_board
+msgid "Board meetings"
+msgstr ""
+
+#. module: mail
+#: constraint:mail.alias:0
+msgid ""
+"Invalid expression, it must be a literal python dictionary definition e.g. "
+"\"{'field': 'value'}\""
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_model_id:0
+msgid "Aliased Model"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,message_id:0
+#: help:mail.message,message_id:0
+msgid "Message unique identifier"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,description:0
+#: field:mail.message.subtype,description:0
+msgid "Description"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_followers
+msgid "Document Followers"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:35
+#, python-format
+msgid "Remove this follower"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Never"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,mail_server_id:0
+msgid "Outgoing mail server"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:930
+#, python-format
+msgid "Partners email addresses not found"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Sent"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,body_html:0
+msgid "Rich-text Contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,to_read:0
+#: help:mail.message,to_read:0
+msgid "Current user has an unread notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: help:res.partner,notification_email_send:0
+msgid ""
+"Choose in which case you want to receive an email when you receive new feeds."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_groups
+#: model:ir.ui.menu,name:mail.mail_allgroups
+msgid "Join a group"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_group_feeds
+msgid ""
+"<p>\n"
+" No message in this group.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:213
+#, python-format
+msgid "Please, wait while the file is uploading."
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"This group is visible by everyone,\n"
+" including your customers if you "
+"installed\n"
+" the portal module."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:243
+#, python-format
+msgid "Set back to Todo"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:126
+#, python-format
+msgid "this document"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,filter_id:0
+msgid "Filters"
+msgstr ""
+
+#. module: mail
+#: field:res.partner,notification_email_send:0
+msgid "Receive Feeds by Email"
+msgstr ""
+
+#. module: mail
+#: help:base.config.settings,alias_domain:0
+msgid ""
+"If you have setup a catch-all email domain redirected to the OpenERP server, "
+"enter the domain name here."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_message
+#: model:ir.ui.menu,name:mail.menu_mail_message
+#: field:mail.group,message_ids:0
+#: view:mail.message:0
+#: field:mail.thread,message_ids:0
+#: field:res.partner,message_ids:0
+msgid "Messages"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:139
+#, python-format
+msgid "others..."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_star_feeds
+#: model:ir.ui.menu,name:mail.mail_starfeeds
+msgid "To-do"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.alias,alias_name:0
+#: field:mail.group,alias_id:0
+#: field:res.users,alias_id:0
+msgid "Alias"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_mail
+msgid "Outgoing Mails"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notification_ids:0
+#: help:mail.message,notification_ids:0
+msgid ""
+"Technical field holding the message notifications. Use notified_partner_ids "
+"to access notified partners."
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_feeds
+#: model:ir.ui.menu,name:mail.mail_feeds_main
+msgid "Messaging"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.message.subtype,res_model:0
+msgid "Model"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Unread"
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,subtype_ids:0
+msgid ""
+"Message subtypes followed, meaning subtypes that will be pushed onto the "
+"user's Wall."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_ids:0
+#: help:mail.thread,message_ids:0
+#: help:res.partner,message_ids:0
+msgid "Messages and communication history"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,references:0
+msgid "Message references, such as identifiers of previous messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,composition_mode:0
+msgid "Composition mode"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,model:0
+#: field:mail.followers,res_model:0
+#: field:mail.message,model:0
+#: field:mail.wizard.invite,res_model:0
+msgid "Related Document Model"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:338
+#, python-format
+msgid "unlike"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,author_id:0
+#: help:mail.message,author_id:0
+msgid ""
+"Author of the message. If not set, email_from may hold an email address that "
+"did not match any partner."
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_cc:0
+msgid "Carbon copy message recipients"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_domain:0
+msgid "Alias domain"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error during communication with the publisher warranty server."
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Private"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_star_feeds
+msgid ""
+"<p>\n"
+" <b>No todo.</b>\n"
+" </p><p>\n"
+" When you process messages in your inbox, you can mark "
+"some\n"
+" as <i>todo</i>. From this menu, you can process all your "
+"todo.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Delivery Failed"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,partner_ids:0
+msgid "Additional contacts"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,parent_id:0
+#: help:mail.message,parent_id:0
+msgid "Initial thread message."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_hr_policies
+msgid "HR Policies"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Emails only"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_inbox_feeds
+#: model:ir.ui.menu,name:mail.mail_inboxfeeds
+msgid "Inbox"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:58
+#, python-format
+msgid "File"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/many2many_tags_email.js:63
+#, python-format
+msgid "Please complete partner's informations and Email"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_message_subtype
+#: model:ir.ui.menu,name:mail.menu_message_subtype
+msgid "Subtypes"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_alias
+msgid "Email Aliases"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_small:0
+msgid "Small-sized photo"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,reply_to:0
+msgid "Preferred response address for the message"
+msgstr ""
diff --git a/addons/mail/i18n/th.po b/addons/mail/i18n/th.po
new file mode 100644
index 00000000000..a80d1aad14f
--- /dev/null
+++ b/addons/mail/i18n/th.po
@@ -0,0 +1,1702 @@
+# Thai translation for openobject-addons
+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2012-12-21 17:04+0000\n"
+"PO-Revision-Date: 2013-06-28 17:26+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Thai <th@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2013-06-30 05:56+0000\n"
+"X-Generator: Launchpad (build 16692)\n"
+
+#. module: mail
+#: view:mail.followers:0
+msgid "Followers Form"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_publisher_warranty_contract
+msgid "publisher_warranty.contract"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,author_id:0
+#: view:mail.mail:0
+#: field:mail.message,author_id:0
+msgid "Author"
+msgstr "ผู้สร้าง"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Message Details"
+msgstr "รายละเอียดข้อความ"
+
+#. module: mail
+#: help:mail.mail,email_to:0
+msgid "Message recipients"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,default:0
+msgid "Activated by default when subscribing."
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Comments"
+msgstr "ความคิดเห็น"
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "Group By..."
+msgstr "กลุ่มตาม..."
+
+#. module: mail
+#: help:mail.compose.message,body:0
+#: help:mail.message,body:0
+msgid "Automatically sanitized HTML contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_name:0
+msgid ""
+"The name of the email alias, e.g. 'jobs' if you want to catch emails for "
+"<jobs@example.my.openerp.com>"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
+#: view:mail.compose.message:0
+msgid "Compose Email"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:132
+#, python-format
+msgid "Add them into recipients and followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Name"
+msgstr "ชื่อกลุ่ม"
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Public"
+msgstr "เปิดเผย"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Body"
+msgstr "เนื้อหา"
+
+#. module: mail
+#: view:mail.message:0
+msgid "Show messages to read"
+msgstr "แสดงข้อความที่จะอ่าน"
+
+#. module: mail
+#: help:mail.compose.message,email_from:0
+#: help:mail.message,email_from:0
+msgid ""
+"Email address of the sender. This field is set when no matching partner is "
+"found for incoming emails."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_compose_message
+msgid "Email composition wizard"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:23
+#, python-format
+msgid "Add others"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,parent_id:0
+msgid "Parent"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_unread:0
+#: field:mail.thread,message_unread:0
+#: field:res.partner,message_unread:0
+msgid "Unread Messages"
+msgstr "ข้อความที่ยังไม่ได้อ่าน"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "show"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,group_ids:0
+msgid ""
+"Members of those groups will automatically added as followers. Note that "
+"they will be able to manage their subscription manually if necessary."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:1029
+#, python-format
+msgid "Do you really want to delete this message?"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+#: field:mail.notification,read:0
+msgid "Read"
+msgstr "อ่าน"
+
+#. module: mail
+#: view:mail.group:0
+msgid "Search Groups"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:157
+#, python-format
+msgid "followers"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:737
+#, python-format
+msgid "Access Denied"
+msgstr "ไม่ได้รับอนุญาตให้เข้าใช้"
+
+#. module: mail
+#: help:mail.group,image_medium:0
+msgid ""
+"Medium-sized photo of the group. It is automatically resized as a 128x128px "
+"image, with aspect ratio preserved. Use this field in form views or some "
+"kanban views."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:212
+#, python-format
+msgid "Uploading error"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_support
+msgid "Support"
+msgstr "สนับสนุน"
+
+#. module: mail
+#: code:addons/mail/mail_message.py:738
+#, python-format
+msgid ""
+"The requested operation cannot be completed due to security restrictions. "
+"Please contact your system administrator.\n"
+"\n"
+"(Document type: %s, Operation: %s)"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Received"
+msgstr "ได้รับ"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Thread"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "Open the full mail composer"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "&ograve"
+msgstr ""
+
+#. module: mail
+#: field:base.config.settings,alias_domain:0
+msgid "Alias Domain"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_ids:0
+msgid "Auto Subscription"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,references:0
+msgid "References"
+msgstr "แหล่งอ้างอิง"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:206
+#, python-format
+msgid "No messages."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_group
+msgid "Discussion group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:96
+#: code:addons/mail/static/src/xml/mail.xml:108
+#, python-format
+msgid "uploading"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "more."
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,type:0
+#: help:mail.message,type:0
+msgid ""
+"Message type: email for email message, notification for system message, "
+"comment for other messages such as user replies"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,relation_field:0
+msgid ""
+"Field used to link the related model to the subtype model when using "
+"automatic subscription on a related document. The field is used to compute "
+"getattr(related_document.relation_field)."
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Cancelled"
+msgstr "ยกเลิกแล้ว"
+
+#. module: mail
+#: field:mail.mail,reply_to:0
+msgid "Reply-To"
+msgstr "ตอบกลับไปยัง"
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:37
+#, python-format
+msgid "<div>You have been invited to follow %s.</div>"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_unread:0
+#: help:mail.thread,message_unread:0
+#: help:res.partner,message_unread:0
+msgid "If checked new messages require your attention."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_medium:0
+msgid "Medium-sized photo"
+msgstr "ภาพขนาดกลาง"
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_to_me_feeds
+#: model:ir.ui.menu,name:mail.mail_tomefeeds
+msgid "To: me"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,name:0
+msgid "Message Type"
+msgstr "ประเภทข้อความ"
+
+#. module: mail
+#: field:mail.mail,auto_delete:0
+msgid "Auto Delete"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:12
+#: view:mail.group:0
+#, python-format
+msgid "Unfollow"
+msgstr "เลิกติดตาม"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:312
+#, python-format
+msgid "show one more message"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid "Invalid Action!"
+msgstr "การกระทำไม่ถูกต้อง!"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:25
+#, python-format
+msgid "User img"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_mail
+#: model:ir.ui.menu,name:mail.menu_mail_mail
+#: view:mail.mail:0
+#: view:mail.message:0
+msgid "Emails"
+msgstr "อีเมล์"
+
+#. module: mail
+#: field:mail.followers,partner_id:0
+msgid "Related Partner"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_summary:0
+#: help:mail.thread,message_summary:0
+#: help:res.partner,message_summary:0
+msgid ""
+"Holds the Chatter summary (number of messages, ...). This summary is "
+"directly in html format in order to be inserted in kanban views."
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_model_id:0
+msgid ""
+"The model (OpenERP Document Kind) to which this alias corresponds. Any "
+"incoming email that does not reply to an existing record will cause the "
+"creation of a new record of this model (e.g. a Project Task)"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,relation_field:0
+msgid "Relation field"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: selection:mail.message,type:0
+msgid "System notification"
+msgstr "การแจ้งเตือนระบบ"
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_partner
+msgid "Partner"
+msgstr "พาร์ทเนอร์"
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_my_stuff
+msgid "Organizer"
+msgstr "ผู้จัด"
+
+#. module: mail
+#: field:mail.compose.message,subject:0
+#: field:mail.message,subject:0
+msgid "Subject"
+msgstr "ชื่อเรื่อง"
+
+#. module: mail
+#: field:mail.wizard.invite,partner_ids:0
+msgid "Partners"
+msgstr "พาร์ทเนอร์"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Retry"
+msgstr "ลองใหม่"
+
+#. module: mail
+#: field:mail.compose.message,email_from:0
+#: field:mail.mail,email_from:0
+#: field:mail.message,email_from:0
+msgid "From"
+msgstr "จาก"
+
+#. module: mail
+#: view:mail.mail:0
+#: view:mail.message.subtype:0
+msgid "Email message"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_base_config_settings
+msgid "base.config.settings"
+msgstr "base.config.settings"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:36
+#: view:mail.compose.message:0
+#, python-format
+msgid "Send"
+msgstr "ส่ง"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:153
+#, python-format
+msgid "No followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Failed"
+msgstr "ล้มเหลว"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:22
+#: model:ir.actions.act_window,name:mail.action_view_followers
+#: model:ir.ui.menu,name:mail.menu_email_followers
+#: view:mail.followers:0
+#: field:mail.group,message_follower_ids:0
+#: field:mail.thread,message_follower_ids:0
+#: field:res.partner,message_follower_ids:0
+#, python-format
+msgid "Followers"
+msgstr "ติดตาม"
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_archives_feeds
+#: model:ir.ui.menu,name:mail.mail_archivesfeeds
+msgid "Archives"
+msgstr "ที่เก็บถาวร"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:95
+#: code:addons/mail/static/src/xml/mail.xml:107
+#, python-format
+msgid "Delete this attachment"
+msgstr "ลบสิ่งที่แนบมานี้"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:245
+#: view:mail.mail:0
+#, python-format
+msgid "Reply"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:155
+#, python-format
+msgid "One follower"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,type:0
+#: field:mail.message,type:0
+msgid "Type"
+msgstr "ชนิด"
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Email"
+msgstr "อีเมล์"
+
+#. module: mail
+#: field:ir.ui.menu,mail_group_id:0
+msgid "Mail Group"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Comments and Emails"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_defaults:0
+msgid "Default Values"
+msgstr "ค่าปริยาย"
+
+#. module: mail
+#: code:addons/mail/res_users.py:89
+#, python-format
+msgid "%s has joined the %s network."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image_small:0
+msgid ""
+"Small-sized photo of the group. It is automatically resized as a 64x64px "
+"image, with aspect ratio preserved. Use this field anywhere a small image is "
+"required."
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: field:mail.message,partner_ids:0
+msgid "Recipients"
+msgstr "ผู้รับ"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:140
+#, python-format
+msgid "<<<"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:43
+#, python-format
+msgid "Write to the followers of this document..."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_public_id:0
+msgid "Authorized Group"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Join Group"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_from:0
+msgid "Message sender, taken from user preferences."
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:40
+#, python-format
+msgid "<div>You have been invited to follow a new document.</div>"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,parent_id:0
+#: field:mail.message,parent_id:0
+msgid "Parent Message"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,res_id:0
+#: field:mail.followers,res_id:0
+#: field:mail.message,res_id:0
+#: field:mail.wizard.invite,res_id:0
+msgid "Related Document ID"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_to_me_feeds
+msgid ""
+"<p>\n"
+" <b>No private message.</b>\n"
+" </p><p>\n"
+" This list contains messages sent to you.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_rd
+msgid "R&D"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:61
+#, python-format
+msgid "/web/binary/upload_attachment"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_thread
+msgid "Email Thread"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Advanced"
+msgstr "ขั้นสูง"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:244
+#, python-format
+msgid "Move to Inbox"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/mail_compose_message.py:193
+#, python-format
+msgid "Re:"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,to_read:0
+#: field:mail.message,to_read:0
+msgid "To read"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid ""
+"You may not create a user. To create new users, you should use the "
+"\"Settings > Users\" menu."
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,res_model:0
+#: help:mail.wizard.invite,res_model:0
+msgid "Model of the followed resource"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:337
+#, python-format
+msgid "like"
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+msgid "Cancel"
+msgstr "ยกเลิก"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:47
+#, python-format
+msgid "Share with my followers..."
+msgstr ""
+
+#. module: mail
+#: field:mail.notification,partner_id:0
+msgid "Contact"
+msgstr "ที่อยู่ติดต่อ"
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"Only the invited followers can read the\n"
+" discussions on this group."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_ir_ui_menu
+msgid "ir.ui.menu"
+msgstr "ir.ui.menu"
+
+#. module: mail
+#: view:mail.message:0
+msgid "Has attachments"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "on"
+msgstr "เปิด"
+
+#. module: mail
+#: code:addons/mail/mail_message.py:926
+#, python-format
+msgid ""
+"The following partners chosen as recipients for the email have no email "
+"address linked :"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_defaults:0
+msgid ""
+"A Python dictionary that will be evaluated to provide default values when "
+"creating new records for this alias."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message_subtype
+msgid "Message subtypes"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notified_partner_ids:0
+#: help:mail.message,notified_partner_ids:0
+msgid ""
+"Partners that have a notification pushing this message in their mailboxes"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Comment"
+msgstr "ความคิดเห็น"
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_inbox_feeds
+msgid ""
+"<p>\n"
+" <b>Good Job!</b> Your inbox is empty.\n"
+" </p><p>\n"
+" Your inbox contains private messages or emails sent to "
+"you\n"
+" as well as information related to documents or people "
+"you\n"
+" follow.\n"
+" </p>\n"
+" "
+msgstr ""
+"<p>\n"
+" <b>เยี่ยมยอด!</b> คุณอ่านข้อความหมดแล้ว\n"
+" </p><p>\n"
+" <b>ขอความร่วมมือ</b> ภาษาไทยสำหรับ OpenERP นั้น "
+"ยังไม่สมบรูณ์และถูกต้อง \n"
+" <p> เลือกหมวด Translations เพื่อแนะนำการแปลได้ที่ </p>\n"
+"\n"
+" https://launchpad.net/openobject-addons : business "
+"application modules\n"
+" https://launchpad.net/openobject-server : the "
+"framework\n"
+" https://launchpad.net/openerp-web : web "
+"modules and frontend\n"
+" </p>\n"
+" "
+
+#. module: mail
+#: field:mail.mail,notification:0
+msgid "Is Notification"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:188
+#, python-format
+msgid "Compose a new message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Send Now"
+msgstr "ส่งทันที"
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#, python-format
+msgid ""
+"Unable to send email, please configure the sender's email address or alias."
+msgstr ""
+
+#. module: mail
+#: help:res.users,alias_id:0
+msgid ""
+"Email address internally associated with this user. Incoming emails will "
+"appear in the user's notifications."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image:0
+msgid "Photo"
+msgstr "รูปภาพ"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:54
+#: code:addons/mail/static/src/xml/mail.xml:191
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+#, python-format
+msgid "or"
+msgstr "หรือ"
+
+#. module: mail
+#: help:mail.compose.message,vote_user_ids:0
+#: help:mail.message,vote_user_ids:0
+msgid "Users that voted for this message"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,alias_id:0
+msgid ""
+"The email address associated with this group. New emails received will "
+"automatically create new topics."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Month"
+msgstr "เดือน"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Email Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,child_ids:0
+#: field:mail.message,child_ids:0
+msgid "Child Messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_user_id:0
+msgid "Owner"
+msgstr "เจ้าของ"
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_users
+msgid "Users"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message
+#: field:mail.mail,mail_message_id:0
+#: view:mail.message:0
+#: field:mail.notification,message_id:0
+#: field:mail.wizard.invite,message:0
+msgid "Message"
+msgstr "ข้อความ"
+
+#. module: mail
+#: help:mail.followers,res_id:0
+#: help:mail.wizard.invite,res_id:0
+msgid "Id of the followed resource"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,body:0
+#: field:mail.message,body:0
+msgid "Contents"
+msgstr "เนื้อหา"
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_alias
+#: model:ir.ui.menu,name:mail.mail_alias_menu
+msgid "Aliases"
+msgstr "นามแฝง"
+
+#. module: mail
+#: help:mail.message.subtype,description:0
+msgid ""
+"Description that will be added in the message posted for this subtype. If "
+"void, the name will be added instead."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,vote_user_ids:0
+#: field:mail.message,vote_user_ids:0
+msgid "Votes"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group"
+msgstr "กลุ่ม"
+
+#. module: mail
+#: help:mail.compose.message,starred:0
+#: help:mail.message,starred:0
+msgid "Current user has a starred notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,public:0
+msgid "Privacy"
+msgstr "ความเป็นส่วนตัว"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Notification"
+msgstr "การแจ้งเตือน"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:654
+#, python-format
+msgid "Please complete partner's informations"
+msgstr ""
+
+#. module: mail
+#: view:mail.wizard.invite:0
+msgid "Add Followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+msgid "Followers of selected items and"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_force_thread_id:0
+msgid "Record Thread ID"
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_group_root
+msgid "My Groups"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_archives_feeds
+msgid ""
+"<p>\n"
+" No message found and no message sent yet.\n"
+" </p><p>\n"
+" Click on the top-right icon to compose a message. This\n"
+" message will be sent by email if it's an internal "
+"contact.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: field:mail.mail,state:0
+msgid "Status"
+msgstr "สถานะ"
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Outgoing"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "All feeds"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,record_name:0
+#: help:mail.message,record_name:0
+msgid "Name get of the related document."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_notifications
+#: model:ir.model,name:mail.model_mail_notification
+#: model:ir.ui.menu,name:mail.menu_email_notifications
+#: field:mail.compose.message,notification_ids:0
+#: view:mail.message:0
+#: field:mail.message,notification_ids:0
+#: view:mail.notification:0
+msgid "Notifications"
+msgstr "การแจ้งให้ทราบ"
+
+#. module: mail
+#: view:mail.alias:0
+msgid "Search Alias"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_force_thread_id:0
+msgid ""
+"Optional ID of a thread (record) to which all incoming messages will be "
+"attached, even if they did not reply to it. If set, this will disable the "
+"creation of new records completely."
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,name:0
+msgid ""
+"Message subtype gives a more precise type on the message, especially for "
+"system notifications. For example, it can be a notification related to a new "
+"record (New), or to a stage change in a process (Stage change). Message "
+"subtypes allow to precisely tune the notifications the user want to receive "
+"on its wall."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "by"
+msgstr "โดย"
+
+#. module: mail
+#: model:mail.group,name:mail.group_best_sales_practices
+msgid "Best Sales Practices"
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Selected Group Only"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_is_follower:0
+#: field:mail.thread,message_is_follower:0
+#: field:res.partner,message_is_follower:0
+msgid "Is a Follower"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "User"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Groups"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Messages Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,date:0
+#: field:mail.message,date:0
+msgid "Date"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:34
+#, python-format
+msgid "Post"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Extended Filters..."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:120
+#, python-format
+msgid "To:"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:193
+#, python-format
+msgid "Write to my followers"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_groups
+msgid "Access Groups"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,default:0
+msgid "Default"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:311
+#, python-format
+msgid "show more message"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:246
+#, python-format
+msgid "Mark as Todo"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,parent_id:0
+msgid "Parent subtype, used for automatic subscription."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_wizard_invite
+msgid "Invite wizard"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_summary:0
+#: field:mail.thread,message_summary:0
+#: field:res.partner,message_summary:0
+msgid "Summary"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,res_model:0
+msgid ""
+"Model the subtype applies to. If False, this subtype applies to all models."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,subtype_id:0
+#: field:mail.followers,subtype_ids:0
+#: field:mail.message,subtype_id:0
+#: view:mail.message.subtype:0
+msgid "Subtype"
+msgstr "ประเภทย่อย"
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Form"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,starred:0
+#: field:mail.message,starred:0
+#: field:mail.notification,starred:0
+msgid "Starred"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "more messages"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:13
+#, python-format
+msgid "Following"
+msgstr ""
+
+#. module: mail
+#: sql_constraint:mail.alias:0
+msgid ""
+"Unfortunately this email alias is already used, please choose a unique one"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_user_id:0
+msgid ""
+"The owner of records created upon receiving emails on this alias. If this "
+"field is not set the system will attempt to find the right owner based on "
+"the sender (From) address, or will use the Administrator account if no "
+"system user is found for that address."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "And"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,message_id:0
+#: field:mail.message,message_id:0
+msgid "Message-Id"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image:0
+msgid ""
+"This field holds the image used as photo for the group, limited to "
+"1024x1024px."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,attachment_ids:0
+#: view:mail.mail:0
+#: field:mail.message,attachment_ids:0
+msgid "Attachments"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,record_name:0
+#: field:mail.message,record_name:0
+msgid "Message Record Name"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_cc:0
+msgid "Cc"
+msgstr ""
+
+#. module: mail
+#: help:mail.notification,starred:0
+msgid "Starred message that goes into the todo mailbox"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:123
+#: view:mail.compose.message:0
+#, python-format
+msgid "Followers of"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,auto_delete:0
+msgid "Permanently delete this email after sending it, to save space"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_group_feeds
+msgid "Discussion Group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:242
+#, python-format
+msgid "Done"
+msgstr ""
+
+#. module: mail
+#: model:mail.message.subtype,name:mail.mt_comment
+msgid "Discussions"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:11
+#, python-format
+msgid "Follow"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,name:0
+msgid "Name"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_all_employees
+msgid "Whole Company"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:129
+#: code:addons/mail/static/src/xml/mail.xml:292
+#: view:mail.compose.message:0
+#, python-format
+msgid "and"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,body_html:0
+msgid "Rich-text/HTML message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Creation Month"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:323
+#, python-format
+msgid "Compose new Message"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,menu_id:0
+msgid "Related Menu"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Content"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_to:0
+msgid "To"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,notified_partner_ids:0
+#: field:mail.message,notified_partner_ids:0
+msgid "Notified partners"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,public:0
+msgid ""
+"This group is visible by non members. Invisible groups can add "
+"members through the invite button."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_board
+msgid "Board meetings"
+msgstr ""
+
+#. module: mail
+#: constraint:mail.alias:0
+msgid ""
+"Invalid expression, it must be a literal python dictionary definition e.g. "
+"\"{'field': 'value'}\""
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_model_id:0
+msgid "Aliased Model"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,message_id:0
+#: help:mail.message,message_id:0
+msgid "Message unique identifier"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,description:0
+#: field:mail.message.subtype,description:0
+msgid "Description"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_followers
+msgid "Document Followers"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:35
+#, python-format
+msgid "Remove this follower"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Never"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,mail_server_id:0
+msgid "Outgoing mail server"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:930
+#, python-format
+msgid "Partners email addresses not found"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Sent"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,body_html:0
+msgid "Rich-text Contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,to_read:0
+#: help:mail.message,to_read:0
+msgid "Current user has an unread notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: help:res.partner,notification_email_send:0
+msgid ""
+"Choose in which case you want to receive an email when you receive new feeds."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_groups
+#: model:ir.ui.menu,name:mail.mail_allgroups
+msgid "Join a group"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_group_feeds
+msgid ""
+"<p>\n"
+" No message in this group.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:213
+#, python-format
+msgid "Please, wait while the file is uploading."
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"This group is visible by everyone,\n"
+" including your customers if you "
+"installed\n"
+" the portal module."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:243
+#, python-format
+msgid "Set back to Todo"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:126
+#, python-format
+msgid "this document"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,filter_id:0
+msgid "Filters"
+msgstr ""
+
+#. module: mail
+#: field:res.partner,notification_email_send:0
+msgid "Receive Feeds by Email"
+msgstr ""
+
+#. module: mail
+#: help:base.config.settings,alias_domain:0
+msgid ""
+"If you have setup a catch-all email domain redirected to the OpenERP server, "
+"enter the domain name here."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_message
+#: model:ir.ui.menu,name:mail.menu_mail_message
+#: field:mail.group,message_ids:0
+#: view:mail.message:0
+#: field:mail.thread,message_ids:0
+#: field:res.partner,message_ids:0
+msgid "Messages"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:139
+#, python-format
+msgid "others..."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_star_feeds
+#: model:ir.ui.menu,name:mail.mail_starfeeds
+msgid "To-do"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.alias,alias_name:0
+#: field:mail.group,alias_id:0
+#: field:res.users,alias_id:0
+msgid "Alias"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_mail
+msgid "Outgoing Mails"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notification_ids:0
+#: help:mail.message,notification_ids:0
+msgid ""
+"Technical field holding the message notifications. Use notified_partner_ids "
+"to access notified partners."
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_feeds
+#: model:ir.ui.menu,name:mail.mail_feeds_main
+msgid "Messaging"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.message.subtype,res_model:0
+msgid "Model"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Unread"
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,subtype_ids:0
+msgid ""
+"Message subtypes followed, meaning subtypes that will be pushed onto the "
+"user's Wall."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_ids:0
+#: help:mail.thread,message_ids:0
+#: help:res.partner,message_ids:0
+msgid "Messages and communication history"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,references:0
+msgid "Message references, such as identifiers of previous messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,composition_mode:0
+msgid "Composition mode"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,model:0
+#: field:mail.followers,res_model:0
+#: field:mail.message,model:0
+#: field:mail.wizard.invite,res_model:0
+msgid "Related Document Model"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:338
+#, python-format
+msgid "unlike"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,author_id:0
+#: help:mail.message,author_id:0
+msgid ""
+"Author of the message. If not set, email_from may hold an email address that "
+"did not match any partner."
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_cc:0
+msgid "Carbon copy message recipients"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_domain:0
+msgid "Alias domain"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error during communication with the publisher warranty server."
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Private"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_star_feeds
+msgid ""
+"<p>\n"
+" <b>No todo.</b>\n"
+" </p><p>\n"
+" When you process messages in your inbox, you can mark "
+"some\n"
+" as <i>todo</i>. From this menu, you can process all your "
+"todo.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Delivery Failed"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,partner_ids:0
+msgid "Additional contacts"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,parent_id:0
+#: help:mail.message,parent_id:0
+msgid "Initial thread message."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_hr_policies
+msgid "HR Policies"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Emails only"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_inbox_feeds
+#: model:ir.ui.menu,name:mail.mail_inboxfeeds
+msgid "Inbox"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:58
+#, python-format
+msgid "File"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/many2many_tags_email.js:63
+#, python-format
+msgid "Please complete partner's informations and Email"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_message_subtype
+#: model:ir.ui.menu,name:mail.menu_message_subtype
+msgid "Subtypes"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_alias
+msgid "Email Aliases"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_small:0
+msgid "Small-sized photo"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,reply_to:0
+msgid "Preferred response address for the message"
+msgstr ""
diff --git a/addons/mail/i18n/vi.po b/addons/mail/i18n/vi.po
new file mode 100644
index 00000000000..9f7569bddf7
--- /dev/null
+++ b/addons/mail/i18n/vi.po
@@ -0,0 +1,1687 @@
+# Vietnamese translation for openobject-addons
+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2012-12-21 17:04+0000\n"
+"PO-Revision-Date: 2013-06-27 06:35+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Vietnamese <vi@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2013-06-28 05:42+0000\n"
+"X-Generator: Launchpad (build 16681)\n"
+
+#. module: mail
+#: view:mail.followers:0
+msgid "Followers Form"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_publisher_warranty_contract
+msgid "publisher_warranty.contract"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,author_id:0
+#: view:mail.mail:0
+#: field:mail.message,author_id:0
+msgid "Author"
+msgstr "Tác giả"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Message Details"
+msgstr "Chi tiết tin nhắn"
+
+#. module: mail
+#: help:mail.mail,email_to:0
+msgid "Message recipients"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,default:0
+msgid "Activated by default when subscribing."
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Comments"
+msgstr "Bình luận"
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "Group By..."
+msgstr "Nhóm theo..."
+
+#. module: mail
+#: help:mail.compose.message,body:0
+#: help:mail.message,body:0
+msgid "Automatically sanitized HTML contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_name:0
+msgid ""
+"The name of the email alias, e.g. 'jobs' if you want to catch emails for "
+"<jobs@example.my.openerp.com>"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
+#: view:mail.compose.message:0
+msgid "Compose Email"
+msgstr "Soạn thư mới"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:132
+#, python-format
+msgid "Add them into recipients and followers"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Name"
+msgstr "Tên nhóm"
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Public"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Body"
+msgstr "Nội dung"
+
+#. module: mail
+#: view:mail.message:0
+msgid "Show messages to read"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,email_from:0
+#: help:mail.message,email_from:0
+msgid ""
+"Email address of the sender. This field is set when no matching partner is "
+"found for incoming emails."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_compose_message
+msgid "Email composition wizard"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:23
+#, python-format
+msgid "Add others"
+msgstr "Thêm"
+
+#. module: mail
+#: field:mail.message.subtype,parent_id:0
+msgid "Parent"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_unread:0
+#: field:mail.thread,message_unread:0
+#: field:res.partner,message_unread:0
+msgid "Unread Messages"
+msgstr "Tin chưa đọc"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "show"
+msgstr "hiển thị"
+
+#. module: mail
+#: help:mail.group,group_ids:0
+msgid ""
+"Members of those groups will automatically added as followers. Note that "
+"they will be able to manage their subscription manually if necessary."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:1029
+#, python-format
+msgid "Do you really want to delete this message?"
+msgstr "Bạn có chắc muốn xóa tin nhắn này?"
+
+#. module: mail
+#: view:mail.message:0
+#: field:mail.notification,read:0
+msgid "Read"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Search Groups"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:157
+#, python-format
+msgid "followers"
+msgstr "theo dõi"
+
+#. module: mail
+#: code:addons/mail/mail_message.py:737
+#, python-format
+msgid "Access Denied"
+msgstr "Từ chối truy cập"
+
+#. module: mail
+#: help:mail.group,image_medium:0
+msgid ""
+"Medium-sized photo of the group. It is automatically resized as a 128x128px "
+"image, with aspect ratio preserved. Use this field in form views or some "
+"kanban views."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:212
+#, python-format
+msgid "Uploading error"
+msgstr "Lỗi tải lên"
+
+#. module: mail
+#: model:mail.group,name:mail.group_support
+msgid "Support"
+msgstr "Hỗ trợ"
+
+#. module: mail
+#: code:addons/mail/mail_message.py:738
+#, python-format
+msgid ""
+"The requested operation cannot be completed due to security restrictions. "
+"Please contact your system administrator.\n"
+"\n"
+"(Document type: %s, Operation: %s)"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Received"
+msgstr "Đã nhận"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Thread"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "Open the full mail composer"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:29
+#, python-format
+msgid "&ograve"
+msgstr ""
+
+#. module: mail
+#: field:base.config.settings,alias_domain:0
+msgid "Alias Domain"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_ids:0
+msgid "Auto Subscription"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,references:0
+msgid "References"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:206
+#, python-format
+msgid "No messages."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_group
+msgid "Discussion group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:96
+#: code:addons/mail/static/src/xml/mail.xml:108
+#, python-format
+msgid "uploading"
+msgstr "đang tải lên"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "more."
+msgstr "thêm nữa."
+
+#. module: mail
+#: help:mail.compose.message,type:0
+#: help:mail.message,type:0
+msgid ""
+"Message type: email for email message, notification for system message, "
+"comment for other messages such as user replies"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,relation_field:0
+msgid ""
+"Field used to link the related model to the subtype model when using "
+"automatic subscription on a related document. The field is used to compute "
+"getattr(related_document.relation_field)."
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Cancelled"
+msgstr "Đã hủy"
+
+#. module: mail
+#: field:mail.mail,reply_to:0
+msgid "Reply-To"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:37
+#, python-format
+msgid "<div>You have been invited to follow %s.</div>"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_unread:0
+#: help:mail.thread,message_unread:0
+#: help:res.partner,message_unread:0
+msgid "If checked new messages require your attention."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_medium:0
+msgid "Medium-sized photo"
+msgstr "Ảnh cỡ trung"
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_to_me_feeds
+#: model:ir.ui.menu,name:mail.mail_tomefeeds
+msgid "To: me"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,name:0
+msgid "Message Type"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,auto_delete:0
+msgid "Auto Delete"
+msgstr "Tự động xóa"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:12
+#: view:mail.group:0
+#, python-format
+msgid "Unfollow"
+msgstr "Hủy theo dõi"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:312
+#, python-format
+msgid "show one more message"
+msgstr "hiện theo một tin nữa"
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid "Invalid Action!"
+msgstr "Hành động không hợp lệ!"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:25
+#, python-format
+msgid "User img"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_mail
+#: model:ir.ui.menu,name:mail.menu_mail_mail
+#: view:mail.mail:0
+#: view:mail.message:0
+msgid "Emails"
+msgstr ""
+
+#. module: mail
+#: field:mail.followers,partner_id:0
+msgid "Related Partner"
+msgstr "Đối tác liên quan"
+
+#. module: mail
+#: help:mail.group,message_summary:0
+#: help:mail.thread,message_summary:0
+#: help:res.partner,message_summary:0
+msgid ""
+"Holds the Chatter summary (number of messages, ...). This summary is "
+"directly in html format in order to be inserted in kanban views."
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_model_id:0
+msgid ""
+"The model (OpenERP Document Kind) to which this alias corresponds. Any "
+"incoming email that does not reply to an existing record will cause the "
+"creation of a new record of this model (e.g. a Project Task)"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,relation_field:0
+msgid "Relation field"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: selection:mail.message,type:0
+msgid "System notification"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_partner
+msgid "Partner"
+msgstr "Đối tác"
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_my_stuff
+msgid "Organizer"
+msgstr "Tổ chức"
+
+#. module: mail
+#: field:mail.compose.message,subject:0
+#: field:mail.message,subject:0
+msgid "Subject"
+msgstr "Chủ đề"
+
+#. module: mail
+#: field:mail.wizard.invite,partner_ids:0
+msgid "Partners"
+msgstr "Các đối tác"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Retry"
+msgstr "Thử lại"
+
+#. module: mail
+#: field:mail.compose.message,email_from:0
+#: field:mail.mail,email_from:0
+#: field:mail.message,email_from:0
+msgid "From"
+msgstr "Từ"
+
+#. module: mail
+#: view:mail.mail:0
+#: view:mail.message.subtype:0
+msgid "Email message"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_base_config_settings
+msgid "base.config.settings"
+msgstr "base.config.settings"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:36
+#: view:mail.compose.message:0
+#, python-format
+msgid "Send"
+msgstr "Gửi"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:153
+#, python-format
+msgid "No followers"
+msgstr "Không có theo dõi"
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Failed"
+msgstr "Thất bại"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:22
+#: model:ir.actions.act_window,name:mail.action_view_followers
+#: model:ir.ui.menu,name:mail.menu_email_followers
+#: view:mail.followers:0
+#: field:mail.group,message_follower_ids:0
+#: field:mail.thread,message_follower_ids:0
+#: field:res.partner,message_follower_ids:0
+#, python-format
+msgid "Followers"
+msgstr "Người theo dõi"
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_archives_feeds
+#: model:ir.ui.menu,name:mail.mail_archivesfeeds
+msgid "Archives"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:95
+#: code:addons/mail/static/src/xml/mail.xml:107
+#, python-format
+msgid "Delete this attachment"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:245
+#: view:mail.mail:0
+#, python-format
+msgid "Reply"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail_followers.js:155
+#, python-format
+msgid "One follower"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,type:0
+#: field:mail.message,type:0
+msgid "Type"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Email"
+msgstr ""
+
+#. module: mail
+#: field:ir.ui.menu,mail_group_id:0
+msgid "Mail Group"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Comments and Emails"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_defaults:0
+msgid "Default Values"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/res_users.py:89
+#, python-format
+msgid "%s has joined the %s network."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image_small:0
+msgid ""
+"Small-sized photo of the group. It is automatically resized as a 64x64px "
+"image, with aspect ratio preserved. Use this field anywhere a small image is "
+"required."
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: field:mail.message,partner_ids:0
+msgid "Recipients"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:140
+#, python-format
+msgid "<<<"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:43
+#, python-format
+msgid "Write to the followers of this document..."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,group_public_id:0
+msgid "Authorized Group"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Join Group"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_from:0
+msgid "Message sender, taken from user preferences."
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/invite.py:40
+#, python-format
+msgid "<div>You have been invited to follow a new document.</div>"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,parent_id:0
+#: field:mail.message,parent_id:0
+msgid "Parent Message"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,res_id:0
+#: field:mail.followers,res_id:0
+#: field:mail.message,res_id:0
+#: field:mail.wizard.invite,res_id:0
+msgid "Related Document ID"
+msgstr "ID tài liệu liên quan"
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_to_me_feeds
+msgid ""
+"<p>\n"
+" <b>No private message.</b>\n"
+" </p><p>\n"
+" This list contains messages sent to you.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_rd
+msgid "R&D"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:61
+#, python-format
+msgid "/web/binary/upload_attachment"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_thread
+msgid "Email Thread"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Advanced"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:244
+#, python-format
+msgid "Move to Inbox"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/wizard/mail_compose_message.py:193
+#, python-format
+msgid "Re:"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,to_read:0
+#: field:mail.message,to_read:0
+msgid "To read"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/res_users.py:69
+#, python-format
+msgid ""
+"You may not create a user. To create new users, you should use the "
+"\"Settings > Users\" menu."
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,res_model:0
+#: help:mail.wizard.invite,res_model:0
+msgid "Model of the followed resource"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:337
+#, python-format
+msgid "like"
+msgstr ""
+
+#. module: mail
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+msgid "Cancel"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:47
+#, python-format
+msgid "Share with my followers..."
+msgstr ""
+
+#. module: mail
+#: field:mail.notification,partner_id:0
+msgid "Contact"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"Only the invited followers can read the\n"
+" discussions on this group."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_ir_ui_menu
+msgid "ir.ui.menu"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Has attachments"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "on"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:926
+#, python-format
+msgid ""
+"The following partners chosen as recipients for the email have no email "
+"address linked :"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_defaults:0
+msgid ""
+"A Python dictionary that will be evaluated to provide default values when "
+"creating new records for this alias."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message_subtype
+msgid "Message subtypes"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notified_partner_ids:0
+#: help:mail.message,notified_partner_ids:0
+msgid ""
+"Partners that have a notification pushing this message in their mailboxes"
+msgstr ""
+
+#. module: mail
+#: selection:mail.compose.message,type:0
+#: view:mail.mail:0
+#: selection:mail.message,type:0
+msgid "Comment"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_inbox_feeds
+msgid ""
+"<p>\n"
+" <b>Good Job!</b> Your inbox is empty.\n"
+" </p><p>\n"
+" Your inbox contains private messages or emails sent to "
+"you\n"
+" as well as information related to documents or people "
+"you\n"
+" follow.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,notification:0
+msgid "Is Notification"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:188
+#, python-format
+msgid "Compose a new message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Send Now"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_mail.py:75
+#, python-format
+msgid ""
+"Unable to send email, please configure the sender's email address or alias."
+msgstr ""
+
+#. module: mail
+#: help:res.users,alias_id:0
+msgid ""
+"Email address internally associated with this user. Incoming emails will "
+"appear in the user's notifications."
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image:0
+msgid "Photo"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:54
+#: code:addons/mail/static/src/xml/mail.xml:191
+#: view:mail.compose.message:0
+#: view:mail.wizard.invite:0
+#, python-format
+msgid "or"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,vote_user_ids:0
+#: help:mail.message,vote_user_ids:0
+msgid "Users that voted for this message"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,alias_id:0
+msgid ""
+"The email address associated with this group. New emails received will "
+"automatically create new topics."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Month"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Email Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,child_ids:0
+#: field:mail.message,child_ids:0
+msgid "Child Messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_user_id:0
+msgid "Owner"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_users
+msgid "Users"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_message
+#: field:mail.mail,mail_message_id:0
+#: view:mail.message:0
+#: field:mail.notification,message_id:0
+#: field:mail.wizard.invite,message:0
+msgid "Message"
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,res_id:0
+#: help:mail.wizard.invite,res_id:0
+msgid "Id of the followed resource"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,body:0
+#: field:mail.message,body:0
+msgid "Contents"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_alias
+#: model:ir.ui.menu,name:mail.mail_alias_menu
+msgid "Aliases"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,description:0
+msgid ""
+"Description that will be added in the message posted for this subtype. If "
+"void, the name will be added instead."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,vote_user_ids:0
+#: field:mail.message,vote_user_ids:0
+msgid "Votes"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,starred:0
+#: help:mail.message,starred:0
+msgid "Current user has a starred notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,public:0
+msgid "Privacy"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Notification"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/mail.js:654
+#, python-format
+msgid "Please complete partner's informations"
+msgstr ""
+
+#. module: mail
+#: view:mail.wizard.invite:0
+msgid "Add Followers"
+msgstr "Thêm theo dõi"
+
+#. module: mail
+#: view:mail.compose.message:0
+msgid "Followers of selected items and"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_force_thread_id:0
+msgid "Record Thread ID"
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_group_root
+msgid "My Groups"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_archives_feeds
+msgid ""
+"<p>\n"
+" No message found and no message sent yet.\n"
+" </p><p>\n"
+" Click on the top-right icon to compose a message. This\n"
+" message will be sent by email if it's an internal "
+"contact.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: field:mail.mail,state:0
+msgid "Status"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Outgoing"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "All feeds"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,record_name:0
+#: help:mail.message,record_name:0
+msgid "Name get of the related document."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_notifications
+#: model:ir.model,name:mail.model_mail_notification
+#: model:ir.ui.menu,name:mail.menu_email_notifications
+#: field:mail.compose.message,notification_ids:0
+#: view:mail.message:0
+#: field:mail.message,notification_ids:0
+#: view:mail.notification:0
+msgid "Notifications"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+msgid "Search Alias"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_force_thread_id:0
+msgid ""
+"Optional ID of a thread (record) to which all incoming messages will be "
+"attached, even if they did not reply to it. If set, this will disable the "
+"creation of new records completely."
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,name:0
+msgid ""
+"Message subtype gives a more precise type on the message, especially for "
+"system notifications. For example, it can be a notification related to a new "
+"record (New), or to a stage change in a process (Stage change). Message "
+"subtypes allow to precisely tune the notifications the user want to receive "
+"on its wall."
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "by"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_best_sales_practices
+msgid "Best Sales Practices"
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Selected Group Only"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_is_follower:0
+#: field:mail.thread,message_is_follower:0
+#: field:res.partner,message_is_follower:0
+msgid "Is a Follower"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: view:mail.mail:0
+msgid "User"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Groups"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Messages Search"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,date:0
+#: field:mail.message,date:0
+msgid "Date"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:34
+#, python-format
+msgid "Post"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Extended Filters..."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:120
+#, python-format
+msgid "To:"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:193
+#, python-format
+msgid "Write to my followers"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_res_groups
+msgid "Access Groups"
+msgstr ""
+
+#. module: mail
+#: field:mail.message.subtype,default:0
+msgid "Default"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:311
+#, python-format
+msgid "show more message"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:246
+#, python-format
+msgid "Mark as Todo"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,parent_id:0
+msgid "Parent subtype, used for automatic subscription."
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_wizard_invite
+msgid "Invite wizard"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,message_summary:0
+#: field:mail.thread,message_summary:0
+#: field:res.partner,message_summary:0
+msgid "Summary"
+msgstr ""
+
+#. module: mail
+#: help:mail.message.subtype,res_model:0
+msgid ""
+"Model the subtype applies to. If False, this subtype applies to all models."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,subtype_id:0
+#: field:mail.followers,subtype_ids:0
+#: field:mail.message,subtype_id:0
+#: view:mail.message.subtype:0
+msgid "Subtype"
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid "Group Form"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,starred:0
+#: field:mail.message,starred:0
+#: field:mail.notification,starred:0
+msgid "Starred"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:313
+#, python-format
+msgid "more messages"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:13
+#, python-format
+msgid "Following"
+msgstr ""
+
+#. module: mail
+#: sql_constraint:mail.alias:0
+msgid ""
+"Unfortunately this email alias is already used, please choose a unique one"
+msgstr ""
+
+#. module: mail
+#: help:mail.alias,alias_user_id:0
+msgid ""
+"The owner of records created upon receiving emails on this alias. If this "
+"field is not set the system will attempt to find the right owner based on "
+"the sender (From) address, or will use the Administrator account if no "
+"system user is found for that address."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:52
+#, python-format
+msgid "And"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,message_id:0
+#: field:mail.message,message_id:0
+msgid "Message-Id"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,image:0
+msgid ""
+"This field holds the image used as photo for the group, limited to "
+"1024x1024px."
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,attachment_ids:0
+#: view:mail.mail:0
+#: field:mail.message,attachment_ids:0
+msgid "Attachments"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,record_name:0
+#: field:mail.message,record_name:0
+msgid "Message Record Name"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_cc:0
+msgid "Cc"
+msgstr ""
+
+#. module: mail
+#: help:mail.notification,starred:0
+msgid "Starred message that goes into the todo mailbox"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:123
+#: view:mail.compose.message:0
+#, python-format
+msgid "Followers of"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,auto_delete:0
+msgid "Permanently delete this email after sending it, to save space"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_group_feeds
+msgid "Discussion Group"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:242
+#, python-format
+msgid "Done"
+msgstr ""
+
+#. module: mail
+#: model:mail.message.subtype,name:mail.mt_comment
+msgid "Discussions"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:11
+#, python-format
+msgid "Follow"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,name:0
+msgid "Name"
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_all_employees
+msgid "Whole Company"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:129
+#: code:addons/mail/static/src/xml/mail.xml:292
+#: view:mail.compose.message:0
+#, python-format
+msgid "and"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,body_html:0
+msgid "Rich-text/HTML message"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+msgid "Creation Month"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:323
+#, python-format
+msgid "Compose new Message"
+msgstr "Soạn tin nhắn mới"
+
+#. module: mail
+#: field:mail.group,menu_id:0
+msgid "Related Menu"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Content"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,email_to:0
+msgid "To"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,notified_partner_ids:0
+#: field:mail.message,notified_partner_ids:0
+msgid "Notified partners"
+msgstr ""
+
+#. module: mail
+#: help:mail.group,public:0
+msgid ""
+"This group is visible by non members. Invisible groups can add "
+"members through the invite button."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_board
+msgid "Board meetings"
+msgstr ""
+
+#. module: mail
+#: constraint:mail.alias:0
+msgid ""
+"Invalid expression, it must be a literal python dictionary definition e.g. "
+"\"{'field': 'value'}\""
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_model_id:0
+msgid "Aliased Model"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,message_id:0
+#: help:mail.message,message_id:0
+msgid "Message unique identifier"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,description:0
+#: field:mail.message.subtype,description:0
+msgid "Description"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_followers
+msgid "Document Followers"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail_followers.xml:35
+#, python-format
+msgid "Remove this follower"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Never"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,mail_server_id:0
+msgid "Outgoing mail server"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/mail_message.py:930
+#, python-format
+msgid "Partners email addresses not found"
+msgstr ""
+
+#. module: mail
+#: view:mail.mail:0
+#: selection:mail.mail,state:0
+msgid "Sent"
+msgstr ""
+
+#. module: mail
+#: field:mail.mail,body_html:0
+msgid "Rich-text Contents"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,to_read:0
+#: help:mail.message,to_read:0
+msgid "Current user has an unread notification linked to this message"
+msgstr ""
+
+#. module: mail
+#: help:res.partner,notification_email_send:0
+msgid ""
+"Choose in which case you want to receive an email when you receive new feeds."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_groups
+#: model:ir.ui.menu,name:mail.mail_allgroups
+msgid "Join a group"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_group_feeds
+msgid ""
+"<p>\n"
+" No message in this group.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:213
+#, python-format
+msgid "Please, wait while the file is uploading."
+msgstr ""
+
+#. module: mail
+#: view:mail.group:0
+msgid ""
+"This group is visible by everyone,\n"
+" including your customers if you "
+"installed\n"
+" the portal module."
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:243
+#, python-format
+msgid "Set back to Todo"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:126
+#, python-format
+msgid "this document"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,filter_id:0
+msgid "Filters"
+msgstr "Các bộ lọc"
+
+#. module: mail
+#: field:res.partner,notification_email_send:0
+msgid "Receive Feeds by Email"
+msgstr ""
+
+#. module: mail
+#: help:base.config.settings,alias_domain:0
+msgid ""
+"If you have setup a catch-all email domain redirected to the OpenERP server, "
+"enter the domain name here."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_mail_message
+#: model:ir.ui.menu,name:mail.menu_mail_message
+#: field:mail.group,message_ids:0
+#: view:mail.message:0
+#: field:mail.thread,message_ids:0
+#: field:res.partner,message_ids:0
+msgid "Messages"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:139
+#, python-format
+msgid "others..."
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_star_feeds
+#: model:ir.ui.menu,name:mail.mail_starfeeds
+msgid "To-do"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.alias,alias_name:0
+#: field:mail.group,alias_id:0
+#: field:res.users,alias_id:0
+msgid "Alias"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_mail
+msgid "Outgoing Mails"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,notification_ids:0
+#: help:mail.message,notification_ids:0
+msgid ""
+"Technical field holding the message notifications. Use notified_partner_ids "
+"to access notified partners."
+msgstr ""
+
+#. module: mail
+#: model:ir.ui.menu,name:mail.mail_feeds
+#: model:ir.ui.menu,name:mail.mail_feeds_main
+msgid "Messaging"
+msgstr ""
+
+#. module: mail
+#: view:mail.alias:0
+#: field:mail.message.subtype,res_model:0
+msgid "Model"
+msgstr ""
+
+#. module: mail
+#: view:mail.message:0
+msgid "Unread"
+msgstr ""
+
+#. module: mail
+#: help:mail.followers,subtype_ids:0
+msgid ""
+"Message subtypes followed, meaning subtypes that will be pushed onto the "
+"user's Wall."
+msgstr ""
+
+#. module: mail
+#: help:mail.group,message_ids:0
+#: help:mail.thread,message_ids:0
+#: help:res.partner,message_ids:0
+msgid "Messages and communication history"
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,references:0
+msgid "Message references, such as identifiers of previous messages"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,composition_mode:0
+msgid "Composition mode"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,model:0
+#: field:mail.followers,res_model:0
+#: field:mail.message,model:0
+#: field:mail.wizard.invite,res_model:0
+msgid "Related Document Model"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:338
+#, python-format
+msgid "unlike"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,author_id:0
+#: help:mail.message,author_id:0
+msgid ""
+"Author of the message. If not set, email_from may hold an email address that "
+"did not match any partner."
+msgstr ""
+
+#. module: mail
+#: help:mail.mail,email_cc:0
+msgid "Carbon copy message recipients"
+msgstr ""
+
+#. module: mail
+#: field:mail.alias,alias_domain:0
+msgid "Alias domain"
+msgstr ""
+
+#. module: mail
+#: code:addons/mail/update.py:93
+#, python-format
+msgid "Error during communication with the publisher warranty server."
+msgstr ""
+
+#. module: mail
+#: selection:mail.group,public:0
+msgid "Private"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,help:mail.action_mail_star_feeds
+msgid ""
+"<p>\n"
+" <b>No todo.</b>\n"
+" </p><p>\n"
+" When you process messages in your inbox, you can mark "
+"some\n"
+" as <i>todo</i>. From this menu, you can process all your "
+"todo.\n"
+" </p>\n"
+" "
+msgstr ""
+
+#. module: mail
+#: selection:mail.mail,state:0
+msgid "Delivery Failed"
+msgstr ""
+
+#. module: mail
+#: field:mail.compose.message,partner_ids:0
+msgid "Additional contacts"
+msgstr ""
+
+#. module: mail
+#: help:mail.compose.message,parent_id:0
+#: help:mail.message,parent_id:0
+msgid "Initial thread message."
+msgstr ""
+
+#. module: mail
+#: model:mail.group,name:mail.group_hr_policies
+msgid "HR Policies"
+msgstr ""
+
+#. module: mail
+#: selection:res.partner,notification_email_send:0
+msgid "Emails only"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.client,name:mail.action_mail_inbox_feeds
+#: model:ir.ui.menu,name:mail.mail_inboxfeeds
+msgid "Inbox"
+msgstr "Hộp thư đến"
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/xml/mail.xml:58
+#, python-format
+msgid "File"
+msgstr ""
+
+#. module: mail
+#. openerp-web
+#: code:addons/mail/static/src/js/many2many_tags_email.js:63
+#, python-format
+msgid "Please complete partner's informations and Email"
+msgstr ""
+
+#. module: mail
+#: model:ir.actions.act_window,name:mail.action_view_message_subtype
+#: model:ir.ui.menu,name:mail.menu_message_subtype
+msgid "Subtypes"
+msgstr ""
+
+#. module: mail
+#: model:ir.model,name:mail.model_mail_alias
+msgid "Email Aliases"
+msgstr ""
+
+#. module: mail
+#: field:mail.group,image_small:0
+msgid "Small-sized photo"
+msgstr "Ảnh cỡ nhỏ"
+
+#. module: mail
+#: help:mail.mail,reply_to:0
+msgid "Preferred response address for the message"
+msgstr ""
diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py
index 870695e074e..97d2842d15c 100644
--- a/addons/mail/mail_alias.py
+++ b/addons/mail/mail_alias.py
@@ -39,6 +39,7 @@ def remove_accents(input_str):
nkfd_form = unicodedata.normalize('NFKD', input_str)
return u''.join([c for c in nkfd_form if not unicodedata.combining(c)])
+
class mail_alias(osv.Model):
"""A Mail Alias is a mapping of an email address with a given OpenERP Document
model. It is used by OpenERP's mail gateway when processing incoming emails
@@ -47,7 +48,7 @@ class mail_alias(osv.Model):
of that alias. If the message is a reply it will be attached to the
existing discussion on the corresponding record, otherwise a new
record of the corresponding model will be created.
-
+
This is meant to be used in combination with a catch-all email configuration
on the company's mail server, so that as soon as a new mail.alias is
created, it becomes immediately usable and OpenERP will accept email for it.
@@ -63,9 +64,8 @@ class mail_alias(osv.Model):
return dict.fromkeys(ids, domain or "")
_columns = {
- 'alias_name': fields.char('Alias', required=True,
- help="The name of the email alias, e.g. 'jobs' "
- "if you want to catch emails for <jobs@example.my.openerp.com>",),
+ 'alias_name': fields.char('Alias',
+ help="The name of the email 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, ondelete="cascade",
help="The model (OpenERP Document Kind) to which this alias "
"corresponds. Any incoming email that does not reply to an "
@@ -87,13 +87,29 @@ class mail_alias(osv.Model):
"messages will be attached, even if they did not reply to it. "
"If set, this will disable the creation of new records completely."),
'alias_domain': fields.function(_get_alias_domain, string="Alias domain", type='char', size=None),
+ 'alias_parent_model_id': fields.many2one('ir.model', 'Parent Model',
+ help="Parent model holding the alias. The model holding the alias reference\n"
+ "is not necessarily the model given by alias_model_id\n"
+ "(example: project (parent_model) and task (model))"),
+ 'alias_parent_thread_id': fields.integer('Parent Record Thread ID',
+ help="ID of the parent record holding the alias (example: project holding the task creation alias)"),
+ 'alias_contact': fields.selection([
+ ('everyone', 'Everyone'),
+ ('partners', 'Authenticated Partners'),
+ ('followers', 'Followers only'),
+ ], string='Alias Contact Security', required=True,
+ help="Policy to post a message on the document using the mailgateway.\n"
+ "- everyone: everyone can post\n"
+ "- partners: only authenticated partners\n"
+ "- followers: only followers of the related document\n"),
}
_defaults = {
'alias_defaults': '{}',
- 'alias_user_id': lambda self,cr,uid,context: uid,
+ 'alias_user_id': lambda self, cr, uid, context: uid,
# looks better when creating new aliases - even if the field is informative only
- 'alias_domain': lambda self,cr,uid,context: self._get_alias_domain(cr, SUPERUSER_ID,[1],None,None)[1]
+ 'alias_domain': lambda self, cr, uid, context: self._get_alias_domain(cr, SUPERUSER_ID, [1], None, None)[1],
+ 'alias_contact': 'everyone',
}
_sql_constraints = [
@@ -139,13 +155,15 @@ class mail_alias(osv.Model):
return new_name
def migrate_to_alias(self, cr, child_model_name, child_table_name, child_model_auto_init_fct,
- alias_id_column, alias_key, alias_prefix='', alias_force_key='', alias_defaults={}, context=None):
+ alias_model_name, alias_id_column, alias_key, alias_prefix='', alias_force_key='', alias_defaults={},
+ alias_generate_name=False, context=None):
""" Installation hook to create aliases for all users and avoid constraint errors.
:param child_model_name: model name of the child class (i.e. res.users)
:param child_table_name: table name of the child class (i.e. res_users)
:param child_model_auto_init_fct: pointer to the _auto_init function
(i.e. super(res_users,self)._auto_init(cr, context=context))
+ :param alias_model_name: name of the aliased model
:param alias_id_column: alias_id column (i.e. self._columns['alias_id'])
:param alias_key: name of the column used for the unique name (i.e. 'login')
:param alias_prefix: prefix for the unique name (i.e. 'jobs' + ...)
@@ -153,6 +171,8 @@ class mail_alias(osv.Model):
if empty string, not taken into account
:param alias_defaults: dict, keys = mail.alias columns, values = child
model column name used for default values (i.e. {'job_id': 'id'})
+ :param alias_generate_name: automatically generate alias name using prefix / alias key;
+ default alias_name value is False because since 8.0 it is not required anymore
"""
if context is None:
context = {}
@@ -170,13 +190,17 @@ class mail_alias(osv.Model):
no_alias_ids = child_class_model.search(cr, SUPERUSER_ID, [('alias_id', '=', False)], context={'active_test': False})
# Use read() not browse(), to avoid prefetching uninitialized inherited fields
for obj_data in child_class_model.read(cr, SUPERUSER_ID, no_alias_ids, [alias_key]):
- alias_vals = {'alias_name': '%s%s' % (alias_prefix, obj_data[alias_key])}
+ alias_vals = {'alias_name': False}
+ if alias_generate_name:
+ alias_vals['alias_name'] = '%s%s' % (alias_prefix, obj_data[alias_key])
if alias_force_key:
alias_vals['alias_force_thread_id'] = obj_data[alias_force_key]
alias_vals['alias_defaults'] = dict((k, obj_data[v]) for k, v in alias_defaults.iteritems())
- alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, alias_vals, model_name=context.get('alias_model_name', child_model_name))
+ alias_vals['alias_parent_thread_id'] = obj_data['id']
+ alias_create_ctx = dict(context, alias_model_name=alias_model_name, alias_parent_model_name=child_model_name)
+ alias_id = mail_alias.create(cr, SUPERUSER_ID, alias_vals, context=alias_create_ctx)
child_class_model.write(cr, SUPERUSER_ID, obj_data['id'], {'alias_id': alias_id})
- _logger.info('Mail alias created for %s %s (uid %s)', child_model_name, obj_data[alias_key], obj_data['id'])
+ _logger.info('Mail alias created for %s %s (id %s)', child_model_name, obj_data[alias_key], obj_data['id'])
# Finally attempt to reinstate the missing constraint
try:
@@ -189,22 +213,53 @@ class mail_alias(osv.Model):
# set back the unique alias_id constraint
alias_id_column.required = True
-
return res
- def create_unique_alias(self, cr, uid, vals, model_name=None, context=None):
- """Creates an email.alias record according to the values provided in ``vals``,
- with 2 alterations: the ``alias_name`` value may be suffixed in order to
- make it unique (and certain unsafe characters replaced), and
- he ``alias_model_id`` value will set to the model ID of the ``model_name``
- value, if provided,
+ def create(self, cr, uid, vals, context=None):
+ """ Creates an email.alias record according to the values provided in ``vals``,
+ with 2 alterations: the ``alias_name`` value may be suffixed in order to
+ make it unique (and certain unsafe characters replaced), and
+ he ``alias_model_id`` value will set to the model ID of the ``model_name``
+ context value, if provided.
"""
- # when an alias name appears to already be an email, we keep the local part only
- alias_name = remove_accents(vals['alias_name']).lower().split('@')[0]
- alias_name = re.sub(r'[^\w+.]+', '-', alias_name)
- alias_name = self._find_unique(cr, uid, alias_name, context=context)
- vals['alias_name'] = alias_name
+ if context is None:
+ context = {}
+ model_name = context.get('alias_model_name')
+ parent_model_name = context.get('alias_parent_model_name')
+ if vals.get('alias_name'):
+ # when an alias name appears to already be an email, we keep the local part only
+ alias_name = remove_accents(vals['alias_name']).lower().split('@')[0]
+ alias_name = re.sub(r'[^\w+.]+', '-', alias_name)
+ alias_name = self._find_unique(cr, uid, alias_name, context=context)
+ vals['alias_name'] = alias_name
if model_name:
model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', model_name)], context=context)[0]
vals['alias_model_id'] = model_id
- return self.create(cr, uid, vals, context=context)
+ if parent_model_name:
+ model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', parent_model_name)], context=context)[0]
+ vals['alias_parent_model_id'] = model_id
+ return super(mail_alias, self).create(cr, uid, vals, context=context)
+
+ def open_document(self, cr, uid, ids, context=None):
+ alias = self.browse(cr, uid, ids, context=context)[0]
+ if not alias.alias_model_id or not alias.alias_force_thread_id:
+ return False
+ return {
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': alias.alias_model_id.model,
+ 'res_id': alias.alias_force_thread_id,
+ 'type': 'ir.actions.act_window',
+ }
+
+ def open_parent_document(self, cr, uid, ids, context=None):
+ alias = self.browse(cr, uid, ids, context=context)[0]
+ if not alias.alias_parent_model_id or not alias.alias_parent_thread_id:
+ return False
+ return {
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': alias.alias_parent_model_id.model,
+ 'res_id': alias.alias_parent_thread_id,
+ 'type': 'ir.actions.act_window',
+ }
diff --git a/addons/mail/mail_alias_view.xml b/addons/mail/mail_alias_view.xml
index e0d8e173ebc..248bf7e8f3a 100644
--- a/addons/mail/mail_alias_view.xml
+++ b/addons/mail/mail_alias_view.xml
@@ -9,13 +9,23 @@
<field name="arch" type="xml">
<form string="Alias" version="7.0">
<sheet>
- <label for="alias_name" class="oe_edit_only"/>
<h2><field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline"/></h2>
+ <div class="oe_right oe_button_box">
+ <button name="open_document" string="Open Document"
+ type="object" class="oe_link"
+ attrs="{'invisible': ['|', ('alias_model_id', '=', False), ('alias_force_thread_id', '=', 0)]}"/>
+ <button name="open_parent_document" string="Open Parent Document"
+ type="object" class="oe_link"
+ attrs="{'invisible': ['|', ('alias_parent_model_id', '=', False), ('alias_parent_thread_id', '=', 0)]}"/>
+ </div>
<group>
<field name="alias_model_id"/>
- <field name="alias_user_id"/>
<field name="alias_force_thread_id"/>
<field name="alias_defaults"/>
+ <field name="alias_contact"/>
+ <field name="alias_user_id"/>
+ <field name="alias_parent_model_id"/>
+ <field name="alias_parent_thread_id"/>
</group>
</sheet>
</form>
@@ -32,6 +42,7 @@
<field name="alias_model_id"/>
<field name="alias_user_id"/>
<field name="alias_defaults"/>
+ <field name="alias_contact"/>
</tree>
</field>
</record>
@@ -44,8 +55,13 @@
<search string="Search Alias">
<field name="alias_name"/>
<field name="alias_model_id"/>
+ <field name="alias_force_thread_id"/>
+ <field name="alias_parent_model_id"/>
+ <field name="alias_parent_thread_id"/>
+ <separator/>
+ <filter string="Active" name="active" domain="[('alias_name', '!=', False)]"/>
<group expand="0" string="Group By...">
- <filter string="User" name="User" icon="terp-personal" context="{'group_by':'alias_user_id'}"/>
+ <filter string="User" name="User" context="{'group_by':'alias_user_id'}"/>
<filter string="Model" name="Model" context="{'group_by':'alias_model_id'}"/>
</group>
</search>
@@ -55,6 +71,10 @@
<record id="action_view_mail_alias" model="ir.actions.act_window">
<field name="name">Aliases</field>
<field name="res_model">mail.alias</field>
+ <field name="context">{
+ 'search_default_active': True,
+ }
+ </field>
</record>
<menuitem id="mail_alias_menu"
diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py
index 98e88b664e6..ac350810aa6 100644
--- a/addons/mail/mail_followers.py
+++ b/addons/mail/mail_followers.py
@@ -150,11 +150,17 @@ class mail_notification(osv.Model):
return footer
- def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None):
+ def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None,
+ force_send=False, user_signature=True):
""" Send by email the notification depending on the user preferences
:param list partners_to_notify: optional list of partner ids restricting
the notifications to process
+ :param bool force_send: if True, the generated mail.mail is
+ immediately sent after being created, as if the scheduler
+ was executed for this message only.
+ :param bool user_signature: if True, the generated mail.mail body is
+ the body of the related mail.message with the author's signature
"""
if context is None:
context = {}
@@ -189,8 +195,9 @@ class mail_notification(osv.Model):
# add signature
body_html = msg.body
user_id = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0] and msg.author_id.user_ids[0].id or None
- signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
- body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
+ if user_signature:
+ signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
+ body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
references = False
if msg.parent_id:
@@ -203,13 +210,9 @@ class mail_notification(osv.Model):
'recipient_ids': [(4, id) for id in notify_partner_ids],
'references': references,
}
- if msg.email_from:
- mail_values['email_from'] = msg.email_from
- if msg.reply_to:
- mail_values['reply_to'] = msg.reply_to
mail_mail = self.pool.get('mail.mail')
email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
- try:
- return mail_mail.send(cr, uid, [email_notif_id], context=context)
- except Exception:
- return False
+
+ if force_send:
+ mail_mail.send(cr, uid, [email_notif_id], context=context)
+ return True
diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml
index a9d1d80240b..515be85132a 100644
--- a/addons/mail/mail_followers_view.xml
+++ b/addons/mail/mail_followers_view.xml
@@ -66,11 +66,11 @@
<!-- Add followers related menu entries in Settings/Email -->
<menuitem name="Followers" id="menu_email_followers" parent="base.menu_email"
- action="action_view_followers" sequence="30" groups="base.group_no_one"/> -->
+ action="action_view_followers" sequence="30" groups="base.group_no_one"/>
<!-- Add notifications related menu entry in Settings/Email -->
<menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email"
- action="action_view_notifications" sequence="35" groups="base.group_no_one"/>
+ action="action_view_notifications" sequence="13" groups="base.group_no_one"/>
</data>
</openerp>
diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py
index 977608f487e..9e363065481 100644
--- a/addons/mail/mail_group.py
+++ b/addons/mail/mail_group.py
@@ -93,17 +93,16 @@ class mail_group(osv.Model):
'public': 'groups',
'group_public_id': _get_default_employee_group,
'image': _get_default_image,
- 'alias_domain': False, # always hide alias during creation
}
def _generate_header_description(self, cr, uid, group, context=None):
header = ''
if group.description:
header = '%s' % group.description
- if group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain:
+ if group.alias_id and group.alias_name and group.alias_domain:
if header:
header = '%s<br/>' % header
- return '%sGroup email gateway: %s@%s' % (header, group.alias_id.alias_name, group.alias_id.alias_domain)
+ return '%sGroup email gateway: %s@%s' % (header, group.alias_name, group.alias_domain)
return header
def _subscribe_users(self, cr, uid, ids, context=None):
@@ -114,15 +113,8 @@ class mail_group(osv.Model):
self.message_subscribe(cr, uid, ids, partner_ids, context=context)
def create(self, cr, uid, vals, context=None):
- mail_alias = self.pool.get('mail.alias')
- if not vals.get('alias_id'):
- vals.pop('alias_name', None) # prevent errors during copy()
- alias_id = mail_alias.create_unique_alias(cr, uid,
- # Using '+' allows using subaddressing for those who don't
- # have a catchall domain setup.
- {'alias_name': "group+" + vals['name']},
- model_name=self._name, context=context)
- vals['alias_id'] = alias_id
+ if context is None:
+ context = {}
# get parent menu
menu_parent = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root')
@@ -134,8 +126,10 @@ class mail_group(osv.Model):
vals['menu_id'] = menu_id
# Create group and alias
- mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context)
- mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context)
+ create_context = dict(context, alias_model_name=self._name, alias_parent_model_name=self._name)
+ mail_group_id = super(mail_group, self).create(cr, uid, vals, context=create_context)
+ group = self.browse(cr, uid, mail_group_id, context=context)
+ self.pool.get('mail.alias').write(cr, uid, [group.alias_id.id], {"alias_force_thread_id": mail_group_id, 'alias_parent_thread_id': mail_group_id}, context)
group = self.browse(cr, uid, mail_group_id, context=context)
# Create client action for this group and link the menu to it
@@ -208,3 +202,12 @@ class mail_group(osv.Model):
""" Wrapper because message_unsubscribe_users take a user_ids=None
that receive the context without the wrapper. """
return self.message_unsubscribe_users(cr, uid, ids, context=context)
+
+ def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
+ """Show the suggestion of groups if display_groups_suggestions if the
+ user perference allows it."""
+ user = self.pool.get('res.users').browse(cr, uid, uid, context)
+ if not user.display_groups_suggestions:
+ return []
+ else:
+ return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml
index 04212b2f58a..8bf61dbdb1e 100644
--- a/addons/mail/mail_group_view.xml
+++ b/addons/mail/mail_group_view.xml
@@ -44,7 +44,7 @@
<div class="oe_group_details">
<h4><a type="open"><field name="name"/></a></h4>
<div class="oe_kanban_alias" t-if="record.alias_id.value">
- <span class="oe_e">%%</span><small><field name="alias_id"/></small>
+ <span class="oe_e oe_e_alias">%%</span><small><field name="alias_id"/></small>
</div>
<div class="oe_grey">
<field name="description"/>
@@ -78,17 +78,19 @@
<label for="name" string="Group Name"/>
</div>
<h1><field name="name" readonly="0"/></h1>
- <div name="group_alias"
+ <group colspan="2" name="group_alias"
attrs="{'invisible': [('alias_domain', '=', False)]}">
- <label for="alias_id" string="Email Alias"/>
- <field name="alias_id" class="oe_inline oe_read_only" required="0" nolabel="1"/>
- <span name="edit_alias" class="oe_edit_only">
- <field name="alias_name" class="oe_inline"
- attrs="{'required': [('alias_id', '!=', False)]}"/>
- @
- <field name="alias_domain" class="oe_inline" readonly="1"/>
- </span>
- </div>
+ <label for="alias_id" string="%%" class="oe_e oe_e_alias" style="min-width: 20px;"/>
+ <div name="alias_def">
+ <field name="alias_id" class="oe_read_only oe_inline"
+ string="Email Alias" required="0"/>
+ <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" >
+ <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
+ </div>
+ </div>
+ <label for="alias_contact" string="V" class="oe_e oe_e_alias" style="min-width: 20px;"/>
+ <field name="alias_contact" class="oe_inline" nolabel="1"/>
+ </group>
</div>
<field name="description" placeholder="Topics discussed in this group..."/>
<div class="oe_clear"/>
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py
index bd2b9c3f18a..9fa80b9be46 100644
--- a/addons/mail/mail_mail.py
+++ b/addons/mail/mail_mail.py
@@ -44,7 +44,6 @@ class mail_mail(osv.Model):
_columns = {
'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'),
- 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1),
'state': fields.selection([
('outgoing', 'Outgoing'),
('sent', 'Sent'),
@@ -55,7 +54,6 @@ class mail_mail(osv.Model):
'auto_delete': fields.boolean('Auto Delete',
help="Permanently delete this email after sending it, to save space"),
'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1),
- 'email_from': fields.char('From', help='Message sender, taken from user preferences.'),
'email_to': fields.text('To', help='Message recipients (emails)'),
'recipient_ids': fields.many2many('res.partner', string='To (Partners)'),
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
@@ -67,16 +65,13 @@ class mail_mail(osv.Model):
}
def _get_default_from(self, cr, uid, context=None):
- this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
- if this.alias_domain:
- return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain)
- elif this.email:
- return '%s <%s>' % (this.name, this.email)
- raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
+ """ Kept for compatibility
+ TDE TODO: remove me in 8.0
+ """
+ return self.pool['mail.message']._get_default_from(cr, uid, context=context)
_defaults = {
'state': 'outgoing',
- 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
}
def default_get(self, cr, uid, fields, context=None):
@@ -93,19 +88,25 @@ class mail_mail(osv.Model):
# if value specified: directly return it
if values.get('reply_to'):
return values.get('reply_to')
+ format_name = True # whether to use a 'Followers of Pigs <pigs@openerp.com' format
+ email_reply_to = None
- mailgateway = True # tells whether the answer will go through the mailgateway, leading to the formatting of reply_to <Followers of ...>
ir_config_parameter = self.pool.get("ir.config_parameter")
catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context)
- # model, res_id, email_from, reply_to: comes from values OR related message
- message = None
+ # model, res_id, email_from: comes from values OR related message
+ model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from')
if values.get('mail_message_id'):
message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context)
- model = values.get('model', message and message.model or False)
- res_id = values.get('res_id', message and message.res_id or False)
- email_from = values.get('email_from', message and message.email_from or False)
- email_reply_to = message and message.reply_to or False
+ if message.reply_to:
+ email_reply_to = message.reply_to
+ format_name = False
+ if not model:
+ model = message.model
+ if not res_id:
+ res_id = message.res_id
+ if not email_from:
+ email_from = message.email_from
# if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
if not email_reply_to and model and res_id and hasattr(self.pool[model], 'message_get_reply_to'):
@@ -115,16 +116,16 @@ class mail_mail(osv.Model):
catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context)
if catchall_domain and catchall_alias:
email_reply_to = '%s@%s' % (catchall_alias, catchall_domain)
- # no alias reply_to -> reply_to will be the email_from, only the email part
+
+ # still no reply_to -> reply_to will be the email_from
if not email_reply_to and email_from:
- emails = tools.email_split(email_from)
- if emails:
- email_reply_to = emails[0]
- if emails[0].split('@')[1] != catchall_domain:
- mailgateway = False
+ email_reply_to = email_from
# format 'Document name <email_address>'
- if email_reply_to and model and res_id and mailgateway:
+ if email_reply_to and model and res_id and format_name:
+ emails = tools.email_split(email_reply_to)
+ if emails:
+ email_reply_to = emails[0]
document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0]
if document_name:
# sanitize document name
@@ -138,10 +139,16 @@ class mail_mail(osv.Model):
# notification field: if not set, set if mail comes from an existing mail.message
if 'notification' not in values and values.get('mail_message_id'):
values['notification'] = True
+ mail_id = super(mail_mail, self).create(cr, uid, values, context=context)
+
# reply_to: if not set, set with default values that require creation values
+ # but delegate after creation because of mail_message.message_id automatic
+ # creation using existence of reply_to
if not values.get('reply_to'):
- values['reply_to'] = self._get_reply_to(cr, uid, values, context=context)
- return super(mail_mail, self).create(cr, uid, values, context=context)
+ reply_to = self._get_reply_to(cr, uid, values, context=context)
+ if reply_to:
+ self.write(cr, uid, [mail_id], {'reply_to': reply_to}, context=context)
+ return mail_id
def unlink(self, cr, uid, ids, context=None):
# cascade-delete the parent message for all mails that are not created for a notification
@@ -174,7 +181,7 @@ class mail_mail(osv.Model):
if context is None:
context = {}
if not ids:
- filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')]
+ filters = [('state', '=', 'outgoing')]
if 'filters' in context:
filters.extend(context['filters'])
ids = self.search(cr, uid, filters, context=context)
@@ -320,6 +327,7 @@ class mail_mail(osv.Model):
email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context))
# build an RFC2822 email.message.Message object and send it without queuing
+ res = None
for email in email_list:
msg = ir_mail_server.build_email(
email_from = mail.email_from,
diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml
index 7384cc24a75..f3728d4653a 100644
--- a/addons/mail/mail_mail_view.xml
+++ b/addons/mail/mail_mail_view.xml
@@ -115,7 +115,7 @@
<field name="res_model">mail.mail</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
- <field name="context">{'search_default_outgoing': 1, 'search_default_type_email': 1}</field>
+ <field name="context">{'search_default_outgoing': 1}</field>
<field name="search_view_id" ref="view_mail_search"/>
</record>
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 252179614c6..f90baa5a320 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -191,11 +191,20 @@ class mail_message(osv.Model):
'vote_user_ids': fields.many2many('res.users', 'mail_vote',
'message_id', 'user_id', string='Votes',
help='Users that voted for this message'),
+ 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1),
}
def _needaction_domain_get(self, cr, uid, context=None):
return [('to_read', '=', True)]
+ def _get_default_from(self, cr, uid, context=None):
+ this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
+ if this.alias_domain:
+ return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain)
+ elif this.email:
+ return '%s <%s>' % (this.name, this.email)
+ raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
+
def _get_default_author(self, cr, uid, context=None):
return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
@@ -204,7 +213,7 @@ class mail_message(osv.Model):
'date': lambda *a: fields.datetime.now(),
'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx),
'body': '',
- 'email_from': lambda self, cr, uid, ctx=None: self.pool.get('mail.mail')._get_default_from(cr, uid, ctx),
+ 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
}
#------------------------------------------------------
@@ -329,8 +338,10 @@ class mail_message(osv.Model):
for key, message in message_tree.iteritems():
if message.author_id:
partner_ids |= set([message.author_id.id])
- if message.notified_partner_ids:
+ if message.subtype_id and message.notified_partner_ids: # take notified people of message with a subtype
partner_ids |= set([partner.id for partner in message.notified_partner_ids])
+ elif not message.subtype_id and message.partner_ids: # take specified people of message without a subtype (log)
+ partner_ids |= set([partner.id for partner in message.partner_ids])
if message.attachment_ids:
attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
# Read partners as SUPERUSER -> display the names like classic m2o even if no access
@@ -350,9 +361,12 @@ class mail_message(osv.Model):
else:
author = (0, message.email_from)
partner_ids = []
- for partner in message.notified_partner_ids:
- if partner.id in partner_tree:
- partner_ids.append(partner_tree[partner.id])
+ if message.subtype_id:
+ partner_ids = [partner_tree[partner.id] for partner in message.notified_partner_ids
+ if partner.id in partner_tree]
+ else:
+ partner_ids = [partner_tree[partner.id] for partner in message.partner_ids
+ if partner.id in partner_tree]
attachment_ids = []
for attachment in message.attachment_ids:
if attachment.id in attachments_tree:
@@ -381,15 +395,21 @@ class mail_message(osv.Model):
has_voted = uid in [user.id for user in message.vote_user_ids]
try:
- body_html = html_email_clean(message.body)
+ if parent_id:
+ max_length = 300
+ else:
+ max_length = 100
+ body_short = html_email_clean(message.body, remove=False, shorten=True, max_length=max_length)
+
except Exception:
- body_html = '<p><b>Encoding Error : </b><br/>Unable to convert this message (id: %s).</p>' % message.id
+ body_short = '<p><b>Encoding Error : </b><br/>Unable to convert this message (id: %s).</p>' % message.id
_logger.exception(Exception)
return {'id': message.id,
'type': message.type,
'subtype': message.subtype_id.name if message.subtype_id else False,
- 'body': body_html,
+ 'body': message.body,
+ 'body_short': body_short,
'model': message.model,
'res_id': message.res_id,
'record_name': message.record_name,
@@ -766,7 +786,9 @@ class mail_message(osv.Model):
elif not values.get('message_id'):
values['message_id'] = tools.generate_tracking_message_id('private')
newid = super(mail_message, self).create(cr, uid, values, context)
- self._notify(cr, uid, newid, context=context)
+ self._notify(cr, uid, newid, context=context,
+ force_send=context.get('mail_notify_force_send', True),
+ user_signature=context.get('mail_notify_user_signature', True))
# TDE FIXME: handle default_starred. Why not setting an inv on starred ?
# Because starred will call set_message_starred, that looks for notifications.
# When creating a new mail_message, it will create a notification to a message
@@ -880,20 +902,16 @@ class mail_message(osv.Model):
return ''
return result
- def _notify(self, cr, uid, newid, context=None):
+ def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True):
""" Add the related record followers to the destination partner_ids if is not a private message.
Call mail_notification.notify to manage the email sending
"""
notification_obj = self.pool.get('mail.notification')
message = self.browse(cr, uid, newid, context=context)
-
partners_to_notify = set([])
- # message has no subtype_id: pure log message -> no partners, no one notified
- if not message.subtype_id:
- return True
- # all followers of the mail.message document have to be added as partners and notified
- if message.model and message.res_id:
+ # all followers of the mail.message document have to be added as partners and notified if a subtype is defined (otherwise: log message)
+ if message.subtype_id and message.model and message.res_id:
fol_obj = self.pool.get("mail.followers")
# browse as SUPERUSER because rules could restrict the search results
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
@@ -903,7 +921,7 @@ class mail_message(osv.Model):
], context=context)
partners_to_notify |= set(fo.partner_id for fo in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context))
# remove me from notified partners, unless the message is written on my own wall
- if message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id:
+ if message.subtype_id and message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id:
partners_to_notify |= set([message.author_id])
elif message.author_id:
partners_to_notify -= set([message.author_id])
@@ -914,7 +932,8 @@ class mail_message(osv.Model):
# notify
if partners_to_notify:
- notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context)
+ notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context,
+ force_send=force_send, user_signature=user_signature)
message.refresh()
# An error appear when a user receive a notification without notifying
diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml
index 02f4ef7fdbe..e4442e366e3 100644
--- a/addons/mail/mail_message_view.xml
+++ b/addons/mail/mail_message_view.xml
@@ -30,9 +30,9 @@
<field name="subject"/>
<field name="author_id"/>
<field name="email_from"/>
+ <field name="reply_to"/>
<field name="date"/>
<field name="type"/>
- <field name="subtype_id"/>
</group>
<group>
<field name="model"/>
@@ -40,6 +40,7 @@
<field name="parent_id"/>
<field name="partner_ids" widget="many2many_tags"/>
<field name="notified_partner_ids" widget="many2many_tags"/>
+ <field name="subtype_id"/>
</group>
</group>
<field name="body"/>
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index a46f416f44b..94e9126f020 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -25,7 +25,6 @@ import dateutil
import email
import logging
import pytz
-import re
import time
import xmlrpclib
from email.message import Message
@@ -71,6 +70,7 @@ class mail_thread(osv.AbstractModel):
_name = 'mail.thread'
_description = 'Email Thread'
_mail_flat_thread = True
+ _mail_post_access = 'write'
# Automatic logging system if mail installed
# _track = {
@@ -102,21 +102,22 @@ class mail_thread(osv.AbstractModel):
if catchall_domain and model and res_id: # specific res_id -> find its alias (i.e. section_id specified)
object_id = self.pool.get(model).browse(cr, uid, res_id, context=context)
# check that the alias effectively creates new records
- if object_id.alias_id and object_id.alias_id.alias_model_id and \
+ if object_id.alias_id and object_id.alias_id.alias_name and \
+ object_id.alias_id.alias_model_id and \
object_id.alias_id.alias_model_id.model == self._name and \
object_id.alias_id.alias_force_thread_id == 0:
alias = object_id.alias_id
elif catchall_domain and model: # no specific res_id given -> generic help message, take an example alias (i.e. alias of some section_id)
model_id = self.pool.get('ir.model').search(cr, uid, [("model", "=", self._name)], context=context)[0]
alias_obj = self.pool.get('mail.alias')
- alias_ids = alias_obj.search(cr, uid, [("alias_model_id", "=", model_id), ('alias_force_thread_id', '=', 0)], context=context, order='id ASC')
+ alias_ids = alias_obj.search(cr, uid, [("alias_model_id", "=", model_id), ("alias_name", "!=", False), ('alias_force_thread_id', '=', 0)], context=context, order='id ASC')
if alias_ids and len(alias_ids) == 1: # if several aliases -> incoherent to propose one guessed from nowhere, therefore avoid if several aliases
alias = alias_obj.browse(cr, uid, alias_ids[0], context=context)
if alias:
alias_email = alias.name_get()[0][1]
return _("""<p class='oe_view_nocontent_create'>
- Click here to add a new %(document)s or send an email to: <a href='mailto:%(email)s'>%(email)s</a>
+ Click here to add new %(document)s or send an email to: <a href='mailto:%(email)s'>%(email)s</a>
</p>
%(static_help)s"""
) % {
@@ -126,7 +127,7 @@ class mail_thread(osv.AbstractModel):
}
if document_name != 'document' and help and help.find("oe_view_nocontent_create") == -1:
- return _("<p class='oe_view_nocontent_create'>Click here to add a new %(document)s</p>%(static_help)s") % {
+ return _("<p class='oe_view_nocontent_create'>Click here to add new %(document)s</p>%(static_help)s") % {
'document': document_name,
'static_help': help or '',
}
@@ -156,12 +157,26 @@ class mail_thread(osv.AbstractModel):
res[id]['message_summary'] = "<span class='oe_kanban_mail_new' title='%s'><span class='oe_e'>9</span> %d %s</span>" % (title, res[id].pop('message_unread_count'), _("New"))
return res
- def _get_subscription_data(self, cr, uid, ids, name, args, context=None):
+ def read_followers_data(self, cr, uid, follower_ids, context=None):
+ result = []
+ technical_group = self.pool.get('ir.model.data').get_object(cr, uid, 'base', 'group_no_one')
+ for follower in self.pool.get('res.partner').browse(cr, uid, follower_ids, context=context):
+ is_editable = uid in map(lambda x: x.id, technical_group.users)
+ is_uid = uid in map(lambda x: x.id, follower.user_ids)
+ data = (follower.id,
+ follower.name,
+ {'is_editable': is_editable, 'is_uid': is_uid},
+ )
+ result.append(data)
+ return result
+
+ def _get_subscription_data(self, cr, uid, ids, name, args, user_pid=None, context=None):
""" Computes:
- message_subtype_data: data about document subtypes: which are
available, which are followed if any """
res = dict((id, dict(message_subtype_data='')) for id in ids)
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ if user_pid is None:
+ user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
# find current model subtypes, add them to a dictionary
subtype_obj = self.pool.get('mail.message.subtype')
@@ -240,21 +255,41 @@ class mail_thread(osv.AbstractModel):
fol_obj.create(cr, SUPERUSER_ID, {'res_model': self._name, 'res_id': id, 'partner_id': partner_id})
def _search_followers(self, cr, uid, obj, name, args, context):
+ """Search function for message_follower_ids
+
+ Do not use with operator 'not in'. Use instead message_is_followers
+ """
fol_obj = self.pool.get('mail.followers')
res = []
for field, operator, value in args:
assert field == name
+ # TOFIX make it work with not in
+ assert operator != "not in", "Do not search message_follower_ids with 'not in'"
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('partner_id', operator, value)])
res_ids = [fol.res_id for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids)]
res.append(('id', 'in', res_ids))
return res
+ def _search_is_follower(self, cr, uid, obj, name, args, context):
+ """Search function for message_is_follower"""
+ res = []
+ for field, operator, value in args:
+ assert field == name
+ partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
+ if (operator == '=' and value) or (operator == '!=' and not value): # is a follower
+ res_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
+ else: # is not a follower or unknown domain
+ mail_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
+ res_ids = self.search(cr, uid, [('id', 'not in', mail_ids)], context=context)
+ res.append(('id', 'in', res_ids))
+ return res
+
_columns = {
- 'message_is_follower': fields.function(_get_followers,
- type='boolean', string='Is a Follower', multi='_get_followers,'),
+ 'message_is_follower': fields.function(_get_followers, type='boolean',
+ fnct_search=_search_is_follower, string='Is a Follower', multi='_get_followers,'),
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
- fnct_search=_search_followers, type='many2many',
- obj='res.partner', string='Followers', multi='_get_followers'),
+ fnct_search=_search_followers, type='many2many',
+ obj='res.partner', string='Followers', multi='_get_followers'),
'message_ids': fields.one2many('mail.message', 'res_id',
domain=lambda self: [('model', '=', self._name)],
auto_join=True,
@@ -292,7 +327,12 @@ class mail_thread(osv.AbstractModel):
# subscribe uid unless asked not to
if not context.get('mail_create_nosubscribe'):
self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context)
- self.message_auto_subscribe(cr, uid, [thread_id], values.keys(), context=context)
+ # auto_subscribe: take values and defaults into account
+ create_values = set(values.keys())
+ for key, val in context.iteritems():
+ if key.startswith('default_'):
+ create_values.add(key[8:])
+ self.message_auto_subscribe(cr, uid, [thread_id], list(create_values), context=context)
# track values
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
@@ -308,8 +348,8 @@ class mail_thread(osv.AbstractModel):
# Track initial values of tracked fields
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
if tracked_fields:
- initial = self.read(cr, uid, ids, tracked_fields.keys(), context=context)
- initial_values = dict((item['id'], item) for item in initial)
+ records = self.browse(cr, uid, ids, context=context)
+ initial_values = dict((this.id, dict((key, getattr(this, key)) for key in tracked_fields.keys())) for this in records)
# Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
@@ -368,7 +408,7 @@ class mail_thread(osv.AbstractModel):
if not value:
return ''
if col_info['type'] == 'many2one':
- return value[1]
+ return value.name_get()[0][1]
if col_info['type'] == 'selection':
return dict(col_info['selection'])[value]
return value
@@ -387,23 +427,26 @@ class mail_thread(osv.AbstractModel):
if not tracked_fields:
return True
- for record in self.read(cr, uid, ids, tracked_fields.keys(), context=context):
- initial = initial_values[record['id']]
- changes = []
+ for browse_record in self.browse(cr, uid, ids, context=context):
+ initial = initial_values[browse_record.id]
+ changes = set()
tracked_values = {}
# generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}}
for col_name, col_info in tracked_fields.items():
- if record[col_name] == initial[col_name] and getattr(self._all_columns[col_name].column, 'track_visibility', None) == 'always':
+ initial_value = initial[col_name]
+ record_value = getattr(browse_record, col_name)
+
+ if record_value == initial_value and getattr(self._all_columns[col_name].column, 'track_visibility', None) == 'always':
tracked_values[col_name] = dict(col_info=col_info['string'],
- new_value=convert_for_display(record[col_name], col_info))
- elif record[col_name] != initial[col_name]:
+ new_value=convert_for_display(record_value, col_info))
+ elif record_value != initial_value and (record_value or initial_value): # because browse null != False
if getattr(self._all_columns[col_name].column, 'track_visibility', None) in ['always', 'onchange']:
tracked_values[col_name] = dict(col_info=col_info['string'],
- old_value=convert_for_display(initial[col_name], col_info),
- new_value=convert_for_display(record[col_name], col_info))
+ old_value=convert_for_display(initial_value, col_info),
+ new_value=convert_for_display(record_value, col_info))
if col_name in tracked_fields:
- changes.append(col_name)
+ changes.add(col_name)
if not changes:
continue
@@ -413,7 +456,7 @@ class mail_thread(osv.AbstractModel):
if field not in changes:
continue
for subtype, method in track_info.items():
- if method(self, cr, uid, record, context):
+ if method(self, cr, uid, browse_record, context):
subtypes.append(subtype)
posted = False
@@ -424,11 +467,11 @@ class mail_thread(osv.AbstractModel):
_logger.debug('subtype %s not found, giving error "%s"' % (subtype, e))
continue
message = format_message(subtype_rec.description if subtype_rec.description else subtype_rec.name, tracked_values)
- self.message_post(cr, uid, record['id'], body=message, subtype=subtype, context=context)
+ self.message_post(cr, uid, browse_record.id, body=message, subtype=subtype, context=context)
posted = True
if not posted:
message = format_message('', tracked_values)
- self.message_post(cr, uid, record['id'], body=message, context=context)
+ self.message_post(cr, uid, browse_record.id, body=message, context=context)
return True
#------------------------------------------------------
@@ -467,12 +510,22 @@ class mail_thread(osv.AbstractModel):
access rule on the document, for portal document such as issues. """
if not model_obj:
model_obj = self
- if operation in ['create', 'write', 'unlink']:
- model_obj.check_access_rights(cr, uid, 'write')
- model_obj.check_access_rule(cr, uid, mids, 'write', context=context)
+ if hasattr(self, '_mail_post_access'):
+ create_allow = self._mail_post_access
+ else:
+ create_allow = 'write'
+
+ if operation in ['write', 'unlink']:
+ check_operation = 'write'
+ elif operation == 'create' and create_allow in ['create', 'read', 'write', 'unlink']:
+ check_operation = create_allow
+ elif operation == 'create':
+ check_operation = 'write'
else:
- model_obj.check_access_rights(cr, uid, operation)
- model_obj.check_access_rule(cr, uid, mids, operation, context=context)
+ check_operation = operation
+
+ model_obj.check_access_rights(cr, uid, check_operation)
+ model_obj.check_access_rule(cr, uid, mids, check_operation, context=context)
def _get_formview_action(self, cr, uid, id, model=None, context=None):
""" Return an action to open the document. This method is meant to be
@@ -543,6 +596,8 @@ class mail_thread(osv.AbstractModel):
#------------------------------------------------------
def message_get_reply_to(self, cr, uid, ids, context=None):
+ """ Returns the preferred reply-to email address that is basically
+ the alias of the document, if it exists. """
if not self._inherits.get('mail.alias'):
return [False for id in ids]
return ["%s@%s" % (record['alias_name'], record['alias_domain'])
@@ -559,34 +614,136 @@ class mail_thread(osv.AbstractModel):
ret_dict = {}
for model_name in self.pool.obj_list():
model = self.pool[model_name]
- if 'mail.thread' in getattr(model, '_inherit', []):
+ if hasattr(model, "message_process") and hasattr(model, "message_post"):
ret_dict[model_name] = model._description
return ret_dict
def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None):
""" Find partners related to some header fields of the message.
- TDE TODO: merge me with other partner finding methods in 8.0 """
- partner_obj = self.pool.get('res.partner')
- partner_ids = []
+ :param string message: an email.message instance """
s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)])
- for email_address in tools.email_split(s):
- related_partners = partner_obj.search(cr, uid, [('email', 'ilike', email_address), ('user_ids', '!=', False)], limit=1, context=context)
- if not related_partners:
- related_partners = partner_obj.search(cr, uid, [('email', 'ilike', email_address)], limit=1, context=context)
- partner_ids += related_partners
- return partner_ids
+ return filter(lambda x: x, self._find_partner_from_emails(cr, uid, None, tools.email_split(s), context=context))
+
+ def message_route_verify(self, cr, uid, message, message_dict, route, update_author=True, assert_model=True, create_fallback=True, context=None):
+ """ Verify route validity. Check and rules:
+ 1 - if thread_id -> check that document effectively exists; otherwise
+ fallback on a message_new by resetting thread_id
+ 2 - check that message_update exists if thread_id is set; or at least
+ that message_new exist
+ [ - find author_id if udpate_author is set]
+ 3 - if there is an alias, check alias_contact:
+ 'followers' and thread_id:
+ check on target document that the author is in the followers
+ 'followers' and alias_parent_thread_id:
+ check on alias parent document that the author is in the
+ followers
+ 'partners': check that author_id id set
+ """
- def _message_find_user_id(self, cr, uid, message, context=None):
- """ TDE TODO: check and maybe merge me with other user finding methods in 8.0 """
- from_local_part = tools.email_split(decode(message.get('From')))[0]
- # FP Note: canonification required, the minimu: .lower()
- user_ids = self.pool.get('res.users').search(cr, uid, ['|',
- ('login', '=', from_local_part),
- ('email', '=', from_local_part)], context=context)
- return user_ids[0] if user_ids else uid
+ assert isinstance(route, (list, tuple)), 'A route should be a list or a tuple'
+ assert len(route) == 5, 'A route should contain 5 elements: model, thread_id, custom_values, uid, alias record'
+
+ message_id = message.get('Message-Id')
+ email_from = decode_header(message, 'From')
+ author_id = message_dict.get('author_id')
+ model, thread_id, alias = route[0], route[1], route[4]
+ model_pool = None
+
+ def _create_bounce_email():
+ mail_mail = self.pool.get('mail.mail')
+ mail_id = mail_mail.create(cr, uid, {
+ 'body_html': '<div><p>Hello,</p>'
+ '<p>The following email sent to %s cannot be accepted because this is '
+ 'a private email address. Only allowed people can contact us at this address.</p></div>'
+ '<blockquote>%s</blockquote>' % (message.get('to'), message_dict.get('body')),
+ 'subject': 'Re: %s' % message.get('subject'),
+ 'email_to': message.get('from'),
+ 'auto_delete': True,
+ }, context=context)
+ mail_mail.send(cr, uid, [mail_id], context=context)
+
+ def _warn(message):
+ _logger.warning('Routing mail with Message-Id %s: route %s: %s',
+ message_id, route, message)
+
+ # Wrong model
+ if model and not model in self.pool:
+ if assert_model:
+ assert model in self.pool, 'Routing: unknown target model %s' % model
+ _warn('unknown target model %s' % model)
+ return ()
+ elif model:
+ model_pool = self.pool[model]
+
+ # Private message: should not contain any thread_id
+ if not model and thread_id:
+ if assert_model:
+ assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message).'
+ _warn('posting a message without model should be with a null res_id (private message), resetting thread_id')
+ thread_id = 0
+ # Private message: should have a parent_id (only answers)
+ if not model and not message_dict.get('parent_id'):
+ if assert_model:
+ assert message_dict.get('parent_id'), 'Routing: posting a message without model should be with a parent_id (private mesage).'
+ _warn('posting a message without model should be with a parent_id (private mesage), skipping')
+ return ()
+
+ # Existing Document: check if exists; if not, fallback on create if allowed
+ if thread_id and not model_pool.exists(cr, uid, thread_id):
+ if create_fallback:
+ _warn('reply to missing document (%s,%s), fall back on new document creation' % (model, thread_id))
+ thread_id = None
+ elif assert_model:
+ assert model_pool.exists(cr, uid, thread_id), 'Routing: reply to missing document (%s,%s)' % (model, thread_id)
+ else:
+ _warn('reply to missing document (%s,%s), skipping' % (model, thread_id))
+ return ()
+
+ # Existing Document: check model accepts the mailgateway
+ if thread_id and model and not hasattr(model_pool, 'message_update'):
+ if create_fallback:
+ _warn('model %s does not accept document update, fall back on document creation' % model)
+ thread_id = None
+ elif assert_model:
+ assert hasattr(model_pool, 'message_update'), 'Routing: model %s does not accept document update, crashing' % model
+ else:
+ _warn('model %s does not accept document update, skipping' % model)
+ return ()
+
+ # New Document: check model accepts the mailgateway
+ if not thread_id and model and not hasattr(model_pool, 'message_new'):
+ if assert_model:
+ assert hasattr(model_pool, 'message_new'), 'Model %s does not accept document creation, crashing' % model
+ _warn('model %s does not accept document creation, skipping' % model)
+ return ()
+
+ # Update message author if asked
+ # We do it now because we need it for aliases (contact settings)
+ if not author_id and update_author:
+ author_ids = self._find_partner_from_emails(cr, uid, thread_id, [email_from], model=model, context=context)
+ if author_ids:
+ author_id = author_ids[0]
+ message_dict['author_id'] = author_id
- def message_route(self, cr, uid, message, model=None, thread_id=None,
+ # Alias: check alias_contact settings
+ if alias and alias.alias_contact == 'followers' and (thread_id or alias.alias_parent_thread_id):
+ if thread_id:
+ obj = self.pool[model].browse(cr, uid, thread_id, context=context)
+ else:
+ obj = self.pool[alias.alias_parent_model_id.model].browse(cr, uid, alias.alias_parent_thread_id, context=context)
+ if not author_id or not author_id in [fol.id for fol in obj.message_follower_ids]:
+ _warn('alias %s restricted to internal followers, skipping' % alias.alias_name)
+ _create_bounce_email()
+ return ()
+ elif alias and alias.alias_contact == 'partners' and not author_id:
+ _warn('alias %s does not accept unknown author, skipping' % alias.alias_name)
+ _create_bounce_email()
+ return ()
+
+ return (model, thread_id, route[2], route[3], route[4])
+
+ def message_route(self, cr, uid, message, message_dict, model=None, thread_id=None,
custom_values=None, context=None):
"""Attempt to figure out the correct target model, thread_id,
custom_values and user_id to use for an incoming message.
@@ -606,6 +763,7 @@ class mail_thread(osv.AbstractModel):
4. If all the above fails, raise an exception.
:param string message: an email.message instance
+ :param dict message_dict: dictionary holding message variables
:param string model: the fallback model to use if the message
does not match any of the currently configured mail aliases
(may be None if a matching alias is supposed to be present)
@@ -616,9 +774,12 @@ class mail_thread(osv.AbstractModel):
:param int thread_id: optional ID of the record/thread from ``model``
to which this mail should be attached. Only used if the message
does not reply to an existing thread and does not match any mail alias.
- :return: list of [model, thread_id, custom_values, user_id]
+ :return: list of [model, thread_id, custom_values, user_id, alias]
"""
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
+ fallback_model = model
+
+ # Get email.message.Message variables for future processing
message_id = message.get('Message-Id')
email_from = decode_header(message, 'From')
email_to = decode_header(message, 'To')
@@ -628,30 +789,35 @@ class mail_thread(osv.AbstractModel):
# 1. Verify if this is a reply to an existing thread
thread_references = references or in_reply_to
ref_match = thread_references and tools.reference_re.search(thread_references)
-
if ref_match:
thread_id = int(ref_match.group(1))
- model = ref_match.group(2) or model
+ model = ref_match.group(2) or fallback_model
if thread_id and model in self.pool:
model_obj = self.pool[model]
if model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'):
_logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s',
email_from, email_to, message_id, model, thread_id, custom_values, uid)
- return [(model, thread_id, custom_values, uid)]
+ route = self.message_route_verify(cr, uid, message, message_dict,
+ (model, thread_id, custom_values, uid, None),
+ update_author=True, assert_model=True, create_fallback=True, context=context)
+ return route and [route] or []
- # Verify whether this is a reply to a private message
+ # 2. Reply to a private message
if in_reply_to:
- message_ids = self.pool.get('mail.message').search(cr, uid, [
+ mail_message_ids = self.pool.get('mail.message').search(cr, uid, [
('message_id', '=', in_reply_to),
'!', ('message_id', 'ilike', 'reply_to')
], limit=1, context=context)
- if message_ids:
- message = self.pool.get('mail.message').browse(cr, uid, message_ids[0], context=context)
+ if mail_message_ids:
+ mail_message = self.pool.get('mail.message').browse(cr, uid, mail_message_ids[0], context=context)
_logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
- email_from, email_to, message_id, message.id, custom_values, uid)
- return [(message.model, message.res_id, custom_values, uid)]
+ email_from, email_to, message_id, mail_message.id, custom_values, uid)
+ route = self.message_route_verify(cr, uid, message, message_dict,
+ (mail_message.model, mail_message.res_id, custom_values, uid, None),
+ update_author=True, assert_model=True, create_fallback=True, context=context)
+ return route and [route] or []
- # 2. Look for a matching mail.alias entry
+ # 3. Look for a matching mail.alias entry
# Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values
# for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value.
rcpt_tos = \
@@ -676,14 +842,16 @@ class mail_thread(osv.AbstractModel):
# user_id = self._message_find_user_id(cr, uid, message, context=context)
user_id = uid
_logger.info('No matching user_id for the alias %s', alias.alias_name)
- routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, \
- eval(alias.alias_defaults), user_id))
- _logger.info('Routing mail from %s to %s with Message-Id %s: direct alias match: %r',
- email_from, email_to, message_id, routes)
+ route = (alias.alias_model_id.model, alias.alias_force_thread_id, eval(alias.alias_defaults), user_id, alias)
+ _logger.info('Routing mail from %s to %s with Message-Id %s: direct alias match: %r',
+ email_from, email_to, message_id, route)
+ route = self.message_route_verify(cr, uid, message, message_dict, route,
+ update_author=True, assert_model=True, create_fallback=True, context=context)
+ if route:
+ routes.append(route)
return routes
- # 3. Fallback to the provided parameters, if they work
- model_pool = self.pool.get(model)
+ # 4. Fallback to the provided parameters, if they work
if not thread_id:
# Legacy: fallback to matching [ID] in the Subject
match = tools.res_re.search(decode_header(message, 'Subject'))
@@ -693,16 +861,18 @@ class mail_thread(osv.AbstractModel):
thread_id = int(thread_id)
except:
thread_id = False
- assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
+ _logger.info('Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s',
+ email_from, email_to, message_id, fallback_model, thread_id, custom_values, uid)
+ route = self.message_route_verify(cr, uid, message, message_dict,
+ (fallback_model, thread_id, custom_values, uid, None),
+ update_author=True, assert_model=True, context=context)
+ if route:
+ return [route]
+
+ # AssertionError if no routes found and if no bounce occured
+ assert False, \
"No possible route found for incoming message from %s to %s (Message-Id %s:)." \
"Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
- if thread_id and not model_pool.exists(cr, uid, thread_id):
- _logger.warning('Received mail reply to missing document %s! Ignoring and creating new document instead for Message-Id %s',
- thread_id, message_id)
- thread_id = None
- _logger.info('Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s',
- email_from, email_to, message_id, model, thread_id, custom_values, uid)
- return [(model, thread_id, custom_values, uid)]
def message_process(self, cr, uid, model, message, custom_values=None,
save_original=False, strip_attachments=False,
@@ -756,25 +926,21 @@ class mail_thread(osv.AbstractModel):
msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context)
if strip_attachments:
msg.pop('attachments', None)
+ # postpone setting msg.partner_ids after message_post, to avoid double notifications
+ partner_ids = msg.pop('partner_ids', [])
if msg.get('message_id'): # should always be True as message_parse generate one if missing
existing_msg_ids = self.pool.get('mail.message').search(cr, SUPERUSER_ID, [
('message_id', '=', msg.get('message_id')),
], context=context)
if existing_msg_ids:
- _logger.info('Ignored mail from %s to %s with Message-Id %s:: found duplicated Message-Id during processing',
+ _logger.info('Ignored mail from %s to %s with Message-Id %s: found duplicated Message-Id during processing',
msg.get('from'), msg.get('to'), msg.get('message_id'))
return False
# find possible routes for the message
- routes = self.message_route(cr, uid, msg_txt, model,
- thread_id, custom_values,
- context=context)
-
- # postpone setting msg.partner_ids after message_post, to avoid double notifications
- partner_ids = msg.pop('partner_ids', [])
-
+ routes = self.message_route(cr, uid, msg_txt, msg, model, thread_id, custom_values, context=context)
thread_id = False
- for model, thread_id, custom_values, user_id in routes:
+ for model, thread_id, custom_values, user_id, alias in routes:
if self._name == 'mail.thread':
context.update({'thread_model': model})
if model:
@@ -785,15 +951,17 @@ class mail_thread(osv.AbstractModel):
# disabled subscriptions during message_new/update to avoid having the system user running the
# email gateway become a follower of all inbound messages
- nosub_ctx = dict(context, mail_create_nosubscribe=True)
+ nosub_ctx = dict(context, mail_create_nosubscribe=True, mail_create_nolog=True)
if thread_id and hasattr(model_pool, 'message_update'):
model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
else:
- nosub_ctx = dict(nosub_ctx, mail_create_nolog=True)
thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
else:
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
model_pool = self.pool.get('mail.thread')
+ if not hasattr(model_pool, 'message_post'):
+ context['thread_model'] = model
+ model_pool = self.pool['mail.thread']
new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
if partner_ids:
@@ -923,7 +1091,6 @@ class mail_thread(osv.AbstractModel):
"""
msg_dict = {
'type': 'email',
- 'author_id': False,
}
if not isinstance(message, Message):
if isinstance(message, unicode):
@@ -946,12 +1113,7 @@ class mail_thread(osv.AbstractModel):
msg_dict['from'] = decode(message.get('from'))
msg_dict['to'] = decode(message.get('to'))
msg_dict['cc'] = decode(message.get('cc'))
-
- if message.get('From'):
- author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context)
- if author_ids:
- msg_dict['author_id'] = author_ids[0]
- msg_dict['email_from'] = decode(message.get('from'))
+ msg_dict['email_from'] = decode(message.get('from'))
partner_ids = self._message_find_partners(cr, uid, message, ['To', 'Cc'], context=context)
msg_dict['partner_ids'] = [(4, partner_id) for partner_id in partner_ids]
@@ -1005,7 +1167,7 @@ class mail_thread(osv.AbstractModel):
partner_id, partner_name<partner_email> or partner_name, reason """
if email and not partner:
# get partner info from email
- partner_info = self.message_get_partner_info_from_emails(cr, uid, [email], context=context, res_id=obj.id)[0]
+ partner_info = self.message_partner_info_from_emails(cr, uid, obj.id, [email], context=context)[0]
if partner_info.get('partner_id'):
partner = self.pool.get('res.partner').browse(cr, SUPERUSER_ID, [partner_info.get('partner_id')], context=context)[0]
if email and email in [val[1] for val in result[obj.id]]: # already existing email -> skip
@@ -1033,53 +1195,76 @@ class mail_thread(osv.AbstractModel):
self._message_add_suggested_recipient(cr, uid, result, obj, partner=obj.user_id.partner_id, reason=self._all_columns['user_id'].column.string, context=context)
return result
- def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None, res_id=None):
- """ Wrapper with weird order parameter because of 7.0 fix.
+ def _find_partner_from_emails(self, cr, uid, id, emails, model=None, context=None, check_followers=True):
+ """ Utility method to find partners from email addresses. The rules are :
+ 1 - check in document (model | self, id) followers
+ 2 - try to find a matching partner that is also an user
+ 3 - try to find a matching partner
- TDE TODO: remove me in 8.0 """
- return self.message_find_partner_from_emails(cr, uid, res_id, emails, link_mail=link_mail, context=context)
+ :param list emails: list of email addresses
+ :param string model: model to fetch related record; by default self
+ is used.
+ :param boolean check_followers: check in document followers
+ """
+ partner_obj = self.pool['res.partner']
+ partner_ids = []
+ obj = None
+ if id and (model or self._name != 'mail.thread') and check_followers:
+ if model:
+ obj = self.pool[model].browse(cr, uid, id, context=context)
+ else:
+ obj = self.browse(cr, uid, id, context=context)
+ for contact in emails:
+ partner_id = False
+ email_address = tools.email_split(contact)
+ if not email_address:
+ partner_ids.append(partner_id)
+ continue
+ email_address = email_address[0]
+ # first try: check in document's followers
+ if obj:
+ for follower in obj.message_follower_ids:
+ if follower.email == email_address:
+ partner_id = follower.id
+ # second try: check in partners that are also users
+ if not partner_id:
+ ids = partner_obj.search(cr, SUPERUSER_ID, [
+ ('email', 'ilike', email_address),
+ ('user_ids', '!=', False)
+ ], limit=1, context=context)
+ if ids:
+ partner_id = ids[0]
+ # third try: check in partners
+ if not partner_id:
+ ids = partner_obj.search(cr, SUPERUSER_ID, [
+ ('email', 'ilike', email_address)
+ ], limit=1, context=context)
+ if ids:
+ partner_id = ids[0]
+ partner_ids.append(partner_id)
+ return partner_ids
- def message_find_partner_from_emails(self, cr, uid, id, emails, link_mail=False, context=None):
+ def message_partner_info_from_emails(self, cr, uid, id, emails, link_mail=False, context=None):
""" Convert a list of emails into a list partner_ids and a list
new_partner_ids. The return value is non conventional because
it is meant to be used by the mail widget.
- :return dict: partner_ids and new_partner_ids
-
- TDE TODO: merge me with other partner finding methods in 8.0 """
+ :return dict: partner_ids and new_partner_ids """
mail_message_obj = self.pool.get('mail.message')
- partner_obj = self.pool.get('res.partner')
+ partner_ids = self._find_partner_from_emails(cr, uid, id, emails, context=context)
result = list()
- if id and self._name != 'mail.thread':
- obj = self.browse(cr, SUPERUSER_ID, id, context=context)
- else:
- obj = None
- for email in emails:
- partner_info = {'full_name': email, 'partner_id': False}
- m = re.search(r"((.+?)\s*<)?([^<>]+@[^<>]+)>?", email, re.IGNORECASE | re.DOTALL)
- if not m:
- continue
- email_address = m.group(3)
- # first try: check in document's followers
- if obj:
- for follower in obj.message_follower_ids:
- if follower.email == email_address:
- partner_info['partner_id'] = follower.id
- # second try: check in partners
- if not partner_info.get('partner_id'):
- ids = partner_obj.search(cr, SUPERUSER_ID, [('email', 'ilike', email_address), ('user_ids', '!=', False)], limit=1, context=context)
- if not ids:
- ids = partner_obj.search(cr, SUPERUSER_ID, [('email', 'ilike', email_address)], limit=1, context=context)
- if ids:
- partner_info['partner_id'] = ids[0]
+ for idx in range(len(emails)):
+ email_address = emails[idx]
+ partner_id = partner_ids[idx]
+ partner_info = {'full_name': email_address, 'partner_id': partner_id}
result.append(partner_info)
# link mail with this from mail to the new partner id
if link_mail and partner_info['partner_id']:
message_ids = mail_message_obj.search(cr, SUPERUSER_ID, [
'|',
- ('email_from', '=', email),
- ('email_from', 'ilike', '<%s>' % email),
+ ('email_from', '=', email_address),
+ ('email_from', 'ilike', '<%s>' % email_address),
('author_id', '=', False)
], context=context)
if message_ids:
@@ -1128,22 +1313,11 @@ class mail_thread(osv.AbstractModel):
model = False
if thread_id:
model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name
- if model != self._name:
+ if model != self._name and hasattr(self.pool[model], 'message_post'):
del context['thread_model']
return self.pool[model].message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs)
- # 0: Parse email-from, try to find a better author_id based on document's followers for incoming emails
- email_from = kwargs.get('email_from')
- if email_from and thread_id and type == 'email' and kwargs.get('author_id'):
- email_list = tools.email_split(email_from)
- doc = self.browse(cr, uid, thread_id, context=context)
- if email_list and doc:
- author_ids = self.pool.get('res.partner').search(cr, uid, [
- ('email', 'ilike', email_list[0]),
- ('id', 'in', [f.id for f in doc.message_follower_ids])
- ], limit=1, context=context)
- if author_ids:
- kwargs['author_id'] = author_ids[0]
+ #0: Find the message's author, because we need it for private discussion
author_id = kwargs.get('author_id')
if author_id is None: # keep False values
author_id = self.pool.get('mail.message')._get_default_author(cr, uid, context=context)
@@ -1255,27 +1429,12 @@ class mail_thread(osv.AbstractModel):
return msg_id
#------------------------------------------------------
- # Compatibility methods: do not use
- # TDE TODO: remove me in 8.0
- #------------------------------------------------------
-
- def message_create_partners_from_emails(self, cr, uid, emails, context=None):
- return {'partner_ids': [], 'new_partner_ids': []}
-
- def message_post_user_api(self, cr, uid, thread_id, body='', parent_id=False,
- attachment_ids=None, content_subtype='plaintext',
- context=None, **kwargs):
- return self.message_post(cr, uid, thread_id, body=body, parent_id=parent_id,
- attachment_ids=attachment_ids, content_subtype=content_subtype,
- context=context, **kwargs)
-
- #------------------------------------------------------
# Followers API
#------------------------------------------------------
- def message_get_subscription_data(self, cr, uid, ids, context=None):
+ def message_get_subscription_data(self, cr, uid, ids, user_pid=None, context=None):
""" Wrapper to get subtypes data. """
- return self._get_subscription_data(cr, uid, ids, None, None, context=context)
+ return self._get_subscription_data(cr, uid, ids, None, None, user_pid=user_pid, context=context)
def message_subscribe_users(self, cr, uid, ids, user_ids=None, subtype_ids=None, context=None):
""" Wrapper on message_subscribe, using users. If user_ids is not
@@ -1287,7 +1446,10 @@ class mail_thread(osv.AbstractModel):
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
""" Add partners to the records followers. """
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ mail_followers_obj = self.pool.get('mail.followers')
+ subtype_obj = self.pool.get('mail.message.subtype')
+
+ user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
if set(partner_ids) == set([user_pid]):
try:
self.check_access_rights(cr, uid, 'read')
@@ -1296,27 +1458,35 @@ class mail_thread(osv.AbstractModel):
else:
self.check_access_rights(cr, uid, 'write')
- # subscribe partners
- self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context)
-
- # subtype specified: update the subscriptions
- fol_obj = self.pool.get('mail.followers')
- if subtype_ids is not None:
- fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
- fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
- # no subtypes: default ones for new subscription, do not update existing subscriptions
- else:
- # search new subscriptions: subtype_ids is False
- fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', self._name),
- ('res_id', 'in', ids),
- ('partner_id', 'in', partner_ids),
- ('subtype_ids', '=', False)
- ], context=context)
- if fol_ids:
- subtype_obj = self.pool.get('mail.message.subtype')
- subtype_ids = subtype_obj.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
- fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
+ for record in self.browse(cr, SUPERUSER_ID, ids, context=context):
+ existing_pids = set([f.id for f in record.message_follower_ids
+ if f.id in partner_ids])
+ new_pids = set(partner_ids) - existing_pids
+
+ # subtype_ids specified: update already subscribed partners
+ if subtype_ids and existing_pids:
+ fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, [
+ ('res_model', '=', self._name),
+ ('res_id', '=', record.id),
+ ('partner_id', 'in', list(existing_pids)),
+ ], context=context)
+ mail_followers_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
+ # subtype_ids not specified: do not update already subscribed partner, fetch default subtypes for new partners
+ elif subtype_ids is None:
+ subtype_ids = subtype_obj.search(cr, uid, [
+ ('default', '=', True),
+ '|',
+ ('res_model', '=', self._name),
+ ('res_model', '=', False)
+ ], context=context)
+ # subscribe new followers
+ for new_pid in new_pids:
+ mail_followers_obj.create(cr, SUPERUSER_ID, {
+ 'res_model': self._name,
+ 'res_id': record.id,
+ 'partner_id': new_pid,
+ 'subtype_ids': [(6, 0, subtype_ids)],
+ }, context=context)
return True
@@ -1454,4 +1624,33 @@ class mail_thread(osv.AbstractModel):
''', (ids, self._name, partner_id))
return True
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+ #------------------------------------------------------
+ # Thread suggestion
+ #------------------------------------------------------
+
+ def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
+ """Return a list of suggested threads, sorted by the numbers of followers"""
+ if context is None:
+ context = {}
+
+ # TDE HACK: originally by MAT from portal/mail_mail.py but not working until the inheritance graph bug is not solved in trunk
+ # TDE FIXME: relocate in portal when it won't be necessary to reload the hr.employee model in an additional bridge module
+ if self.pool['res.groups']._all_columns.get('is_portal'):
+ user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
+ if any(group.is_portal for group in user.groups_id):
+ return []
+
+ threads = []
+ if removed_suggested_threads is None:
+ removed_suggested_threads = []
+
+ thread_ids = self.search(cr, uid, [('id', 'not in', removed_suggested_threads), ('message_is_follower', '=', False)], context=context)
+ for thread in self.browse(cr, uid, thread_ids, context=context):
+ data = {
+ 'id': thread.id,
+ 'popularity': len(thread.message_follower_ids),
+ 'name': thread.name,
+ 'image_small': thread.image_small
+ }
+ threads.append(data)
+ return sorted(threads, key=lambda x: (x['popularity'], x['id']), reverse=True)[:3]
diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml
index c9c5141d303..7b5e7f299f6 100644
--- a/addons/mail/mail_thread_view.xml
+++ b/addons/mail/mail_thread_view.xml
@@ -17,7 +17,8 @@
],
'view_mailbox': True,
'view_inbox': True,
- 'read_action': 'read'
+ 'read_action': 'read',
+ 'show_compose_message': False
}&quot;"/>
<field name="help" type="html">
<p>
diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py
index 8527e60aba0..8da2441f3bc 100644
--- a/addons/mail/res_partner.py
+++ b/addons/mail/res_partner.py
@@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
+
from openerp.tools.translate import _
from openerp.osv import fields, osv
@@ -52,21 +53,4 @@ class res_partner_mail(osv.Model):
self._message_add_suggested_recipient(cr, uid, recipients, partner, partner=partner, reason=_('Partner Profile'))
return recipients
- def message_post(self, cr, uid, thread_id, **kwargs):
- """ Override related to res.partner. In case of email message, set it as
- private:
- - add the target partner in the message partner_ids
- - set thread_id as None, because this will trigger the 'private'
- aspect of the message (model=False, res_id=False)
- """
- if isinstance(thread_id, (list, tuple)):
- thread_id = thread_id[0]
- if kwargs.get('type') == 'email':
- partner_ids = kwargs.get('partner_ids', [])
- if thread_id not in [command[1] for command in partner_ids]:
- partner_ids.append((4, thread_id))
- kwargs['partner_ids'] = partner_ids
- thread_id = False
- return super(res_partner_mail, self).message_post(cr, uid, thread_id, **kwargs)
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py
index 5545745b235..7bc39c438d1 100644
--- a/addons/mail/res_users.py
+++ b/addons/mail/res_users.py
@@ -23,11 +23,13 @@ from openerp.osv import fields, osv
from openerp import SUPERUSER_ID
from openerp.tools.translate import _
+
class res_users(osv.Model):
""" Update of res.users class
- add a preference about sending emails about notifications
- make a new user follow itself
- add a welcome message
+ - add suggestion preference
"""
_name = 'res.users'
_inherit = ['res.users']
@@ -37,10 +39,11 @@ class res_users(osv.Model):
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
help="Email address internally associated with this user. Incoming "\
"emails will appear in the user's notifications."),
+ 'display_groups_suggestions': fields.boolean("Display Groups Suggestions"),
}
_defaults = {
- 'alias_domain': False, # always hide alias during creation
+ 'display_groups_suggestions': True,
}
def __init__(self, pool, cr):
@@ -51,34 +54,29 @@ class res_users(osv.Model):
init_res = super(res_users, self).__init__(pool, cr)
# duplicate list to avoid modifying the original reference
self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
- self.SELF_WRITEABLE_FIELDS.append('notification_email_send')
+ self.SELF_WRITEABLE_FIELDS.extend(['notification_email_send', 'display_groups_suggestions'])
# duplicate list to avoid modifying the original reference
self.SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS)
- self.SELF_READABLE_FIELDS.extend(['notification_email_send', 'alias_domain', 'alias_name'])
+ self.SELF_READABLE_FIELDS.extend(['notification_email_send', 'alias_domain', 'alias_name', 'display_groups_suggestions'])
return init_res
def _auto_init(self, cr, context=None):
""" Installation hook: aliases, partner following themselves """
# create aliases for all users and avoid constraint errors
- res = self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(res_users, self)._auto_init,
- self._columns['alias_id'], 'login', alias_force_key='id', context=context)
- return res
+ return self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(res_users, self)._auto_init,
+ self._name, self._columns['alias_id'], 'login', alias_force_key='id', context=context)
def create(self, cr, uid, data, context=None):
- # create default alias same as the login
if not data.get('login', False):
raise osv.except_osv(_('Invalid Action!'), _('You may not create a user. To create new users, you should use the "Settings > Users" menu.'))
+ if context is None:
+ context = {}
- mail_alias = self.pool.get('mail.alias')
- alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context)
- data['alias_id'] = alias_id
- data.pop('alias_name', None) # prevent errors during copy()
-
- # create user
- user_id = super(res_users, self).create(cr, uid, data, context=context)
+ create_context = dict(context, alias_model_name=self._name, alias_parent_model_name=self._name)
+ user_id = super(res_users, self).create(cr, uid, data, context=create_context)
user = self.browse(cr, uid, user_id, context=context)
- # alias
- mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context)
+ self.pool.get('mail.alias').write(cr, SUPERUSER_ID, [user.alias_id.id], {"alias_force_thread_id": user_id, "alias_parent_thread_id": user_id}, context)
+
# create a welcome message
self._create_welcome_message(cr, uid, user, context=context)
return user_id
@@ -92,12 +90,6 @@ class res_users(osv.Model):
return self.pool.get('res.partner').message_post(cr, SUPERUSER_ID, [user.partner_id.id],
body=body, context=context)
- def write(self, cr, uid, ids, vals, context=None):
- # User alias is sync'ed with login
- if vals.get('login'):
- vals['alias_name'] = vals['login']
- return super(res_users, self).write(cr, uid, ids, vals, context=context)
-
def unlink(self, cr, uid, ids, context=None):
# Cascade-delete mail aliases as well, as they should not exist without the user.
alias_pool = self.pool.get('mail.alias')
@@ -109,7 +101,7 @@ class res_users(osv.Model):
def _message_post_get_pid(self, cr, uid, thread_id, context=None):
assert thread_id, "res.users does not support posting global messages"
if context and 'thread_model' in context:
- context['thread_model'] = 'res.partner'
+ context['thread_model'] = 'res.users'
if isinstance(thread_id, (list, tuple)):
thread_id = thread_id[0]
return self.browse(cr, SUPERUSER_ID, thread_id).partner_id.id
@@ -118,44 +110,32 @@ class res_users(osv.Model):
""" Redirect the posting of message on res.users to the related partner.
This is done because when giving the context of Chatter on the
various mailboxes, we do not have access to the current partner_id. """
+ if isinstance(thread_id, (list, tuple)):
+ thread_id = thread_id[0]
+ partner_ids = kwargs.get('partner_ids', [])
partner_id = self._message_post_get_pid(cr, uid, thread_id, context=context)
- return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs)
+ if partner_id not in [command[1] for command in partner_ids]:
+ partner_ids.append(partner_id)
+ kwargs['partner_ids'] = partner_ids
+ return self.pool.get('mail.thread').message_post(cr, uid, False, **kwargs)
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
- for id in ids:
- partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
- self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict, update_vals=update_vals, context=context)
return True
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
- for id in ids:
- partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
- self.pool.get('res.partner').message_subscribe(cr, uid, [partner_id], partner_ids, subtype_ids=subtype_ids, context=context)
return True
def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None):
- return self.pool.get('res.partner').message_get_partner_info_from_emails(cr, uid, emails, link_mail=link_mail, context=context)
+ return self.pool.get('mail.thread').message_get_partner_info_from_emails(cr, uid, emails, link_mail=link_mail, context=context)
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
- partner_ids = []
- for id in ids:
- partner_ids.append(self.browse(cr, SUPERUSER_ID, id).partner_id.id)
- return self.pool.get('res.partner').message_get_suggested_recipients(cr, uid, partner_ids, context=context)
-
- #------------------------------------------------------
- # Compatibility methods: do not use
- # TDE TODO: remove me in 8.0
- #------------------------------------------------------
-
- def message_post_user_api(self, cr, uid, thread_id, context=None, **kwargs):
- """ Redirect the posting of message on res.users to the related partner.
- This is done because when giving the context of Chatter on the
- various mailboxes, we do not have access to the current partner_id. """
- partner_id = self._message_post_get_pid(cr, uid, thread_id, context=context)
- return self.pool.get('res.partner').message_post_user_api(cr, uid, partner_id, context=context, **kwargs)
+ return dict.fromkeys(ids, list())
- def message_create_partners_from_emails(self, cr, uid, emails, context=None):
- return self.pool.get('res.partner').message_create_partners_from_emails(cr, uid, emails, context=context)
+ def stop_showing_groups_suggestions(self, cr, uid, user_id, context=None):
+ """Update display_groups_suggestions value to False"""
+ if context is None:
+ context = {}
+ self.write(cr, uid, user_id, {"display_groups_suggestions": False}, context)
class res_users_mail_group(osv.Model):
diff --git a/addons/mail/res_users_view.xml b/addons/mail/res_users_view.xml
index 1cacadfc898..2bf54b4bafd 100644
--- a/addons/mail/res_users_view.xml
+++ b/addons/mail/res_users_view.xml
@@ -22,15 +22,25 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
- <data>
- <field name="signature" position="before">
- <field name="notification_email_send"/>
- </field>
- <field name="signature" position="before">
- <field name="alias_domain" invisible="1"/>
- <field name="alias_id" readonly="1" required="0" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
- </field>
- </data>
+ <data>
+ <field name="signature" position="before">
+ <field name="notification_email_send"/>
+ </field>
+ <field name="signature" position="before">
+ <label for="alias_id" string="Messaging Alias" class="oe_read_only"
+ attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+ <field name="alias_id" class="oe_read_only" required="0" nolabel="1"
+ attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+ <label for="alias_name" string="Messaging Alias" class="oe_edit_only"
+ attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+ <div class="oe_edit_only" attrs="{'invisible': [('alias_domain', '=', False)]}">
+ <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
+ </div>
+ <field name="alias_contact" string="Alias Accepts Emails From"
+ attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+ <field name="display_groups_suggestions" groups="base.group_no_one"/>
+ </field>
+ </data>
</field>
</record>
diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml
index c369e96102b..5384e2aba95 100644
--- a/addons/mail/security/mail_security.xml
+++ b/addons/mail/security/mail_security.xml
@@ -14,11 +14,22 @@
<record id="mail_followers_read_write_own" model="ir.rule">
<field name="name">mail.followers: read and write its own entries</field>
<field name="model_id" ref="model_mail_followers"/>
+ <field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
+ <!-- If technical rights then read and write others subscriptions -->
+ <record id="mail_followers_read_write_others" model="ir.rule">
+ <field name="name">mail.followers: read and write others entries</field>
+ <field name="model_id" ref="model_mail_followers"/>
+ <field name="groups" eval="[(4, ref('base.group_no_one'))]"/>
+ <field name="domain_force">[]</field>
+ <field name="perm_create" eval="False"/>
+ <field name="perm_unlink" eval="False"/>
+ </record>
+
<record id="mail_notification_read_write_own" model="ir.rule">
<field name="name">mail.notification: read and write its own entries</field>
<field name="model_id" ref="model_mail_notification"/>
diff --git a/addons/mail/static/src/img/icon.png b/addons/mail/static/description/icon.png
index 53ccc469127..53ccc469127 100644
--- a/addons/mail/static/src/img/icon.png
+++ b/addons/mail/static/description/icon.png
Binary files differ
diff --git a/addons/mail/html/index.html b/addons/mail/static/description/index.html
index b564cf3ab1e..aa55e63008d 100644
--- a/addons/mail/html/index.html
+++ b/addons/mail/static/description/index.html
@@ -21,7 +21,7 @@ email overload.
</p>
<div class="oe_centeralign oe_websiteonly">
- <a href="http://www.openerp.com/start" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
+ <a href="http://www.openerp.com/start?app=mail" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
</div>
</div>
</div>
diff --git a/addons/mail/html/mail_illustration.png b/addons/mail/static/description/mail_illustration.png
index 4fff4837faa..4fff4837faa 100644
--- a/addons/mail/html/mail_illustration.png
+++ b/addons/mail/static/description/mail_illustration.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_00.png b/addons/mail/static/description/mail_sc_00.png
index 3011ef129b5..3011ef129b5 100644
--- a/addons/mail/html/mail_sc_00.png
+++ b/addons/mail/static/description/mail_sc_00.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_01.png b/addons/mail/static/description/mail_sc_01.png
index 1b1099f6536..1b1099f6536 100644
--- a/addons/mail/html/mail_sc_01.png
+++ b/addons/mail/static/description/mail_sc_01.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_02.png b/addons/mail/static/description/mail_sc_02.png
index 4e59c9d1e0f..4e59c9d1e0f 100644
--- a/addons/mail/html/mail_sc_02.png
+++ b/addons/mail/static/description/mail_sc_02.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_03.png b/addons/mail/static/description/mail_sc_03.png
index dbb14b4da36..dbb14b4da36 100644
--- a/addons/mail/html/mail_sc_03.png
+++ b/addons/mail/static/description/mail_sc_03.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_04.png b/addons/mail/static/description/mail_sc_04.png
index b8f47ba2935..b8f47ba2935 100644
--- a/addons/mail/html/mail_sc_04.png
+++ b/addons/mail/static/description/mail_sc_04.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_05.png b/addons/mail/static/description/mail_sc_05.png
index 251b4ebc676..251b4ebc676 100644
--- a/addons/mail/html/mail_sc_05.png
+++ b/addons/mail/static/description/mail_sc_05.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_06.png b/addons/mail/static/description/mail_sc_06.png
index 251b4ebc676..251b4ebc676 100644
--- a/addons/mail/html/mail_sc_06.png
+++ b/addons/mail/static/description/mail_sc_06.png
Binary files differ
diff --git a/addons/mail/html/mail_sc_07.png b/addons/mail/static/description/mail_sc_07.png
index 9a33332817e..9a33332817e 100644
--- a/addons/mail/html/mail_sc_07.png
+++ b/addons/mail/static/description/mail_sc_07.png
Binary files differ
diff --git a/addons/mail/html/photo.png b/addons/mail/static/description/photo.png
index e605e5c6f8e..e605e5c6f8e 100644
--- a/addons/mail/html/photo.png
+++ b/addons/mail/static/description/photo.png
Binary files differ
diff --git a/addons/mail/static/lib/jquery.expander/jquery.expander.js b/addons/mail/static/lib/jquery.expander/jquery.expander.js
deleted file mode 100644
index 91cc03db918..00000000000
--- a/addons/mail/static/lib/jquery.expander/jquery.expander.js
+++ /dev/null
@@ -1,385 +0,0 @@
-/*!
- * jQuery Expander Plugin v1.4.2
- *
- * Date: Fri Mar 16 14:29:56 2012 EDT
- * Requires: jQuery v1.3+
- *
- * Copyright 2011, Karl Swedberg
- * Dual licensed under the MIT and GPL licenses (just like jQuery):
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- *
- *
- *
-*/
-
-(function($) {
- $.expander = {
- version: '1.4.2',
- defaults: {
- // the number of characters at which the contents will be sliced into two parts.
- slicePoint: 100,
-
- // whether to keep the last word of the summary whole (true) or let it slice in the middle of a word (false)
- preserveWords: true,
-
- // a threshold of sorts for whether to initially hide/collapse part of the element's contents.
- // If after slicing the contents in two there are fewer words in the second part than
- // the value set by widow, we won't bother hiding/collapsing anything.
- widow: 4,
-
- // text displayed in a link instead of the hidden part of the element.
- // clicking this will expand/show the hidden/collapsed text
- expandText: 'read more',
- expandPrefix: '&hellip; ',
-
- expandAfterSummary: false,
-
- // class names for summary element and detail element
- summaryClass: 'summary',
- detailClass: 'details',
-
- // class names for <span> around "read-more" link and "read-less" link
- moreClass: 'read-more',
- lessClass: 'read-less',
-
- // number of milliseconds after text has been expanded at which to collapse the text again.
- // when 0, no auto-collapsing
- collapseTimer: 0,
-
- // effects for expanding and collapsing
- expandEffect: 'fadeIn',
- expandSpeed: 250,
- collapseEffect: 'fadeOut',
- collapseSpeed: 200,
-
- // allow the user to re-collapse the expanded text.
- userCollapse: true,
-
- // text to use for the link to re-collapse the text
- userCollapseText: 'read less',
- userCollapsePrefix: ' ',
-
-
- // all callback functions have the this keyword mapped to the element in the jQuery set when .expander() is called
-
- onSlice: null, // function() {}
- beforeExpand: null, // function() {},
- afterExpand: null, // function() {},
- onCollapse: null // function(byUser) {}
- }
- };
-
- $.fn.expander = function(options) {
- var meth = 'init';
-
- if (typeof options == 'string') {
- meth = options;
- options = {};
- }
-
- var opts = $.extend({}, $.expander.defaults, options),
- rSelfClose = /^<(?:area|br|col|embed|hr|img|input|link|meta|param).*>$/i,
- rAmpWordEnd = opts.wordEnd || /(&(?:[^;]+;)?|[a-zA-Z\u00C0-\u0100]+)$/,
- rOpenCloseTag = /<\/?(\w+)[^>]*>/g,
- rOpenTag = /<(\w+)[^>]*>/g,
- rCloseTag = /<\/(\w+)>/g,
- rLastCloseTag = /(<\/[^>]+>)\s*$/,
- rTagPlus = /^<[^>]+>.?/,
- delayedCollapse;
-
- var methods = {
- init: function() {
- this.each(function() {
- var i, l, tmp, newChar, summTagless, summOpens, summCloses,
- lastCloseTag, detailText, detailTagless,
- $thisDetails, $readMore,
- openTagsForDetails = [],
- closeTagsForsummaryText = [],
- defined = {},
- thisEl = this,
- $this = $(this),
- $summEl = $([]),
- o = $.meta ? $.extend({}, opts, $this.data()) : opts,
- hasDetails = !!$this.find('.' + o.detailClass).length,
- hasBlocks = !!$this.find('*').filter(function() {
- var display = $(this).css('display');
- return (/^block|table|list/).test(display);
- }).length,
- el = hasBlocks ? 'div' : 'span',
- detailSelector = el + '.' + o.detailClass,
- moreSelector = 'span.' + o.moreClass,
- expandSpeed = o.expandSpeed || 0,
- allHtml = $.trim( $this.html() ),
- allText = $.trim( $this.text() ),
- summaryText = allHtml.slice(0, o.slicePoint);
-
- // bail out if we've already set up the expander on this element
- if ( $.data(this, 'expander') ) {
- return;
- }
-
- $.data(this, 'expander', true);
-
- // determine which callback functions are defined
- $.each(['onSlice','beforeExpand', 'afterExpand', 'onCollapse'], function(index, val) {
- defined[val] = $.isFunction(o[val]);
- });
-
- // back up if we're in the middle of a tag or word
- summaryText = backup(summaryText);
-
- // summary text sans tags length
- summTagless = summaryText.replace(rOpenCloseTag, '').length;
-
- // add more characters to the summary, one for each character in the tags
- while (summTagless < o.slicePoint) {
- newChar = allHtml.charAt(summaryText.length);
- if (newChar == '<') {
- newChar = allHtml.slice(summaryText.length).match(rTagPlus)[0];
- }
- summaryText += newChar;
- summTagless++;
- }
-
- summaryText = backup(summaryText, o.preserveWords);
-
- // separate open tags from close tags and clean up the lists
- summOpens = summaryText.match(rOpenTag) || [];
- summCloses = summaryText.match(rCloseTag) || [];
-
- // filter out self-closing tags
- tmp = [];
- $.each(summOpens, function(index, val) {
- if ( !rSelfClose.test(val) ) {
- tmp.push(val);
- }
- });
- summOpens = tmp;
-
- // strip close tags to just the tag name
- l = summCloses.length;
- for (i = 0; i < l; i++) {
- summCloses[i] = summCloses[i].replace(rCloseTag, '$1');
- }
-
- // tags that start in summary and end in detail need:
- // a). close tag at end of summary
- // b). open tag at beginning of detail
- $.each(summOpens, function(index, val) {
- var thisTagName = val.replace(rOpenTag, '$1');
- var closePosition = $.inArray(thisTagName, summCloses);
- if (closePosition === -1) {
- openTagsForDetails.push(val);
- closeTagsForsummaryText.push('</' + thisTagName + '>');
-
- } else {
- summCloses.splice(closePosition, 1);
- }
- });
-
- // reverse the order of the close tags for the summary so they line up right
- closeTagsForsummaryText.reverse();
-
- // create necessary summary and detail elements if they don't already exist
- if ( !hasDetails ) {
-
- // end script if there is no detail text or if detail has fewer words than widow option
- detailText = allHtml.slice(summaryText.length);
- detailTagless = $.trim( detailText.replace(rOpenCloseTag, '') );
-
- if ( detailTagless === '' || detailTagless.split(/\s+/).length < o.widow ) {
- return;
- }
- // otherwise, continue...
- lastCloseTag = closeTagsForsummaryText.pop() || '';
- summaryText += closeTagsForsummaryText.join('');
- detailText = openTagsForDetails.join('') + detailText;
-
- } else {
- // assume that even if there are details, we still need readMore/readLess/summary elements
- // (we already bailed out earlier when readMore el was found)
- // but we need to create els differently
-
- // remove the detail from the rest of the content
- detailText = $this.find(detailSelector).remove().html();
-
- // The summary is what's left
- summaryText = $this.html();
-
- // allHtml is the summary and detail combined (this is needed when content has block-level elements)
- allHtml = summaryText + detailText;
-
- lastCloseTag = '';
- }
- o.moreLabel = $this.find(moreSelector).length ? '' : buildMoreLabel(o);
-
- if (hasBlocks) {
- detailText = allHtml;
- }
- summaryText += lastCloseTag;
-
- // onSlice callback
- o.summary = summaryText;
- o.details = detailText;
- o.lastCloseTag = lastCloseTag;
-
- if (defined.onSlice) {
- // user can choose to return a modified options object
- // one last chance for user to change the options. sneaky, huh?
- // but could be tricky so use at your own risk.
- tmp = o.onSlice.call(thisEl, o);
-
- // so, if the returned value from the onSlice function is an object with a details property, we'll use that!
- o = tmp && tmp.details ? tmp : o;
- }
-
- // build the html with summary and detail and use it to replace old contents
- var html = buildHTML(o, hasBlocks);
-
- $this.html( html );
-
- // set up details and summary for expanding/collapsing
- $thisDetails = $this.find(detailSelector);
- $readMore = $this.find(moreSelector);
- $thisDetails.hide();
- $readMore.find('a').unbind('click.expander').bind('click.expander', expand);
-
- $summEl = $this.find('div.' + o.summaryClass);
-
- if ( o.userCollapse && !$this.find('span.' + o.lessClass).length ) {
- $this
- .find(detailSelector)
- .append('<span class="' + o.lessClass + '">' + o.userCollapsePrefix + '<a href="#">' + o.userCollapseText + '</a></span>');
- }
-
- $this
- .find('span.' + o.lessClass + ' a')
- .unbind('click.expander')
- .bind('click.expander', function(event) {
- event.preventDefault();
- clearTimeout(delayedCollapse);
- var $detailsCollapsed = $(this).closest(detailSelector);
- reCollapse(o, $detailsCollapsed);
- if (defined.onCollapse) {
- o.onCollapse.call(thisEl, true);
- }
- });
-
- function expand(event) {
- event.preventDefault();
- $readMore.hide();
- $summEl.hide();
- if (defined.beforeExpand) {
- o.beforeExpand.call(thisEl);
- }
-
- $thisDetails.stop(false, true)[o.expandEffect](expandSpeed, function() {
- $thisDetails.css({zoom: ''});
- if (defined.afterExpand) {o.afterExpand.call(thisEl);}
- delayCollapse(o, $thisDetails, thisEl);
- });
- }
-
- }); // this.each
- },
- destroy: function() {
- if ( !this.data('expander') ) {
- return;
- }
- this.removeData('expander');
- this.each(function() {
- var $this = $(this),
- o = $.meta ? $.extend({}, opts, $this.data()) : opts,
- details = $this.find('.' + o.detailClass).contents();
-
- $this.find('.' + o.moreClass).remove();
- $this.find('.' + o.summaryClass).remove();
- $this.find('.' + o.detailClass).after(details).remove();
- $this.find('.' + o.lessClass).remove();
-
- });
- }
- };
-
- // run the methods (almost always "init")
- if ( methods[meth] ) {
- methods[ meth ].call(this);
- }
-
- // utility functions
- function buildHTML(o, blocks) {
- var el = 'span',
- summary = o.summary;
- if ( blocks ) {
- el = 'div';
- // if summary ends with a close tag, tuck the moreLabel inside it
- if ( rLastCloseTag.test(summary) && !o.expandAfterSummary) {
- summary = summary.replace(rLastCloseTag, o.moreLabel + '$1');
- } else {
- // otherwise (e.g. if ends with self-closing tag) just add moreLabel after summary
- // fixes #19
- summary += o.moreLabel;
- }
-
- // and wrap it in a div
- summary = '<div class="' + o.summaryClass + '">' + summary + '</div>';
- } else {
- summary += o.moreLabel;
- }
-
- return [
- summary,
- '<',
- el + ' class="' + o.detailClass + '"',
- '>',
- o.details,
- '</' + el + '>'
- ].join('');
- }
-
- function buildMoreLabel(o) {
- var ret = '<span class="' + o.moreClass + '">' + o.expandPrefix;
- ret += '<a href="#">' + o.expandText + '</a></span>';
- return ret;
- }
-
- function backup(txt, preserveWords) {
- if ( txt.lastIndexOf('<') > txt.lastIndexOf('>') ) {
- txt = txt.slice( 0, txt.lastIndexOf('<') );
- }
- if (preserveWords) {
- txt = txt.replace(rAmpWordEnd,'');
- }
-
- return $.trim(txt);
- }
-
- function reCollapse(o, el) {
- el.stop(true, true)[o.collapseEffect](o.collapseSpeed, function() {
- var prevMore = el.prev('span.' + o.moreClass).show();
- if (!prevMore.length) {
- el.parent().children('div.' + o.summaryClass).show()
- .find('span.' + o.moreClass).show();
- }
- });
- }
-
- function delayCollapse(option, $collapseEl, thisEl) {
- if (option.collapseTimer) {
- delayedCollapse = setTimeout(function() {
- reCollapse(option, $collapseEl);
- if ( $.isFunction(option.onCollapse) ) {
- option.onCollapse.call(thisEl, false);
- }
- }, option.collapseTimer);
- }
- }
-
- return this;
- };
-
- // plugin defaults
- $.fn.expander.defaults = $.expander.defaults;
-})(jQuery);
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 958c2bccd5f..a4beca607ba 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -22,10 +22,20 @@
width: 32px;
height: 32px;
padding: 0px;
- margin: 0px
+ margin: 0px;
border-radius: 0px;
}
+/* ---- GENERIC FOR MAIL-RELATED STUFF ---- */
+.openerp .oe_e.oe_e_alias {
+ font-size: 30px;
+ line-height: 15px;
+ vertical-align: top;
+ margin-right: 3px;
+ color: white;
+ text-shadow: 0px 0px 2px black;
+}
+
/* ------------ MAIL WIDGET --------------- */
.openerp .oe_mail, .openerp .oe_mail *{
-webkit-box-sizing: border-box;
@@ -108,6 +118,9 @@
text-overflow:ellipsis;
word-wrap: break-word;
}
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body .oe_mail_cleaned {
+ display: none;
+}
/* a) Indented Messages */
@@ -622,11 +635,10 @@
}
.openerp .oe_followers .oe_partner {
height: 32px;
- margin-right: 8px;
+ margin-right: 24px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
- margin-right: 10px;
}
.openerp .oe_followers .oe_partner img{
width: 32px;
@@ -639,7 +651,18 @@
right: 0px;
line-height: 20px;
}
-
+.openerp .oe_followers .oe_edit_subtype{
+ cursor: pointer;
+ position: absolute;
+ right: 12px;
+ line-height: 20px;
+}
+.openerp .oe_followers .oe_partner .oe_hidden{
+ display: none;
+}
+.openerp.ui-dialog .ui-dialog-titlebar .ui-dialog-title{
+ padding-right: 20px;
+}
.openerp .oe_followers .oe_show_more{
cursor: pointer;
}
@@ -665,8 +688,72 @@
.openerp .oe_mail_wall .oe_mail{
margin: 16px;
width: 600px;
+ display: inline-block;
}
.openerp .oe_mail .oe_view_nocontent > p {
padding-left: 15px;
}
+
+
+/* ------------- WALL SIDEBAR ------------- */
+
+.openerp .oe_mail_wall .oe_mail_wall_aside {
+ margin: 16px;
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+ width: 260px;
+}
+.openerp .oe_mail_wall_aside .oe_sidebar_suggestion {
+ background-color: #EDEDF6;
+ border-radius: 2px;
+ padding-top: 1px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_title h2 {
+ font-size: 14px;
+ font-weight: bold;
+ margin-left: 10px;
+ padding: 0px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item {
+ border-radius: 2px;
+ width: 100%;
+ margin-left: 10px;
+ min-height: 67px; /* image_small 66x66px */
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_image {
+ float: left;
+ padding-right: 10px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_image img {
+ border-radius: 2px;
+ border: solid 1px rgba(0,0,0,0.03);
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content button {
+ margin-top: 10px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content a.oe_suggestion_item_name {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ width: 90%;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_title a.oe_suggestion_remove {
+ line-height: 15px;
+ margin-top: -2px;
+ float: right;
+ visibility: hidden;
+ margin-right: 7px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content a.oe_suggestion_remove_item {
+ line-height: 15px;
+ margin-top: -2px;
+ float: right;
+ visibility: hidden;
+ margin-right: 16px;
+}
+.openerp .oe_sidebar_suggestion .oe_suggest_title:hover a.oe_suggestion_remove,
+.openerp .oe_sidebar_suggestion .oe_suggest_items:hover a.oe_suggestion_remove_item {
+ visibility: visible;
+}
+
diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css
index 5ba817aa1a5..39cdf5c0b4c 100644
--- a/addons/mail/static/src/css/mail_group.css
+++ b/addons/mail/static/src/css/mail_group.css
@@ -48,7 +48,6 @@
text-align: center;
overflow: hidden;
-moz-border-radius: 3px;
- border-collapse: separate;
-webkit-border-radius: 3px;
-o-border-radius: 3px;
-ms-border-radius: 3px;
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 7df6cdbef0d..62e9241cf4f 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -2,10 +2,10 @@ openerp.mail = function (session) {
var _t = session.web._t,
_lt = session.web._lt;
- var mail = session.mail = {};
+ var mail = session.mail;
- openerp_mail_followers(session, mail); // import mail_followers.js
- openerp_FieldMany2ManyTagsEmail(session); // import manyy2many_tags_email.js
+ openerp_mail_followers(session, mail); // import mail_followers.js
+ openerp_FieldMany2ManyTagsEmail(session); // import manyy2many_tags_email.js
/**
* ------------------------------------------------------------
@@ -225,6 +225,7 @@ openerp.mail = function (session) {
this.name = datasets.name || false,
this.record_name = datasets.record_name || false,
this.body = datasets.body || '',
+ this.body_short = datasets.body_short || '',
this.vote_nb = datasets.vote_nb || 0,
this.has_voted = datasets.has_voted || false,
this.is_favorite = datasets.is_favorite || false,
@@ -237,8 +238,15 @@ openerp.mail = function (session) {
this.format_data();
+ // update record_name: Partner profile
+ if (this.model == 'res.partner') {
+ this.record_name = 'Partner Profile of ' + this.record_name;
+ }
+ else if (this.model == 'hr.employee') {
+ this.record_name = 'News from ' + this.record_name;
+ }
// record options and data
- this.show_record_name = this.options.show_record_name && this.record_name && !this.thread_level && this.model != 'res.partner';
+ this.show_record_name = this.options.show_record_name && this.record_name && !this.thread_level;
this.options.show_read = false;
this.options.show_unread = false;
if (this.options.show_read_unread_button) {
@@ -257,8 +265,10 @@ openerp.mail = function (session) {
/* Convert date, timerelative and avatar in displayable data. */
format_data: function () {
+
//formating and add some fields for render
this.date = this.date ? session.web.str_to_datetime(this.date) : false;
+ this.display_date = this.date.toString('ddd MMM dd yyyy HH:mm');
if (this.date && new Date().getTime()-this.date.getTime() < 7*24*60*60*1000) {
this.timerelative = $.timeago(this.date);
}
@@ -623,10 +633,7 @@ openerp.mail = function (session) {
// have unknown names -> call message_get_partner_info_from_emails to try to find partner_id
var find_done = $.Deferred();
if (names_to_find.length > 0) {
- var values = {
- 'res_id': this.context.default_res_id,
- }
- find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [names_to_find], values);
+ find_done = self.parent_thread.ds_thread._model.call('message_partner_info_from_emails', [this.context.default_res_id, names_to_find]);
}
else {
find_done.resolve([]);
@@ -672,11 +679,7 @@ openerp.mail = function (session) {
var new_names_to_find = _.difference(names_to_find, names_to_remove);
find_done = $.Deferred();
if (new_names_to_find.length > 0) {
- var values = {
- 'link_mail': true,
- 'res_id': self.context.default_res_id,
- }
- find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [new_names_to_find], values);
+ find_done = self.parent_thread.ds_thread._model.call('message_partner_info_from_emails', [self.context.default_res_id, new_names_to_find, true]);
}
else {
find_done.resolve([]);
@@ -745,7 +748,7 @@ openerp.mail = function (session) {
}
// create object and attach to the thread object
thread.message_fetch([["id", "=", message_id]], false, [message_id], function (arg, data) {
- var message = thread.create_message_object( data[0] );
+ var message = thread.create_message_object( data.slice(-1)[0] );
// insert the message on dom
thread.insert_message( message, root ? undefined : self.$el, root );
});
@@ -942,7 +945,6 @@ openerp.mail = function (session) {
start: function () {
this._super.apply(this, arguments);
- this.expender();
this.bind_events();
if(this.thread_level < this.options.display_indented_thread) {
this.create_thread();
@@ -965,6 +967,26 @@ openerp.mail = function (session) {
this.$('.oe_reply').on('click', this.on_message_reply);
this.$('.oe_star').on('click', this.on_star);
this.$('.oe_msg_vote').on('click', this.on_vote);
+ this.$('.oe_mail_expand').on('click', this.on_expand);
+ this.$('.oe_mail_reduce').on('click', this.on_expand);
+ this.$('.oe_mail_action_model').on('click', this.on_record_clicked);
+ },
+
+ on_record_clicked: function (event) {
+ event.stopPropagation();
+ var state = {
+ 'model': this.model,
+ 'id': this.res_id,
+ 'title': this.record_name
+ };
+ session.webclient.action_manager.do_push_state(state);
+ this.do_action({
+ res_model: state.model,
+ res_id: state.id,
+ type: 'ir.actions.act_window',
+ views: [[false, 'form']]
+ });
+ return false;
},
/* Call the on_compose_message on the thread of this message. */
@@ -975,15 +997,11 @@ openerp.mail = function (session) {
return false;
},
- expender: function () {
- this.$('.oe_msg_body:first').expander({
- slicePoint: this.options.truncate_limit,
- expandText: 'read more',
- userCollapseText: 'read less',
- detailClass: 'oe_msg_tail',
- moreClass: 'oe_mail_expand',
- lessClass: 'oe_mail_reduce',
- });
+ on_expand: function (event) {
+ event.stopPropagation();
+ this.$('.oe_msg_body_short:first').toggle();
+ this.$('.oe_msg_body_long:first').toggle();
+ return false;
},
/**
@@ -1842,6 +1860,19 @@ openerp.mail = function (session) {
/**
* ------------------------------------------------------------
+ * Aside Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a sidebar on the Wall. Its main use
+ * is to display group and employees suggestion (if hr is installed).
+ */
+ mail.WallSidebar = session.web.Widget.extend({
+ template: 'mail.wall.sidebar',
+ });
+
+
+ /**
+ * ------------------------------------------------------------
* Wall Widget
* ------------------------------------------------------------
*
@@ -1901,6 +1932,9 @@ openerp.mail = function (session) {
if (! this.searchview.has_defaults) {
this.message_render();
}
+ // render sidebar
+ var wall_sidebar = new mail.WallSidebar(this);
+ wall_sidebar.appendTo(this.$el.find('.oe_mail_wall_aside'));
},
/**
@@ -2018,4 +2052,16 @@ openerp.mail = function (session) {
});
},
});
+
+
+ /**
+ * ------------------------------------------------------------
+ * Sub-widgets loading
+ * ------------------------------------------------------------
+ *
+ * Load here widgets that could depend on widgets defined in mail.js
+ */
+
+ openerp.mail.suggestions(session, mail); // import suggestion.js (suggestion widget)
+
};
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index f812844194b..a0dad1fcdbc 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -29,7 +29,6 @@ openerp_mail_followers = function(session, mail) {
this.displayed_limit = this.node.attrs.displayed_nb || 10;
this.displayed_nb = this.displayed_limit;
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
- this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
this.value = [];
@@ -74,10 +73,33 @@ openerp_mail_followers = function(session, mail) {
this.$el.on('click', '.oe_subtype_list input', self.do_update_subscription);
// event: click on 'invite' button, that opens the invite wizard
this.$('.oe_invite').on('click', self.on_invite_follower);
+ // event: click on 'edit_subtype(pencil)' button to edit subscription
+ this.$el.on('click', '.oe_edit_subtype', self.on_edit_subtype);
this.$el.on('click', '.oe_remove_follower', self.on_remove_follower);
this.$el.on('click', '.oe_show_more', self.on_show_more_followers)
},
+ on_edit_subtype: function(event) {
+ var self = this;
+ var $currentTarget = $(event.currentTarget);
+ var user_pid = $currentTarget.data('id');
+ $('div.oe_edit_actions').remove();
+ self.$dialog = new session.web.dialog($('<div class="oe_edit_actions">'), {
+ modal: true,
+ width: 'auto',
+ height: 'auto',
+ title: _t('Edit Subscription of ') + $currentTarget.siblings('a').text(),
+ buttons: [
+ { text: _t("Apply"), click: function() {
+ self.do_update_subscription(event, user_pid);
+ $(this).dialog("close");
+ }},
+ { text: _t("Cancel"), click: function() { $(this).dialog("close"); }}
+ ],
+ });
+ return self.fetch_subtypes(user_pid);
+ },
+
on_invite_follower: function (event) {
var self = this;
var action = {
@@ -130,7 +152,7 @@ openerp_mail_followers = function(session, mail) {
fetch_followers: function (value_) {
this.value = value_ || {};
- return this.ds_follow.call('read', [this.value, ['name', 'user_ids']])
+ return this.ds_model.call('read_followers_data', [this.value])
.then(this.proxy('display_followers'), this.proxy('fetch_generic'))
.then(this.proxy('display_buttons'))
.then(this.proxy('fetch_subtypes'));
@@ -147,6 +169,7 @@ openerp_mail_followers = function(session, mail) {
self.message_is_follower = (_.indexOf(self.value, pid) != -1);
}).then(self.proxy('display_generic'));
},
+
_format_followers: function(count){
var str = '';
if(count <= 0){
@@ -158,6 +181,7 @@ openerp_mail_followers = function(session, mail) {
}
return str;
},
+
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function () {
var self = this;
@@ -168,16 +192,32 @@ openerp_mail_followers = function(session, mail) {
/** Display the followers */
display_followers: function (records) {
var self = this;
+ this.message_is_follower = false;
this.followers = records || this.followers;
- this.message_is_follower = this.set_is_follower(this.followers);
// clean and display title
var node_user_list = this.$('.oe_follower_list').empty();
this.$('.oe_follower_title').html(this._format_followers(this.followers.length));
// truncate number of displayed followers
var truncated = this.followers.slice(0, this.displayed_nb);
_(truncated).each(function (record) {
- record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
- $(session.web.qweb.render('mail.followers.partner', {'record': record, 'widget': self})).appendTo(node_user_list);
+ partner = {
+ 'id': record[0],
+ 'name': record[1],
+ 'is_uid': record[2]['is_uid'],
+ 'is_editable': record[2]['is_editable'],
+ 'avatar_url': mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record[0]),
+ }
+ if (partner.is_uid) {
+ self.message_is_follower = partner.is_uid;
+ }
+ $(session.web.qweb.render('mail.followers.partner', {'record': partner, 'widget': self})).appendTo(node_user_list);
+ // On mouse-enter it will show the edit_subtype pencil.
+ if (partner.is_editable) {
+ self.$('.oe_follower_list').on('mouseenter mouseleave', function(e) {
+ self.$('.oe_edit_subtype').toggleClass('oe_hidden', e.type == 'mouseleave');
+ self.$('.oe_follower_list').find('.oe_partner').toggleClass('oe_partner_name', e.type == 'mouseenter');
+ });
+ }
});
// FVA note: be sure it is correctly translated
if (truncated.length < this.followers.length) {
@@ -185,12 +225,6 @@ openerp_mail_followers = function(session, mail) {
}
},
- /** Computes whether the current user is in the followers */
- set_is_follower: function (records) {
- var user_ids = _.pluck(_.pluck(records, 'user_ids'), 0);
- return _.indexOf(user_ids, this.session.uid) != -1;
- },
-
display_buttons: function () {
if (this.message_is_follower) {
this.$('button.oe_follower').removeClass('oe_notfollow').addClass('oe_following');
@@ -206,25 +240,37 @@ openerp_mail_followers = function(session, mail) {
},
/** Fetch subtypes, only if current user is follower */
- fetch_subtypes: function () {
+ fetch_subtypes: function (user_pid) {
var self = this;
- var subtype_list_ul = this.$('.oe_subtype_list').empty();
- if (! this.message_is_follower) return;
+ var dialog = false;
+ if (user_pid) {
+ dialog = true;
+ } else {
+ var subtype_list_ul = this.$('.oe_subtype_list').empty();
+ if (! this.message_is_follower) return;
+ }
var id = this.view.datarecord.id;
- this.ds_model.call('message_get_subscription_data', [[id], new session.web.CompoundContext(this.build_context(), {})])
- .then(function (data) {self.display_subtypes(data, id);});
+ this.ds_model.call('message_get_subscription_data', [[id], user_pid, new session.web.CompoundContext(this.build_context(), {})])
+ .then(function (data) {self.display_subtypes(data, id, dialog);});
},
/** Display subtypes: {'name': default, followed} */
- display_subtypes:function (data, id) {
+ display_subtypes:function (data, id, dialog) {
var self = this;
- var $list = this.$('.oe_subtype_list');
+ if (dialog) {
+ var $list = self.$dialog;
+ }
+ else {
+ var $list = this.$('.oe_subtype_list');
+ }
$list.empty().hide();
var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
+ this.records_length = $.map(records, function(value, index) { return index; }).length;
+ if (this.records_length > 1) { self.display_followers(); }
_(records).each(function (record, record_name) {
record.name = record_name;
record.followed = record.followed || undefined;
- $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('.oe_subtype_list') );
+ $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo($list);
});
if (_.size(records) > 1) {
$list.show();
@@ -241,35 +287,49 @@ openerp_mail_followers = function(session, mail) {
});
},
- do_unfollow: function () {
+ do_unfollow: function (user_pid) {
if (confirm(_t("Warning! \nYou won't be notified of any email or discussion on this document. Do you really want to unfollow this document ?"))) {
_(this.$('.oe_msg_subtype_check')).each(function (record) {
$(record).attr('checked',false);
});
+ var action_unsubscribe = 'message_unsubscribe_users';
+ var follower_ids = [this.session.uid];
+ if (user_pid) {
+ action_unsubscribe = 'message_unsubscribe';
+ follower_ids = [user_pid];
+ }
var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
- .then(this.proxy('read_value'));
+ return this.ds_model.call(action_unsubscribe, [[this.view.datarecord.id], follower_ids, context])
+ .then(this.proxy('read_value'));
}
return false;
},
- do_update_subscription: function (event) {
+ do_update_subscription: function (event, user_pid) {
var self = this;
+ var action_subscribe = 'message_subscribe_users';
+ var follower_ids = [this.session.uid];
+ var oe_action = this.$('.oe_actions input[type="checkbox"]');
+ if (user_pid) {
+ action_subscribe = 'message_subscribe';
+ follower_ids = [user_pid];
+ oe_action = $('.oe_edit_actions input[type="checkbox"]');
+ }
var checklist = new Array();
- _(this.$('.oe_actions input[type="checkbox"]')).each(function (record) {
+ _(oe_action).each(function (record) {
if ($(record).is(':checked')) {
checklist.push(parseInt($(record).data('id')));
}
});
if (!checklist.length) {
- if (!this.do_unfollow()) {
+ if (!this.do_unfollow(user_pid)) {
$(event.target).attr("checked", "checked");
}
} else {
var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], checklist, context])
+ return this.ds_model.call(action_subscribe, [[this.view.datarecord.id], follower_ids, checklist, context])
.then(this.proxy('read_value'));
}
},
diff --git a/addons/mail/static/src/js/suggestions.js b/addons/mail/static/src/js/suggestions.js
new file mode 100644
index 00000000000..4d0fcec32bf
--- /dev/null
+++ b/addons/mail/static/src/js/suggestions.js
@@ -0,0 +1,77 @@
+openerp.mail.suggestions = function(session, mail) {
+ var _t = session.web._t;
+ var QWeb = session.web.qweb;
+
+ var suggestions = session.suggestions = {};
+ var removed_suggested_group = session.suggestions.removed_suggested_group = [];
+
+ suggestions.Groups = session.web.Widget.extend({
+ events: {
+ 'click .oe_suggestion_remove.oe_suggestion_group': 'stop_group_suggestion',
+ 'click .oe_suggestion_remove_item.oe_suggestion_group': 'remove_group_suggestion',
+ 'click .oe_suggestion_join': 'join_group',
+ },
+
+ init: function () {
+ this._super.apply(this, arguments);
+ this.mail_group = new session.web.DataSetSearch(this, 'mail.group');
+ this.res_users = new session.web.DataSetSearch(this, 'res.users');
+ this.groups = [];
+ },
+
+ start: function () {
+ this._super.apply(this, arguments);
+ return this.fetch_suggested_groups();
+ },
+
+ fetch_suggested_groups: function () {
+ var self = this;
+ var group = self.mail_group.call('get_suggested_thread', {'removed_suggested_threads': removed_suggested_group}).then(function (res) {
+ _(res).each(function (result) {
+ result['image']=self.session.url('/web/binary/image', {model: 'mail.group', field: 'image_small', id: result.id});
+ });
+ self.groups = res;
+ });
+ return $.when(group).then(this.proxy('display_suggested_groups'));
+ },
+
+ display_suggested_groups: function () {
+ var suggested_groups = this.$('.oe_sidebar_suggestion.oe_suggestion_group');
+ if (suggested_groups) {
+ suggested_groups.empty();
+ }
+ if (this.groups.length === 0) {
+ return this.$el.empty();
+ }
+ return this.$el.empty().html(QWeb.render('mail.suggestions.groups', {'widget': this}));
+ },
+
+ join_group: function (event) {
+ var self = this;
+ return this.mail_group.call('message_subscribe_users', [[$(event.currentTarget).attr('id')],[this.session.uid]]).then(function(res) {
+ self.fetch_suggested_groups();
+ });
+ },
+
+ remove_group_suggestion: function (event) {
+ removed_suggested_group.push($(event.currentTarget).attr('id'));
+ return this.fetch_suggested_groups();
+ },
+
+ stop_group_suggestion: function (event) {
+ var self = this;
+ return this.res_users.call('stop_showing_groups_suggestions', [this.session.uid]).then(function(res) {
+ self.$(".oe_sidebar_suggestion.oe_suggestion_group").hide();
+ });
+ }
+ });
+
+ session.mail.WallSidebar.include({
+ start: function () {
+ this._super.apply(this, arguments);
+ var sug_groups = new suggestions.Groups(this);
+ return sug_groups.appendTo(this.$('.oe_suggestions_groups'));
+ },
+ });
+
+};
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index f5004941a8d..1be9754e1c7 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -121,9 +121,11 @@
To:
<t t-if="!widget.is_private">
<span class="oe_all_follower">
- Followers of
- <t t-if="widget.parent_thread.parent_message.record_name" t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'"/>
- <t t-if="!widget.parent_thread.parent_message.record_name">this document</t>
+ <t t-if="widget.parent_thread.parent_message.record_name" t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'">
+ Followers of <t t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'"/>
+ </t>
+ <t t-if="!widget.parent_thread.parent_message.record_name and widget.options.view_inbox">My Followers</t>
+ <t t-if="!widget.parent_thread.parent_message.record_name and !widget.options.view_inbox">Followers of this document</t>
</span>
</t>
<t t-if="!widget.is_private and (widget.partner_ids.length or (widget.author_id and widget.author_id[0]))"> and </t>
@@ -197,9 +199,17 @@
</tbody>
</table>
<div class="oe_mail-placeholder"></div>
+ <aside class="oe_mail_wall_aside"></aside>
</div>
<!--
+ Empty template that holds the sidebar of the Wall
+ -->
+ <t t-name="mail.wall.sidebar">
+ <div class="oe_mail_wall_sidebar"></div>
+ </t>
+
+ <!--
display message on the wall when there are no message
-->
<t t-name="mail.wall_no_message">
@@ -256,7 +266,13 @@
<t t-if="widget.subject" t-raw="widget.subject"/>
</h1>
<div class="oe_msg_body">
- <t t-raw="widget.body"/>
+ <t t-if="widget.body_short">
+ <div class="oe_msg_body_short"><t t-raw="widget.body_short"/></div>
+ <div class="oe_msg_body_long" style="display: none;"><t t-raw="widget.body"/><span class="oe_mail_reduce"><a href="#">read less</a></span></div>
+ </t>
+ <t t-if="! widget.body_short">
+ <t t-raw="widget.body"/>
+ </t>
</div>
</div>
<div class="oe_msg_footer">
@@ -271,16 +287,16 @@
<span class='oe_subtle'>•</span>
</t>
</t>
- <t t-if="widget.type == 'comment' and ! widget.subtype">
+ <t t-if="widget.type == 'comment' and ! widget.subtype and widget.partner_ids.length == 0">
logged a note
</t>
- <t t-if="(widget.type == 'comment' or widget.type == 'email') and widget.subtype">
+ <t t-if="(widget.type == 'comment' or widget.type == 'email') and (widget.subtype or widget.partner_ids.length > 0)">
to
<t t-if="widget.partner_ids.length == 0">
nobody
</t>
</t>
- <t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and widget.subtype)"
+ <t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
<span t-attf-class="oe_partner_follower">
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-raw="partner[1]"/></a>
@@ -295,7 +311,10 @@
notified
</t>
<span class='oe_subtle'>•</span>
- <span t-att-title="widget.date"><t t-if="widget.timerelative" t-raw="widget.timerelative"/><t t-if="!widget.timerelative" t-raw="widget.date"/></span>
+ <span t-att-title="widget.date">
+ <t t-if="widget.timerelative" t-raw="widget.timerelative"/>
+ <t t-if="!widget.timerelative" t-raw="widget.display_date"/>
+ </span>
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
<t t-if="!widget.options.readonly" t-call="mail.thread.message.vote"/>
</div>
@@ -339,7 +358,4 @@
</a>
</span>
- <!-- mail.thread.message.star
- Template used to display stared/unstared message in a mail.message
- -->
</template>
diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml
index 5ad3ff09bb3..a936b8237d6 100644
--- a/addons/mail/static/src/xml/mail_followers.xml
+++ b/addons/mail/static/src/xml/mail_followers.xml
@@ -31,6 +31,7 @@
<div t-name="mail.followers.partner" class='oe_partner'>
<img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
<a t-attf-href="#model=res.partner&amp;id=#{record.id}" t-att-title="record.name"><t t-esc="record.name"/></a>
+ <span t-if="record.is_editable and (widget.records_length &gt; 1)" class="oe_edit_subtype oe_e oe_hidden" title="Edit subscription" t-att-data-id="record.id">&amp;</span>
<span t-if="widget.view_is_editable" class="oe_remove_follower oe_e" title="Remove this follower" t-att-data-id="record.id">X</span>
</div>
diff --git a/addons/mail/static/src/xml/suggestions.xml b/addons/mail/static/src/xml/suggestions.xml
new file mode 100644
index 00000000000..9157dcbe32e
--- /dev/null
+++ b/addons/mail/static/src/xml/suggestions.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template>
+
+ <!-- Groups placeholder in sidebar -->
+ <t t-extend="mail.wall.sidebar">
+ <t t-jquery=".oe_mail_wall_sidebar" t-operation="append">
+ <div class="oe_suggestions_groups"></div>
+ </t>
+ </t>
+
+
+ <!-- Suggested groups -->
+ <div t-name="mail.suggestions.groups" class="oe_sidebar_suggestion oe_suggestion_group">
+ <div class="oe_suggest_title">
+ <a class="oe_suggestion_remove oe_suggestion_group oe_e">X</a>
+ <h2>Suggested Groups</h2>
+ </div>
+ <div class="oe_suggest_items">
+ <t t-foreach="widget.groups" t-as="result">
+ <div class="oe_suggested_item">
+ <div class="oe_suggested_item_image">
+ <a t-attf-href="#model=mail.group&amp;id=#{result.id}">
+ <img t-attf-src="{result.image}" t-attf-alt="{result.name}"/>
+ </a>
+ </div>
+ <div class="oe_suggested_item_content">
+ <a class="oe_suggestion_item_name" t-attf-href="#model=mail.group&amp;id=#{result.id}"><t t-esc="result.name"/></a>
+ <a class="oe_suggestion_remove_item oe_suggestion_group oe_e" t-attf-id="{result.id}">X</a>
+ <br/>
+ <button t-att-id="result.id" class="oe_suggestion_join">Join Group</button>
+ </div>
+ </div>
+ </t>
+ </div>
+ </div>
+
+
+</template>
diff --git a/addons/mail/tests/test_mail_base.py b/addons/mail/tests/test_mail_base.py
index a3b90288f0b..b0f9f72b6f7 100644
--- a/addons/mail/tests/test_mail_base.py
+++ b/addons/mail/tests/test_mail_base.py
@@ -69,10 +69,11 @@ class TestMailBase(common.TransactionCase):
self.group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Test users to use through the various tests
+ self.res_users.write(cr, uid, uid, {'name': 'Administrator'})
self.user_raoul_id = self.res_users.create(cr, uid,
- {'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
+ {'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'alias_name': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
self.user_bert_id = self.res_users.create(cr, uid,
- {'name': 'Bert Tartignole', 'signature': 'SignBert', 'email': 'bert@bert.fr', 'login': 'bert', 'groups_id': [(6, 0, [])]})
+ {'name': 'Bert Tartignole', 'signature': 'SignBert', 'email': 'bert@bert.fr', 'login': 'bert', 'alias_name': 'bert', 'groups_id': [(6, 0, [])]})
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id)
self.user_admin = self.res_users.browse(cr, uid, uid)
@@ -82,7 +83,7 @@ class TestMailBase(common.TransactionCase):
# Test 'pigs' group to use through the various tests
self.group_pigs_id = self.mail_group.create(cr, uid,
- {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'},
+ {'name': 'Pigs', 'description': 'Fans of Pigs, unite !', 'alias_name': 'group+pigs'},
{'mail_create_nolog': True})
self.group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index 46f4555349d..ae5555c01ca 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -22,7 +22,7 @@
from openerp.addons.mail.mail_mail import mail_mail
from openerp.addons.mail.mail_thread import mail_thread
from openerp.addons.mail.tests.test_mail_base import TestMailBase
-from openerp.tools import mute_logger
+from openerp.tools import mute_logger, email_split
from openerp.tools.mail import html_sanitize
@@ -32,17 +32,17 @@ class test_mail(TestMailBase):
""" Test basic mail.alias setup works, before trying to use them for routing """
cr, uid = self.cr, self.uid
self.user_valentin_id = self.res_users.create(cr, uid,
- {'name': 'Valentin Cognito', 'email': 'valentin.cognito@gmail.com', 'login': 'valentin.cognito'})
+ {'name': 'Valentin Cognito', 'email': 'valentin.cognito@gmail.com', 'login': 'valentin.cognito', 'alias_name': 'valentin.cognito'})
self.user_valentin = self.res_users.browse(cr, uid, self.user_valentin_id)
self.assertEquals(self.user_valentin.alias_name, self.user_valentin.login, "Login should be used as alias")
self.user_pagan_id = self.res_users.create(cr, uid,
- {'name': 'Pagan Le Marchant', 'email': 'plmarchant@gmail.com', 'login': 'plmarchant@gmail.com'})
+ {'name': 'Pagan Le Marchant', 'email': 'plmarchant@gmail.com', 'login': 'plmarchant@gmail.com', 'alias_name': 'plmarchant@gmail.com'})
self.user_pagan = self.res_users.browse(cr, uid, self.user_pagan_id)
self.assertEquals(self.user_pagan.alias_name, 'plmarchant', "If login is an email, the alias should keep only the local part")
self.user_barty_id = self.res_users.create(cr, uid,
- {'name': 'Bartholomew Ironside', 'email': 'barty@gmail.com', 'login': 'b4r+_#_R3wl$$'})
+ {'name': 'Bartholomew Ironside', 'email': 'barty@gmail.com', 'login': 'b4r+_#_R3wl$$', 'alias_name': 'b4r+_#_R3wl$$'})
self.user_barty = self.res_users.browse(cr, uid, self.user_barty_id)
self.assertEquals(self.user_barty.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens')
@@ -479,7 +479,7 @@ class test_mail(TestMailBase):
'message_post: notification email sent to more than one email address instead of a precise partner')
self.assertIn(sent_email['email_to'][0], test_emailto,
'message_post: notification email email_to incorrect')
- self.assertEqual(sent_email['reply_to'], 'r@r', # was '"Followers of Pigs" <r@r>', but makes no sense
+ self.assertEqual(email_split(sent_email['reply_to']), ['r@r'], # was '"Followers of Pigs" <r@r>', but makes no sense
'message_post: notification email reply_to incorrect: should have raoul email')
self.assertEqual(_mail_subject, sent_email['subject'],
'message_post: notification email subject incorrect')
@@ -640,7 +640,7 @@ class test_mail(TestMailBase):
})
compose = mail_compose.browse(cr, uid, compose_id)
- # D: Post the comment, get created message for each group
+ # Do: Post the comment, get created message for each group
mail_compose.send_mail(cr, user_raoul.id, [compose_id], context={
'default_res_id': -1,
'active_ids': [self.group_pigs_id, group_bird_id]
@@ -679,6 +679,37 @@ class test_mail(TestMailBase):
self.assertEqual(set(bird_pids), set(test_pids),
'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account')
+ # Do: Compose in mass_mail, coming from list_view, we have an active_domain that should be supported
+ compose_id = mail_compose.create(cr, user_raoul.id,
+ {
+ 'subject': _subject,
+ 'body': '${object.description}',
+ 'partner_ids': [(4, p_c_id), (4, p_d_id)],
+ }, context={
+ 'default_composition_mode': 'mass_mail',
+ 'default_model': 'mail.group',
+ 'default_res_id': False,
+ 'active_ids': [self.group_pigs_id],
+ 'active_domain': [('name', 'in', ['Pigs', 'Bird'])],
+ })
+ compose = mail_compose.browse(cr, uid, compose_id)
+
+ # Do: Post the comment, get created message for each group
+ mail_compose.send_mail(
+ cr, user_raoul.id, [compose_id], context={
+ 'default_res_id': -1,
+ 'active_ids': [self.group_pigs_id, group_bird_id]
+ })
+ group_pigs.refresh()
+ group_bird.refresh()
+ message1 = group_pigs.message_ids[0]
+ message2 = group_bird.message_ids[0]
+
+ # Test: Pigs and Bird did receive their message
+ test_msg_ids = self.mail_message.search(cr, uid, [], limit=2)
+ self.assertIn(message1.id, test_msg_ids, 'compose wizard: Pigs did not receive its mass mailing message')
+ self.assertIn(message2.id, test_msg_ids, 'compose wizard: Bird did not receive its mass mailing message')
+
def test_30_needaction(self):
""" Tests for mail.message needaction. """
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
@@ -739,18 +770,21 @@ class test_mail(TestMailBase):
self.ir_model_data.create(cr, uid, {'name': 'mt_private', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_private_id})
mt_name_supername_id = self.mail_message_subtype.create(cr, uid, {'name': 'name_supername', 'description': 'Supername name'})
self.ir_model_data.create(cr, uid, {'name': 'mt_name_supername', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_name_supername_id})
+ mt_group_public_set_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public_set', 'description': 'Group set'})
+ self.ir_model_data.create(cr, uid, {'name': 'mt_group_public_set', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_set_id})
mt_group_public_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public', 'description': 'Group changed'})
self.ir_model_data.create(cr, uid, {'name': 'mt_group_public', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_id})
# Data: alter mail_group model for testing purposes (test on classic, selection and many2one fields)
self.mail_group._track = {
'public': {
- 'mail.mt_private': lambda self, cr, uid, obj, ctx=None: obj['public'] == 'private',
+ 'mail.mt_private': lambda self, cr, uid, obj, ctx=None: obj.public == 'private',
},
'name': {
- 'mail.mt_name_supername': lambda self, cr, uid, obj, ctx=None: obj['name'] == 'supername',
+ 'mail.mt_name_supername': lambda self, cr, uid, obj, ctx=None: obj.name == 'supername',
},
'group_public_id': {
+ 'mail.mt_group_public_set': lambda self, cr, uid, obj, ctx=None: obj.group_public_id,
'mail.mt_group_public': lambda self, cr, uid, obj, ctx=None: True,
},
}
@@ -787,21 +821,37 @@ class test_mail(TestMailBase):
self.assertIn(u'Public\u2192Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field')
- # Test: change public as public, group_public_id -> 1 subtype, name always tracked
+ # Test: change public as public, group_public_id -> 2 subtypes, name always tracked
self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'public': 'public', 'group_public_id': group_system_id})
self.group_pigs.refresh()
- self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: one message should have been produced')
- # Test: first produced message: mt_group_public_id, with name always tracked, public tracked on change
+ self.assertEqual(len(self.group_pigs.message_ids), 5, 'tracked: one message should have been produced')
+ # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change
last_msg = self.group_pigs.message_ids[-4]
- self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should not be linked to any subtype')
+ self.assertEqual(last_msg.subtype_id.id, mt_group_public_set_id, 'tracked: message should be linked to mt_group_public_set_id')
+ self.assertIn('Group set', last_msg.body, 'tracked: message body does not hold the subtype description')
+ self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
+ self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
+ # Test: second produced message: mt_group_public_id, with name always tracked, public tracked on change
+ last_msg = self.group_pigs.message_ids[-5]
+ self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id')
self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description')
self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
+ # Test: change group_public_id to False -> 1 subtype, name always tracked
+ self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'group_public_id': False})
+ self.group_pigs.refresh()
+ self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: one message should have been produced')
+ # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change
+ last_msg = self.group_pigs.message_ids[-6]
+ self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id')
+ self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description')
+ self.assertIn(u'Administration/Settings\u2192', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
+
# Test: change not tracked field, no tracking message
self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'description': 'Dummy'})
self.group_pigs.refresh()
- self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: No message should have been produced')
+ self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: No message should have been produced')
# Data: removed changes
public_col.track_visibility = None
diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py
index 5615e22b2c2..fade38869f1 100644
--- a/addons/mail/tests/test_mail_gateway.py
+++ b/addons/mail/tests/test_mail_gateway.py
@@ -99,20 +99,20 @@ class TestMailgateway(TestMailBase):
# --------------------------------------------------
# Do: find partner with email -> first partner should be found
- partner_info = self.mail_thread.message_find_partner_from_emails(cr, uid, None, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
+ partner_info = self.mail_thread.message_partner_info_from_emails(cr, uid, None, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
self.assertEqual(partner_info['full_name'], 'Maybe Raoul <test@test.fr>',
- 'mail_thread: message_find_partner_from_emails did not handle email')
+ 'mail_thread: message_partner_info_from_emails did not handle email')
self.assertEqual(partner_info['partner_id'], p_a_id,
- 'mail_thread: message_find_partner_from_emails wrong partner found')
+ 'mail_thread: message_partner_info_from_emails wrong partner found')
# Data: add some data about partners
# 2 - User BRaoul
p_b_id = self.res_partner.create(cr, uid, {'name': 'BRaoul', 'email': 'test@test.fr', 'user_ids': [(4, user_raoul.id)]})
# Do: find partner with email -> first user should be found
- partner_info = self.mail_thread.message_find_partner_from_emails(cr, uid, None, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
+ partner_info = self.mail_thread.message_partner_info_from_emails(cr, uid, None, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
self.assertEqual(partner_info['partner_id'], p_b_id,
- 'mail_thread: message_find_partner_from_emails wrong partner found')
+ 'mail_thread: message_partner_info_from_emails wrong partner found')
# --------------------------------------------------
# CASE1: with object
@@ -120,90 +120,106 @@ class TestMailgateway(TestMailBase):
# Do: find partner in group where there is a follower with the email -> should be taken
self.mail_group.message_subscribe(cr, uid, [group_pigs.id], [p_b_id])
- partner_info = self.mail_group.message_find_partner_from_emails(cr, uid, group_pigs.id, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
+ partner_info = self.mail_group.message_partner_info_from_emails(cr, uid, group_pigs.id, ['Maybe Raoul <test@test.fr>'], link_mail=False)[0]
self.assertEqual(partner_info['partner_id'], p_b_id,
- 'mail_thread: message_find_partner_from_emails wrong partner found')
+ 'mail_thread: message_partner_info_from_emails wrong partner found')
def test_05_mail_message_mail_mail(self):
""" Tests designed for testing email values based on mail.message, aliases, ... """
- cr, uid = self.cr, self.uid
-
- # Data: clean catchall domain
+ cr, uid, user_raoul_id = self.cr, self.uid, self.user_raoul_id
+
+ # Data: update + generic variables
+ reply_to1 = '_reply_to1@example.com'
+ reply_to2 = '_reply_to2@example.com'
+ email_from1 = 'from@example.com'
+ alias_domain = 'schlouby.fr'
+ raoul_from = 'Raoul Grosbedon <raoul@raoul.fr>'
+ raoul_from_alias = 'Raoul Grosbedon <raoul@schlouby.fr>'
+ raoul_reply = '"Followers of Pigs" <raoul@raoul.fr>'
+ raoul_reply_alias = '"Followers of Pigs" <group+pigs@schlouby.fr>'
+ # Data: remove alias_domain to see emails with alias
param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')])
self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
- # Do: create a mail_message with a reply_to, without message-id
- msg_id = self.mail_message.create(cr, uid, {'subject': 'Subject', 'body': 'Body', 'reply_to': 'custom@example.com'})
- msg = self.mail_message.browse(cr, uid, msg_id)
+ # Do: free message; specified values > default values
+ msg_id = self.mail_message.create(cr, user_raoul_id, {'reply_to': reply_to1, 'email_from': email_from1})
+ msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
# Test: message content
self.assertIn('reply_to', msg.message_id,
- 'mail_message: message_id should be specific to a mail_message with a given reply_to')
- self.assertEqual('custom@example.com', msg.reply_to,
- 'mail_message: incorrect reply_to')
- # Do: create a mail_mail with the previous mail_message and specified reply_to
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'reply_to': 'other@example.com', 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
- # Test: mail_mail content
- self.assertEqual(mail.reply_to, 'other@example.com',
- 'mail_mail: reply_to should be equal to the one coming from creation values')
+ 'mail_message: message_id should be specific to a mail_message with a given reply_to')
+ self.assertEqual(msg.reply_to, reply_to1,
+ 'mail_message: incorrect reply_to: should come from values')
+ self.assertEqual(msg.email_from, email_from1,
+ 'mail_message: incorrect email_from: should come from values')
# Do: create a mail_mail with the previous mail_message
- self.mail_message.write(cr, uid, [msg_id], {'reply_to': 'custom@example.com'})
- msg.refresh()
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
# Test: mail_mail content
- self.assertEqual(mail.reply_to, msg.reply_to,
- 'mail_mail: reply_to should be equal to the one coming from the mail_message')
-
- # Do: create a mail_message without a reply_to
- msg_id = self.mail_message.create(cr, uid, {'subject': 'Subject', 'body': 'Body', 'model': 'mail.group', 'res_id': self.group_pigs_id, 'email_from': False})
- msg = self.mail_message.browse(cr, uid, msg_id)
+ self.assertEqual(mail.reply_to, reply_to1,
+ 'mail_mail: incorrect reply_to: should come from mail.message')
+ self.assertEqual(mail.email_from, email_from1,
+ 'mail_mail: incorrect email_from: should come from mail.message')
+ # Do: create a mail_mail with the previous mail_message + specified reply_to
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel', 'reply_to': reply_to2})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
+ # Test: mail_mail content
+ self.assertEqual(mail.reply_to, reply_to2,
+ 'mail_mail: incorrect reply_to: should come from values')
+ self.assertEqual(mail.email_from, email_from1,
+ 'mail_mail: incorrect email_from: should come from mail.message')
+
+ # Do: mail_message attached to a document
+ msg_id = self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_pigs_id})
+ msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
# Test: message content
self.assertIn('mail.group', msg.message_id,
- 'mail_message: message_id should contain model')
+ 'mail_message: message_id should contain model')
self.assertIn('%s' % self.group_pigs_id, msg.message_id,
- 'mail_message: message_id should contain res_id')
+ 'mail_message: message_id should contain res_id')
self.assertFalse(msg.reply_to,
- 'mail_message: should not generate a reply_to address when not specified')
- # Do: create a mail_mail based on the previous mail_message
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
- # Test: mail_mail content
- self.assertFalse(mail.reply_to,
- 'mail_mail: reply_to should not have been guessed')
- # Update message
- self.mail_message.write(cr, uid, [msg_id], {'email_from': 'someone@example.com'})
- msg.refresh()
+ 'mail_message: incorrect reply_to: should not be generated if not specified')
+ self.assertEqual(msg.email_from, raoul_from,
+ 'mail_message: incorrect email_from: should be Raoul')
# Do: create a mail_mail based on the previous mail_message
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
# Test: mail_mail content
- self.assertEqual(mail.reply_to, msg.email_from,
- 'mail_mail: reply_to should be equal to mail_message.email_from when having no document or default alias')
+ self.assertEqual(mail.reply_to, raoul_reply,
+ 'mail_mail: incorrect reply_to: should be Raoul')
# Data: set catchall domain
- self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', 'schlouby.fr')
+ self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', alias_domain)
self.registry('ir.config_parameter').unlink(cr, uid, self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.alias')]))
# Update message
- self.mail_message.write(cr, uid, [msg_id], {'email_from': False, 'reply_to': False})
+ self.mail_message.write(cr, user_raoul_id, [msg_id], {'email_from': False, 'reply_to': False})
msg.refresh()
# Do: create a mail_mail based on the previous mail_message
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
# Test: mail_mail content
- self.assertEqual(mail.reply_to, '"Followers of Pigs" <group+pigs@schlouby.fr>',
- 'mail_mail: reply_to should equal the mail.group alias')
+ self.assertEqual(mail.reply_to, raoul_reply_alias,
+ 'mail_mail: incorrect reply_to: should be Pigs alias')
+
+ # Update message: test alias on email_from
+ msg_id = self.mail_message.create(cr, user_raoul_id, {})
+ msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
+ # Do: create a mail_mail based on the previous mail_message
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
+ # Test: mail_mail content
+ self.assertEqual(mail.reply_to, raoul_from_alias,
+ 'mail_mail: incorrect reply_to: should be message email_from using Raoul alias')
# Update message
- self.mail_message.write(cr, uid, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False})
+ self.mail_message.write(cr, user_raoul_id, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False})
msg.refresh()
# Do: create a mail_mail based on the previous mail_message
- mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
- mail = self.mail_mail.browse(cr, uid, mail_id)
+ mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
+ mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
# Test: mail_mail content
self.assertEqual(mail.reply_to, msg.email_from,
- 'mail_mail: reply_to should equal the mail_message email_from')
+ 'mail_mail: incorrect reply_to: should be message email_from')
# Data: set catchall alias
self.registry('ir.config_parameter').set_param(self.cr, self.uid, 'mail.catchall.alias', 'gateway')
@@ -214,23 +230,23 @@ class TestMailgateway(TestMailBase):
# Do: create a mail_mail based on the previous mail_message
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
mail = self.mail_mail.browse(cr, uid, mail_id)
- # Test: mail_mail content
+ # Test: mail_mail Content-Type
self.assertEqual(mail.reply_to, 'gateway@schlouby.fr',
- 'mail_mail: reply_to should equal the catchall email alias')
+ 'mail_mail: reply_to should equal the catchall email alias')
# Do: create a mail_mail
mail_id = self.mail_mail.create(cr, uid, {'state': 'cancel'})
mail = self.mail_mail.browse(cr, uid, mail_id)
# Test: mail_mail content
self.assertEqual(mail.reply_to, 'gateway@schlouby.fr',
- 'mail_mail: reply_to should equal the catchall email alias')
+ 'mail_mail: reply_to should equal the catchall email alias')
# Do: create a mail_mail
mail_id = self.mail_mail.create(cr, uid, {'state': 'cancel', 'reply_to': 'someone@example.com'})
mail = self.mail_mail.browse(cr, uid, mail_id)
# Test: mail_mail content
self.assertEqual(mail.reply_to, 'someone@example.com',
- 'mail_mail: reply_to should equal the rpely_to given to create')
+ 'mail_mail: reply_to should equal the rpely_to given to create')
@mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
def test_10_message_process(self):
@@ -238,9 +254,9 @@ class TestMailgateway(TestMailBase):
cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
def format_and_process(template, to='groups@example.com, other@gmail.com', subject='Frogs',
- extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
- msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
- model=None):
+ extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
+ msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
+ model=None):
self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', subject)]), [])
mail = template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
self.mail_thread.message_process(cr, uid, model, mail)
@@ -255,7 +271,10 @@ class TestMailgateway(TestMailBase):
alias_id = self.mail_alias.create(cr, uid, {
'alias_name': 'groups',
'alias_user_id': False,
- 'alias_model_id': self.mail_group_model_id})
+ 'alias_model_id': self.mail_group_model_id,
+ 'alias_parent_model_id': self.mail_group_model_id,
+ 'alias_parent_thread_id': self.group_pigs_id,
+ 'alias_contact': 'everyone'})
# --------------------------------------------------
# Test1: new record creation
@@ -270,155 +289,244 @@ class TestMailgateway(TestMailBase):
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
self.assertEqual(res[0].get('create_uid'), uid,
- 'message_process: group should have been created by uid as alias_user__id is False on the alias')
+ 'message_process: group should have been created by uid as alias_user__id is False on the alias')
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
- 'message_process: newly created group should have the incoming email in message_ids')
+ 'message_process: newly created group should have the incoming email in message_ids')
msg = frog_group.message_ids[0]
self.assertEqual('Frogs', msg.subject,
- 'message_process: newly created group should have the incoming email as first message')
+ 'message_process: newly created group should have the incoming email as first message')
self.assertIn('Please call me as soon as possible this afternoon!', msg.body,
- 'message_process: newly created group should have the incoming email as first message')
+ 'message_process: newly created group should have the incoming email as first message')
self.assertEqual('email', msg.type,
- 'message_process: newly created group should have an email as first message')
+ 'message_process: newly created group should have an email as first message')
self.assertEqual('Discussions', msg.subtype_id.name,
- 'message_process: newly created group should not have a log first message but an email')
+ 'message_process: newly created group should not have a log first message but an email')
# Test: message: unknown email address -> message has email_from, not author_id
self.assertFalse(msg.author_id,
- 'message_process: message on created group should not have an author_id')
+ 'message_process: message on created group should not have an author_id')
self.assertIn('test.sylvie.lelitre@agrolait.com', msg.email_from,
- 'message_process: message on created group should have an email_from')
+ 'message_process: message on created group should have an email_from')
# Test: followers: nobody
self.assertEqual(len(frog_group.message_follower_ids), 0, 'message_process: newly create group should not have any follower')
# Test: sent emails: no-one
self.assertEqual(len(sent_emails), 0,
- 'message_process: should create emails without any follower added')
+ 'message_process: should create emails without any follower added')
# Data: unlink group
frog_group.unlink()
- # Do: incoming email from a known partner on an alias with known recipients, alias is owned by user that can create a group
- self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id})
+ # Do: incoming email from an unknown partner on a Partners only alias -> bounce
+ self._init_mock_build_email()
+ self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'partners'})
+ frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other2@gmail.com')
+ # Test: no group created
+ self.assertTrue(len(frog_groups) == 0)
+ # Test: email bounced
+ sent_emails = self._build_email_kwargs_list
+ self.assertEqual(len(sent_emails), 1,
+ 'message_process: incoming email on Partners alias should send a bounce email')
+ self.assertIn('Frogs', sent_emails[0].get('subject'),
+ 'message_process: bounce email on Partners alias should contain the original subject')
+ self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
+ 'message_process: bounce email on Partners alias should have original email sender as recipient')
+
+ # Do: incoming email from an unknown partner on a Followers only alias -> bounce
+ self._init_mock_build_email()
+ self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'followers'})
+ frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other3@gmail.com')
+ # Test: no group created
+ self.assertTrue(len(frog_groups) == 0)
+ # Test: email bounced
+ sent_emails = self._build_email_kwargs_list
+ self.assertEqual(len(sent_emails), 1,
+ 'message_process: incoming email on Followers alias should send a bounce email')
+ self.assertIn('Frogs', sent_emails[0].get('subject'),
+ 'message_process: bounce email on Followers alias should contain the original subject')
+ self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
+ 'message_process: bounce email on Followers alias should have original email sender as recipient')
+
+ # Do: incoming email from a known partner on a Partners alias -> ok (+ test on alias.user_id)
+ self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id, 'alias_contact': 'partners'})
p1id = self.res_partner.create(cr, uid, {'name': 'Sylvie Lelitre', 'email': 'test.sylvie.lelitre@agrolait.com'})
- p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other@gmail.com'})
+ p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other4@gmail.com'})
self._init_mock_build_email()
- frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
+ frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other4@gmail.com')
sent_emails = self._build_email_kwargs_list
# Test: one group created by Raoul
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
self.assertEqual(res[0].get('create_uid'), self.user_raoul_id,
- 'message_process: group should have been created by alias_user_id')
+ 'message_process: group should have been created by alias_user_id')
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
- 'message_process: newly created group should have the incoming email in message_ids')
+ 'message_process: newly created group should have the incoming email in message_ids')
msg = frog_group.message_ids[0]
- # Test: message: unknown email address -> message has email_from, not author_id
+ # Test: message: author found
self.assertEqual(p1id, msg.author_id.id,
- 'message_process: message on created group should have Sylvie as author_id')
+ 'message_process: message on created group should have Sylvie as author_id')
self.assertIn('Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', msg.email_from,
- 'message_process: message on created group should have have an email_from')
- # Test: author (not recipient and not raoul (as alias owner)) added as follower
+ 'message_process: message on created group should have have an email_from')
+ # Test: author (not recipient and not Raoul (as alias owner)) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id]),
- 'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
+ 'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
# Test: sent emails: no-one, no bounce effet
+ sent_emails = self._build_email_kwargs_list
self.assertEqual(len(sent_emails), 0,
- 'message_process: should not bounce incoming emails')
+ 'message_process: should not bounce incoming emails')
# Data: unlink group
frog_group.unlink()
- # Do: incoming email from a known partner that is also an user that can create a mail.group
- self.res_users.create(cr, uid, {'partner_id': p1id, 'login': 'sylvie', 'groups_id': [(6, 0, [self.group_employee_id])]})
- frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
+ # Do: incoming email from a not follower Partner on a Followers only alias -> bounce
+ self._init_mock_build_email()
+ self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': False, 'alias_contact': 'followers'})
+ frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other5@gmail.com')
+ # Test: no group created
+ self.assertTrue(len(frog_groups) == 0)
+ # Test: email bounced
+ sent_emails = self._build_email_kwargs_list
+ self.assertEqual(len(sent_emails), 1,
+ 'message_process: incoming email on Partners alias should send a bounce email')
+
+ # Do: incoming email from a parent document follower on a Followers only alias -> ok
+ self._init_mock_build_email()
+ self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [p1id])
+ frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other6@gmail.com')
# Test: one group created by Raoul (or Sylvie maybe, if we implement it)
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
- 'message_process: newly created group should have the incoming email in message_ids')
+ 'message_process: newly created group should have the incoming email in message_ids')
# Test: author (and not recipient) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id]),
- 'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
+ 'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
# Test: sent emails: no-one, no bounce effet
+ sent_emails = self._build_email_kwargs_list
self.assertEqual(len(sent_emails), 0,
- 'message_process: should not bounce incoming emails')
+ 'message_process: should not bounce incoming emails')
# --------------------------------------------------
- # Test2: discussion update
+ # Test2: update-like alias
# --------------------------------------------------
- # Do: even with a wrong destination, a reply should end up in the correct thread
- frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
- msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
- to='erroneous@example.com>', subject='Re: news',
- extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ # Do: Pigs alias is restricted, should bounce
+ self._init_mock_build_email()
+ self.mail_group.write(cr, uid, [frog_group.id], {'alias_name': 'frogs', 'alias_contact': 'followers', 'alias_force_thread_id': frog_group.id})
+ frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
+ msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
+ to='frogs@example.com>', subject='Re: news')
+ # Test: no group 'Re: news' created, still only 1 Frogs group
+ self.assertEqual(len(frog_groups), 0,
+ 'message_process: reply on Frogs should not have created a new group with new subject')
+ frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
+ self.assertEqual(len(frog_groups), 1,
+ 'message_process: reply on Frogs should not have created a duplicate group with old subject')
+ frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
+ # Test: email bounced
+ sent_emails = self._build_email_kwargs_list
+ self.assertEqual(len(sent_emails), 1,
+ 'message_process: incoming email on Followers alias should send a bounce email')
+ self.assertIn('Re: news', sent_emails[0].get('subject'),
+ 'message_process: bounce email on Followers alias should contain the original subject')
+
+ # Do: Pigs alias is restricted, should accept Followers
+ self._init_mock_build_email()
+ self.mail_group.message_subscribe(cr, uid, [frog_group.id], [p2id])
+ frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
+ msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>',
+ to='frogs@example.com>', subject='Re: cats')
# Test: no group 'Re: news' created, still only 1 Frogs group
self.assertEqual(len(frog_groups), 0,
- 'message_process: reply on Frogs should not have created a new group with new subject')
+ 'message_process: reply on Frogs should not have created a new group with new subject')
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
self.assertEqual(len(frog_groups), 1,
- 'message_process: reply on Frogs should not have created a duplicate group with old subject')
+ 'message_process: reply on Frogs should not have created a duplicate group with old subject')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one new message
self.assertEqual(len(frog_group.message_ids), 2, 'message_process: group should contain 2 messages after reply')
+ # Test: sent emails: 1 (Sylvie copy of the incoming email, but no bounce)
+ sent_emails = self._build_email_kwargs_list
+ self.assertEqual(len(sent_emails), 1,
+ 'message_process: one email should have been generated')
+ self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to')[0],
+ 'message_process: email should be sent to Sylvie')
+ self.mail_group.message_unsubscribe(cr, uid, [frog_group.id], [p2id])
+
+ # --------------------------------------------------
+ # Test3: discussion and replies
+ # --------------------------------------------------
+
+ # Do: even with a wrong destination, a reply should end up in the correct thread
+ frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
+ msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
+ to='erroneous@example.com>', subject='Re: news',
+ extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ # Test: no group 'Re: news' created, still only 1 Frogs group
+ self.assertEqual(len(frog_groups), 0,
+ 'message_process: reply on Frogs should not have created a new group with new subject')
+ frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
+ self.assertEqual(len(frog_groups), 1,
+ 'message_process: reply on Frogs should not have created a duplicate group with old subject')
+ frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
+ # Test: one new message
+ self.assertEqual(len(frog_group.message_ids), 3, 'message_process: group should contain 2 messages after reply')
# Test: author (and not recipient) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id, p2id]),
- 'message_process: after reply, group should have 2 followers')
+ 'message_process: after reply, group should have 2 followers')
# Do: due to some issue, same email goes back into the mailgateway
- frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
- msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
- subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
+ msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
+ subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
# Test: no group 'Re: news' created, still only 1 Frogs group
self.assertEqual(len(frog_groups), 0,
- 'message_process: reply on Frogs should not have created a new group with new subject')
+ 'message_process: reply on Frogs should not have created a new group with new subject')
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
self.assertEqual(len(frog_groups), 1,
- 'message_process: reply on Frogs should not have created a duplicate group with old subject')
+ 'message_process: reply on Frogs should not have created a duplicate group with old subject')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: no new message
- self.assertEqual(len(frog_group.message_ids), 2, 'message_process: message with already existing message_id should not have been duplicated')
+ self.assertEqual(len(frog_group.message_ids), 3, 'message_process: message with already existing message_id should not have been duplicated')
# Test: message_id is still unique
msg_ids = self.mail_message.search(cr, uid, [('message_id', 'ilike', '<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>')])
self.assertEqual(len(msg_ids), 1,
- 'message_process: message with already existing message_id should not have been duplicated')
+ 'message_process: message with already existing message_id should not have been duplicated')
# --------------------------------------------------
- # Test3: email_from and partner finding
+ # Test4: email_from and partner finding
# --------------------------------------------------
# Data: extra partner with Raoul's email -> test the 'better author finding'
extra_partner_id = self.res_partner.create(cr, uid, {'name': 'A-Raoul', 'email': 'test_raoul@email.com'})
- # extra_user_id = self.res_users.create(cr, uid, {'name': 'B-Raoul', 'email': self.user_raoul.email})
- # extra_user_pid = self.res_users.browse(cr, uid, extra_user_id).partner_id.id
# Do: post a new message, with a known partner -> duplicate emails -> partner
format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
- to='erroneous@example.com>', subject='Re: news (2)',
- msg_id='<1198923581.41972151344608186760.JavaMail.new1@agrolait.com>',
- extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ to='erroneous@example.com>', subject='Re: news (2)',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new1@agrolait.com>',
+ extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: author is A-Raoul (only existing)
self.assertEqual(frog_group.message_ids[0].author_id.id, extra_partner_id,
- 'message_process: email_from -> author_id wrong')
+ 'message_process: email_from -> author_id wrong')
# Do: post a new message, with a known partner -> duplicate emails -> user
frog_group.message_unsubscribe([extra_partner_id])
raoul_email = self.user_raoul.email
self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'})
format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
- to='erroneous@example.com>', subject='Re: news (3)',
- msg_id='<1198923581.41972151344608186760.JavaMail.new2@agrolait.com>',
- extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ to='erroneous@example.com>', subject='Re: news (3)',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new2@agrolait.com>',
+ extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: author is Raoul (user), not A-Raoul
self.assertEqual(frog_group.message_ids[0].author_id.id, self.partner_raoul_id,
- 'message_process: email_from -> author_id wrong')
+ 'message_process: email_from -> author_id wrong')
# Do: post a new message, with a known partner -> duplicate emails -> partner because is follower
frog_group.message_unsubscribe([self.partner_raoul_id])
@@ -426,54 +534,54 @@ class TestMailgateway(TestMailBase):
raoul_email = self.user_raoul.email
self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'})
format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
- to='erroneous@example.com>', subject='Re: news (3)',
- msg_id='<1198923581.41972151344608186760.JavaMail.new3@agrolait.com>',
- extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
+ to='erroneous@example.com>', subject='Re: news (3)',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new3@agrolait.com>',
+ extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: author is Raoul (user), not A-Raoul
self.assertEqual(frog_group.message_ids[0].author_id.id, extra_partner_id,
- 'message_process: email_from -> author_id wrong')
+ 'message_process: email_from -> author_id wrong')
self.res_users.write(cr, uid, self.user_raoul_id, {'email': raoul_email})
# --------------------------------------------------
- # Test4: misc gateway features
+ # Test5: misc gateway features
# --------------------------------------------------
# Do: incoming email with model that does not accepts incoming emails must raise
self.assertRaises(AssertionError,
- format_and_process,
- MAIL_TEMPLATE,
- to='noone@example.com', subject='spam', extra='', model='res.country',
- msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
+ format_and_process,
+ MAIL_TEMPLATE,
+ to='noone@example.com', subject='spam', extra='', model='res.country',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
# Do: incoming email without model and without alias must raise
self.assertRaises(AssertionError,
- format_and_process,
- MAIL_TEMPLATE,
- to='noone@example.com', subject='spam', extra='',
- msg_id='<1198923581.41972151344608186760.JavaMail.new5@agrolait.com>')
+ format_and_process,
+ MAIL_TEMPLATE,
+ to='noone@example.com', subject='spam', extra='',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new5@agrolait.com>')
# Do: incoming email with model that accepting incoming emails as fallback
frog_groups = format_and_process(MAIL_TEMPLATE,
- to='noone@example.com',
- subject='Spammy', extra='', model='mail.group',
- msg_id='<1198923581.41972151344608186760.JavaMail.new6@agrolait.com>')
+ to='noone@example.com',
+ subject='Spammy', extra='', model='mail.group',
+ msg_id='<1198923581.41972151344608186760.JavaMail.new6@agrolait.com>')
self.assertEqual(len(frog_groups), 1,
- 'message_process: erroneous email but with a fallback model should have created a new mail.group')
+ 'message_process: erroneous email but with a fallback model should have created a new mail.group')
# Do: incoming email in plaintext should be stored as html
frog_groups = format_and_process(MAIL_TEMPLATE_PLAINTEXT,
- to='groups@example.com', subject='Frogs Return', extra='',
- msg_id='<deadcafe.1337@smtp.agrolait.com>')
+ to='groups@example.com', subject='Frogs Return', extra='',
+ msg_id='<deadcafe.1337@smtp.agrolait.com>')
# Test: one group created with one message
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
msg = frog_group.message_ids[0]
# Test: plain text content should be wrapped and stored as html
- self.assertEqual(msg.body, '<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>',
- 'message_process: plaintext incoming email incorrectly parsed')
+ self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', msg.body,
+ 'message_process: plaintext incoming email incorrectly parsed')
@mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
def test_20_thread_parent_resolution(self):
@@ -494,26 +602,26 @@ class TestMailgateway(TestMailBase):
# Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms
# 0. Direct alias match
reply_msg1 = format(MAIL_TEMPLATE, to='Pretty Pigs <group+pigs@example.com>',
- extra='In-Reply-To: %s' % msg1.message_id,
- msg_id='<1198923581.41972151344608186760.JavaMail.2@agrolait.com>')
+ extra='In-Reply-To: %s' % msg1.message_id,
+ msg_id='<1198923581.41972151344608186760.JavaMail.2@agrolait.com>')
self.mail_group.message_process(cr, uid, None, reply_msg1)
# 1. In-Reply-To header
reply_msg2 = format(MAIL_TEMPLATE, to='erroneous@example.com',
- extra='In-Reply-To: %s' % msg1.message_id,
- msg_id='<1198923581.41972151344608186760.JavaMail.3@agrolait.com>')
+ extra='In-Reply-To: %s' % msg1.message_id,
+ msg_id='<1198923581.41972151344608186760.JavaMail.3@agrolait.com>')
self.mail_group.message_process(cr, uid, None, reply_msg2)
# 2. References header
reply_msg3 = format(MAIL_TEMPLATE, to='erroneous@example.com',
- extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id,
- msg_id='<1198923581.41972151344608186760.JavaMail.4@agrolait.com>')
+ extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id,
+ msg_id='<1198923581.41972151344608186760.JavaMail.4@agrolait.com>')
self.mail_group.message_process(cr, uid, None, reply_msg3)
# 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, but not to mail (not in msg1.child_ids)
reply_msg4 = format(MAIL_TEMPLATE, to='erroneous@example.com',
- extra='', subject='Re: [%s] 1' % self.group_pigs_id,
- msg_id='<1198923581.41972151344608186760.JavaMail.5@agrolait.com>')
+ extra='', subject='Re: [%s] 1' % self.group_pigs_id,
+ msg_id='<1198923581.41972151344608186760.JavaMail.5@agrolait.com>')
self.mail_group.message_process(cr, uid, 'mail.group', reply_msg4)
group_pigs.refresh()
@@ -525,12 +633,19 @@ class TestMailgateway(TestMailBase):
""" Testing private discussion between partners. """
cr, uid = self.cr, self.uid
+ def format(template, to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
+ extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
+ msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
+ return template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
+
# Do: Raoul writes to Bert and Administrator, with a thread_model in context that should not be taken into account
msg1_pids = [self.partner_admin_id, self.partner_bert_id]
- msg1_id = self.mail_thread.message_post(cr, self.user_raoul_id, False,
- partner_ids=msg1_pids,
- subtype='mail.mt_comment',
- context={'thread_model': 'mail.group'})
+ msg1_id = self.mail_thread.message_post(
+ cr, self.user_raoul_id, False,
+ partner_ids=msg1_pids,
+ subtype='mail.mt_comment',
+ context={'thread_model': 'mail.group'}
+ )
# Test: message recipients
msg = self.mail_message.browse(cr, uid, msg1_id)
@@ -539,16 +654,26 @@ class TestMailgateway(TestMailBase):
test_pids = msg1_pids
test_nids = msg1_pids
self.assertEqual(set(msg_pids), set(test_pids),
- 'message_post: private discussion: incorrect recipients')
+ 'message_post: private discussion: incorrect recipients')
self.assertEqual(set(msg_nids), set(test_nids),
- 'message_post: private discussion: incorrect notified recipients')
+ 'message_post: private discussion: incorrect notified recipients')
self.assertEqual(msg.model, False,
- 'message_post: private discussion: context key "thread_model" not correctly ignored when having no res_id')
+ 'message_post: private discussion: context key "thread_model" not correctly ignored when having no res_id')
+ # Test: message reply_to and message-id
+ self.assertFalse(msg.reply_to,
+ 'message_post: private discussion: initial message should not have any reply_to specified')
+ self.assertIn('openerp-private', msg.message_id,
+ 'message_post: private discussion: message-id should contain the private keyword')
# Do: Bert replies through mailgateway (is a customer)
- msg2_id = self.mail_thread.message_post(cr, uid, False,
- author_id=self.partner_bert_id,
- parent_id=msg1_id, subtype='mail.mt_comment')
+ reply_message = format(MAIL_TEMPLATE, to='not_important@mydomain.com',
+ email_from='bert@bert.fr',
+ extra='In-Reply-To: %s' % msg.message_id,
+ msg_id='<test30.JavaMail.0@agrolait.com>')
+ self.mail_thread.message_process(cr, uid, None, reply_message)
+
+ # Test: last mail_message created
+ msg2_id = self.mail_message.search(cr, uid, [], limit=1)[0]
# Test: message recipients
msg = self.mail_message.browse(cr, uid, msg2_id)
@@ -556,14 +681,32 @@ class TestMailgateway(TestMailBase):
msg_nids = [p.id for p in msg.notified_partner_ids]
test_pids = [self.partner_admin_id, self.partner_raoul_id]
test_nids = test_pids
+ self.assertEqual(msg.author_id.id, self.partner_bert_id,
+ 'message_post: private discussion: wrong author through mailgatewya based on email')
+ self.assertEqual(set(msg_pids), set(test_pids),
+ 'message_post: private discussion: incorrect recipients when replying')
+ self.assertEqual(set(msg_nids), set(test_nids),
+ 'message_post: private discussion: incorrect notified recipients when replying')
+
+ # Do: Bert replies through chatter (is a customer)
+ msg3_id = self.mail_thread.message_post(
+ cr, uid, False,
+ author_id=self.partner_bert_id,
+ parent_id=msg1_id, subtype='mail.mt_comment')
+
+ # Test: message recipients
+ msg = self.mail_message.browse(cr, uid, msg3_id)
+ msg_pids = [p.id for p in msg.partner_ids]
+ msg_nids = [p.id for p in msg.notified_partner_ids]
+ test_pids = [self.partner_admin_id, self.partner_raoul_id]
+ test_nids = test_pids
self.assertEqual(set(msg_pids), set(test_pids),
- 'message_post: private discussion: incorrect recipients when replying')
+ 'message_post: private discussion: incorrect recipients when replying')
self.assertEqual(set(msg_nids), set(test_nids),
- 'message_post: private discussion: incorrect notified recipients when replying')
+ 'message_post: private discussion: incorrect notified recipients when replying')
# Do: Administrator replies
- msg3_id = self.mail_thread.message_post(cr, uid, False,
- parent_id=msg2_id, subtype='mail.mt_comment')
+ msg3_id = self.mail_thread.message_post(cr, uid, False, parent_id=msg3_id, subtype='mail.mt_comment')
# Test: message recipients
msg = self.mail_message.browse(cr, uid, msg3_id)
@@ -572,6 +715,6 @@ class TestMailgateway(TestMailBase):
test_pids = [self.partner_bert_id, self.partner_raoul_id]
test_nids = test_pids
self.assertEqual(set(msg_pids), set(test_pids),
- 'message_post: private discussion: incorrect recipients when replying')
+ 'message_post: private discussion: incorrect recipients when replying')
self.assertEqual(set(msg_nids), set(test_nids),
- 'message_post: private discussion: incorrect notified recipients when replying')
+ 'message_post: private discussion: incorrect notified recipients when replying')
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 7bf4d47d1ff..6ab1f0e81ff 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -73,6 +73,11 @@ class mail_compose_message(osv.TransientModel):
res_id = context.get('default_res_id', context.get('active_id'))
message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id')))
active_ids = context.get('active_ids')
+ if 'active_domain' in context: # not context.get() because we want to keep global [] domains
+ result['use_active_domain'] = True
+ result['active_domain'] = '%s' % context.get('active_domain')
+ else:
+ result['active_domain'] = ''
# get default values according to the composition mode
if composition_mode == 'reply':
@@ -112,6 +117,8 @@ class mail_compose_message(osv.TransientModel):
'partner_ids': fields.many2many('res.partner',
'mail_compose_message_res_partner_rel',
'wizard_id', 'partner_id', 'Additional contacts'),
+ 'use_active_domain': fields.boolean('Use active domain'),
+ 'active_domain': fields.char('Active domain', readonly=True),
'post': fields.boolean('Post a copy in the document',
help='Post a copy of the message on the document communication history.'),
'notify': fields.boolean('Notify followers',
@@ -126,11 +133,9 @@ class mail_compose_message(osv.TransientModel):
_defaults = {
'composition_mode': 'comment',
- 'email_from': lambda self, cr, uid, ctx={}: self.pool.get('mail.mail')._get_default_from(cr, uid, context=ctx),
'body': lambda self, cr, uid, ctx={}: '',
'subject': lambda self, cr, uid, ctx={}: False,
'partner_ids': lambda self, cr, uid, ctx={}: [],
- 'notify': lambda self, cr, uid, ctx={}: False,
'post': lambda self, cr, uid, ctx={}: True,
'same_thread': lambda self, cr, uid, ctx={}: True,
}
@@ -158,7 +163,7 @@ class mail_compose_message(osv.TransientModel):
return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context)
- def _notify(self, cr, uid, newid, context=None):
+ def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True):
""" Override specific notify method of mail.message, because we do
not want that feature in the wizard. """
return
@@ -235,9 +240,18 @@ class mail_compose_message(osv.TransientModel):
for wizard in self.browse(cr, uid, ids, context=context):
mass_mail_mode = wizard.composition_mode == 'mass_mail'
active_model_pool = self.pool[wizard.model if wizard.model else 'mail.thread']
+ if not hasattr(active_model_pool, 'message_post'):
+ context['thread_model'] = wizard.model
+ active_model_pool = self.pool['mail.thread']
+
+ # wizard works in batch mode: [res_id] or active_ids or active_domain
+ if mass_mail_mode and wizard.use_active_domain and wizard.model:
+ res_ids = self.pool[wizard.model].search(cr, uid, eval(wizard.active_domain), context=context)
+ elif mass_mail_mode and wizard.model and active_ids:
+ res_ids = active_ids
+ else:
+ res_ids = [wizard.res_id]
- # wizard works in batch mode: [res_id] or active_ids
- res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id]
for res_id in res_ids:
# mail.message values, according to the wizard options
post_values = {
@@ -257,13 +271,15 @@ class mail_compose_message(osv.TransientModel):
new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context)
attachment_ids.append(new_attach_id)
post_values['attachment_ids'] = attachment_ids
- post_values.update(email_dict)
# email_from: mass mailing only can specify another email_from
if email_dict.get('email_from'):
post_values['email_from'] = email_dict.pop('email_from')
# replies redirection: mass mailing only
if not wizard.same_thread:
post_values['reply_to'] = email_dict.pop('reply_to')
+ else:
+ email_dict.pop('reply_to')
+ post_values.update(email_dict)
# clean the context (hint: mass mailing sets some default values that
# could be wrongly interpreted by mail_mail)
context.pop('default_email_to', None)
@@ -280,11 +296,10 @@ class mail_compose_message(osv.TransientModel):
elif mass_mail_mode: # mass mail: is a log pushed to recipients unless specified, author not added
if not wizard.notify:
subtype = False
- context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author
- msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
- # mass_mailing, post without notify: notify specific partners
- if mass_mail_mode and not wizard.notify and post_values['partner_ids']:
- self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context)
+ context = dict(context,
+ mail_notify_force_send=False, # do not send emails directly but use the queue instead
+ mail_create_nosubscribe=True) # add context key to avoid subscribing the author
+ active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml
index 1ca07b6a94c..07d39bb0965 100644
--- a/addons/mail/wizard/mail_compose_message_view.xml
+++ b/addons/mail/wizard/mail_compose_message_view.xml
@@ -12,6 +12,9 @@
<field name="model" invisible="1"/>
<field name="res_id" invisible="1"/>
<field name="parent_id" invisible="1"/>
+ <field name="mail_server_id" invisible="1"/>
+ <field name="use_active_domain" invisible="1"/>
+ <field name="active_domain" invisible="1"/>
<!-- visible wizard -->
<field name="email_from"
attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>