diff options
| author | Christophe Matthieu <chm@openerp.com> | 2013-09-06 10:35:00 +0200 |
|---|---|---|
| committer | Christophe Matthieu <chm@openerp.com> | 2013-09-06 10:35:00 +0200 |
| commit | 8a3f1742e0e9c7acaae726e5c7106d2d7df7e970 (patch) | |
| tree | 78ae5d981fcd25677caea5c8b3999b1e77bf4547 /addons/mail | |
| parent | 6f6c59ec1de1881a70e4bf728ea5d8b56bd609f9 (diff) | |
| parent | c80cf7d0646463b290137c26fbada568a6942b00 (diff) | |
[MERGE] from website-al
bzr revid: chm@openerp.com-20130906083500-4n5knaxiqt4cg2ht
Diffstat (limited to 'addons/mail')
| -rw-r--r-- | addons/mail/__openerp__.py | 1 | ||||
| -rw-r--r-- | addons/mail/data/mail_data.xml | 5 | ||||
| -rw-r--r-- | addons/mail/i18n/bs.po | 1687 | ||||
| -rw-r--r-- | addons/mail/mail_mail.py | 11 | ||||
| -rw-r--r-- | addons/mail/mail_message.py | 18 | ||||
| -rw-r--r-- | addons/mail/mail_thread.py | 24 | ||||
| -rw-r--r-- | addons/mail/static/lib/jquery.expander/jquery.expander.js | 385 | ||||
| -rw-r--r-- | addons/mail/static/src/css/mail.css | 3 | ||||
| -rw-r--r-- | addons/mail/static/src/js/mail.js | 18 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/mail.xml | 8 | ||||
| -rw-r--r-- | addons/mail/tests/test_mail_features.py | 2 | ||||
| -rw-r--r-- | addons/mail/tests/test_mail_gateway.py | 364 |
12 files changed, 1987 insertions, 539 deletions
diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 8f072dd9082..1b9111d3040 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -83,7 +83,6 @@ 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', diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 4e935e4aa88..e1ca797670b 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -40,6 +40,11 @@ <field name="args">()</field> </record> + <!-- Admin should not receivve emails at creation --> + <record id="base.partner_root" model="res.partner"> + <field name="notification_email_send">none</field> + </record> + <!-- Catchall Email Alias --> <record id="icp_mail_catchall_alias" model="ir.config_parameter"> <field name="key">mail.catchall.alias</field> 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 "ò" +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/mail_mail.py b/addons/mail/mail_mail.py index bf76216e7e4..9fa80b9be46 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -139,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 @@ -321,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_message.py b/addons/mail/mail_message.py index a5d8f348a2b..bcb66433964 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -395,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, @@ -673,14 +679,14 @@ class mail_message(osv.Model): - no model, no res_id, I create a private message OR - pid in message_follower_ids if model, res_id OR - mail_notification (parent_id.id, pid) exists, uid has been notified of the parent, OR - - uid have write access on the related document if model, res_id, OR + - uid have write or create access on the related document if model, res_id, OR - otherwise: raise - write: if - author_id == pid, uid is the author, OR - - uid has write access on the related document if model, res_id + - uid has write or create access on the related document if model, res_id - otherwise: raise - unlink: if - - uid has write access on the related document if model, res_id + - uid has write or create access on the related document if model, res_id - otherwise: raise """ def _generate_model_record_ids(msg_val, msg_ids=[]): diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 306e5fd5905..ebb7a40c719 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -682,6 +682,12 @@ class mail_thread(osv.AbstractModel): 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): @@ -695,7 +701,7 @@ class mail_thread(osv.AbstractModel): return () # Existing Document: check model accepts the mailgateway - if thread_id and not hasattr(model_pool, 'message_update'): + 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 @@ -706,7 +712,7 @@ class mail_thread(osv.AbstractModel): return () # New Document: check model accepts the mailgateway - if not thread_id and not hasattr(model_pool, 'message_new'): + 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) @@ -798,16 +804,16 @@ class mail_thread(osv.AbstractModel): # 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) + email_from, email_to, message_id, mail_message.id, custom_values, uid) route = self.message_route_verify(cr, uid, message, message_dict, - (message.model, message.res_id, custom_values, uid, None), + (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 [] @@ -1032,8 +1038,10 @@ class mail_thread(osv.AbstractModel): # text/plain -> <pre/> body = tools.append_content_to_html(u'', body, preserve=True) else: - alternative = (message.get_content_type() == 'multipart/alternative') + alternative = False for part in message.walk(): + if part.get_content_type() == 'multipart/alternative': + alternative = True if part.get_content_maintype() == 'multipart': continue # skip container filename = part.get_filename() # None if normal part 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: '… ', - - 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 c6f7c911f8a..a4beca607ba 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -118,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 */ diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 1d3b5a91176..79694cfc9f5 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.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, @@ -943,7 +944,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(); @@ -966,6 +966,8 @@ 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); }, @@ -994,15 +996,11 @@ openerp.mail = function (session) { return false; }, - expender: function () { - this.$('.oe_msg_body:first').expander({ - slicePoint: this.options.truncate_limit, - expandText: _t('read more'), - userCollapseText: _t('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; }, /** diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 94ee1ff452f..1be9754e1c7 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -266,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"> diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py index ae5555c01ca..e9563e19246 100644 --- a/addons/mail/tests/test_mail_features.py +++ b/addons/mail/tests/test_mail_features.py @@ -294,7 +294,7 @@ class test_mail(TestMailBase): # Data creation # -------------------------------------------------- # 0 - Update existing users-partners - self.res_users.write(cr, uid, [uid], {'email': 'a@a'}) + self.res_users.write(cr, uid, [uid], {'email': 'a@a', 'notification_email_send': 'comment'}) self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'}) # 1 - Bert Tartopoils, with email, should receive emails for comments and emails p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py index 2e0ba336dc4..3fd8ebd3101 100644 --- a/addons/mail/tests/test_mail_gateway.py +++ b/addons/mail/tests/test_mail_gateway.py @@ -20,7 +20,7 @@ ############################################################################## from openerp.addons.mail.tests.test_mail_base import TestMailBase -from openerp.tools import mute_logger, email_split +from openerp.tools import mute_logger MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com> To: {to} @@ -81,6 +81,67 @@ Please call me as soon as possible this afternoon! Sylvie """ +MAIL_MULTIPART_MIXED = """Return-Path: <ignasse.carambar@gmail.com> +X-Original-To: raoul@grosbedon.fr +Delivered-To: raoul@grosbedon.fr +Received: by mail1.grosbedon.com (Postfix, from userid 10002) + id E8166BFACA; Fri, 23 Aug 2013 13:18:01 +0200 (CEST) +X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail1.grosbedon.com +X-Spam-Level: +X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,FREEMAIL_FROM, + HTML_MESSAGE,RCVD_IN_DNSWL_LOW autolearn=unavailable version=3.3.1 +Received: from mail-ie0-f173.google.com (mail-ie0-f173.google.com [209.85.223.173]) + by mail1.grosbedon.com (Postfix) with ESMTPS id 9BBD7BFAAA + for <raoul@openerp.fr>; Fri, 23 Aug 2013 13:17:55 +0200 (CEST) +Received: by mail-ie0-f173.google.com with SMTP id qd12so575130ieb.4 + for <raoul@grosbedon.fr>; Fri, 23 Aug 2013 04:17:54 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=mime-version:date:message-id:subject:from:to:content-type; + bh=dMNHV52EC7GAa7+9a9tqwT9joy9z+1950J/3A6/M/hU=; + b=DGuv0VjegdSrEe36ADC8XZ9Inrb3Iu+3/52Bm+caltddXFH9yewTr0JkCRQaJgMwG9 + qXTQgP8qu/VFEbCh6scu5ZgU1hknzlNCYr3LT+Ih7dAZVUEHUJdwjzUU1LFV95G2RaCd + /Lwff6CibuUvrA+0CBO7IRKW0Sn5j0mukYu8dbaKsm6ou6HqS8Nuj85fcXJfHSHp6Y9u + dmE8jBh3fHCHF/nAvU+8aBNSIzl1FGfiBYb2jCoapIuVFitKR4q5cuoodpkH9XqqtOdH + DG+YjEyi8L7uvdOfN16eMr7hfUkQei1yQgvGu9/5kXoHg9+Gx6VsZIycn4zoaXTV3Nhn + nu4g== +MIME-Version: 1.0 +X-Received: by 10.50.124.65 with SMTP id mg1mr1144467igb.43.1377256674216; + Fri, 23 Aug 2013 04:17:54 -0700 (PDT) +Received: by 10.43.99.71 with HTTP; Fri, 23 Aug 2013 04:17:54 -0700 (PDT) +Date: Fri, 23 Aug 2013 13:17:54 +0200 +Message-ID: <CAP76m_V4BY2F7DWHzwfjteyhW8L2LJswVshtmtVym+LUJ=rASQ@mail.gmail.com> +Subject: Test mail multipart/mixed +From: =?ISO-8859-1?Q?Raoul Grosbedon=E9e?= <ignasse.carambar@gmail.com> +To: Followers of ASUSTeK-Joseph-Walters <raoul@grosbedon.fr> +Content-Type: multipart/mixed; boundary=089e01536c4ed4d17204e49b8e96 + +--089e01536c4ed4d17204e49b8e96 +Content-Type: multipart/alternative; boundary=089e01536c4ed4d16d04e49b8e94 + +--089e01536c4ed4d16d04e49b8e94 +Content-Type: text/plain; charset=ISO-8859-1 + +Should create a multipart/mixed: from gmail, *bold*, with attachment. + +-- +Marcel Boitempoils. + +--089e01536c4ed4d16d04e49b8e94 +Content-Type: text/html; charset=ISO-8859-1 + +<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>-- <br>Marcel Boitempoils.</div> + +--089e01536c4ed4d16d04e49b8e94-- +--089e01536c4ed4d17204e49b8e96 +Content-Type: text/plain; charset=US-ASCII; name="test.txt" +Content-Disposition: attachment; filename="test.txt" +Content-Transfer-Encoding: base64 +X-Attachment-Id: f_hkpb27k00 + +dGVzdAo= +--089e01536c4ed4d17204e49b8e96--""" + class TestMailgateway(TestMailBase): @@ -101,9 +162,9 @@ class TestMailgateway(TestMailBase): # Do: find partner with email -> first partner should be found 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_partner_info_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_partner_info_from_emails wrong partner found') + 'mail_thread: message_partner_info_from_emails wrong partner found') # Data: add some data about partners # 2 - User BRaoul @@ -112,7 +173,7 @@ class TestMailgateway(TestMailBase): # Do: find partner with email -> first user should be found 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_partner_info_from_emails wrong partner found') + 'mail_thread: message_partner_info_from_emails wrong partner found') # -------------------------------------------------- # CASE1: with object @@ -122,7 +183,7 @@ class TestMailgateway(TestMailBase): self.mail_group.message_subscribe(cr, uid, [group_pigs.id], [p_b_id]) 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_partner_info_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, ... """ @@ -146,46 +207,46 @@ class TestMailgateway(TestMailBase): 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') + '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') + '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') + 'mail_message: incorrect email_from: should come from values') # Do: create a mail_mail with 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, reply_to1, - 'mail_mail: incorrect reply_to: should come from mail.message') + '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') + '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') + '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') + '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: incorrect reply_to: should not be generated if not specified') + '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') + '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, 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_reply, - 'mail_mail: incorrect reply_to: should be Raoul') + 'mail_mail: incorrect reply_to: should be Raoul') # Data: set catchall domain self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', alias_domain) @@ -199,7 +260,7 @@ class TestMailgateway(TestMailBase): mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) # Test: mail_mail content self.assertEqual(mail.reply_to, raoul_reply_alias, - 'mail_mail: incorrect reply_to: should be Pigs 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, {}) @@ -209,7 +270,7 @@ class TestMailgateway(TestMailBase): 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') + 'mail_mail: incorrect reply_to: should be message email_from using Raoul alias') # Update message self.mail_message.write(cr, user_raoul_id, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False}) @@ -219,7 +280,7 @@ class TestMailgateway(TestMailBase): 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: incorrect reply_to: should be 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') @@ -232,21 +293,39 @@ class TestMailgateway(TestMailBase): mail = self.mail_mail.browse(cr, uid, mail_id) # 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') + + def test_09_message_parse(self): + """ Testing incoming emails parsing """ + cr, uid = self.cr, self.uid + + res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE_PLAINTEXT) + self.assertIn('Please call me as soon as possible this afternoon!', res.get('body', ''), + 'message_parse: missing text in text/plain body after parsing') + + res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE) + self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res.get('body', ''), + 'message_parse: missing html in multipart/alternative body after parsing') + + res = self.mail_thread.message_parse(cr, uid, MAIL_MULTIPART_MIXED) + self.assertNotIn('Should create a multipart/mixed: from gmail, *bold*, with attachment', res.get('body', ''), + 'message_parse: text version should not be in body after parsing multipart/mixed') + self.assertIn('<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res.get('body', ''), + 'message_parse: html version should be in body after parsing multipart/mixed') @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') def test_10_message_process(self): @@ -254,9 +333,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) @@ -289,29 +368,29 @@ 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() @@ -324,11 +403,11 @@ class TestMailgateway(TestMailBase): # 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') + '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') + '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') + '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() @@ -339,11 +418,11 @@ class TestMailgateway(TestMailBase): # 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') + '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') + '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') + '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'}) @@ -357,24 +436,24 @@ 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'), 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: 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') + '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() @@ -387,7 +466,7 @@ class TestMailgateway(TestMailBase): # 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') + '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() @@ -398,15 +477,15 @@ class TestMailgateway(TestMailBase): 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: update-like alias @@ -416,43 +495,43 @@ class TestMailgateway(TestMailBase): 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') + 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') + '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: 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') + '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') + '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') + 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') + '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') + 'message_process: email should be sent to Sylvie') self.mail_group.message_unsubscribe(cr, uid, [frog_group.id], [p2id]) # -------------------------------------------------- @@ -461,40 +540,40 @@ class TestMailgateway(TestMailBase): # 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) + 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') + '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), 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='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) + 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), 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') # -------------------------------------------------- # Test4: email_from and partner finding @@ -505,28 +584,28 @@ class TestMailgateway(TestMailBase): # 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]) @@ -534,14 +613,14 @@ 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}) @@ -551,37 +630,37 @@ class TestMailgateway(TestMailBase): # 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.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') + 'message_process: plaintext incoming email incorrectly parsed') @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') def test_20_thread_parent_resolution(self): @@ -602,26 +681,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() @@ -633,12 +712,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) @@ -647,16 +733,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) @@ -664,14 +760,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) @@ -680,6 +794,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') |
