summaryrefslogtreecommitdiff
path: root/addons/mail/static/src
diff options
context:
space:
mode:
authorTejas Tank <tta@openerp.com>2012-08-17 16:16:10 +0530
committerTejas Tank <tta@openerp.com>2012-08-17 16:16:10 +0530
commitf4f0d42a70d62320b0d0b0d3aab16336b696a485 (patch)
treeac01c07c02a5284eb67d044e92e3255506e70a08 /addons/mail/static/src
parentae1ed5900caf1d0429ef731343af4ae364d989ae (diff)
parent6666d5993f9753c6ec3aec721b41ebd3fbc6c18c (diff)
Merged with latest.
bzr revid: tta@openerp.com-20120817104610-h7pova57capzm1by
Diffstat (limited to 'addons/mail/static/src')
-rw-r--r--addons/mail/static/src/css/mail.css271
-rw-r--r--addons/mail/static/src/css/mail_compose_message.css16
-rw-r--r--addons/mail/static/src/css/mail_group.css14
-rw-r--r--addons/mail/static/src/img/_fp.pngbin7725 -> 0 bytes
-rw-r--r--addons/mail/static/src/img/attachment.pngbin0 -> 1142 bytes
-rw-r--r--addons/mail/static/src/img/checklist.pngbin0 -> 976 bytes
-rw-r--r--addons/mail/static/src/img/feeds-hover.pngbin15696 -> 0 bytes
-rw-r--r--addons/mail/static/src/img/feeds.pngbin5647 -> 0 bytes
-rw-r--r--addons/mail/static/src/img/formatting.pngbin0 -> 3466 bytes
-rw-r--r--addons/mail/static/src/js/mail.js1124
-rw-r--r--addons/mail/static/src/js/mail_followers.js122
-rw-r--r--addons/mail/static/src/xml/mail.xml131
-rw-r--r--addons/mail/static/src/xml/mail_followers.xml31
13 files changed, 1117 insertions, 592 deletions
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 0cf932f9a25..28a32b8c7a3 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -1,7 +1,6 @@
-
-/* ------------------------------ */
-/* Wall */
-/* ------------------------------ */
+/* ------------------------------------------------------------ */
+/* Wall
+/* ------------------------------------------------------------ */
.openerp div.oe_mail_wall {
overflow: auto;
@@ -9,6 +8,17 @@
background: white;
}
+.openerp div.oe_mail_wall_main {
+ float: left;
+ width: 560px;
+ margin: 8px;
+}
+
+.openerp div.oe_mail_wall_aside {
+ margin-left: 565px;
+ margin: 8px;
+}
+
.openerp div.oe_mail_wall_action {
padding: 8px;
background: #eee;
@@ -21,36 +31,32 @@
clear: both;
}
-.openerp .oe_mail_wall_action textarea {
+.openerp div.oe_mail_wall_action .oe_mail_msg_content {
+ width: 484px;
+}
+
+.openerp div.oe_mail_wall_action textarea.oe_mail_compose_textarea,
+.openerp div.oe_mail_wall_action div.oe_mail_compose_message_body_text textarea {
width: 474px;
height: 60px;
padding: 4px;
- margin-bottom: 8px;
- float: right;
+ margin-bottom: 2px;
}
-/* 2 columns view */
-.openerp div.oe_mail_wall_main {
- float: left;
- width: 560px;
- margin: 8px;
+.openerp ul.oe_mail_wall_threads {
+ margin-top: 8px;
}
-.openerp div.oe_mail_wall_aside {
- margin-left: 565px;
- margin: 8px;
-}
+/* Specific display of threads in the wall */
+/* ------------------------------------------------------------ */
-/* Threads */
-.openerp .oe_mail_wall_threads {
- margin-top: 8px;
-}
-.openerp .oe_mail_wall_threads textarea {
- height: 40px;
+.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea {
+ width: 434px;
+ height: 30px;
padding: 4px;
}
-.openerp .oe_mail_wall_thread:first .oe_mail_msg_notification {
+.openerp li.oe_mail_wall_thread:first .oe_mail_msg_notification {
border-top: 0;
}
@@ -64,7 +70,7 @@
width: 486px;
}
-.openerp div.oe_mail_msg_content li{
+.openerp div.oe_mail_msg_content > li {
float: left;
margin-right: 3px;
}
@@ -79,40 +85,32 @@
}
-/* ------------------------------ */
-/* RecordThread */
-/* ------------------------------ */
+/* ------------------------------------------------------------ */
+/* RecordThread
+/* ------------------------------------------------------------ */
-.openerp div.oe_mail_recthread {
+.openerp .oe_form div.oe_chatter {
overflow: auto;
}
-/* Left-side CSS */
-.openerp div.oe_mail_recthread_actions {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_recthread_followers {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_recthread_followers img.oe_mail_msg_image {
- width: 28px;
- height: 28px;
- margin: 4px;
+.openerp div.oe_mail_recthread {
+ /*overflow: auto;*/
}
-/* RecordThread: 2 columns view */
-.openerp div.oe_mail_recthread_left {
+.openerp div.oe_mail_recthread_main {
float: left;
width: 560px;
}
-.openerp div.oe_mail_recthread_right {
+.openerp div.oe_mail_recthread_aside {
float: right;
width: 250px;
}
+.openerp div.oe_mail_recthread_actions {
+ margin-bottom: 8px;
+}
+
.openerp div.oe_mail_recthread_actions button {
width: 120px;
}
@@ -144,16 +142,17 @@
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
}
-.openerp textarea.oe_mail_action_textarea {
- height: 60px;
- padding: 5px;
+.openerp div.oe_mail_recthread_followers {
+ margin-bottom: 8px;
}
-/* ------------------------------ */
-/* ThreadDisplay */
-/* ------------------------------ */
+
+/* ------------------------------------------------------------ */
+/* Thread
+/* ------------------------------------------------------------ */
.openerp div.oe_mail_thread_action {
+ display: none;
white-space: normal;
padding: 8px;
background: #eee;
@@ -166,6 +165,26 @@
clear: both;
}
+/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */
+.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea,
+.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea {
+ width: 474px;
+ height: 60px;
+ padding: 4px;
+ font-size: 12px;
+ border: 1px solid #cccccc;
+}
+
+/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */
+.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea:focus,
+.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea:focus {
+ outline: 0;
+ border-color: rgba(82, 168, 236, 0.8);
+ -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+ -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
.openerp div.oe_mail_thread_display {
white-space: normal;
}
@@ -174,7 +193,7 @@
margin-left: 66px;
}
-.openerp div.oe_mail_thread_subthread .oe_mail_thread_msg:last-child {
+.openerp div.oe_mail_thread_subthread li.oe_mail_thread_msg:last-child {
margin-bottom: 8px;
}
@@ -183,18 +202,23 @@
border-bottom: 1px solid #D2D9E7;
}
-.openerp .oe_mail_thread_msg:after {
+.openerp li.oe_mail_thread_msg:after {
content: "";
display: block;
clear: both;
}
-.openerp .oe_mail_thread_msg > div:after {
+.openerp li.oe_mail_thread_msg > div:after {
content: "";
display: block;
clear: both;
}
+.openerp div.oe_mail_msg {
+ padding: 0;
+ margin: 0 0 4px 0;
+}
+
.openerp .oe_mail_msg_notification,
.openerp .oe_mail_msg_comment,
.openerp .oe_mail_msg_email {
@@ -203,11 +227,6 @@
border-top: 1px solid #ccc;
}
-.openerp .oe_email_icon {
- width: 50px;
- height: 50px;
-}
-
.openerp div.oe_mail_thread_subthread .oe_mail_msg_comment {
background: #eee;
}
@@ -230,9 +249,18 @@
clear: both;
}
-.openerp img.oe_mail_msg_image {
+.openerp img.oe_mail_icon {
width: 50px;
height: 50px;
+}
+
+.openerp img.oe_mail_thumbnail {
+ width: 28px;
+ height: 28px;
+ margin: 4px;
+}
+
+.openerp img.oe_mail_frame {
text-align: center;
overflow: hidden;
-moz-border-radius: 3px;
@@ -247,48 +275,60 @@
clip: rect(5px, 40px, 45px, 0px);
}
-/* ------------------------------ */
-/* Styling (should be openerp) */
-/* ------------------------------ */
+.openerp .oe_mail_invisible {
+ display: none;
+}
+
+/* ------------------------------------------------------------ */
+/* mail.compose.message form view & OpenERP hacks
+/* ------------------------------------------------------------ */
-.openerp input.oe_mail, textarea.oe_mail {
- width: 432px;
- padding: 4px;
- font-size: 12px;
- border: 1px solid #cccccc;
- -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
- -ms-transition: border linear 0.2s, box-shadow linear 0.2s;
- -o-transition: border linear 0.2s, box-shadow linear 0.2s;
- transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
+/* form_view: delete white background */
+.openerp .oe_mail_msg_content div.oe_formview {
+ background-color: transparent;
}
-.openerp input.oe_mail:focus, textarea.oe_mail:focus {
- outline: 0;
- border-color: rgba(82, 168, 236, 0.8);
- -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+.openerp .oe_mail_msg_content div.oe_form_nosheet {
+ margin: 0px;
}
-.openerp div.oe_mail_msg {
- padding: 0;
- margin: 0 0 4px 0;
+.openerp .oe_mail_msg_content table.oe_form_group {
+ margin: 0px;
}
-.openerp .oe_mail_oe_bold {
- font-weight: bold;
+.openerp .oe_mail_msg_content table.oe_form_field,
+.openerp .oe_mail_msg_content div.oe_form_field {
+ padding: 0px;
+}
+
+.openerp .oe_mail_msg_content td.oe_form_group_cell {
+ vertical-align: bottom;
+}
+
+/* subject: change width */
+.openerp .oe_mail_msg_content .oe_form .oe_form_field input {
+ width: 472px;
+}
+
+/* body_html: cleditor */
+.openerp .oe_mail_msg_content div.cleditorMain {
+ border: 1px solid #cccccc;
}
-/* ------------------------------ */
-/* Messages layout */
-/* ------------------------------ */
+/* destination_partner_ids */
+.openerp .oe_mail_msg_content div.text-core {
+ height: 22px !important;
+ width: 472px;
+}
+
+/* buttons */
+.openerp .oe_mail_msg_content .oe_mail_compose_message_icons button.oe_form_button {
+ padding: 1px;
+}
+
+/* ------------------------------------------------------------ */
+/* Messages layout
+/* ------------------------------------------------------------ */
.openerp .oe_mail_msg .oe_mail_msg_title {
margin: 0;
@@ -309,6 +349,11 @@
color: #888;
}
+.openerp .oe_mail_msg_footer li {
+ float: left;
+ margin-right: 3px;
+}
+
.openerp .oe_mail_msg_body {
margin-bottom: .5em;
text-align: justify;
@@ -318,12 +363,16 @@
display: inline;
}
-/* Read more/less link */
-.openerp .oe_mail_msg_content .expand,
-.openerp .oe_mail_msg_content .reduce {
+.openerp .oe_mail_oe_bold {
font-weight: bold;
}
+/* Read more/less link */
+.openerp .oe_mail_msg_content .oe_mail_reduce {
+ position: absolute;
+ right: 0;
+}
+
/* Dropdown menu */
.openerp .oe_mail_msg_content .oe_dropdown_toggle {
position: absolute;
@@ -360,35 +409,3 @@
padding: 0;
list-style-type: square;
}
-
-
-/* ------------------------------ */
-/* Group Form */
-/* ------------------------------ */
-
-div.oe_mail_group_advanced_details {
- display: none;
-}
-
-.oe_form_sheetbg.openerp_mail_group_sheet {
- min-height: 0px;
- max-height: none;
-}
-
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_sheet {
- min-height: 0px;
- max-height: none;
- padding: 0px 18px;
- max-width: 80%;
-}
-
-/* Resize group logo */
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_image > img {
- max-width: 100px;
- max-height: 100px;
-}
-
-/* Resize group description */
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_text > textarea {
- height: 40px;
-}
diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css
new file mode 100644
index 00000000000..4cd1339ded4
--- /dev/null
+++ b/addons/mail/static/src/css/mail_compose_message.css
@@ -0,0 +1,16 @@
+/* ------------------------------ */
+/* Compose Message Wizard Form */
+/* ------------------------------ */
+
+.openerp tr td .oe_form_field.oe_mail_compose_message_invisible {
+ display: none;
+}
+
+.openerp .oe_mail_compose_message_icons {
+ text-align: right;
+}
+
+.openerp .oe_mail_compose_message_icons img {
+ width: 20px;
+ height: 20px;
+}
diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css
index b03be086fc6..8f4ca61d531 100644
--- a/addons/mail/static/src/css/mail_group.css
+++ b/addons/mail/static/src/css/mail_group.css
@@ -20,12 +20,6 @@
max-width: 80%;
}
-/* Resize group logo */
-.openerp .oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_image > img {
- max-width: 100px;
- max-height: 100px;
-}
-
/* Resize group description */
.openerp .oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_text > textarea {
height: 40px;
@@ -63,14 +57,8 @@
.oe_group_photo {
width: 100px;
- height: auto;
- clip: rect(0px, 100px, 100px, 0px);
-}
-
-.oe_group_photo_wide {
height: 100px;
- width: auto;
- clip: rect(0px, 115px, 100px, 15px);
+ clip: rect(0px, 100px, 100px, 0px);
}
.oe_group_details {
diff --git a/addons/mail/static/src/img/_fp.png b/addons/mail/static/src/img/_fp.png
deleted file mode 100644
index 17dce2a047c..00000000000
--- a/addons/mail/static/src/img/_fp.png
+++ /dev/null
Binary files differ
diff --git a/addons/mail/static/src/img/attachment.png b/addons/mail/static/src/img/attachment.png
new file mode 100644
index 00000000000..5cc0c33257d
--- /dev/null
+++ b/addons/mail/static/src/img/attachment.png
Binary files differ
diff --git a/addons/mail/static/src/img/checklist.png b/addons/mail/static/src/img/checklist.png
new file mode 100644
index 00000000000..d252606ff53
--- /dev/null
+++ b/addons/mail/static/src/img/checklist.png
Binary files differ
diff --git a/addons/mail/static/src/img/feeds-hover.png b/addons/mail/static/src/img/feeds-hover.png
deleted file mode 100644
index 67070d3e7df..00000000000
--- a/addons/mail/static/src/img/feeds-hover.png
+++ /dev/null
Binary files differ
diff --git a/addons/mail/static/src/img/feeds.png b/addons/mail/static/src/img/feeds.png
deleted file mode 100644
index ac2293c0bf5..00000000000
--- a/addons/mail/static/src/img/feeds.png
+++ /dev/null
Binary files differ
diff --git a/addons/mail/static/src/img/formatting.png b/addons/mail/static/src/img/formatting.png
new file mode 100644
index 00000000000..cf45fdf192f
--- /dev/null
+++ b/addons/mail/static/src/img/formatting.png
Binary files differ
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 2140bbe474a..8443c6dbf86 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -4,211 +4,656 @@ openerp.mail = function(session) {
var mail = session.mail = {};
+ openerp_mail_followers(session, mail); // import mail_followers.js
+
/**
- * Add records to sorted_comments array
- * @param {Array} records records from mail.message sorted by date desc
- * @returns {Object} cs comments_structure: dict
- * cs.model_to_root_ids = {model: [root_ids], }
- * cs.new_root_ids = [new_root_ids]
- * cs.root_ids = [root_ids]
- * cs.msgs = {record.id: record,}
- * cs.tree_struct = {record.id: {
- * 'level': record_level in hierarchy, 0 is root,
- * 'msg_nbr': number of childs,
- * 'direct_childs': [msg_ids],
- * 'all_childs': [msg_ids],
- * 'for_thread_msgs': [records],
- * 'ancestors': [msg_ids], } }
+ * ------------------------------------------------------------
+ * FormView
+ * ------------------------------------------------------------
+ *
+ * Override of formview do_action method, to catch all return action about
+ * mail.compose.message. The purpose is to bind 'Send by e-mail' buttons
+ * and redirect them to the Chatter.
*/
- function tools_sort_comments(cs, records, parent_id) {
- var cur_iter = 0; var max_iter = 10; var modif = true;
- while ( modif && (cur_iter++) < max_iter) {
- modif = false;
- _(records).each(function (record) {
- // root and not yet recorded
- if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) {
- // add to model -> root_list ids
- if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id];
- else cs['model_to_root_ids'][record.model].push(record.id);
- // add root data
- cs['new_root_ids'].push(record.id);
- // add record
- cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []};
- cs['msgs'][record.id] = record;
- modif = true;
- }
- // not yet recorded, but parent is recorded
- else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) {
- var parent_level = cs['tree_struct'][record.parent_id[0]]['level'];
- // update parent structure
- cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id);
- cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record);
- // update ancestors structure
- for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) {
- cs['tree_struct'][ancestor_id]['all_childs'].push(record.id);
+
+ session.web.FormView = session.web.FormView.extend({
+ do_action: function(action, on_close) {
+ if (action.res_model == 'mail.compose.message' && this.fields && this.fields.message_ids && this.fields.message_ids.view.get("actual_mode") != 'create') {
+ var record_thread = this.fields.message_ids;
+ var thread = record_thread.thread;
+ thread.instantiate_composition_form('comment', true, false, 0, action.context);
+ return false;
+ }
+ else {
+ return this._super(action, on_close);
+ }
+ },
+ });
+
+
+ /**
+ * ------------------------------------------------------------
+ * ChatterUtils
+ * ------------------------------------------------------------
+ *
+ * This class holds a few tools method that will be used by
+ * the various Chatter widgets.
+ */
+
+ mail.ChatterUtils = {
+
+ /**
+ * mail_int_mapping: structure to keep a trace of internal links mapping
+ * mail_int_mapping['model'] = {
+ * 'name_get': [[id,label], [id,label], ...]
+ * 'fetch_ids': [id, id, ...] } */
+ //var mail_int_mapping = {};
+
+ /**
+ * mail_msg_struct: structure to orrganize chatter messages
+ */
+ //var mail_msg_struct = {}; // TODO: USE IT OR NOT :)
+
+ /* generic chatter events binding */
+ bind_events: function(widget) {
+ // event: click on an internal link to a document: model, login
+ widget.$element.delegate('a.oe_mail_internal_link', 'click', function (event) {
+ event.preventDefault();
+ // lazy implementation: fetch data and try to redirect
+ if (! event.srcElement.dataset.resModel) return false;
+ else var res_model = event.srcElement.dataset.resModel;
+ var res_login = event.srcElement.dataset.resLogin;
+ if (! res_login) return false;
+ var ds = new session.web.DataSet(widget, res_model);
+ var defer = ds.call('search', [[['login', '=', res_login]]]).pipe(function (records) {
+ if (records[0]) {
+ widget.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]});
}
- // add record
- cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []};
- cs['msgs'][record.id] = record;
- modif = true;
+ else return false;
+ });
+ });
+ },
+
+ /** get an image in /web/binary/image?... */
+ get_image: function(session_prefix, session_id, model, field, id) {
+ return session_prefix + '/web/binary/image?session_id=' + session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
+ },
+
+ /** checks if tue current user is the message author */
+ is_author: function (widget, message_user_id) {
+ return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id);
+ },
+
+ /**
+ * Add records to comments_structure array
+ * @param {Array} records records from mail.message sorted by date desc
+ * @returns {Object} cs comments_structure: dict
+ * cs.model_to_root_ids = {model: [root_ids], }
+ * cs.new_root_ids = [new_root_ids]
+ * cs.root_ids = [root_ids]
+ * cs.msgs = {record.id: record,}
+ * cs.tree_struct = {record.id: {
+ * 'level': record_level in hierarchy, 0 is root,
+ * 'msg_nbr': number of childs,
+ * 'direct_childs': [msg_ids],
+ * 'all_childs': [msg_ids],
+ * 'for_thread_msgs': [records],
+ * 'ancestors': [msg_ids], } }
+ */
+ records_struct_add_records: function(cs, records, parent_id) {
+ var cur_iter = 0; var max_iter = 10; var modif = true;
+ while ( modif && (cur_iter++) < max_iter) {
+ modif = false;
+ _(records).each(function (record) {
+ // root and not yet recorded
+ if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) {
+ // add to model -> root_list ids
+ if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id];
+ else cs['model_to_root_ids'][record.model].push(record.id);
+ // add root data
+ cs['new_root_ids'].push(record.id);
+ // add record
+ cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []};
+ cs['msgs'][record.id] = record;
+ modif = true;
+ }
+ // not yet recorded, but parent is recorded
+ else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) {
+ var parent_level = cs['tree_struct'][record.parent_id[0]]['level'];
+ // update parent structure
+ cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id);
+ cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record);
+ // update ancestors structure
+ for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) {
+ cs['tree_struct'][ancestor_id]['all_childs'].push(record.id);
+ }
+ // add record
+ cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []};
+ cs['msgs'][record.id] = record;
+ modif = true;
+ }
+ });
+ }
+ return cs;
+ },
+
+ /* copy cs.new_root_ids into cs.root_ids */
+ records_struct_update_after_display: function(cs) {
+ // update TODO
+ cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']);
+ cs['new_root_ids'] = [];
+ return cs;
+ },
+
+ /**
+ * CONTENT MANIPULATION
+ *
+ * Regular expressions
+ * - (^|\s)@((\w|@|\.)*): @login@log.log, supports inner '@' for
+ * logins that are emails
+ * 1. '(void)'
+ * 2. login@log.log
+ * - (^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]: [ir.attachment,3|My Label],
+ * for internal links to model ir.attachment, id=3, and with
+ * optional label 'My Label'. Note that having a '|Label' is not
+ * mandatory, because the regex should still be correct.
+ * 1. '(void)'
+ * 2. 'ir'
+ * 3. 'attachment'
+ * 4. '3'
+ * 5. 'My Label'
+ */
+
+ /** Removes html tags, except b, em, br, ul, li */
+ do_text_remove_html_tags: function (string) {
+ var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('&lt;(/)?(b|em|br|br /|ul|li|div)\\s*&gt;', 'gi'), '<$1$2>');
+ return html;
+ },
+
+ /** Replaces line breaks by html line breaks (br) */
+ do_text_nl2br: function (str, is_xhtml) {
+ var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
+ return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2');
+ },
+
+ /* Add a prefix before each new line of the original string */
+ do_text_quote: function (str, prefix) {
+ return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> ');
+ },
+
+ /**
+ * Replaces some expressions
+ * - @login - shorcut to link to a res.user, given its login
+ * - [ir.attachment,3|My Label] - shortcut to an internal
+ * document
+ * - :name - shortcut to an image
+ */
+ do_replace_expressions: function (string) {
+ var self = this;
+ var icon_list = ['al', 'pinky']
+ /* shortcut to user: @login */
+ var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
+ var regex_res = regex_login.exec(string);
+ while (regex_res != null) {
+ var login = regex_res[2];
+ string = string.replace(regex_res[0], regex_res[1] + '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>');
+ regex_res = regex_login.exec(string);
+ }
+ /* shortcut for internal document */
+ var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g);
+ var regex_res = regex_login.exec(string);
+ while (regex_res != null) {
+ var res_model = regex_res[2] + '.' + regex_res[3];
+ var res_id = regex_res[4];
+ if (! regex_res[5]) {
+ var label = res_model + ':' + res_id }
+ else {
+ var label = regex_res[5];
+ }
+ string = string.replace(regex_res[0], regex_res[1] + '<a href="#model=' + res_model + '&id=' + res_id + '>' + label + '</a>');
+ regex_res = regex_login.exec(string);
+ }
+ /* special shortcut: :name, try to find an icon if in list */
+ var regex_login = new RegExp(/(^|\s):((\w)*)/g);
+ var regex_res = regex_login.exec(string);
+ while (regex_res != null) {
+ var icon_name = regex_res[2];
+ if (_.include(icon_list, icon_name))
+ string = string.replace(regex_res[0], regex_res[1] + '<img src="/mail/static/src/img/_' + icon_name + '.png" width="22px" height="22px" alt="' + icon_name + '"/>');
+ regex_res = regex_login.exec(string);
+ }
+ return string;
+ },
+
+ /**
+ * Checks a string to find an expression that will be replaced
+ * by an internal link and requiring a name_get to replace
+ * the expression.
+ * :param mapping: structure to keep a trace of internal links mapping
+ * mapping['model'] = {
+ * name_get': [[id,label], [id,label], ...]
+ * 'to_fetch_ids': [id, id, ...]
+ * }
+ * CURRENTLY NOT IMPLEMENTED */
+ do_check_for_name_get_mapping: function(string, mapping) {
+ /* shortcut to user: @login */
+ //var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
+ //var regex_res = regex_login.exec(string);
+ //while (regex_res != null) {
+ //var login = regex_res[2];
+ //if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; }
+ //this.map_hash['res.users']['login'].push(login);
+ //regex_res = regex_login.exec(string);
+ //}
+ /* document link with name_get: [res.model,name] */
+ /* internal link with id: [res.model,id], or [res.model,id|display_name] */
+ //var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
+ //regex_res = regex_intlink.exec(string);
+ //while (regex_res != null) {
+ //var res_model = regex_res[2] + '.' + regex_res[3];
+ //var res_name = regex_res[4];
+ //if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; }
+ //this.map_hash[res_model]['name'].push(res_name);
+ //regex_res = regex_intlink.exec(string);
+ //}
+ },
+
+ /**
+ * Updates the mapping; check for to_fetch_ids for each recorded
+ * model, and perform a name_get to update the mapping.
+ * CURRENTLY NOT IMPLEMENTED */
+ do_update_name_get_mapping: function(mapping) {
+ },
+ };
+
+
+ /**
+ * ------------------------------------------------------------
+ * ComposeMessage widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a form to compose a new message.
+ * This form is an OpenERP form_view, build on a mail.compose.message
+ * wizard.
+ */
+
+ mail.ComposeMessage = session.web.Widget.extend({
+ template: 'mail.compose_message',
+
+ /**
+ * @param {Object} parent parent
+ * @param {Object} [params]
+ * @param {String} [params.res_model] res_model of document [REQUIRED]
+ * @param {Number} [params.res_id] res_id of record [REQUIRED]
+ * @param {Number} [params.email_mode] true/false, tells whether
+ * we are in email sending mode
+ * @param {Number} [params.formatting] true/false, tells whether
+ * we are in advance formatting mode
+ * @param {String} [params.model] mail.compose.message.mode (see
+ * composition wizard)
+ * @param {Number} [params.msg_id] id of a message in case we are in
+ * reply mode
+ */
+ init: function(parent, params) {
+ var self = this;
+ this._super(parent);
+ // options
+ this.params = params || {};
+ this.params.context = params.context || {};
+ this.params.email_mode = params.email_mode || false;
+ this.params.formatting = params.formatting || false;
+ this.params.mode = params.mode || 'comment';
+ this.params.form_xml_id = params.form_xml_id || 'email_compose_message_wizard_form_chatter';
+ this.params.form_view_id = false;
+ if (this.params.mode == 'reply') {
+ this.params.active_id = this.params.msg_id;
+ } else {
+ this.params.active_id = this.params.res_id;
+ }
+ this.email_mode = false;
+ this.formatting = false;
+ },
+
+ /**
+ * Reinitialize the widget field values to the default values. The
+ * purpose is to avoid to destroy and re-build a form view. Default
+ * values are therefore given as for an onchange. */
+ reinit: function() {
+ var self = this;
+ if (! this.form_view) return;
+ var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body_html', 'dest_partner_ids'], this.ds_compose.get_context()]).then(
+ function (result) {
+ self.form_view.on_processed_onchange({'value': result}, []);
+ });
+ return call_defer;
+ },
+
+ /**
+ * Override-hack of do_action: clean the form */
+ do_action: function(action, on_close) {
+ // this.init_comments();
+ return this._super(action, on_close);
+ },
+
+ /**
+ * Widget start function
+ * - builds and initializes the form view */
+ start: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ // customize display: add avatar, clean previous content
+ var user_avatar = mail.ChatterUtils.get_image(this.session.prefix,
+ this.session.session_id, 'res.users', 'image_small', this.session.uid);
+ this.$element.find('img.oe_mail_icon').attr('src', user_avatar);
+ this.$element.find('div.oe_mail_msg_content').empty();
+ // create a context for the default_get of the compose form
+ var widget_context = {
+ 'active_model': this.params.res_model,
+ 'active_id': this.params.active_id,
+ 'mail.compose.message.mode': this.params.mode,
+ };
+ var context = _.extend({}, this.params.context, widget_context);
+ this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context);
+ // find the id of the view to display in the chatter form
+ var data_ds = new session.web.DataSetSearch(this, 'ir.model.data');
+ var deferred_form_id =data_ds.call('get_object_reference', ['mail', this.params.form_xml_id]).then( function (result) {
+ if (result) {
+ self.params.form_view_id = result[1];
}
+ }).pipe(this.proxy('create_form_view'));
+ return deferred_form_id;
+ },
+
+ /**
+ * Create a FormView, then append it to the to widget DOM. */
+ create_form_view: function () {
+ var self = this;
+ // destroy previous form_view if any
+ if (this.form_view) { this.form_view.destroy(); }
+ // create the FormView
+ this.form_view = new session.web.FormView(this, this.ds_compose, this.params.form_view_id, {
+ action_buttons: false,
+ pager: false,
+ initial_mode: 'edit',
+ disable_autofocus: true,
});
- }
- return cs;
- }
+ // add the form, bind events, activate the form
+ var msg_node = this.$element.find('div.oe_mail_msg_content');
+ return $.when(this.form_view.appendTo(msg_node)).pipe(function() {
+ self.bind_events();
+ self.form_view.do_show();
+ if (self.params.email_mode) { self.toggle_email_mode(); }
+ if (self.params.formatting) { self.toggle_formatting_mode(); }
+ });
+ },
+
+ destroy: function() {
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Bind events in the widget. Each event is slightly described
+ * in the function. */
+ bind_events: function() {
+ var self = this;
+ this.$element.find('button.oe_form_button').click(function (event) {
+ event.preventDefault();
+ });
+ // event: click on 'Send an Email' link that toggles the form for
+ // sending an email (partner_ids)
+ this.$element.find('a.oe_mail_compose_message_email').click(function (event) {
+ event.preventDefault();
+ self.toggle_email_mode();
+ });
+ // event: click on 'Formatting' icon-link that toggles the advanced
+ // formatting options for writing a message (subject, body_html)
+ this.$element.find('a.oe_mail_compose_message_formatting').click(function (event) {
+ event.preventDefault();
+ self.toggle_formatting_mode();
+ });
+ // event: click on 'Attachment' icon-link that opens the dialog to
+ // add an attachment.
+ this.$element.find('a.oe_mail_compose_message_attachment').click(function (event) {
+ event.preventDefault();
+ // not yet implemented
+ self.set_body_value('attachment', 'attachment');
+ });
+ // event: click on 'Checklist' icon-link that toggles the options
+ // for adding checklist.
+ this.$element.find('a.oe_mail_compose_message_checklist').click(function (event) {
+ event.preventDefault();
+ // not yet implemented
+ self.set_body_value('checklist', 'checklist');
+ });
+ },
+
+ /**
+ * Toggle the formatting mode. */
+ toggle_formatting_mode: function() {
+ var self = this;
+ this.formatting = ! this.formatting;
+ // calls onchange
+ var call_defer = this.ds_compose.call('onchange_formatting', [[], this.formatting, this.params.res_model, this.params.res_id]).then(
+ function (result) {
+ self.form_view.on_processed_onchange(result, []);
+ });
+ // update context of datasetsearch
+ this.ds_compose.context.formatting = this.formatting;
+ // toggle display
+ this.$element.find('span.oe_mail_compose_message_subject').toggleClass('oe_mail_compose_message_invisible');
+ this.$element.find('div.oe_mail_compose_message_body_text').toggleClass('oe_mail_compose_message_invisible');
+ this.$element.find('div.oe_mail_compose_message_body_html').toggleClass('oe_mail_compose_message_invisible');
+ },
+
+ /**
+ * Toggle the email mode. */
+ toggle_email_mode: function() {
+ var self = this;
+ this.email_mode = ! this.email_mode;
+ // calls onchange
+ var call_defer = this.ds_compose.call('onchange_email_mode', [[], this.email_mode, this.params.res_model, this.params.res_id]).then(
+ function (result) {
+ self.form_view.on_processed_onchange(result, []);
+ });
+ // update context of datasetsearch
+ this.ds_compose.context.email_mode = this.email_mode;
+ // update 'Post' button -> 'Send'
+ // update 'Send an Email' link -> 'Post a comment'
+ if (this.email_mode) {
+ this.$element.find('button.oe_mail_compose_message_button_send').html('<span>Send</span>');
+ this.$element.find('a.oe_mail_compose_message_email').html('Comment');
+ } else {
+ this.$element.find('button.oe_mail_compose_message_button_send').html('<span>Post</span>');
+ this.$element.find('a.oe_mail_compose_message_email').html('Send an Email');
+ }
+ // toggle display
+ this.$element.find('div.oe_mail_compose_message_partner_ids').toggleClass('oe_mail_compose_message_invisible');
+ },
+
+ /**
+ * Update the values of the composition form; with possible different
+ * values for body_text and body_html. */
+ set_body_value: function(body_text, body_html) {
+ this.form_view.fields.body_text.set_value(body_text);
+ this.form_view.fields.body_html.set_value(body_html);
+ },
+ }),
/**
- * ThreadDisplay widget: this widget handles the display of a thread of
- * messages. The [thread_level] parameter sets the thread level number:
+ * ------------------------------------------------------------
+ * Thread Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a thread of messages. The
+ * [thread_level] parameter sets the thread level number:
* - root message
* - - sub message (parent_id = root message)
* - - - sub sub message (parent id = sub message)
* - - sub message (parent_id = root message)
- * This widget has 2 ways of initialization, either you give records to be rendered,
- * either it will fetch [limit] messages related to [res_model]:[res_id].
+ * This widget has 2 ways of initialization, either you give records
+ * to be rendered, either it will fetch [limit] messages related to
+ * [res_model]:[res_id].
*/
+
mail.Thread = session.web.Widget.extend({
- template: 'mail.Thread',
+ template: 'mail.thread',
/**
* @param {Object} parent parent
* @param {Object} [params]
- * @param {String} [params.res_model] res_model of mail.thread object
- * @param {Number} [params.res_id] res_id of record
- * @param {Number} [params.parent_id=false] parent_id of message
- * @param {Number} [params.uid] user id
- * @param {Number} [params.thread_level=0] number of levels in the thread (only 0 or 1 currently)
- * @param {Number} [params.msg_more_limit=100] number of character to display before having a "show more" link;
- * note that the text will not be truncated if it does not have 110% of
- * the parameter (ex: 110 characters needed to be truncated and be displayed
- * as a 100-characters message)
- * @param {Number} [params.limit=10] maximum number of messages to fetch
+ * @param {String} [params.res_model] res_model of document [REQUIRED]
+ * @param {Number} [params.res_id] res_id of record [REQUIRED]
+ * @param {Number} [params.uid] user id [REQUIRED]
+ * @param {Bool} [params.parent_id=false] parent_id of message
+ * @param {Number} [params.thread_level=0] number of levels in the thread
+ * (only 0 or 1 currently)
+ * @param {Bool} [params.is_wall=false] thread is displayed in the wall
+ * @param {Number} [params.msg_more_limit=150] number of character to
+ * display before having a "show more" link; note that the text
+ * will not be truncated if it does not have 110% of the parameter
+ * (ex: 110 characters needed to be truncated and be displayed as
+ * a 100-characters message)
+ * @param {Number} [params.limit=100] maximum number of messages to fetch
* @param {Number} [params.offset=0] offset for fetching messages
* @param {Number} [params.records=null] records to show instead of fetching messages
*/
init: function(parent, params) {
this._super(parent);
+ // options
this.params = params;
this.params.parent_id = this.params.parent_id || false;
this.params.thread_level = this.params.thread_level || 0;
- this.params.msg_more_limit = this.params.msg_more_limit || 290;
+ this.params.is_wall = this.params.is_wall || (this.params.records != undefined) || false;
+ this.params.msg_more_limit = this.params.msg_more_limit || 250;
this.params.limit = this.params.limit || 100;
+ // this.params.limit = 3; // tmp for testing
this.params.offset = this.params.offset || 0;
this.params.records = this.params.records || null;
// datasets and internal vars
- this.ds = new session.web.DataSet(this, this.params.res_model);
- this.ds_users = new session.web.DataSet(this, 'res.users');
- this.ds_msg = new session.web.DataSet(this, 'mail.message');
- this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
+ this.ds = new session.web.DataSetSearch(this, this.params.res_model);
+ this.ds_users = new session.web.DataSetSearch(this, 'res.users');
+ this.ds_msg = new session.web.DataSetSearch(this, 'mail.message');
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
// display customization vars
this.display = {};
this.display.show_post_comment = this.params.show_post_comment || false;
- this.display.show_reply = (this.params.thread_level > 0);
- this.display.show_delete = true;
- this.display.show_hide = this.params.show_hide || false;
+ this.display.show_reply = (this.params.thread_level > 0 && this.params.is_wall);
+ this.display.show_delete = ! this.params.is_wall;
+ this.display.show_hide = this.params.is_wall;
+ this.display.show_reply_by_email = ! this.params.is_wall;
this.display.show_more = (this.params.thread_level == 0);
- // not used currently
- this.intlinks_mapping = {};
},
start: function() {
- var self = this;
-
this._super.apply(this, arguments);
- // customize display
- if (! this.display.show_post_comment) {
- this.$element.find('div.oe_mail_thread_action').hide();
- }
// add events
- this.add_events();
-
+ this.bind_events();
// display user, fetch comments
this.display_current_user();
-
- if (this.params.records) {
- var display_done = this.display_comments_from_parameters(this.params.records);
- } else {
- var display_done = this.init_comments();
+ if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records);
+ else var display_done = this.init_comments();
+ // customize display
+ $.when(display_done).then(this.proxy('do_customize_display'));
+ // add message composition form view
+ if (this.display.show_post_comment) {
+ var compose_done = this.instantiate_composition_form();
}
- return display_done
+ return display_done && compose_done;
},
-
- add_events: function() {
+
+ /**
+ * Override-hack of do_action: automatically reload the chatter.
+ * Normally it should be called only when clicking on 'Post/Send'
+ * in the composition form. */
+ do_action: function(action, on_close) {
+ this.init_comments();
+ if (this.compose_message_widget) {
+ this.compose_message_widget.reinit(); }
+ return this._super(action, on_close);
+ },
+
+ instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) {
+ if (this.compose_message_widget) {
+ this.compose_message_widget.destroy();
+ }
+ this.compose_message_widget = new mail.ComposeMessage(this, {
+ 'extended_mode': false, 'uid': this.params.uid, 'res_model': this.params.res_model,
+ 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id,
+ 'email_mode': email_mode || false, 'formatting': formatting || false,
+ 'context': context || false } );
+ var composition_node = this.$element.find('div.oe_mail_thread_action');
+ composition_node.empty();
+ var compose_done = this.compose_message_widget.appendTo(composition_node);
+ return compose_done;
+ },
+
+ do_customize_display: function() {
+ if (this.display.show_post_comment) { this.$element.find('div.oe_mail_thread_action').eq(0).show(); }
+ },
+
+ /**
+ * Bind events in the widget. Each event is slightly described
+ * in the function. */
+ bind_events: function() {
var self = this;
+ // generic events from Chatter Mixin
+ mail.ChatterUtils.bind_events(this);
// event: click on 'more' at bottom of thread
this.$element.find('button.oe_mail_button_more').click(function () {
self.do_more();
});
- // event: writing in textarea
- this.$element.find('textarea.oe_mail_action_textarea').keyup(function (event) {
+ // event: writing in basic textarea of composition form (quick reply)
+ this.$element.find('textarea.oe_mail_compose_textarea').keyup(function (event) {
var charCode = (event.which) ? event.which : window.event.keyCode;
if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; }
else if (charCode == 13) { return self.do_comment(); }
});
- // event: click on 'reply' in msg
+ // event: click on 'Reply' in msg
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) {
var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_action:first');
act_dom.toggle();
event.preventDefault();
});
- // event: click on 'delete' in msg
+ // event: click on 'attachment(s)' in msg
+ this.$element.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) {
+ var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments');
+ act_dom.toggle();
+ event.preventDefault();
+ });
+ // event: click on 'Delete' in msg side menu
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) {
- //console.log('deleting');
if (! confirm(_t("Do you really want to delete this message?"))) { return false; }
var msg_id = event.srcElement.dataset.id;
if (! msg_id) return false;
var call_defer = self.ds_msg.unlink([parseInt(msg_id)]);
- $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide();
+ $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide();
if (self.params.thread_level > 0) {
$(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
}
- return false;
+ event.preventDefault();
+ return call_defer;
});
- // event: click on 'hide' in msg
+ // event: click on 'Hide' in msg side menu
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) {
- //console.log('hiding');
if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; }
var msg_id = event.srcElement.dataset.id;
if (! msg_id) return false;
- //console.log(msg_id);
var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]);
- $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide();
+ $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide();
if (self.params.thread_level > 0) {
$(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
}
- return false;
- });
- // event: click on an internal link
- this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_internal_link', 'click', function (event) {
- // lazy implementation: fetch data and try to redirect
- if (! event.srcElement.dataset.resModel) return false;
- else var res_model = event.srcElement.dataset.resModel;
- var res_login = event.srcElement.dataset.resLogin;
- var res_id = event.srcElement.dataset.resId;
- if ((! res_login) && (! res_id)) return false;
- if (! res_id) {
- var ds = new session.web.DataSet(self, res_model);
- var defer = ds.call('search', [[['login', '=', res_login]]]).then(function (records) {
- if (records[0]) {
- self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]});
- }
- else return false;
- });
- }
- else self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(res_id), views: [[false, 'form']]});
- });
- // event: click on 'attachment(s)' in msg
- this.$element.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) {
- var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments');
- act_dom.toggle();
- return false;
+ event.preventDefault();
+ return call_defer;
});
- // see more
- this.$element.on('click','a.oe_mail_msg_more', function (event) {
- $(this).siblings('.oe_mail_msg_tail').show();
- $(this).hide();
- return false;
+ // event: click on "Reply" in msg side menu (email style)
+ this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply_by_email', 'click', function (event) {
+ var msg_id = event.srcElement.dataset.msg_id;
+ var email_mode = (event.srcElement.dataset.type == 'email');
+ var formatting = (event.srcElement.dataset.formatting == 'html');
+ if (! msg_id) return false;
+ self.instantiate_composition_form('reply', email_mode, formatting, msg_id);
+ event.preventDefault();
});
},
@@ -219,21 +664,22 @@ openerp.mail = function(session) {
init_comments: function() {
var self = this;
this.params.offset = 0;
- this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
this.$element.find('div.oe_mail_thread_display').empty();
- domain = this.get_fetch_domain(this.sorted_comments);
+ var domain = this.get_fetch_domain(this.comments_structure);
return this.fetch_comments(this.params.limit, this.params.offset, domain).then();
},
fetch_comments: function (limit, offset, domain) {
var self = this;
- var defer = this.ds.call('message_load', [[this.params.res_id], ( (limit+1)||(this.params.limit+1) ), (offset||this.params.offset), (domain||[]), (this.params.thread_level > 0), (this.sorted_comments['root_ids'])]);
- $.when(defer).then(function (records) {
+ var defer = this.ds.call('message_read', [[this.params.res_id], (this.params.thread_level > 0), (this.comments_structure['root_ids']),
+ (limit+1) || (this.params.limit+1), offset||this.params.offset, domain||undefined ]).then(function (records) {
if (records.length <= self.params.limit) self.display.show_more = false;
- else { self.display.show_more = true; records.pop(); }
-
+ // else { self.display.show_more = true; records.pop(); }
+ // else { self.display.show_more = true; records.splice(0, 1); }
+ else { self.display.show_more = true; }
self.display_comments(records);
+ // TODO: move to customize display
if (self.display.show_more == true) self.$element.find('div.oe_mail_thread_more:last').show();
else self.$element.find('div.oe_mail_thread_more:last').hide();
});
@@ -245,6 +691,7 @@ openerp.mail = function(session) {
if (records.length > 0 && records.length < (records[0].child_ids.length+1) ) this.display.show_more = true;
else this.display.show_more = false;
var defer = this.display_comments(records);
+ // TODO: move to customize display
if (this.display.show_more == true) $('div.oe_mail_thread_more').eq(-2).show();
else $('div.oe_mail_thread_more').eq(-2).hide();
return defer;
@@ -252,7 +699,8 @@ openerp.mail = function(session) {
display_comments: function (records) {
var self = this;
-
+ // sort the records
+ mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id);
//build attachments download urls and compute time-relative from dates
for (var k in records) {
records[k].timerelative = $.timeago(records[k].date);
@@ -263,11 +711,10 @@ openerp.mail = function(session) {
}
}
}
- this.cs = this.sort_comments_tmp(records);
_(records).each(function (record) {
var sub_msgs = [];
if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) {
- var sub_list = self.cs['tree_struct'][record.id]['direct_childs'];
+ var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs'];
_(records).each(function (record) {
//if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return;
if (_.indexOf(sub_list, record.id) != -1) {
@@ -276,124 +723,78 @@ openerp.mail = function(session) {
});
self.display_comment(record);
self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid,
- 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id});
- self.$element.find('.oe_mail_thread_msg:last').append('<div class="oe_mail_thread_subthread"/>');
+ 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id,
+ 'is_wall': self.params.is_wall});
+ self.$element.find('li.oe_mail_thread_msg:last').append('<div class="oe_mail_thread_subthread"/>');
self.thread.appendTo(self.$element.find('div.oe_mail_thread_subthread:last'));
}
else if (self.params.thread_level == 0) {
self.display_comment(record);
}
});
+ mail.ChatterUtils.records_struct_update_after_display(this.comments_structure);
// update offset for "More" buttons
if (this.params.thread_level == 0) this.params.offset += records.length;
},
- /**
- * Display a record
- */
+ /** Displays a record, performs text/link formatting */
display_comment: function (record) {
- record.body = this.do_text_nl2br(record.body, true);
+ record.body = mail.ChatterUtils.do_text_nl2br($.trim(record.body), true);
+ // if (record.type == 'email' && record.state == 'received') {
if (record.type == 'email') {
record.mini_url = ('/mail/static/src/img/email_icon.png');
- } else {
- record.mini_url = this.thread_get_avatar('res.users', 'avatar', record.user_id[0]);
+ } else {
+ record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', record.user_id[0]);
}
// body text manipulation
- record.body = this.do_clean_text(record.body);
- record.body = this.do_replace_internal_links(record.body);
-
+ if (record.subtype == 'plain') {
+ record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body);
+ }
+ record.body = mail.ChatterUtils.do_replace_expressions(record.body);
// format date according to the user timezone
record.date = session.web.format_value(record.date, {type:"datetime"});
- var rendered = session.web.qweb.render('mail.Thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display});
- $(rendered).appendTo(this.$element.children('div.oe_mail_thread_display:first'));
+ // is the user the author ?
+ record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]);
+ // render
+ var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display});
// expand feature
- this.$element.find('div.oe_mail_msg_body:last').expander({
+ $(rendered).appendTo(this.$element.children('div.oe_mail_thread_display:first'));
+ this.$element.find('div.oe_mail_msg_record_body').expander({
slicePoint: this.params.msg_more_limit,
- expandText: 'see more',
- userCollapseText: 'see less',
+ expandText: 'read more',
+ userCollapseText: '[^]',
detailClass: 'oe_mail_msg_tail',
moreClass: 'oe_mail_expand',
- lesClass: 'oe_mail_reduce',
+ lessClass: 'oe_mail_reduce',
});
},
-
- /**
- * Add records to sorted_comments array
- * @param {Array} records records from mail.message sorted by date desc
- * @returns {Object} sc sorted_comments: dict {
- * 'root_id_list': list or root_ids
- * 'root_id_msg_list': {'record_id': [ancestor_ids]}, still sorted by date desc
- * 'id_to_root': {'root_id': [records]}, still sorted by date desc
- * }
- */
- sort_comments: function (records) {
- var self = this;
- sc = {'root_id_list': [], 'root_id_msg_list': {}, 'id_to_root': {}}
- var cur_iter = 0; var max_iter = 10; var modif = true;
- /* step1: get roots */
- while ( modif && (cur_iter++) < max_iter) {
- modif = false;
- _(records).each(function (record) {
- if ( (record.parent_id == false || record.parent_id[0] == self.params.parent_id) && (_.indexOf(sc['root_id_list'], record.id) == -1)) {
- sc['root_id_list'].push(record.id);
- sc['root_id_msg_list'][record.id] = [];
- self.sorted_comments['root_ids'].push(record.id);
- modif = true;
- }
- else {
- if (_.indexOf(sc['root_id_list'], record.parent_id[0]) != -1) {
- sc['id_to_root'][record.id] = record.parent_id[0];
- modif = true;
- }
- else if ( sc['id_to_root'][record.parent_id[0]] ) {
- sc['id_to_root'][record.id] = sc['id_to_root'][record.parent_id[0]];
- modif = true;
- }
- }
- });
- }
- /* step2: add records */
- _(records).each(function (record) {
- var root_id = sc['id_to_root'][record.id];
- if (! root_id) return;
- sc['root_id_msg_list'][root_id].push(record);
- //self.sorted_comments['root_id_msg_list'][root_id].push(record.id);
- });
- return sc;
- },
-
- /**
- * Add records to comments_structure object: see function for details
- */
- sort_comments_tmp: function(records) {
- return tools_sort_comments(this.comments_structure, records, this.params.parent_id);
- },
-
+
display_current_user: function () {
- return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.params.uid));
+ var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.params.uid);
+ return this.$element.find('img.oe_mail_icon').attr('src', avatar);
},
do_comment: function () {
var comment_node = this.$element.find('textarea');
var body_text = comment_node.val();
comment_node.val('');
- return this.ds.call('message_append_note', [[this.params.res_id], 'Reply', body_text, this.params.parent_id, 'comment', 'html']).then(
+ return this.ds.call('message_append_note', [[this.params.res_id], '', body_text, this.params.parent_id, 'comment', 'plain']).then(
this.proxy('init_comments'));
},
/**
* Create a domain to fetch new comments according to
- * comment already present in sorted_comments
- * @param {Object} sorted_comments (see sort_comments)
+ * comment already present in comments_structure
+ * @param {Object} comments_structure (see chatter utils)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
- get_fetch_domain: function (sorted_comments) {
+ get_fetch_domain: function (comments_structure) {
var domain = [];
- var ids = sorted_comments.root_ids.slice();
+ var ids = comments_structure.root_ids.slice();
var ids2 = [];
// must be child of current parent
if (this.params.parent_id) { domain.push(['id', 'child_of', this.params.parent_id]); }
- _(sorted_comments.root_ids).each(function (id) { // each record
+ _(comments_structure.root_ids).each(function (id) { // each record
ids.push(id);
ids2.push(id);
});
@@ -413,208 +814,100 @@ openerp.mail = function(session) {
},
do_more: function () {
- domain = this.get_fetch_domain(this.sorted_comments);
+ domain = this.get_fetch_domain(this.comments_structure);
return this.fetch_comments(this.params.limit, this.params.offset, domain);
},
-
- /**
- *
- * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- */
- do_replace_internal_links: function (string) {
- var self = this;
- var icon_list = ['al', 'pinky']
- /* shortcut to user: @login */
- var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var login = regex_res[2];
- string = string.replace(regex_res[0], regex_res[1] + '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>');
- regex_res = regex_login.exec(string);
- }
- /* special shortcut: :name, try to find an icon if in list */
- var regex_login = new RegExp(/(^|\s):((\w)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var icon_name = regex_res[2];
- if (_.include(icon_list, icon_name))
- string = string.replace(regex_res[0], regex_res[1] + '<img src="/mail/static/src/img/_' + icon_name + '.png" width="22px" height="22px" alt="' + icon_name + '"/>');
- regex_res = regex_login.exec(string);
- }
- return string;
- },
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
- },
-
- /** Removes html tags, except b, em, br */
- do_clean_text: function (string) {
- var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('&lt;(/)?(b|em|br|br /)\\s*&gt;', 'gi'), '<$1$2>');
- return html;
- },
-
- /** Replaces line bracks by html line breaks (br) */
- do_text_nl2br: function (str, is_xhtml) {
- var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
- return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2');
- },
-
- /**
- *
- * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- */
- do_check_internal_links: function(string) {
- /* shortcut to user: @login */
- var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var login = regex_res[2];
- if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; }
- this.map_hash['res.users']['login'].push(login);
- regex_res = regex_login.exec(string);
- }
- /* internal links: #res.model,name */
- var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- regex_res = regex_intlink.exec(string);
- while (regex_res != null) {
- var res_model = regex_res[2] + '.' + regex_res[3];
- var res_name = regex_res[4];
- if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; }
- this.map_hash[res_model]['name'].push(res_name);
- regex_res = regex_intlink.exec(string);
- }
- },
-
- /** checks if tue current user is the message author */
- _is_author: function (id) {
- return (this.session.uid == id);
- },
-
});
- session.web.form.widgets.add( 'Thread', 'openerp.mail.Thread');
+
+
+ /**
+ * ------------------------------------------------------------
+ * mail_thread Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of the Chatter on documents.
+ */
+
+ /* Add mail_thread widget to registry */
+ session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread');
/** mail_thread widget: thread of comments */
mail.RecordThread = session.web.form.AbstractField.extend({
- template: 'mail.RecordThread',
+ // QWeb template to use when rendering the object
+ template: 'mail.record_thread',
init: function() {
this._super.apply(this, arguments);
- this.see_subscribers = true;
- this.thread = null;
this.params = this.get_definition_options();
this.params.thread_level = this.params.thread_level || 0;
- // datasets
+ this.thread = null;
this.ds = new session.web.DataSet(this, this.view.model);
this.ds_users = new session.web.DataSet(this, 'res.users');
},
-
+
start: function() {
- this._super.apply(this, arguments);
- var self = this;
- // bind buttons
- this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }).hide();
- this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); })
- .mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
- .mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
+ // NB: all the widget should be modified to check the actual_mode property on view, not use
+ // any other method to know if the view is in create mode anymore
+ this.view.on("change:actual_mode", this, this._check_visibility);
+ this._check_visibility();
+ mail.ChatterUtils.bind_events(this);
+ this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); });
+ if (! this.params.see_subscribers_options) {
+ this.$element.find('button.oe_mail_button_followers').hide(); }
+ this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); });
this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); })
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.reinit();
},
-
- destroy: function () {
- this._super.apply(this, arguments);
+
+ _check_visibility: function() {
+ this.$element.toggle(this.view.get("actual_mode") !== "create");
},
- reinit: function() {
- this.see_subscribers = true;
- this.$element.find('button.oe_mail_button_followers').html('Hide followers')
- this.$element.find('button.oe_mail_button_follow').hide();
- this.$element.find('button.oe_mail_button_unfollow').hide();
+ destroy: function () {
+ this._super.apply(this, arguments);
},
set_value: function() {
this._super.apply(this, arguments);
- var self = this;
- this.reinit();
if (! this.view.datarecord.id ||
session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$element.find('.oe_mail_thread').hide();
return;
}
- // fetch followers
- var fetch_sub_done = this.fetch_subscribers();
// create and render Thread widget
- this.$element.find('div.oe_mail_recthread_left').empty();
+ this.$element.find('div.oe_mail_recthread_main').empty();
if (this.thread) this.thread.destroy();
this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid,
- 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15});
- var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_left'));
- return fetch_sub_done && thread_done;
- },
-
- fetch_subscribers: function () {
- return this.ds.call('message_read_subscribers', [[this.view.datarecord.id]]).then(this.proxy('display_subscribers'));
- },
-
- display_subscribers: function (records) {
- var self = this;
- this.is_subscriber = false;
- var user_list = this.$element.find('ul.oe_mail_followers_display').empty();
- this.$element.find('div.oe_mail_recthread_followers h4').html('Followers (' + records.length + ')');
- _(records).each(function (record) {
- if (record.id == self.session.uid) { self.is_subscriber = true; }
- var mini_url = self.thread_get_avatar('res.users', 'avatar', record.id);
- $('<li><img class="oe_mail_oe_left oe_mail_msg_image" src="' + mini_url + '"/>' +
- '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-id="' + record.id + '">' + record.name + '</a></li>').appendTo(user_list);
- });
- if (self.is_subscriber) {
- self.$element.find('button.oe_mail_button_follow').hide();
- self.$element.find('button.oe_mail_button_unfollow').show(); }
- else {
- self.$element.find('button.oe_mail_button_follow').show();
- self.$element.find('button.oe_mail_button_unfollow').hide(); }
- },
-
- do_follow: function () {
- return this.ds.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('fetch_subscribers'));
- },
-
- do_unfollow: function () {
- var self = this;
- return this.ds.call('message_unsubscribe', [[this.view.datarecord.id]]).then(function (record) {
- if (record == false) self.do_notify("Impossible to unsubscribe", "You are automatically subscribed to this record. You cannot unsubscribe.");
- }).pipe(this.proxy('fetch_subscribers'));
- },
-
- do_toggle_followers: function () {
- this.see_subscribers = ! this.see_subscribers;
- if (this.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
- else { this.$element.find('button.oe_mail_button_followers').html('Display followers'); }
- this.$element.find('div.oe_mail_recthread_followers').toggle();
- },
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
+ 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15});
+ var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_main'));
+ return thread_done;
},
});
- session.web.form.widgets.add( 'mail_thread', 'openerp.mail.RecordThread');
- /** WallView widget: a wall of messages */
- mail.WallView = session.web.Widget.extend({
- template: 'mail.Wall',
+
+ /**
+ * ------------------------------------------------------------
+ * WallView Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of the Chatter on the Wall.
+ */
+
+ /* Add WallView widget to registry */
+ session.web.client_actions.add('mail.wall', 'session.mail.Wall');
+
+ /* WallView widget: a wall of messages */
+ mail.Wall = session.web.Widget.extend({
+ template: 'mail.wall',
/**
* @param {Object} parent parent
* @param {Object} [params]
* @param {Number} [params.limit=20] number of messages to show and fetch
* @param {Number} [params.search_view_id=false] search view id for messages
- * @var {Array} sorted_comments records sorted by res_model and res_id
- * records.res_model = {res_ids}
- * records.res_model.res_id = [records]
+ * @var {Array} comments_structure (see chatter utils)
*/
init: function (parent, params) {
this._super(parent);
@@ -622,6 +915,8 @@ openerp.mail = function(session) {
this.params.limit = params.limit || 25;
this.params.domain = params.domain || [];
this.params.context = params.context || {};
+ this.params.res_model = params.res_model || false;
+ this.params.res_id = params.res_id || false;
this.params.search_view_id = params.search_view_id || false;
this.params.thread_level = params.thread_level || 1;
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
@@ -637,26 +932,49 @@ openerp.mail = function(session) {
start: function () {
this._super.apply(this, arguments);
- var self = this;
this.display_current_user();
// add events
this.add_event_handlers();
// load mail.message search view
var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false);
+ // load composition form
+ var compose_done = this.instantiate_composition_form();
// fetch first threads
var comments_ready = this.init_and_fetch_comments(this.params.limit, 0);
- return (search_view_ready && comments_ready);
+ return (search_view_ready && comments_ready && compose_done);
},
-
- stop: function () {
+
+ /**
+ * Override-hack of do_action: automatically reload the chatter.
+ * Normally it should be called only when clicking on 'Post/Send'
+ * in the composition form. */
+ do_action: function(action, on_close) {
+ this.init_and_fetch_comments();
+ if (this.compose_message_widget) {
+ this.compose_message_widget.reinit(); }
+ return this._super(action, on_close);
+ },
+
+ destroy: function () {
this._super.apply(this, arguments);
},
-
+
+ instantiate_composition_form: function(mode, msg_id) {
+ if (this.compose_message_widget) {
+ this.compose_message_widget.destroy();
+ }
+ this.compose_message_widget = new mail.ComposeMessage(this, {
+ 'extended_mode': false, 'uid': this.session.uid, 'res_model': this.params.res_model,
+ 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id });
+ var composition_node = this.$element.find('div.oe_mail_wall_action');
+ composition_node.empty();
+ var compose_done = this.compose_message_widget.appendTo(composition_node);
+ return compose_done;
+ },
+
/** Add events */
add_event_handlers: function () {
var self = this;
- // post a comment
- this.$element.find('.oe_mail_wall_action button').click(function () { return self.do_comment(); });
// display more threads
this.$element.find('button.oe_mail_wall_button_more').click(function () { return self.do_more(); });
},
@@ -699,13 +1017,9 @@ openerp.mail = function(session) {
},
display_current_user: function () {
- return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid));
- },
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
- },
-
+ //return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid));
+ },
+
/**
* Initializes the wall and calls fetch_comments
* @param {Number} limit: number of notifications to fetch
@@ -735,8 +1049,8 @@ openerp.mail = function(session) {
else var fetch_domain = this.search['domain'];
if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context);
else var fetch_context = this.search['context'];
- return this.ds_thread.call('get_pushed_messages',
- [[this.session.uid], (limit || 0), (offset || 0), fetch_domain, true, [], fetch_context]).then(this.proxy('display_comments'));
+ return this.ds_thread.call('message_get_pushed_messages',
+ [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments'));
},
/**
@@ -745,16 +1059,16 @@ openerp.mail = function(session) {
display_comments: function (records) {
var self = this;
this.do_update_show_more(records.length >= self.params.limit);
- this.sort_comments(records);
+ mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false);
_(this.comments_structure['new_root_ids']).each(function (root_id) {
var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs'];
var model_name = self.comments_structure.msgs[root_id]['model'];
var res_id = self.comments_structure.msgs[root_id]['res_id'];
- var render_res = session.web.qweb.render('mail.WallThreadContainer', {});
+ var render_res = session.web.qweb.render('mail.wall_thread_container', {});
$('<li class="oe_mail_wall_thread">').html(render_res).appendTo(self.$element.find('ul.oe_mail_wall_threads'));
var thread = new mail.Thread(self, {
'res_model': model_name, 'res_id': res_id, 'uid': self.session.uid, 'records': records,
- 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true}
+ 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true, 'is_wall': true}
);
self.thread_list.push(thread);
return thread.appendTo(self.$element.find('li.oe_mail_wall_thread:last'));
@@ -765,18 +1079,10 @@ openerp.mail = function(session) {
},
/**
- * Add records to comments_structure object: see function for details
- */
- sort_comments: function(records) {
- tools_sort_comments(this.comments_structure, records, false);
- },
-
- /**
* Create a domain to fetch new comments according to
- * comments already present in sorted_comments
+ * comments already present in comments_structure
* - for each model:
* -- should not be child of already displayed ids
- * @param {Object} sorted_comments (see sort_comments)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
get_fetch_domain: function () {
@@ -801,17 +1107,5 @@ openerp.mail = function(session) {
var domain = this.get_fetch_domain();
return this.fetch_comments(this.params.limit, 0, domain);
},
-
- /** Action: Posts a comment */
- do_comment: function () {
- var comment_node = this.$element.find('.oe_mail_wall_action textarea');
- var body_text = comment_node.val();
- comment_node.val('');
- var call_done = this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, false, 'comment', 'html']).then(this.proxy('init_and_fetch_comments'));
- },
});
- session.web.client_actions.add('mail.all_feeds', 'session.mail.WallView');
-
};
-
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
new file mode 100644
index 00000000000..0e920f73041
--- /dev/null
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -0,0 +1,122 @@
+openerp_mail_followers = function(session, mail) {
+ var _t = session.web._t,
+ _lt = session.web._lt;
+
+ var mail_followers = session.mail_followers = {};
+
+ /**
+ * ------------------------------------------------------------
+ * mail_followers Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a list of records as a vetical
+ * list, with an image on the left. The widget itself is a floatting
+ * right-sided box.
+ * This widget is mainly used to display the followers of records
+ * in OpenChatter.
+ */
+
+ /* Add the widget to registry */
+ session.web.form.widgets.add('mail_followers', 'openerp.mail_followers.Followers');
+
+ mail_followers.Followers = session.web.form.AbstractField.extend({
+ template: 'mail.followers',
+
+ init: function() {
+ this._super.apply(this, arguments);
+ this.params = {};
+ this.params.image = this.node.attrs.image || 'image_small';
+ this.params.title = this.node.attrs.title || 'Followers';
+ this.params.display_followers = true;
+ this.params.display_control = this.node.attrs.display_control || false;
+ this.params.display_actions = this.node.attrs.display_actions || false;
+ this.ds_model = new session.web.DataSetSearch(this, this.view.model);
+ this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
+ },
+
+ start: function() {
+ var self = this;
+ // NB: all the widget should be modified to check the actual_mode property on view, not use
+ // any other method to know if the view is in create mode anymore
+ this.view.on("change:actual_mode", this, this._check_visibility);
+ this._check_visibility();
+ this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); });
+ if (! this.params.display_control) {
+ this.$element.find('button.oe_mail_button_followers').hide(); }
+ this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); })
+ .mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
+ .mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
+ this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); })
+ .mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
+ .mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
+ this.reinit();
+ },
+
+ _check_visibility: function() {
+ this.$element.toggle(this.view.get("actual_mode") !== "create");
+ },
+
+ destroy: function () {
+ this._super.apply(this, arguments);
+ },
+
+ reinit: function() {
+ this.params.display_followers = true;
+ this.params.display_control = this.node.attrs.display_control || false;
+ this.params.display_actions = this.node.attrs.display_actions || false;
+ this.$element.find('button.oe_mail_button_followers').html('Hide followers')
+ this.$element.find('button.oe_mail_button_follow').hide();
+ this.$element.find('button.oe_mail_button_unfollow').hide();
+ },
+
+ set_value: function(value_) {
+ this.reinit();
+ if (! this.view.datarecord.id ||
+ session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
+ this.$element.find('div.oe_mail_recthread_aside').hide();
+ return;
+ }
+ return this.fetch_subscribers(value_);
+ },
+
+ fetch_subscribers: function (value_) {
+ return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_subscribers'));
+ },
+
+ /**
+ * Display the followers.
+ * TODO: replace the is_subscriber check by fields read */
+ display_subscribers: function (records) {
+ var self = this;
+ this.is_subscriber = false;
+ var user_list = this.$element.find('ul.oe_mail_followers_display').empty();
+ this.$element.find('div.oe_mail_recthread_followers h4').html(this.params.title + ' (' + records.length + ')');
+ _(records).each(function (record) {
+ if (record.id == self.session.uid) { self.is_subscriber = true; }
+ record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'image_small', record.id);
+ $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(user_list);
+ });
+ if (this.is_subscriber) {
+ this.$element.find('button.oe_mail_button_follow').hide();
+ this.$element.find('button.oe_mail_button_unfollow').show(); }
+ else {
+ this.$element.find('button.oe_mail_button_follow').show();
+ this.$element.find('button.oe_mail_button_unfollow').hide(); }
+ },
+
+ do_follow: function () {
+ return this.ds_model.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value'));
+ },
+
+ do_unfollow: function () {
+ return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value'));
+ },
+
+ do_toggle_followers: function () {
+ this.params.see_subscribers = ! this.params.see_subscribers;
+ if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
+ else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); }
+ this.$element.find('div.oe_mail_recthread_followers').toggle();
+ },
+ });
+};
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index 982fb8aeb2f..f67c4e8676f 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
- <div t-name="mail.Wall" class="oe_view_manager oe_mail_wall oe_view_manager_global">
+ <!--
+ wall main template
+ Template used to display the communication history in the wall.
+ -->
+ <div t-name="mail.wall" class="oe_view_manager oe_mail_wall oe_view_manager_current">
<table class="oe_view_manager_header">
<colgroup>
<col width="33%"/>
@@ -9,10 +13,13 @@
<col width="34%"/>
</colgroup>
<tbody>
- <tr class="oe_header_row">
+ <tr class="oe_header_row oe_header_row_top">
<td colspan="2">
<h2 class="oe_view_title">
- <span class="oe_view_title_text">All Feeds</span>
+ <span class="oe_view_title_text">
+ <t t-if="! widget.params.res_model">News Feeds</t>
+ <t t-if="widget.params.res_model" t-esc="widget.params.res_model"/>
+ </span>
</h2>
</td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
@@ -21,30 +28,44 @@
</table>
<div class="oe_mail_wall_main">
<div class="oe_mail_wall_action">
- <img class="oe_mail_msg_image oe_left" alt="User img"/>
- <textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="What are you working on?"/>
- <button class="oe_right oe_mail_wall_button_comment" type="button">Post comment</button>
+ <!-- call the composition form -->
+ <t t-call ="mail.compose_message"/>
</div>
- <div class="oe_clear"></div>
+ <div class="oe_clear"/>
<ul class="oe_mail_wall_threads">
+ <!-- contains threads -->
</ul>
<div class="oe_mail_wall_more">
- <button class="oe_mail_wall_button_more" type="button">See more discussions</button>
+ <button class="oe_mail_wall_button_more" type="button">See more...</button>
</div>
</div>
- <div class="oe_mail_wall_aside"></div>
+ <div class="oe_mail_wall_aside">
+ <!-- contains currently nothing -->
+ </div>
</div>
- <t t-name="mail.WallThreadContainer">
+ <!--
+ wall_thread_container template for the wall
+ Each discussion thread is contained inside this template
+ -->
+ <t t-name="mail.wall_thread_container">
</t>
- <div t-name="mail.RecordThread" class="oe_mail_recthread">
- <div class="oe_mail_recthread_left"></div>
- <div class="oe_mail_recthread_right">
+ <!--
+ record_thread main template
+ Template used to display the communication history in documents
+ form view.
+ -->
+ <div t-name="mail.record_thread" class="oe_mail_recthread">
+ <!-- <h4>History and Comments</h4> -->
+ <div class="oe_mail_recthread_main">
+ <!-- contains the document thread -->
+ </div>
+ <div class="oe_mail_recthread_aside">
<div class="oe_mail_recthread_actions">
- <button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button>
+ <button type="button" class="oe_mail_button_follow">Follow</button>
<button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
- <button type="button" class="oe_mail_button_followers">Display followers</button>
+ <button type="button" class="oe_mail_button_followers">Show followers</button>
</div>
<div class="oe_mail_recthread_followers">
<h4>Followers</h4>
@@ -53,45 +74,80 @@
</div>
</div>
- <div t-name="mail.Thread" class="oe_mail oe_mail_thread">
+ <!--
+ mail.compose_message template
+ This template holds the composition form to write a note or send
+ an e-mail. It contains by default a textarea, that will be replaced
+ by another composition form in the main wall composition form, or
+ for main thread composition form in document form view.
+ -->
+ <t t-name="mail.compose_message">
+ <div>
+ <img class="oe_mail_icon oe_mail_frame oe_left" alt="User img"/>
+ <div class="oe_mail_msg_content">
+ <!-- contains the composition form -->
+ <!-- default content: old basic textarea -->
+ <textarea class="oe_mail_compose_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/>
+ </div>
+ <div class="oe_clear"/>
+ </div>
+ </t>
+
+ <!--
+ thread template
+ This template holds a thread of comments. It begins with an actions
+ container, holding the composition form. Then come the various
+ messages. Then comes the 'more' button.
+ -->
+ <ul t-name="mail.thread" class="oe_mail oe_mail_thread">
<div class="oe_mail_thread_action">
- <img class="oe_mail_msg_image oe_left" alt="User img"/>
- <textarea class="oe_mail oe_mail_action_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/>
+ <!-- contains the composition box (form + image) -->
+ <t t-call="mail.compose_message"/>
+ </div>
+ <div class="oe_mail_thread_display">
+ <!-- contains the threads -->
</div>
- <div class="oe_mail_thread_display"></div>
<div class="oe_mail_thread_more">
<button class="oe_mail_button_more" type="button">Load more messages</button>
</div>
- </div>
+ </ul>
<!-- default layout -->
- <div t-name="mail.Thread.message" class="oe_mail oe_mail_thread_msg">
+ <li t-name="mail.thread.message" class="oe_mail oe_mail_thread_msg">
<div t-attf-class="oe_mail_msg_#{record.type}">
- <img class="oe_email_icon oe_left" t-att-src="record.mini_url"/>
+ <img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="record.mini_url"/>
<div class="oe_mail_msg_content">
+ <!-- dropdown menu with message options and actions -->
<span class="oe_dropdown_toggle oe_dropdown_arrow">
<ul class="oe_dropdown_menu">
- <t t-if="thread._is_author(record.user_id[0]) &amp;&amp; display['show_delete']">
- <li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_delete">Delete</a></li>
- </t>
- <t t-if="!thread._is_author(record.user_id[0]) &amp;&amp; display['show_hide']">
- <li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_hide">Hide</a></li>
+ <t t-if="display['show_delete']">
+ <li t-if="record.is_author"><a href="#" class="oe_mail_msg_delete" t-attf-data-id='{record.id}'>Delete</a></li>
</t>
- <li t-if="record.type == 'email'"><a href="#" class="oe_mail_msg_details">Details</a></li>
+ <li t-if="display['show_hide']"><a href="#" class="oe_mail_msg_hide" t-attf-data-id='{record.id}'>Hide</a></li>
+<!-- Uncomment when adding subtype hiding
+ <li t-if="display['show_hide']">
+ <a href="#" class="oe_mail_msg_hide_type" t-attf-data-subtype='{record.subtype}'>Hide '<t t-esc="record.subtype"/>' for this document</a>
+ </li> -->
+ <li><a href="#" t-attf-data-msg_id="{record.id}" t-attf-data-type="{record.type}" t-attf-data-formatting="{record.content_subtype}" class="oe_mail_msg_reply_by_email">Reply by email</a></li>
+ <li t-if="record.type == 'email'"><a t-attf-href="#model=mail.message&amp;id=#{record.id}" class="oe_mail_msg_details">Details</a></li>
</ul>
</span>
+ <!-- message itself -->
<div class="oe_mail_msg">
- <h1 t-if="record.subject &amp;&amp; record.subject != 'Reply'" class="oe_mail_msg_title">
+ <h1 t-if="record.subject" class="oe_mail_msg_title">
<t t-raw="record.subject"/>
</h1>
- <div t-if="params.thread_level > 0" class="oe_mail_msg_subtitle">
- <a href="#" t-attf-data-res-model='{params.res_model}' t-attf-data-res-id='{params.res_id}' class="oe_mail_internal_link"><t t-raw="record.record_name"/></a>
- </div>
<div class="oe_mail_msg_body">
- <a href="#" data-res-model='res.users' t-attf-data-res-id='{record.user_id[0]}' class="oe_mail_internal_link"><t t-raw="record.user_id[1]"/></a>
- <t t-raw="record.body"/>
+
+ <div class="oe_mail_msg_record_body">
+ <a t-attf-href="#model=#{params.res_model}&amp;id=#{params.res_id}" t-if="(params.thread_level > 0) &amp; (!record.subject)"><t t-raw="record.record_name"/></a>
+ <t t-raw="record.body"/>
+ </div>
</div>
+ <div class="oe_clear"/>
<ul class="oe_mail_msg_footer">
+ <li t-if="record.subject &amp; params.thread_level > 0"><a t-attf-href="#model=#{params.res_model}&amp;id=#{params.res_id}"><t t-raw="record.record_name"/></a></li>
+ <li><a t-attf-href="#model=res.users&amp;id=#{record.user_id[0]}"><t t-raw="record.user_id[1]"/></a></li>
<li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li>
<li t-if="display['show_reply']"><a href="#" class="oe_mail_msg_reply">Reply</a></li>
<!-- uncomment when merging vote
@@ -99,8 +155,8 @@
-->
<li t-if="record.attachment_ids.length > 0">
<a href="#" class="oe_mail_msg_view_attachments">
- <t t-if="record.attachment_ids.length == 1">Attachment</t>
- <t t-if="record.attachment_ids.length > 1">Attachments (<t t-raw="record.attachment_ids.length"/>)</t>
+ <t t-if="record.attachment_ids.length == 1">1 Attachment</t>
+ <t t-if="record.attachment_ids.length > 1"><t t-raw="record.attachment_ids.length"/> Attachments</t>
</a>
</li>
</ul>
@@ -115,5 +171,6 @@
</div>
</div>
</div>
- </div>
+ </li>
+
</template>
diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml
new file mode 100644
index 00000000000..53c899b9569
--- /dev/null
+++ b/addons/mail/static/src/xml/mail_followers.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template>
+
+ <!--
+ followers main template
+ Template used to display the followers and the actions in a record.
+ -->
+ <div t-name="mail.followers" class="oe_mail_recthread_aside">
+ <div class="oe_mail_recthread_actions">
+ <button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button>
+ <button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
+ <button type="button" class="oe_mail_button_followers">Show followers</button>
+ </div>
+ <div class="oe_mail_recthread_followers">
+ <t t-if="widget.params.title">
+ <h4><t t-raw="widget.params.title"/></h4>
+ </t>
+ <ul class="oe_mail_followers_display"></ul>
+ </div>
+ </div>
+
+ <!--
+ followers.partner template
+ Template used to display a partner following the record
+ -->
+ <li t-name="mail.followers.partner">
+ <img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
+ <a t-attf-href="#model=res.users&amp;id=#{record.id}"><t t-raw="record.name"/></a>
+ </li>
+
+</template>