summaryrefslogtreecommitdiff
path: root/addons/mail/static/src
diff options
context:
space:
mode:
authorVidhin Mehta (OpenERP) <vme@tinyerp.com>2012-10-15 17:03:32 +0530
committerVidhin Mehta (OpenERP) <vme@tinyerp.com>2012-10-15 17:03:32 +0530
commit287b9d09eb740f6adfafdbbc65265f2aecf515ba (patch)
treed0d686830d71c964f03bc69f85c3ef26dec6f006 /addons/mail/static/src
parent3eb5cfb61b6bbb84043f4fa6b3fdefb0fbb320c7 (diff)
parent7f23ad5de563eacfcae958d0011637800b097d4a (diff)
[MERGE]trunk.
bzr revid: vme@tinyerp.com-20121015113332-i8g3d8na27j08h3l
Diffstat (limited to 'addons/mail/static/src')
-rw-r--r--addons/mail/static/src/css/mail.css150
-rw-r--r--addons/mail/static/src/css/mail_compose_message.css166
-rw-r--r--addons/mail/static/src/js/mail.js790
-rw-r--r--addons/mail/static/src/js/mail_followers.js48
-rw-r--r--addons/mail/static/src/xml/mail.xml201
-rw-r--r--addons/mail/static/src/xml/mail_followers.xml6
6 files changed, 822 insertions, 539 deletions
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 364ac7e3165..1592d79e18d 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -82,9 +82,13 @@
overflow: auto;
}
+.openerp .oe_mail_record_wall {
+ margin: auto;
+ width: 560px;
+}
+
.openerp .oe_mail_record_wall > .oe_mail_wall_threads {
float: left;
- width: 560px;
}
.openerp div.oe_mail_recthread_aside {
@@ -145,6 +149,10 @@
z-index: 5;
}
.openerp .oe_mouse_subtypes .oe_recthread_subtypes {
+ background: #fff;
+ padding: 2px;
+ border: 1px solid #aaaaaa;
+ border-top: 0px;
position: absolute;
z-index: 2;
}
@@ -160,7 +168,6 @@
/* ------------------------------------------------------------ */
.openerp div.oe_mail_thread_action {
- display: none;
white-space: normal;
padding: 8px;
z-index:5;
@@ -174,16 +181,6 @@
}
/* default textarea (oe_mail_compose_textarea), and body 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 textarea {
- width: 474px;
- height: 60px;
- padding: 4px;
- font-size: 12px;
- border: 1px solid #cccccc;
-}
-
-/* default textarea (oe_mail_compose_textarea), and body 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 textarea:focus {
outline: 0;
@@ -206,6 +203,10 @@
margin-left: 66px;
}
+.openerp li.oe_mail_thread_msg {
+ width: 560px;
+}
+
.openerp div.oe_thread_placeholder li.oe_mail_thread_msg:last-child {
margin-bottom: 8px;
}
@@ -223,13 +224,18 @@
.openerp li.oe_mail_thread_msg.oe_mail_read,
.openerp li.oe_mail_thread_msg.oe_mail_read div {
- background-color: #F0F0F0;
+ border-left: #F0F0F0;
}
.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread,
.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread div {
background-color: #F6F6F6;
}
+.openerp li.oe_mail_thread_msg.oe_mail_unread>div>ul>li.oe_unread,
+.openerp li.oe_mail_thread_msg.oe_mail_read>div>ul>li.oe_read {
+ display: none;
+}
+
.openerp li.oe_mail_thread_msg > div:after {
content: "";
display: block;
@@ -247,7 +253,7 @@
.openerp .oe_mail_msg_email {
padding: 8px;
background: white;
- border-top: 1px solid #ebebeb;
+ position: relative;
}
.openerp .oe_mail_msg_notification:after,
@@ -259,7 +265,7 @@
}
.openerp div.oe_mail_msg_content {
- float: right;
+ float: left;
position: relative;
width: 486px;
}
@@ -310,76 +316,6 @@
}
/* ------------------------------------------------------------ */
-/* mail.compose.message form view & OpenERP hacks
-/* ------------------------------------------------------------ */
-
-/* form_view: delete white background */
-.openerp .oe_mail_msg_content div.oe_formview {
- background-color: transparent;
-}
-
-.openerp .oe_mail_msg_content div.oe_form_nosheet {
- margin: 0px;
-}
-
-.openerp .oe_mail_msg_content table.oe_form_group {
- margin: 0px;
-}
-
-.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;
-}
-
-/* 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;
-}
-
-/* attachment button: override of openerp values */
-.openerp .oe_mail_msg_content .oe_mail_compose_message_icons div.oe_hidden_input_file {
- display: inline-block;
- width: 24px;
- height: 24px;
- margin: 2px;
-}
-.openerp .oe_mail_msg_content .oe_mail_compose_message_icons div.oe_hidden_input_file button {
- margin: 0px;
-}
-.openerp .oe_mail_msg_content .oe_mail_compose_message_icons input.oe_form_binary_file {
- bottom: 0px;
- top: auto;
- left: auto;
- right: 28px;
- height: 26px;
- width: 26px;
- min-width: 22px;
- font-size: 0px;
- margin: 0px;
- padding: 0px;
-}
-
-/* ------------------------------------------------------------ */
/* Messages layout
/* ------------------------------------------------------------ */
@@ -413,39 +349,27 @@
}
/* Dropdown menu */
-/*.openerp .oe_mail_msg_content .oe_dropdown_toggle {
- position: absolute;
- top: 0px;
- right: 3px;
-}*/
-.openerp .oe_mail .oe_semantic_html_override {
+.openerp .oe_mail ul.oe_mail_thread_display ul.oe_mail_thread_display {
position: relative;
+ border-left: 1px #DDD dashed;
}
.openerp .oe_mail ul.oe_header {
position: absolute;
right: 3px;
top: -6px;
- display: none;
z-index: 10;
- height: 18px;
}
.openerp .oe_mail ul.oe_header a {
text-decoration: none;
}
-.openerp .oe_mail .oe_semantic_html_override:hover > ul.oe_header {
- display: block;
-}
-
.openerp .oe_mail ul.oe_header>li {
display: inline-block;
-}
-
-.openerp .oe_mail_msg_content .oe_dropdown_arrow:after {
- border-top: 4px solid #404040;
+ height: 20px;
+ text-align: right;
}
/* Message footer */
@@ -465,16 +389,36 @@
/* Attachments list */
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments {
- display: none;
width: 100%;
- border-top: 1px solid #CCC;
margin: .5em 0 0 0;
padding: .5em 0;
list-style-position: inside;
}
+.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments.oe_hidden {
+ display: none;
+}
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments li {
float: none;
+ height: 20px;
+ line-height: 20px;
margin: 0;
padding: 0;
list-style-type: square;
}
+.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process {
+ float: right;
+ width: 200px;
+ height: 16px;
+}
+.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process div {
+ float: left;
+ width: 38px;
+ height: 16px;
+ margin-right: 2px;
+ background: #66FF66;
+}
+.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process span {
+ color: #aaaaaa;
+ position: absolute;
+}
+
diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css
index ba7ed36a695..e2f136eb755 100644
--- a/addons/mail/static/src/css/mail_compose_message.css
+++ b/addons/mail/static/src/css/mail_compose_message.css
@@ -1,12 +1,166 @@
/* ------------------------------ */
-/* Compose Message Wizard Form */
+/* Compose Message */
/* ------------------------------ */
-.openerp .oe_mail_compose_message_icons {
- text-align: right;
+.openerp .oe_mail_msg_content .oe_mail_compose_message_footer {
+ height: 24px;
}
-.openerp .oe_mail_compose_message_icons img {
- width: 20px;
- height: 20px;
+.openerp .oe_mail_msg_content .oe_mail_compose_message_footer button.oe_mail_compose_message_button_send {
+ float: left;
}
+
+.openerp .oe_mail .oe_mail_compose_textarea
+{
+ display: none;
+}
+
+.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_header,
+.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_footer,
+{
+ position: relative;
+}
+
+.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel {
+ position: absolute;
+ right: -8px;
+ top: -8px;
+}
+.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel:first-of-type {
+ display:none;
+}
+
+.openerp .oe_mail .oe_mail_compose_textarea button.oe_full {
+ float: right;
+ position: relative;
+ right: -10px;
+}
+
+/* ------------------------------------------------------------ */
+/* mail.compose.message : list_recipients
+/* ------------------------------------------------------------ */
+
+.openerp .oe_mail .oe_mail_list_recipients {
+ display: inline;
+}
+.openerp .oe_mail .oe_mail_list_recipients .oe_all_follower {
+ color: blue;
+}
+.openerp .oe_mail .oe_mail_list_recipients .oe_partner_follower a {
+ color: red;
+}
+.openerp .oe_mail .oe_mail_list_recipients .oe_hidden,
+.openerp .oe_mail .oe_mail_list_recipients .oe_more_hidden {
+ display: none;
+}
+
+/* ------------------------------------------------------------ */
+/* mail.compose.message : attachment
+/* ------------------------------------------------------------ */
+
+.openerp .oe_mail .oe_attachment_file {
+ display: inline-block;
+}
+
+.openerp .oe_mail .oe_attachment_file .oe_add {
+ float: left;
+ width: 24px;
+ height: 24px;
+ position: relative;
+ z-index: 10;
+ left: +2px;
+ top: +7px;
+ overflow: hidden;
+}
+
+/* attachment button: override of openerp values */
+.openerp .oe_mail .oe_attachment_file .oe_add button,
+.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
+ position: absolute;
+ bottom: +0px;
+ left: +0px;
+ height: 24px;
+ width: 24px;
+ margin: 0px;
+ padding: 0px;
+}
+.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
+ z-index:2;
+ width: 300px;
+ left: -100px;
+ background: transparent;
+ border: 0;
+ color: transparent;
+}
+.openerp .oe_mail .oe_attachment_file .oe_add button span {
+ position: relative;
+ bottom: +4px;
+ font-size: 30px;
+}
+
+.openerp .oe_mail .oe_mail_msg_attachments input {
+ visibility: hidden;
+}
+
+.openerp .oe_mail .oe_mail_compose_attachment_list {
+ clear: both;
+}
+
+/* ------------------------------------------------------------ */
+/* mail.compose.message
+/* ------------------------------------------------------------ */
+
+
+/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
+.openerp .oe_mail.oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text,
+.openerp .oe_mail div.oe_mail_compose_message_body textarea.field_text {
+ width: 100%;
+ min-height: 120px;
+ height: auto;
+ padding: 4px;
+ font-size: 12px;
+ border: 1px solid #cccccc;
+}
+
+/* not top textarea */
+.openerp .oe_mail.oe_semantic_html_override .oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text {
+ height: 60px;
+}
+
+/* form_view: delete white background */
+.openerp .oe_mail_msg_content div.oe_formview {
+ background-color: transparent;
+}
+
+.openerp .oe_mail_msg_content div.oe_form_nosheet {
+ margin: 0px;
+}
+
+.openerp .oe_mail_msg_content table.oe_form_group {
+ margin: 0px;
+}
+
+.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[type='text'] {
+ width: 472px;
+}
+
+/* body_html: cleditor */
+.openerp .oe_mail_msg_content div.cleditorMain {
+ border: 1px solid #cccccc;
+}
+
+/* destination_partner_ids */
+.openerp .oe_mail_msg_content div.text-core {
+ height: 22px !important;
+ width: 472px;
+} \ No newline at end of file
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index e0c9ac9b7fc..15a81be4c54 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -18,17 +18,33 @@ openerp.mail = function(session) {
session.web.FormView = session.web.FormView.extend({
do_action: function(action, on_close) {
- if (action.res_model == 'mail.compose.message' &&
- action.context && action.context.redirect == true &&
- this.fields && this.fields.message_ids && this.fields.message_ids.view.get("actual_mode") != 'create') {
- var thread = this.fields.message_ids.thread;
+ if (action.res_model == 'mail.compose.message') {
+
+ /* hack for stop context propagation of wrong value
+ * delete this hack when a global method to clean context is create
+ */
+ for(var key in action.context){
+ if( key!='default_template_id' &&
+ key!='default_use_template' &&
+ key!='default_is_private' &&
+ key!='default_model' &&
+ key!='default_res_id' &&
+ key!='default_subtype' &&
+ key!='active_id' &&
+ key!='lang' &&
+ key!='bin_raw' &&
+ key!='tz' &&
+ key!='active_model' &&
+ key!='edi_web_url_view' &&
+ key!='active_ids')
+ action.context[key]=null;
+ };
+ /* end hack */
+
+ $('.openerp .oe_mail_wall_threads .oe_mail_thread button.oe_mail_wall_button_fetch').click();
- thread.refresh(action.context);
- return false;
- }
- else {
- return this._super(action, on_close);
}
+ return this._super(action, on_close);
},
});
@@ -73,6 +89,15 @@ openerp.mail = function(session) {
}
return string;
},
+
+ /* replace textarea text into html text
+ * (add <p>, <a>)
+ */
+ get_text2html: function(text){
+ return text
+ .replace(/[\n\r]/g,'<br/>')
+ .replace(/((?:https?|ftp):\/\/[\S]+)/g,'<a href="$1">$1</a> ')
+ }
};
@@ -84,160 +109,301 @@ openerp.mail = function(session) {
* This widget handles the display of a form to compose a new message.
* This form is a mail.compose.message form_view.
*/
-
- mail.ComposeMessage = session.web.Widget.extend({
+
+ mail.ThreadComposeMessage = session.web.Widget.extend({
template: 'mail.compose_message',
-
+
/**
* @param {Object} parent parent
* @param {Object} [options]
- * @param {Object} [options.context] context passed to the
- * mail.compose.message DataSetSearch. Please refer to this model
- * for more details about fields and default values.
+ * @param {Object} [context] context passed to the
+ * mail.compose.message DataSetSearch. Please refer to this model
+ * for more details about fields and default values.
+ * @param {Boolean} [show_attachment_delete]
*/
init: function (parent, options) {
var self = this;
this._super(parent);
this.attachment_ids = [];
- // options
- this.options = options || {};
- this.options.context = options.context || {};
- this.options.form_xml_id = options.form_xml_id || 'email_compose_message_wizard_form_chatter';
- this.options.form_view_id = options.form_view_id || false;
- this.show_attachment_delete = true;
+
+ this.context = options.context || {};
+
+ this.id = options.parameters.id;
+ this.model = options.parameters.model;
+ this.res_id = options.parameters.res_id;
+ this.is_private = options.parameters.is_private;
+ this.partner_ids = options.parameters.partner_ids;
+ this.options={thread:{}};
+ this.options.thread.show_header_compose = options.parameters.options.thread.show_header_compose;
+ this.options.thread.display_on_flat = options.parameters.options.thread.display_on_flat;
+
+ this.attachment_ids = [];
+ this.options.thread.show_attachment_delete = true;
+ this.options.thread.show_attachment_link = true;
+
+ this.parent_thread= parent.messages!= undefined ? parent : false;
+
+
+ this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
+ this.fileupload_id = _.uniqueId('oe_fileupload_temp');
+ $(window).on(self.fileupload_id, self.on_attachment_loaded);
},
- start: function () {
- this._super.apply(this, arguments);
- // customize display: add avatar, clean previous content
+ start: function(){
+ this.display_attachments();
+ this.bind_events();
+
+ //load avatar img
var user_avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
this.$('img.oe_mail_icon').attr('src', user_avatar);
- this.$('div.oe_mail_msg_content').empty();
- // create a context for the dataset and default_get of the wizard
- var context = _.extend({}, this.options.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
- if (this.options.form_view_id) {
- return this.create_form_view();
- }
- else {
- var data_ds = new session.web.DataSetSearch(this, 'ir.model.data');
- return data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).pipe(this.proxy('create_form_view'));
- }
},
- /** Create a FormView, then append it to the to widget DOM. */
- create_form_view: function (new_form_view_id) {
+ /* upload the file on the server, add in the attachments list and reload display
+ */
+ display_attachments: function(){
var self = this;
- this.options.form_view_id = (new_form_view_id && new_form_view_id[1]) || this.options.form_view_id;
- // 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.options.form_view_id, {
- action_buttons: false,
- pager: false,
- initial_mode: 'edit',
- disable_autofocus: true,
- });
- // add the form, bind events, activate the form
- var msg_node = this.$('div.oe_mail_msg_content');
- return $.when(this.form_view.appendTo(msg_node)).pipe(this.proxy('postprocess_create_form_view'));
- },
+ var render = $(session.web.qweb.render('mail.thread.message.attachments', {'widget': self}));
+ if(!this.list_attachment){
+ this.$('.oe_mail_compose_attachment_list').replaceWith( render );
+ } else {
+ this.list_attachment.replaceWith( render );
+ }
+ this.list_attachment = this.$("ul.oe_mail_msg_attachments");
- postprocess_create_form_view: function () {
- // handle attachment button
- this.fileupload_id = _.uniqueId('oe_fileupload');
- var button_attach = this.$('button.oe_mail_compose_message_attachment');
- var rendered = session.web.qweb.render('mail.compose_message.add_attachment', {'widget': this});
- $(rendered).insertBefore(button_attach);
- // move the button inside div.oe_hidden_input_file
- var input_node = this.$('input[name=ufile]');
- button_attach.detach().insertAfter(input_node);
- // set the function called when attachments are added
- this.$('input.oe_form_binary_file').change(this.on_attachment_change);
- this.bind_events();
- this.form_view.do_show();
+ // event: delete an attachment
+ this.$el.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete);
},
-
on_attachment_change: function (event) {
+ event.stopPropagation();
+ var self = this;
var $target = $(event.target);
if ($target.val() !== '') {
- this.$('form.oe_form_binary_form').submit();
- session.web.blockUI();
+
+ var filename = $target.val().replace(/.*[\\\/]/,'');
+
+ // if the files exits for this answer, delete the file before upload
+ var attachments=[];
+ for(var i in this.attachment_ids){
+ if((this.attachment_ids[i].filename || this.attachment_ids[i].name) == filename){
+ if(this.attachment_ids[i].upload){
+ return false;
+ }
+ this.ds_attachment.unlink([this.attachment_ids[i].id]);
+ } else {
+ attachments.push(this.attachment_ids[i]);
+ }
+ }
+ this.attachment_ids = attachments;
+
+ // submit file
+ //session.web.blockUI();
+ self.$('form.oe_form_binary_form').submit();
+ //self.submit_ajax_attachment();
+
+ this.$(".oe_attachment_file").hide();
+
+ this.attachment_ids.push({
+ 'id': 0,
+ 'name': filename,
+ 'filename': filename,
+ 'url': '',
+ 'upload': true
+ });
+ this.display_attachments();
}
},
+
+ submit_ajax_attachment: function(){
+ var self=this;
+ var $form = self.$('form.oe_form_binary_form');
+ var filename = this.$('input.oe_form_binary_file').val().replace(/.*[\\\/]/,'');
+
+ // create form data
+ var fomdata = new FormData();
+ $.each($form.find('input'), function(i, field) {
+ var $field=$(field);
+ if($field.attr('type')!='file'){
+ fomdata.append($field.attr('name'), $field.val());
+ } else {
+ fomdata.append($field.attr('name'), field.files[0]);
+ }
+ });
+
+ var progress=function(event) {
+ self.$("span[name='"+filename+"'] div:lt("+Math.floor(event.loaded / event.total*5)+")").show();
+ };
+
+ $.ajax({
+ url: $form.attr("action"),
+ data: fomdata,
+ cache: false,
+ contentType: false,
+ processData: false,
+ type: 'POST',
+ enctype: 'multipart/form-data',
+ xhr: function() {
+ // custom xhr
+ myXhr = $.ajaxSettings.xhr();
+ if(myXhr.upload){
+ // for handling the progress of the upload
+ myXhr.upload.addEventListener('progress', progress, false);
+ }
+ myXhr.addEventListener('progress', progress, false);
+ return myXhr;
+ },
+ success: function(data){
+ $iframe=$('<iframe style="display:none;"/>').html(data);
+ $iframe.appendTo(self.$el);
+ $iframe.remove();
+ }
+ });
+ },
+
+ on_attachment_loaded: function (event, result) {
+ //session.web.unblockUI();
+ for(var i in this.attachment_ids){
+ if(this.attachment_ids[i].filename == result.filename && this.attachment_ids[i].upload){
+ this.attachment_ids[i]={
+ 'id': result.id,
+ 'name': result.name,
+ 'filename': result.filename,
+ 'url': mail.ChatterUtils.get_attachment_url(this.session, result)
+ };
+ }
+ }
+ this.display_attachments();
+ var $input = this.$('input.oe_form_binary_file');
+ $input.after($input.clone(true)).remove();
+ this.$(".oe_attachment_file").show();
+ },
+ /* unlink the file on the server and reload display
+ */
on_attachment_delete: function (event) {
- if (event.target.dataset && event.target.dataset.id) {
- var attachment_id = parseInt(event.target.dataset.id);
- var idx = _.pluck(this.attachment_ids, 'id').indexOf(attachment_id);
- if (idx == -1) return false;
- new session.web.DataSetSearch(this, 'ir.attachment').unlink(attachment_id);
- this.attachment_ids.splice(idx, 1);
+ event.stopPropagation();
+ var attachment_id=$(event.target).data("id");
+ if (attachment_id) {
+ var attachments=[];
+ for(var i in this.attachment_ids){
+ if(attachment_id!=this.attachment_ids[i].id){
+ attachments.push(this.attachment_ids[i]);
+ }
+ else {
+ this.ds_attachment.unlink([attachment_id]);
+ }
+ }
+ this.attachment_ids = attachments;
this.display_attachments();
}
},
- display_attachments: function () {
- var attach_node = this.$('div.oe_mail_compose_message_attachments');
- var rendered = session.web.qweb.render('mail.thread.message.attachments', {'record': this});
- attach_node.empty();
- $(rendered).appendTo(attach_node);
- this.$('.oe_mail_msg_attachments').show();
- var composer_attachment_ids = _.pluck(this.attachment_ids, 'id');
- var onchange_like = {'value': {'attachment_ids': composer_attachment_ids}}
- this.form_view.on_processed_onchange(onchange_like, []);
- },
-
- /**
- * Reinitialize the widget field values to the default values obtained
- * using default_get on mail.compose.message. This allows to reinitialize
- * the widget without having to rebuild a complete form view.
- * @param {Object} new_context: context of the refresh */
- refresh: function (new_context) {
- if (! this.form_view) return;
- var self = this;
- this.attachments = [];
- this.options.context = _.extend(this.options.context, new_context || {});
- this.ds_compose.context = _.extend(this.ds_compose.context, this.options.context);
- return this.ds_compose.call('default_get', [
- ['subject', 'body_text', 'body', 'partner_ids', 'composition_mode',
- 'use_template', 'template_id', 'model', 'res_id', 'parent_id', 'content_subtype'],
- this.ds_compose.get_context(),
- ]).then( function (result) {
- self.form_view.on_processed_onchange({'value': result}, []);
- self.attachment_ids = [];
+ /* to avoid having unsorted file on the server.
+ we will show the users files of the first message post
+ */
+ set_free_attachments: function(){
+ var self=this;
+ this.parent_thread.ds_message.call('user_free_attachment').then(function(attachments){
+ this.attachment_ids=[];
+ for(var i in attachments){
+ self.attachment_ids[i]={
+ 'id': attachments[i].id,
+ 'name': attachments[i].name,
+ 'filename': attachments[i].filename,
+ 'url': mail.ChatterUtils.get_attachment_url(self.session, attachments[i])
+ };
+ }
self.display_attachments();
});
},
- /**
- * Bind events in the widget. Each event is slightly described
- * in the function. */
bind_events: function() {
var self = this;
- // event: add a new attachment
- $(window).on(this.fileupload_id, function() {
- var args = [].slice.call(arguments).slice(1);
- var attachment = args[0];
- attachment['url'] = mail.ChatterUtils.get_attachment_url(self.session, attachment);
- self.attachment_ids.push(attachment);
- self.display_attachments();
- session.web.unblockUI();
- });
- // event: delete an attachment
- this.$el.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete);
+
+ // set the function called when attachments are added
+ this.$el.on('change', 'input.oe_form_binary_file', self.on_attachment_change );
+ this.$el.on('click', 'a.oe_cancel', self.on_cancel );
+ this.$el.on('click', 'button.oe_post', function(){self.on_message_post()} );
+ this.$el.on('click', 'button.oe_full', function(){self.on_compose_fullmail()} );
},
- }),
-
+
+ on_compose_fullmail: function(){
+ var attachments=[];
+ for(var i in this.attachment_ids){
+ attachments.push(this.attachment_ids[i].id);
+ }
+ var action = {
+ type: 'ir.actions.act_window',
+ res_model: 'mail.compose.message',
+ view_mode: 'form',
+ view_type: 'form',
+ action_from: 'mail.ThreadComposeMessage',
+ views: [[false, 'form']],
+ target: 'new',
+ context: {
+ 'default_res_model': this.context.default_res_model,
+ 'default_res_id': this.context.default_res_id,
+ 'default_content_subtype': 'html',
+ 'default_is_private': true,
+ 'default_parent_id': this.id,
+ 'default_body': mail.ChatterUtils.get_text2html(this.$('textarea').val() || ''),
+ 'default_attachment_ids': attachments
+ },
+ };
+ this.do_action(action);
+ },
+
+ on_cancel: function(){
+ event.stopPropagation();
+ this.$('textarea').val("");
+ this.$('input[data-id]').remove();
+ //this.attachment_ids=[];
+ this.display_attachments();
+ if(!this.options.thread.show_header_compose || !this.options.thread.display_on_flat){
+ this.$el.hide();
+ }
+ },
+
+ /*post a message and fetch the message*/
+ on_message_post: function (body) {
+
+ if (! body) {
+ var comment_node = this.$('textarea');
+ var body = comment_node.val();
+ comment_node.val('');
+ }
+
+ var attachments=[];
+ for(var i in this.attachment_ids){
+ if(this.attachment_ids[i].upload){
+ session.web.dialog($('<div>' + session.web.qweb.render('CrashManager.warning', {message: 'Please, wait while the file is uploading.'}) + '</div>'));
+ return false;
+ }
+ attachments.push(this.attachment_ids[i].id);
+ }
+
+ if(body.match(/\S+/)) {
+ this.parent_thread.ds_thread.call('message_post_api', [
+ this.context.default_res_id,
+ mail.ChatterUtils.get_text2html(body),
+ false,
+ 'comment',
+ false,
+ this.context.default_parent_id,
+ attachments]
+ ).then(this.parent_thread.proxy('switch_new_message'));
+ this.attachment_ids=[];
+ this.on_cancel();
+ return true;
+ }
+ },
+ });
/**
* ------------------------------------------------------------
* Thread Message Expandable Widget
* ------------------------------------------------------------
*
- * This widget handles the display the expandable message in a thread. The
- * [thread_level] parameter sets the thread level number:
+ * This widget handles the display the expandable message in a thread.
* - thread
* - - visible message
* - - expandable
@@ -256,14 +422,13 @@ openerp.mail = function(session) {
default_res_id: 0,
default_parent_id: false }, options.context || {});
- this.id = -1;
+ this.id = options.parameters.id || -1;
this.parent_id= options.parameters.parent_id || false;
this.nb_messages = options.parameters.nb_messages || 0;
- this.type = options.parameters.type || false;
+ this.type = 'expandable';
// record options and data
this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0] ;
-
},
@@ -285,8 +450,8 @@ openerp.mail = function(session) {
* @param {object} mouse envent
*/
on_expandable: function (event) {
- event.stopPropagation();
- this.parent_thread.message_fletch(false, this.domain, this.context);
+ if(event)event.stopPropagation();
+ this.parent_thread.message_fetch(false, this.domain, this.context);
this.destroy();
return false;
},
@@ -302,7 +467,7 @@ openerp.mail = function(session) {
* - record.timerelative: relative time givein by timeago lib
* - record.avatar: image url
* - record.attachment_ids[].url: url of each attachmentThe
- * [thread_level] parameter sets the thread level number:
+ * thread view :
* - root thread
* - - sub message (parent_id = root message)
* - - - sub thread
@@ -322,45 +487,24 @@ openerp.mail = function(session) {
* @param {Object} [options]
* @param {Object} [thread] read obout mail.Thread object
* @param {Object} [message]
- * @param {Number} [message_ids=null] ids for message_fletch
+ * @param {Number} [message_ids=null] ids for message_fetch
* @param {Number} [message_data=null] already formatted message data,
* for subthreads getting data from their parent
* @param {Number} [truncate_limit=250] 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
* @param {Boolean} [show_record_name]
- * @param {Boolean} [show_reply]
- * @param {Boolean} [show_reply_by_email]
* @param {Boolean} [show_dd_delete]
* @param {Boolean} [show_dd_hide]
*/
init: function(parent, options) {
this._super(parent);
- this.domain = options.domain || [];
- this.context = _.extend({
- default_model: 'mail.thread',
- default_res_id: 0,
- default_parent_id: false }, options.context || {});
- // options
- this.options={
- 'thread' : options.options.thread,
- 'message' : {
- 'message_ids': options.options.message.message_ids || null,
- 'message_data': options.options.message.message_data || null,
- 'show_record_name': options.options.message.show_record_name != undefined ? options.options.message.show_record_name: true,
- 'show_reply': options.options.message.show_reply || false,
- 'show_reply_by_email': options.options.message.show_reply_by_email || false,
- 'show_dd_delete': options.options.message.show_dd_delete || false,
- 'show_dd_hide': options.options.message.show_dd_hide || false,
- 'truncate_limit': options.options.message.truncate_limit || 250,
- }
- };
- // record options and data
- this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0] ;
-
- var param = options.parameters;
// record parameters
+ var param = options.parameters;
+ for(var i in param){
+ this[i] = param[i];
+ }
this.id = param.id || -1;
this.model = param.model || false;
this.parent_id= param.parent_id || false;
@@ -372,7 +516,7 @@ openerp.mail = function(session) {
this.record_name = param.record_name || false;
this.body = param.body || false;
this.vote_user_ids =param.vote_user_ids || [];
- this.has_voted = param.has_voted || false;
+ this.has_voted = param.has_voted || false;
this.vote_user_ids = param.vote_user_ids || [];
@@ -381,6 +525,28 @@ openerp.mail = function(session) {
this.author_id = param.author_id || [];
this.attachment_ids = param.attachment_ids || [];
+ // record domain and context
+ this.domain = options.domain || [];
+ this.context = _.extend({
+ default_model: 'mail.thread',
+ default_res_id: 0,
+ default_parent_id: false }, options.context || {});
+
+ // record options
+ this.options={
+ 'thread' : options.options.thread,
+ 'message' : {
+ 'message_ids': options.options.message.message_ids || null,
+ 'message_data': options.options.message.message_data || null,
+ 'show_record_name': options.options.message.show_record_name != undefined ? options.options.message.show_record_name: true,
+ 'show_dd_delete': options.options.message.show_dd_delete || false,
+ 'show_dd_hide': options.options.message.show_dd_hide || false,
+ 'truncate_limit': options.options.message.truncate_limit || 250,
+ }
+ };
+
+ // record options and data
+ this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0];
this.thread = false;
if( param.id > 0 ) {
@@ -420,10 +586,10 @@ openerp.mail = function(session) {
* in the function. */
bind_events: function() {
var self = this;
+
// event: click on 'Attachment(s)' in msg
- this.$el.on('click', 'a.oe_mail_msg_view_attachments', function (event) {
- var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments');
- act_dom.toggle();
+ this.$('a.oe_mail_msg_view_attachments:first').on('click', function (event) {
+ self.$('.oe_mail_msg_attachments:first').toggle();
});
// event: click on icone 'Read' in header
this.$el.on('click', 'a.oe_read', this.on_message_read_unread);
@@ -434,21 +600,13 @@ openerp.mail = function(session) {
// event: click on 'Reply' in msg
this.$el.on('click', 'a.oe_reply', this.on_message_reply);
- // event: click on 'Reply by email' in msg side menu
- this.$el.on('click', 'a.oe_reply_by_email', this.on_message_reply_by_mail);
// event: click on 'Vote' button
this.$el.on('click', 'button.oe_mail_msg_vote', this.on_vote);
},
on_message_reply:function(event){
event.stopPropagation();
- this.thread.on_compose_message($(event.srcElement).hasClass("oe_full_reply"), false);
- return false;
- },
-
- on_message_reply_by_mail:function(event){
- event.stopPropagation();
- this.thread.on_compose_message(true, true);
+ this.thread.on_compose_message();
return false;
},
@@ -468,6 +626,7 @@ openerp.mail = function(session) {
if(this.thread){
return false;
}
+ var param = _.extend(self, {'parent_id': self.id});
/*create thread*/
self.thread = new mail.Thread(self, {
'domain': self.domain,
@@ -480,11 +639,7 @@ openerp.mail = function(session) {
'thread' : self.options.thread,
'message' : self.options.message
},
- 'parameters':{
- 'model': self.model,
- 'id': self.id,
- 'parent_id': self.id
- }
+ 'parameters': param
}
);
/*insert thread in parent message*/
@@ -501,9 +656,6 @@ openerp.mail = function(session) {
} else {
self.destroy();
}
- for(var i in this.thread.messages){
- this.thread.messages[i].animated_destroy({fadeTime:0});
- }
},
on_message_delete: function (event) {
@@ -523,10 +675,17 @@ openerp.mail = function(session) {
*/
on_message_read_unread: function (event) {
event.stopPropagation();
- this.animated_destroy({fadeTime:250});
+ if($(event.srcElement).hasClass("oe_read")) this.animated_destroy({fadeTime:250});
// if this message is read, all childs message display is read
var ids = [this.id].concat( this.get_child_ids() );
- this.ds_notification.call('set_message_read', [ids,$(event.srcElement).hasClass("oe_read")]);
+
+ if($(event.srcElement).hasClass("oe_read")) {
+ this.ds_notification.call('set_message_read', [ids,true]);
+ this.$el.removeClass("oe_mail_unread").addClass("oe_mail_read");
+ } else {
+ this.ds_notification.call('set_message_read', [ids,false]);
+ this.$el.removeClass("oe_mail_read").addClass("oe_mail_unread");
+ }
return false;
},
@@ -598,8 +757,8 @@ openerp.mail = function(session) {
display_vote: function () {
var self = this;
var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self});
- self.$(".placeholder-mail-vote").empty();
- self.$(".placeholder-mail-vote").html(vote_element);
+ self.$(".placeholder-mail-vote:first").empty();
+ self.$(".placeholder-mail-vote:first").html(vote_element);
},
});
@@ -609,11 +768,13 @@ openerp.mail = function(session) {
* ------------------------------------------------------------
*
* This widget handles the display of a thread of messages. The
- * [thread_level] parameter sets the thread level number:
- * - root message
+ * thread view:
+ * - root thread
* - - sub message (parent_id = root message)
- * - - - sub sub message (parent id = sub message)
+ * - - - sub thread
+ * - - - - sub sub message (parent id = sub thread)
* - - sub message (parent_id = root message)
+ * - - - sub thread
*/
mail.Thread = session.web.Widget.extend({
template: 'mail.thread',
@@ -627,7 +788,6 @@ openerp.mail = function(session) {
* @param {Object} [options]
* @param {Object} [message] read about mail.ThreadMessage object
* @param {Object} [thread]
- * @param {Number} [thread_level=0] number of thread levels
* @param {Boolean} [use_composer] use the advanced composer, or
* the default basic textarea if not set
* @param {Number} [expandable_number=5] number message show
@@ -637,7 +797,7 @@ openerp.mail = function(session) {
* @param {Boolean} [display_on_flat] display all thread
* on the wall thread level (no hierarchy)
* @param {Array} [parents] liked with the parents thread
- * use with browse, fletch... [O]= top parent
+ * use with browse, fetch... [O]= top parent
*/
init: function(parent, options) {
this._super(parent);
@@ -650,7 +810,7 @@ openerp.mail = function(session) {
// options
this.options={
'thread' : {
- 'thread_level': options.options.thread.thread_level || 0,
+ 'thread_level': (options.options.thread.thread_level+1) || 0,
'show_header_compose': (options.options.thread.show_header_compose != undefined ? options.options.thread.show_header_compose: false),
'use_composer': options.options.thread.use_composer || false,
'expandable_number': options.options.thread.expandable_number || 5,
@@ -670,6 +830,14 @@ openerp.mail = function(session) {
this.id= param.id || false;
this.model= param.model || false;
this.parent_id= param.parent_id || false;
+ this.is_private = param.is_private || false;
+ this.author_id = param.author_id || false;
+ this.partner_ids = [];
+ for(var i in param.partner_ids){
+ if(param.partner_ids[i][0]!=(param.author_id ? param.author_id[0] : -1)){
+ this.partner_ids.push(param.partner_ids[i]);
+ }
+ }
this.messages = [];
@@ -687,8 +855,7 @@ openerp.mail = function(session) {
this.display_user_avatar();
var display_done = compose_done = false;
- // add message composition form view
- compose_done = this.instantiate_composition_form();
+ this.instantiate_ComposeMessage();
this.bind_events();
@@ -699,23 +866,57 @@ openerp.mail = function(session) {
return display_done && compose_done;
},
- /**
- * Override-hack of do_action: automatically load message on the chatter.
- * Normally it should be called only when clicking on 'Post/Send'
- * in the composition form. */
- do_action: function(action, on_close) {
- this.instantiate_composition_form();
- this.message_fletch(false, false, false, [action.id]);
- return this._super(action, on_close);
+ instantiate_ComposeMessage: function(){
+ // add message composition form view
+ this.ComposeMessage = new mail.ThreadComposeMessage(this,{
+ 'context': this.context,
+ 'parameters': this,
+ 'show_attachment_delete': true,
+ });
+ this.ComposeMessage.appendTo(this.$(".oe_mail_thread_action:first"));
},
/* this method is runing for first parent thread
*/
on_first_thread: function(){
+ var self=this;
// fetch and display message, using message_ids if set
- display_done = this.message_fletch(true);
- //show the first write message
- this.$(">.oe_mail_thread_action").show();
+ this.message_fetch();
+
+ $(document).scroll( self.on_scroll );
+ window.setTimeout( self.on_scroll, 500 );
+
+ $(session.web.qweb.render('mail.wall_no_message', {})).appendTo(this.$('ul.oe_mail_thread_display'));
+
+ if(this.options.thread.show_header_compose){
+ this.ComposeMessage.$el.show();
+ //this.ComposeMessage.set_free_attachments();
+ }
+
+ var button_fetch = $('<button style="display:none;" class="oe_mail_wall_button_fetch"/>').click(function(event){
+ if(event)event.stopPropagation();
+ self.message_fetch();
+ });
+ this.$el.prepend(button_fetch);
+ this.$el.addClass("oe_mail_wall_first_thread");
+ },
+
+ /* When the expandable object is visible on screen (with scrolling)
+ * then the on_expandable function is launch
+ */
+ on_scroll: function(event){
+ if(event)event.stopPropagation();
+ var last=this.messages[0];
+ if(last && last.type=="expandable"){
+ var pos = last.$el.position();
+ if(pos.top){
+ /* bottom of the screen */
+ var bottom = $(window).scrollTop()+$(window).height()+100;
+ if(bottom - pos.top > 0){
+ last.on_expandable();
+ }
+ }
+ }
},
/**
@@ -723,17 +924,8 @@ openerp.mail = function(session) {
* in the function. */
bind_events: function() {
var self = this;
- // event: writing in basic textarea of composition form (quick reply)
- // event: onblur for hide 'Reply'
- this.$('.oe_mail_compose_textarea:first 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.message_post(); }
- })
- .blur(function (event) {
- $(this).parents('.oe_mail_thread_action:first').hide();
- });
+ self.$('.oe_mail_compose_textarea .oe_more').click(function () { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').show(); p.find('.oe_more').hide(); });
+ self.$('.oe_mail_compose_textarea .oe_more_hidden').click(function () { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').hide(); p.find('.oe_more').show(); });
},
/* get all child message/thread id linked
@@ -796,71 +988,13 @@ openerp.mail = function(session) {
return this.options.thread._parents[0].messages[0].browse_message(options);
},
- /** Instantiate the composition form, with every parameters in context
- * or in the widget context.
- * @param {object}
- * @param {boolean} show_header_compose, force to instantiate form
- */
- instantiate_composition_form: function(context) {
- // add message composition form view
- if ((!context || !context.show_header_compose) &&
- (!this.options.thread.show_header_compose || !this.options.thread.use_composer ||
- (this.options.thread.show_header_compose <= this.options.thread._parents.length && this.options.thread._parents[0]!=this))) {
- this.$("textarea:first").val("");
- return false;
- }
- if (this.compose_message_widget) {
- this.compose_message_widget.refresh();
- } else {
- var context = _.extend(context || {}, this.context);
- this.compose_message_widget = new mail.ComposeMessage(this, {'context': context});
- var composition_node = this.$('div.oe_mail_thread_action');
- composition_node.empty();
- return this.compose_message_widget.appendTo(composition_node);
- }
- },
-
/* this function is launch when a user click on "Reply" button
*/
- on_compose_message: function(full_reply, by_mail){
- if(full_reply){
- this.instantiate_composition_form({'show_header_compose':true});
- }
- if(by_mail){
- if (!this.compose_message_widget) return true;
- this.compose_message_widget.refresh({
- 'default_composition_mode': 'reply',
- 'default_parent_id': this.id,
- 'default_content_subtype': 'html'} );
- }
- this.$('div.oe_mail_thread_action:first').toggle();
+ on_compose_message: function(){
+ this.ComposeMessage.$el.toggle();
return false;
},
- refresh: function (action_context) {
- var self=this;
- _(this.messages).each(function(){ self.destroy(); });
- self.message_fletch();
- },
-
- /*post a message and fletch the message*/
- message_post: function (body) {
- var self = this;
- if (! body) {
- var comment_node = this.$('textarea');
- var body = comment_node.val();
- comment_node.val('');
- }
- if(body.match(/\S+/)) {
- this.ds_thread.call('message_post_api', [
- [this.context.default_res_id], body, false, 'comment', false, this.context.default_parent_id, undefined])
- .then(this.proxy('switch_new_message'));
- }
- else {
- return false;
- }
- },
-
/** Fetch messages
* @param {Bool} initial_mode: initial mode: try to use message_data or
* message_ids, if nothing available perform a message_read; otherwise
@@ -868,7 +1002,7 @@ openerp.mail = function(session) {
* @param {Array} replace_domain: added to this.domain
* @param {Object} replace_context: added to this.context
*/
- message_fletch: function (initial_mode, replace_domain, replace_context, ids) {
+ message_fetch: function (initial_mode, replace_domain, replace_context, ids) {
var self = this;
// initial mode: try to use message_data or message_ids
@@ -879,8 +1013,8 @@ openerp.mail = function(session) {
fetch_domain = replace_domain ? replace_domain : this.domain;
fetch_context = replace_context ? replace_context : this.context;
fetch_context.message_loaded= [this.id||0].concat( self.options.thread._parents[0].get_child_ids() );
-
- return this.ds_message.call('message_read', [ids, fetch_domain, (this.options.thread.thread_level+1), fetch_context, this.context.default_parent_id || undefined]
+
+ return this.ds_message.call('message_read', [ids, fetch_domain, fetch_context, 0, this.context.default_parent_id || undefined]
).then(this.proxy('switch_new_message'));
},
@@ -906,6 +1040,8 @@ openerp.mail = function(session) {
insert_message: function (message) {
var self=this;
+ this.$("li.oe_wall_no_message").remove();
+
if(message.type=='expandable'){
var message = new mail.ThreadExpandable(self, {
'domain': message.domain,
@@ -930,42 +1066,46 @@ openerp.mail = function(session) {
});
}
- var thread = self.options.thread.display_on_flat ? self.options.thread._parents[0] : this;
+ var thread_messages = (self.options.thread.display_on_flat && self.options.thread.thread_level ? self.options.thread._parents[0].messages : []).concat(self.messages);
+ var thread = (self.options.thread.display_on_flat && self.options.thread.thread_level ? self.options.thread._parents[0] : self);
// check older and newer message for insert
var parent_newer = false;
var parent_older = false;
- for(var i in thread.messages){
- if(thread.messages[i].id > message.id){
- if(!parent_newer || parent_newer.id>thread.messages[i].id)
- parent_newer = thread.messages[i];
- } else if(thread.messages[i].id>0 && thread.messages[i].id < message.id) {
- if(!parent_older || parent_older.id<thread.messages[i].id)
- parent_older = thread.messages[i];
+ for(var i in thread_messages){
+ if(thread_messages[i].id > message.id){
+ if(!parent_newer || parent_newer.id>=thread_messages[i].id)
+ parent_newer = thread_messages[i];
+ } else if(thread_messages[i].id>0 && thread_messages[i].id < message.id) {
+ if(!parent_older || parent_older.id<thread_messages[i].id)
+ parent_older = thread_messages[i];
}
}
- if(parent_newer)
- message.insertAfter(parent_newer.$el);
- else if(parent_older)
- message.insertBefore(parent_older.$el);
- else
- message.prependTo(thread.list_ul);
+ var sort = self.options.thread.thread_level==0 || (self.options.thread.display_on_flat && self.options.thread.thread_level<=1);
- return message
- },
-
- /* Hide messages if they are more message that _expandable_max
- * display "show more messages"
- */
- display_expandable: function(){
- if(this.messages.length>this.options._expandable_max){
- this.list_ul.find('>li:gt('+(this.options._expandable_max-1)+')').hide();
- this.more_msg.show();
- } else {
- this.list_ul.find('>li').show();
- this.more_msg.hide();
+ if(parent_older){
+ if(sort){
+ message.insertBefore(parent_older.$el);
+ } else {
+ message.insertAfter(parent_older.$el);
+ }
+ }
+ else if(parent_newer){
+ if(sort){
+ message.insertAfter(parent_newer.$el);
+ } else {
+ message.insertBefore(parent_newer.$el);
+ }
}
+ else {
+ if(sort){
+ message.prependTo(thread.list_ul);
+ } else {
+ message.appendTo(thread.list_ul);
+ }
+ }
+ return message
},
display_user_avatar: function () {
@@ -1003,7 +1143,6 @@ openerp.mail = function(session) {
this._super.apply(this, arguments);
this.options.domain = this.options.domain || [];
this.options.context = {'default_model': 'mail.thread', 'default_res_id': false};
- this.options.thread_level = this.options.thread_level || 0;
},
start: function() {
@@ -1017,6 +1156,10 @@ openerp.mail = function(session) {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
+ /**
+ * Reinitialize the widget field and Display the threads
+ * @param {Object} new_context: context of the refresh
+ */
set_value: function() {
var self = this;
this._super.apply(this, arguments);
@@ -1034,30 +1177,26 @@ openerp.mail = function(session) {
var show_header_compose = this.view.is_action_enabled('edit') ||
(this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
+ if(this.thread){
+ this.thread.destroy();
+ }
this.thread = new mail.Thread(self, {
'domain': domain,
'context': this.options.context,
'options':{
'thread':{
- 'thread_level': this.options.thread_level,
'show_header_compose': show_header_compose,
'use_composer': show_header_compose,
'display_on_flat':true
},
'message':{
- 'show_dd_delete': true,
- 'show_reply_by_email': show_header_compose,
+ 'show_dd_delete': true
}
},
'parameters': {},
}
);
-
- this.$('ul.oe_mail_wall_threads').empty();
- var render_res = session.web.qweb.render('mail.wall_thread_container', {});
- $(render_res).appendTo(this.$('ul.oe_mail_wall_threads'));
-
- return this.thread.appendTo( this.$('li.oe_mail_wall_thread:last') );
+ return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
},
});
@@ -1089,7 +1228,6 @@ openerp.mail = function(session) {
this.options = options || {};
this.options.domain = options.domain || [];
this.options.context = options.context || {};
- this.options.thread_level = options.thread_level || 1;
this.search_results = {'domain': [], 'context': {}, 'groupby': {}}
this.ds_msg = new session.web.DataSetSearch(this, 'mail.message');
},
@@ -1098,6 +1236,8 @@ openerp.mail = function(session) {
this._super.apply(this, arguments);
var searchview_ready = this.load_searchview({}, false);
var thread_displayed = this.message_render();
+ this.options.domain = this.options.domain.concat(this.search_results['domain']);
+ this.bind_events();
return (searchview_ready && thread_displayed);
},
@@ -1135,36 +1275,36 @@ openerp.mail = function(session) {
});
},
- /** Clean and display the threads */
- message_render: function (search) {
- var domain = this.options.domain.concat(this.search_results['domain']);
-
- var domain = _.extend(this.options.domain, search&&search.domain? search.domain : {});
- var context = _.extend(this.options.context, search&&search.context ? search.context : {});
+ /**
+ * Display the threads
+ */
+ message_render: function (search) {
this.thread = new mail.Thread(this, {
- 'domain' : domain,
- 'context' : context,
+ 'domain' : this.options.domain.concat(this.search_results['domain']),
+ 'context' : _.extend(this.options.context, search&&search.search_results['context'] ? search.search_results['context'] : {}),
'options': {
'thread' :{
- 'thread_level': this.options.thread_level,
'use_composer': true,
- 'show_header_compose': 1,
+ 'show_header_compose': false,
},
'message': {
- 'show_reply': this.options.thread_level > 0,
+ 'show_reply': true,
'show_dd_hide': true,
+ 'show_dd_delete': true,
},
},
'parameters': {},
}
);
+ return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
- this.$('ul.oe_mail_wall_threads').empty();
- var render_res = session.web.qweb.render('mail.wall_thread_container', {});
- $(render_res).appendTo(this.$('ul.oe_mail_wall_threads'));
-
- return this.thread.appendTo( this.$('li.oe_mail_wall_thread:last') );
},
+
+ bind_events: function(){
+ var self=this;
+ this.$("button.oe_write_full:first").click(function(){ self.thread.ComposeMessage.on_compose_fullmail(); });
+ this.$("button.oe_write_onwall:first").click(function(){ self.thread.ComposeMessage.$el.toggle(); });
+ }
});
};
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index 953b8cd9fc8..236e3081136 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -40,6 +40,7 @@ openerp_mail_followers = function(session, mail) {
this._check_visibility();
this.reinit();
this.bind_events();
+ this.display_subtypes();
},
_check_visibility: function() {
@@ -53,31 +54,15 @@ openerp_mail_followers = function(session, mail) {
bind_events: function() {
var self = this;
- this.$('div.oe_mouse_subtypes')
- .on('mouseover', function () {
- $(this).removeClass('oe_mouseout').addClass('oe_mouseover');
- self.display_subtypes();
- })
- .on('mouseleave', function () {
- $(this).removeClass('oe_mouseover').addClass('oe_mouseout');
- self.display_subtypes();
- });
-
this.$('button.oe_follower')
.on('click', function () {
if($(this).hasClass('oe_notfollow'))
self.do_follow();
else
self.do_unfollow();
- })
- .on('mouseover', function () {
- $(this).removeClass('oe_mouseout').addClass('oe_mouseover');
- })
- .on('mouseleave', function () {
- $(this).removeClass('oe_mouseover').addClass('oe_mouseout');
});
- this.$el.on('click', 'ul.oe_subtypes input', function () { self.do_update_subscription(); })
+ this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription );
this.$el.on('click', 'button.oe_invite', function(event) {
action = {
@@ -149,7 +134,6 @@ openerp_mail_followers = function(session, mail) {
var self = this;
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
this.$('div.oe_mail_recthread_followers h4').html(this.options.title + (records.length>=5 ? ' (' + records.length + ')' : '') );
- console.log(records);
for(var i=0; i<records.length&&i<5; i++) {
var record=records[i];
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
@@ -174,7 +158,8 @@ openerp_mail_followers = function(session, mail) {
set_subtypes:function(data){
var self = this;
- var records = data[this.view.datarecord.id].message_subtype_data;
+ var records = (data[this.view.datarecord.id] || data[null]).message_subtype_data;
+
_(records).each(function (record, record_name) {
record.name = record_name;
record.followed = record.followed || undefined;
@@ -195,16 +180,10 @@ openerp_mail_followers = function(session, mail) {
},
do_follow: function () {
- var self =this;
_(this.$('.oe_msg_subtype_check')).each(function(record){
$(record).attr('checked','checked');
});
- var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], undefined, undefined, context]).pipe(function(value_){
- self.read_value(value_);
- if(!self.$('.oe_recthread_subtypes').is(":visible"))
- self.display_subtypes(true);
- });
+ this.do_update_subscription();
},
do_unfollow: function () {
@@ -212,23 +191,28 @@ openerp_mail_followers = function(session, mail) {
$(record).attr('checked',false);
});
var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], undefined, context]).pipe(this.proxy('read_value'));
+ return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]).pipe(this.proxy('read_value'));
},
- do_update_subscription: function () {
- var context = new session.web.CompoundContext(this.build_context(), {});
+ do_update_subscription: function (event) {
var self = this;
var checklist = new Array();
- _(this.$('.oe_msg_subtype_check')).each(function(record){
+ _(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function(record){
if($(record).is(':checked')) {
checklist.push(parseInt($(record).data('id')))}
});
if(!checklist.length)
return this.do_unfollow();
- else
- return this.ds_model.call('message_subscribe_users',[[self.view.datarecord.id], undefined, checklist, context]).pipe(this.proxy('read_value'));
+ else{
+ var context = new session.web.CompoundContext(this.build_context(), {});
+ return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], undefined, context]).pipe(function(value_){
+ self.read_value(value_);
+ self.display_subtypes(true);
+ });
+ }
+
},
});
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index 07c9b406cdc..7941925415d 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,11 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
+ <!--
+ 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 class="oe_mail_compose_textarea">
+ <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 -->
+ <div class="oe_mail_post_header">
+ <t t-call="mail.thread.list_recipients"/>
+ <a class="oe_cancel oe_e">X</a>
+ </div>
+ <textarea class="field_text" placeholder="Add your comment here..."/>
+ <div class="oe_mail_post_footer">
+ <div class="oe_mail_compose_attachment_list"/>
+ <button class="oe_full">Full mail message</button>
+ <button class="oe_post">Post message</button>
+ <t t-call="mail.compose_message.add_attachment"/>
+ </div>
+ </div>
+ <div class="oe_clear"/>
+ </div>
+ </t>
+
+ <!--
+ mail.compose_message.add_attachment template
+ Small template to be inserted in the composition for add attachments
+ -->
+ <t t-name="mail.compose_message.add_attachment">
+ <div class="oe_attachment_file">
+ <div class="oe_add">
+ <!-- uploader of file -->
+ <button><span class="oe_e">p</span></button>
+ <t t-call="HiddenInputFile">
+ <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+ <t t-set="fileupload_action">/web/binary/upload_attachment</t>
+ <input type="hidden" name="model" value=""/>
+ <input type="hidden" name="id" value="0"/>
+ <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
+ </t>
+ </div>
+ </div>
+ </t>
+
+ <!--
+ mail.thread.message.attachments template
+ Template used to display attachments in a mail.message
+ -->
+ <t t-name="mail.thread.message.attachments">
+ <ul t-attf-class="oe_mail_msg_attachments #{widget.attachment_ids[0] and widget.options.thread.show_attachment_link?'':'oe_hidden'}">
+ <t t-foreach="widget.attachment_ids" t-as="attachment">
+ <li>
+ <span t-if="(attachment.upload or attachment.percent_loaded&lt;100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
+ <div class="oe_upload_in_process">
+ <span>Upload in progress...</span>
+ <div t-attf-style="{attachment.percent_loaded&gt;0?'':'display:none;'}"/>
+ <div t-attf-style="{attachment.percent_loaded&gt;20?'':'display:none;'}"/>
+ <div t-attf-style="{attachment.percent_loaded&gt;40?'':'display:none;'}"/>
+ <div t-attf-style="{attachment.percent_loaded&gt;60?'':'display:none;'}"/>
+ <div t-attf-style="{attachment.percent_loaded&gt;80?'':'display:none;'}"/>
+ </div>
+ <t t-raw="attachment.name || attachment.filename"/>
+ </span>
+ <a t-if="(!attachment.upload or attachment.percent_loaded&gt;=100)" t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
+ <t t-raw="attachment.name || attachment.filename"/>
+ </a>
+ <t t-if="widget.options.thread.show_attachment_delete and (!attachment.upload or attachment.percent_loaded&gt;=100)">
+ <a class="oe_right oe_mail_attachment_delete" title="Delete this attachment" t-attf-data-id="{attachment.id}">x</a>
+ </t>
+
+ </li>
+ </t>
+ </ul>
+ </t>
+
+ <t t-name="mail.thread.message.private">
+ <div>
+ <span class="oe_placeholder_checkbox_private"/>
+ <span class="oe_send_private">This email is private.</span>
+ <span class="oe_send_public">I wrote for contacts and all my followers.</span>
+ </div>
+ </t>
+
+ <!--
+ template to the recipients list
+ -->
+ <t t-name="mail.thread.list_recipients">
+ <div class="oe_mail_list_recipients">
+ Post to:
+ <span t-if="!widget.is_private" class="oe_all_follower">All Followers</span>
+ <t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
+ <t t-set="inc" t-value="0"/>
+ <t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
+ </t>
+ <t t-if="widget.partner_ids.length>=3">
+ <span class="oe_more">, <a><t t-raw="widget.partner_ids.length-3"/> others...</a></span>
+ <a class="oe_more_hidden">&lt;&lt;&lt;</a>
+ </t>
+ </div>
+ </t>
+
<!--
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">
+ <div t-name="mail.wall" class="oe_view_manager oe_mail_wall oe_view_manag
+ er_current">
<table class="oe_view_manager_header">
<colgroup>
<col width="33%"/>
@@ -25,21 +132,30 @@
</td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
</tr>
+ <tr class="oe_header_row">
+ <td colspan="2">
+ <button type="button" class="oe_write_full oe_highlight">
+ Compose a new message
+ </button>
+ <button type="button" class="oe_write_onwall" help="Your followers can read this message">
+ Write to your followers
+ </button>
+ </td>
+ </tr>
</tbody>
</table>
- <ul class="oe_mail_wall_threads">
- <!-- contains threads -->
- </ul>
+ <!-- placeholder for the wall threads -->
+ <div class="oe_mail_wall_threads"/>
<div class="oe_mail_wall_aside">
<!-- contains currently nothing -->
</div>
</div>
<!--
- wall_thread_container template for the wall
- Each discussion thread is contained inside this template
+ display message on the wall when there are no message
-->
- <li t-name="mail.wall_thread_container" class="oe_mail_wall_thread">
+ <li t-name="mail.wall_no_message" class="oe_wall_no_message">
+ You have no messages
</li>
<!--
@@ -55,39 +171,6 @@
</div>
<!--
- 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 class="oe_mail_compose_textarea">
- <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 placeholder="Add your comment here..."/>
- </div>
- <div class="oe_clear"/>
- </div>
- </t>
-
- <!--
- mail.compose_message.add_attachment template
- Small template to be inserted in the composition form to add attachments
- -->
- <t t-name="mail.compose_message.add_attachment">
- <t t-call="HiddenInputFile">
- <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
- <t t-set="fileupload_action">/web/binary/upload_attachment</t>
- <input type="hidden" name="model" t-att-value="widget.form_view.model"/>
- <input type="hidden" name="id" t-att-value="widget.form_view.datarecord.id || 0"/>
- <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
- </t>
- </t>
-
- <!--
thread template
This template holds a thread of comments. It begins with an actions
container, holding the composition form. Then come the various
@@ -96,7 +179,6 @@
<div t-name="mail.thread" class="oe_mail oe_mail_thread oe_semantic_html_override">
<div class="oe_mail_thread_action">
<!-- contains the composition box (form + image) -->
- <t t-call="mail.compose_message"/>
</div>
<ul class="oe_mail_thread_display">
<!-- contains the threads -->
@@ -104,16 +186,15 @@
</div>
<!-- default layout -->
- <li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.unread?'oe_mail_unread':'oe_mail_read'}" t-attf-data-msg_id="{widget.id}">
+ <li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.unread?'oe_mail_unread':'oe_mail_read'}">
<div t-attf-class="oe_mail_msg_#{widget.type} oe_semantic_html_override">
<!-- message actions (read/unread, reply, delete...) -->
<ul class="oe_header">
<li class="placeholder-mail-vote"><t t-call="mail.thread.message.vote"/></li>
- <li t-if="!widget.options.thread.display_on_flat and widget.unread" title="Read"><a class="oe_read oe_e">W</a></li>
- <li t-if="!widget.options.thread.display_on_flat and !widget.unread" title="Set back to unread"><a class="oe_unread oe_e">h</a></li>
- <li t-if="!widget.options.message.show_reply_by_email" title="Reply"><a class="oe_reply oe_e">)</a></li>
- <li t-if="widget.options.message.show_reply_by_email"><a class="oe_reply_by_email oe_e" title="Reply by mail">)</a></li>
- <t t-if="(widget.options.message.show_reply || widget.options.message.show_reply_by_email || (widget.is_author and widget.options.message.show_dd_delete) || widget.type == 'email')">
+ <li t-if="!widget.options.thread.display_on_flat" title="Read" class="oe_read"><a class="oe_read oe_e">W</a></li>
+ <li t-if="!widget.options.thread.display_on_flat" title="Set back to unread" class="oe_unread"><a class="oe_unread oe_e">h</a></li>
+ <li title="Quick reply"><a class="oe_reply oe_e">)</a></li>
+ <t t-if="(widget.is_author and widget.options.message.show_dd_delete) or widget.type == 'email'">
<li>
<span class="oe_dropdown_toggle">
<a class="oe_e" title="More options">í</a>
@@ -123,8 +204,6 @@
<li t-if="display['show_hide']">
<a href="#" class="oe_mail_msg_hide_type" t-attf-data-subtype='{widget.subtype}'>Hide '<t t-esc="widget.subtype"/>' for this document</a>
</li> -->
- <li t-if="widget.options.message.show_reply" title="Reply"><a class="oe_reply oe_full_reply">Full reply</a></li>
- <li t-if="widget.options.message.show_reply_by_email"><a class="oe_reply_by_email oe_full_reply" title="Reply by mail">Full reply</a></li>
<li t-if="widget.type == 'email'"><a class="oe_mail_msg_details" t-attf-href="#model=mail.message&amp;id=#{widget.id}" >Details</a></li>
</ul>
</span>
@@ -145,7 +224,6 @@
<ul class="oe_mail_msg_footer">
<li t-if="widget.author_id"><a t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a></li>
<li><span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span></li>
-
<li t-if="widget.attachment_ids.length > 0">
<a class="oe_mail_msg_view_attachments">
<t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
@@ -155,7 +233,7 @@
</ul>
<div class="oe_clear"/>
<div class="oe_mail_msg_body">
- <t t-if="widget.options.message.show_record_name and widget.record_name and (!widget.subject) and widget.options.thread._parents.length&lt;=widget.options.thread.thread_level and widget.model!='res.partner'">
+ <t t-if="widget.options.message.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread.thread_level and !widget.options.thread.display_on_flat and widget.model!='res.partner'">
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&amp;id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
</t>
<t t-raw="widget.body"/>
@@ -171,7 +249,7 @@
</li>
<!-- expandable message layout -->
- <li t-name="mail.thread.expandable" t-attf-class="oe_mail oe_mail_thread_msg oe_mail_unread" t-attf-data-thread_id="{widget.id}">
+ <li t-name="mail.thread.expandable" class="oe_mail oe_mail_thread_msg oe_mail_unread">
<div t-attf-class="oe_mail_msg_#{widget.type} oe_semantic_html_override">
<div class="oe_mail_msg_content oe_mail_msg_more_message">
<a class="oe_mail_fetch_more">Load more messages <span t-if="widget.nb_messages>0">(<t t-raw="widget.nb_messages"/> messages not display)</span>...</a>
@@ -179,23 +257,6 @@
</div>
</li>
- <!--
- mail.thread.message.attachments template
- Template used to display attachments in a mail.message
- -->
- <t t-name="mail.thread.message.attachments">
- <ul class="oe_mail_msg_attachments">
- <t t-foreach="record.attachment_ids" t-as="attachment">
- <li>
- <a t-att-href="attachment.url"><t t-raw="attachment.name || attachment.filename"/></a>
- <t t-if="record.show_attachment_delete">
- <a class="oe_right oe_mail_attachment_delete" title="Delete this attachmentt" t-attf-data-id="{attachment.id}">x</a>
- </t>
- </li>
- </t>
- </ul>
- </t>
-
<!-- mail.thread.message.vote
Template used to display Like/Unlike in a mail.message
-->
@@ -215,7 +276,7 @@
<button t-attf-class="oe_mail_msg_vote oe_tag">
<span>
<t t-if="!widget.has_voted">Agree</t>
- <t t-if="widget.has_voted">Unagree</t>
+ <t t-if="widget.has_voted">Undo</t>
</span>
</button>
</span>
diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml
index a6df1f0fe8c..f78c91c6305 100644
--- a/addons/mail/static/src/xml/mail_followers.xml
+++ b/addons/mail/static/src/xml/mail_followers.xml
@@ -13,9 +13,9 @@
<span class="oe_unfollow">Unfollow</span>
<span class="oe_following">Following</span>
</button>
- <div class="oe_recthread_subtypes">
- <ul class="oe_subtypes"></ul>
- </div>
+ </div>
+ <div class="oe_recthread_subtypes">
+ <ul class="oe_subtypes"></ul>
</div>
</div>
<div class="oe_grey">