summaryrefslogtreecommitdiff
path: root/addons/mail/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mail/static/src')
-rw-r--r--addons/mail/static/src/css/mail.css643
-rw-r--r--addons/mail/static/src/css/mail_compose_message.css166
-rw-r--r--addons/mail/static/src/js/mail.js1522
-rw-r--r--addons/mail/static/src/js/mail_followers.js55
-rw-r--r--addons/mail/static/src/xml/mail.xml262
-rw-r--r--addons/mail/static/src/xml/mail_followers.xml46
6 files changed, 1353 insertions, 1341 deletions
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 50ce0e000a9..af3cc3ed359 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -1,439 +1,382 @@
-/* ------------------------------------------------------------ */
-/* Reset because of ugly display of end of August
-/* ------------------------------------------------------------ */
-.openerp .oe_mail_wall ul, .openerp .oe_mail_wall li {
- list-style-type: none;
- padding: 0;
- margin: 0;
+/* ------------ MAIL WIDGET --------------- */
+.openerp .oe_mail, .openerp .oe_mail *{
+ box-sizing: border-box;
}
-
-.openerp .oe_chatter ul, .openerp .oe_chatter li {
- list-style-type: none;
- padding: 0;
- margin: 0;
-}
-
-/* ------------------------------------------------------------ */
-/* Wall
-/* ------------------------------------------------------------ */
-
-.openerp div.oe_mail_wall {
- overflow: auto;
- padding: 0;
- background: white;
+.openerp .oe_mail {
+ display: block;
+ position: relative;
+ margin: 0px;
}
-
-.openerp div.oe_mail_wall div.oe_mail_wall_aside {
- margin-left: 565px;
- margin: 8px;
+.openerp .oe_mail .oe_thread{
+ margin-left: 32px;
}
-
-.openerp div.oe_mail_wall ul.oe_mail_wall_threads {
- float: left;
- width: 560px;
- margin: 8px;
- list-style-type: none;
+.openerp .oe_mail > .oe_thread{
+ margin-left: 0px;
}
-/* ------------------------------------------------------------ */
-/* Followers
-/* ------------------------------------------------------------ */
+/* ---------------- MESSAGES ------------------ */
-.openerp div.oe_mail_recthread_aside h4 {
- display: inline-block;
-}
-.openerp div.oe_mail_recthread_aside button {
+.openerp .oe_mail .oe_msg{
position: relative;
+ background: #F4F5FA;
+ border-radius: 2px;
+ margin-bottom: 2px;
+ min-height: 42px;
+ border: solid 1px rgba(0,0,0,0.03);
}
-.openerp div.oe_mail_recthread_aside label,
-.openerp div.oe_mail_recthread_aside input {
- cursor:pointer;
+.openerp .oe_mail .oe_msg .oe_msg_left{
+ position: absolute;
+ left:0; top: 0; bottom: 0; width: 40px;
+ overflow: hidden;
}
-
-/* Specific display of threads in the wall */
-/* ------------------------------------------------------------ */
-
-.openerp ul.oe_mail_wall_threads .oe_msg_content textarea.oe_mail_compose_textarea {
- width: 434px;
- height: 30px;
- padding: 4px;
+.openerp .oe_mail .oe_msg .oe_msg_icon{
+ width: 32px;
+ margin: 4px;
+ border-radius: 2px;
}
-
-.openerp li.oe_mail_wall_thread:first .oe_msg_notification {
- border-top: 0;
+.openerp .oe_mail .oe_msg .oe_msg_center{
+ position: relative;
+ display: block;
+ margin-left: 40px;
}
-
-.openerp div.oe_thread_placeholder img {
- width: 28px;
- height: 28px;
+.openerp .oe_mail .oe_msg .oe_msg_footer{
+ padding-left: 4px;
+ overflow: hidden;
+ opacity:0.8;
+ -webkit-transition: opacity 0.2s linear;
}
-
-.openerp div.oe_thread_placeholder div.oe_msg_content {
- width: 440px;
+.openerp .oe_mail .oe_msg .oe_msg_content{
+ display: block;
+ overflow: hidden;
+ padding: 4px;
+ padding-bottom:1px;
+}
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_title{
+ font-size: 13px;
+ margin-bottom: 0px;
+ margin-top: 2px;
}
+/* a) Indented Messages */
-/* ------------------------------------------------------------ */
-/* RecordThread
-/* ------------------------------------------------------------ */
-
-.openerp .oe_form div.oe_chatter {
- overflow: auto;
+.openerp .oe_mail .oe_msg_indented{
+ background: #FFF;
+ border: none;
+ margin-bottom:0px;
+ min-height:38px;
}
-
-.openerp .oe_mail_record_wall {
- margin: auto;
- width: 560px;
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_icon{
+ width:32px;
+ margin:2px;
+ border-radius:2px;
}
-
-.openerp .oe_mail_record_wall > .oe_mail_wall_threads {
- float: left;
+.openerp .oe_mail .oe_msg .oe_subtle{
+ color: #B7B7D5;
}
-
-.openerp div.oe_mail_recthread_aside {
- float: right;
- width: 250px;
+.openerp .oe_mail .oe_msg_indented .oe_msg_center{
+ margin-left:34px;
}
-
-.openerp div.oe_mail_recthread_actions {
- margin-bottom: 8px;
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
+ padding-top:2px;
}
-
-.openerp div.oe_mail_recthread_actions button {
- width: 120px;
+/* b) Votes (likes) */
+.openerp .oe_mail .oe_mail_vote_count{
+ display: inline;
+ position: relative;
+ background: #7C7BAD;
+ color: white;
+ text-shadow: none;
+ border-radius: 3px;
+ margin: 0px;
+ padding-left: 3px;
+ padding-right: 18px;
+ margin-right: 3px;
}
-
-.openerp .oe_mail_recthread_aside .oe_follower.oe_follow {
- color: white;
- background-color: #8a89ba;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4));
- background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -moz-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -ms-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -o-linear-gradient(top, #8a89ba, #807fb4);
- background-image: linear-gradient(to bottom, #8a89ba, #807fb4);
-}
-.openerp .oe_mail_recthread_aside .oe_follower.oe_following {
- color: white;
- background-color: #dc5f59;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
- background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
- background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
- background-image: -o-linear-gradient(top, #dc5f59, #b33630);
- background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+.openerp .oe_mail .oe_mail_vote_count .oe_e{
+ position: absolute;
+ bottom: 1px;
+ right: 2px;
+ font-size: 26px;
}
+/* c) Message action icons */
-.openerp .oe_mail_recthread_aside .oe_follower span {
+.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
display:none;
}
-.openerp .oe_mail_recthread_aside .oe_following span.oe_following,
-.openerp .oe_mail_recthread_aside .oe_notfollow span.oe_follow {
- display:block;
+.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
+ display:none;
}
-
-.openerp div.oe_mail_recthread_followers {
- margin-bottom: 8px;
+.openerp .oe_mail .oe_msg .oe_msg_icons{
+ float: right;
+ margin-top: 4px;
+ margin-right: 8px;
+ margin-left: 8px;
+ height: 24px;
+ -webkit-user-select: none;
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons span{
+ float:right;
+ width:24px;
+ height:24px;
+ line-height:24px;
+ text-align: center;
}
-
-
-/* ------------------------------------------------------------ */
-/* subtypes
-/* ------------------------------------------------------------ */
-
-.openerp .oe_mail_subtypes {
- display:inline-block;
- position: relative;
- z-index: 5;
+.openerp .oe_mail .oe_msg .oe_msg_icons a {
+ text-decoration: none;
+ color: #FFF;
+ text-shadow: 0px 1px #AAA,0px -1px #AAA, -1px 0px #AAA, 1px 0px #AAA, 0px 3px 3px rgba(0,0,0,0.1);
+ -webkit-transition: all 0.2s linear;
}
-.openerp .oe_mail_subtypes .oe_recthread_subtypes {
- background: #fff;
- padding: 2px;
- border: 1px solid #aaaaaa;
- border-top: 0px;
- position: absolute;
- z-index: 2;
+.openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
+ opacity: 1;
+ -webkit-transition: all 0.1s linear;
}
-.openerp .oe_mail_subtypes.oe_mouseout .oe_recthread_subtypes {
- display: none;
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
+ color: #FFF6C0;
+ text-shadow: 0px 1px #FFA162,0px -1px #FFA162, -1px 0px #FFA162, 1px 0px #FFA162, 0px 3px 3px rgba(0,0,0,0.1);
}
-.openerp .oe_mail_subtypes.oe_mouseover .oe_recthread_subtypes {
- display: block;
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star.oe_starred a{
+ color: #FFE41F;
+ text-shadow: 0px 1px #DF6200,0px -1px #DF6200, -1px 0px #DF6200, 1px 0px #DF6200, 0px 3px 3px rgba(0,0,0,0.1);
}
-
-/* ------------------------------------------------------------ */
-/* Thread
-/* ------------------------------------------------------------ */
-
-.openerp div.oe_mail_thread_action {
- white-space: normal;
- padding: 8px;
- z-index:5;
- background: #fff;
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_reply:hover a{
+ color: #1fc0ff;
+ text-shadow: 0px 1px #184fc5,0px -1px #184fc5, -1px 0px #184fc5, 1px 0px #184fc5, 0px 3px 3px rgba(0,0,0,0.1);
}
-
-.openerp div.oe_mail_thread_action:after {
- content: "";
- display: block;
- clear: both;
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_read:hover a{
+ color: #bbbaff;
+ text-shadow: 0px 1px #7c7bad,0px -1px #7c7bad, -1px 0px #7c7bad, 1px 0px #7c7bad, 0px 3px 3px rgba(0,0,0,0.1);
}
-
-/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
-.openerp .oe_msg_content textarea.oe_mail_compose_textarea:focus,
-.openerp .oe_msg_content div.oe_mail_compose_message_body 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 .oe_mail .oe_msg .oe_msg_icons .oe_unread:hover a{
+ color: #c2ff00;
+ text-shadow: 0px 1px #009441,0px -1px #009441, -1px 0px #009441, 1px 0px #009441, 0px 3px 3px rgba(0,0,0,0.1);
}
-
-.openerp .oe_mail_vote_count,
-.openerp .oe_msg_vote{
- vertical-align: bottom;
+.openerp .oe_mail .oe_msg .oe_msg_content textarea{
+ width: 100%;
+ height: 32px;
+ margin: 0px;
+ padding: 0px;
+ resize: vertical;
+
+ padding: 4px;
}
-
-.openerp button.oe_mail_starbox{
- background: #ff0000;
+.openerp .oe_mail .oe_msg.oe_msg_composer_compact, .openerp .oe_mail .oe_msg.oe_msg_expandable{
+ padding:4px;
+ min-height:0px;
}
-.openerp button.oe_mail_starbox.oe_stared{
- background: #00FF00;
+.openerp .oe_mail .oe_msg.oe_msg_composer_compact textarea{
+ height: 24px;
+ width: 100%;
}
-.openerp div.oe_mail_thread_display {
- white-space: normal;
-}
+/* ---------------- MESSAGE QUICK COMPOSER --------------- */
-.openerp div.oe_thread_placeholder {
- margin-left: 66px;
+.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
+ padding-right:4px;
+ padding-top: 2px;
+ padding-bottom:6px;
}
-
-.openerp li.oe_mail_thread_msg {
- width: 560px;
+.openerp .oe_mail .oe_msg_attachments.oe_hidden,
+.openerp .oe_mail .oe_msg_images.oe_hidden{
+ margin:0px;
+ border: none;
+ display: none;
}
-
-.openerp div.oe_thread_placeholder li.oe_mail_thread_msg:last-child {
- margin-bottom: 8px;
+.openerp .oe_mail .oe_msg_attachments{
+ margin-bottom: 4px;
+ margin-right: 0px;
+ font-size: 12px;
+ border-radius: 2px;
+ border: solid 1px rgba(124,123,173,0.14);
}
-
-.openerp div.oe_mail_thread_more {
- display: none;
- border-bottom: 1px solid #D2D9E7;
+.openerp .oe_mail .oe_msg_attachments .oe_attachment{
+ padding: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
}
-
-.openerp li.oe_mail_thread_msg:after {
- content: "";
- display: block;
- clear: both;
+.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{
+ font-size: 23px;
+ margin-top: -5px;
}
-
-.openerp li.oe_mail_thread_msg.oe_mail_read,
-.openerp li.oe_mail_thread_msg.oe_mail_read div {
- border-left: #F0F0F0;
+.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{
+ text-decoration: none;
}
-.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 .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){
+ background:white;
}
-
-.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 .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){
+ background: #F4F5FA;
}
-
-.openerp li.oe_mail_thread_msg > div:after {
- content: "";
+.openerp .oe_mail .oe_msg_images {
display: block;
- clear: both;
-}
-
-.openerp div.oe_mail_msg {
- padding: 0;
- margin: 0 0 4px 0;
}
-
-.openerp .oe_msg_notification,
-.openerp .oe_msg_expandable,
-.openerp .oe_msg_comment,
-.openerp .oe_msg_email {
- padding: 8px;
- background: white;
- position: relative;
+.openerp .oe_mail .oe_msg_footer button{
+ display: inline;
+ height: 24px;
+ font-size: 12px;
+ line-height: 12px;
+ vertical-align: middle;
}
-
-.openerp .oe_msg_notification:after,
-.openerp .oe_msg_comment:after,
-.openerp .oe_msg_email:after {
- content: "";
- display: block;
- clear: both;
+.openerp .oe_mail .oe_msg_footer button.oe_attach{
+ width: 24px;
+ overflow: hidden;
}
-
-.openerp div.oe_msg_content {
- float: left;
+.openerp .oe_mail .oe_msg_footer button.oe_attach .oe_e{
position: relative;
- width: 486px;
+ top: -1px;
+ left: -9px;
}
-
-.openerp div.oe_msg_content > li {
- float: left;
- margin-right: 3px;
+.openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{
+ display:inline;
}
-
-.openerp .oe_msg_content:after {
- content: "";
- display: block;
- clear: both;
+.openerp .oe_mail .oe_msg_footer button.oe_full{
+ width:24px;
+ overflow:hidden;
+ float: right;
}
-
-.openerp .oe_chatter a {
- cursor: pointer;
+.openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{
+ position: relative;
+ top: -1px;
+ left: -9px;
}
-
-.openerp img.oe_mail_icon {
- width: 50px;
- height: 50px;
+.openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{
+ background: transparent;
+ color: #7C7BAD;
+ box-shadow: none;
+ border: none;
+ text-shadow: none;
}
-
-.openerp img.oe_mail_thumbnail {
- width: 28px;
+.openerp .oe_mail .oe_attach_label{
+ color: #7C7BAD;
+ margin-left: -3px;
+}
+.openerp .oe_mail .oe_msg_footer .oe_attachment_file .oe_form_binary_file{
+ display: inline-block;
+ margin-left: -47px;
height: 28px;
- margin: 4px;
+ width: 52px;
+ margin-top: -6px;
}
-
-.openerp img.oe_mail_frame {
- text-align: center;
- overflow: hidden;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- -o-border-radius: 3px;
- -ms-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- clip: rect(5px, 40px, 45px, 0px);
+.openerp .oe_mail .oe_mail_list_recipients{
+ font-size: 12px;
+ margin-top: 4px;
+ margin-bottom: 4px;
}
-.openerp .oe_mail_invisible {
- display: none;
-}
-
-/* ------------------------------------------------------------ */
-/* Messages layout
-/* ------------------------------------------------------------ */
+/* ---------------- HIDDEN MESSAGES ------------------ */
-.openerp .oe_mail_msg .oe_msg_title {
- margin: 0;
- font-size: 1.3em;
- font-weight: bold;
-}
-.openerp .oe_mail_msg .oe_msg_title a:link,
-.openerp .oe_mail_msg .oe_msg_title a:visited {
- color: #4C4C4C;
- text-decoration: none;
+.openerp .oe_mail .oe_msg_content.oe_msg_more_message{
+ text-align: right;
}
-
-.openerp .oe_mail_msg .oe_msg_body {
- margin-bottom: .5em;
- text-align: justify;
+.openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
+ height: 0;
+ border-bottom: dashed 1px #e6e6e6;
+ margin-left: -4px;
+ margin-right: 8px;
+ margin-top: 6px;
+ margin-bottom: -9px;
}
-
-.openerp .oe_mail_msg .oe_msg_body pre {
- font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
- margin: 0px;
- white-space: pre-wrap;
- display: inline;
+.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
+ background: white;
+ margin-right: 280px;
+ padding-left: 8px;
+ padding-right: 8px;
+ text-decoration: none;
+ color: #b4b4b4;
}
-
-/* Read more/less link */
-.openerp .oe_mail_msg span.oe_mail_reduce {
- position: absolute;
- right: 0;
+.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more:hover{
+ text-decoration: none;
}
-/* Dropdown menu */
+/* ---------------- FOLLOWERS ------------------ */
-.openerp .oe_mail ul.oe_mail_thread_display ul.oe_mail_thread_display {
+.openerp .oe_followers{
position: relative;
- border-left: 1px #DDD dashed;
+ display: inline-block;
+ padding-top: 5px;
+ width: 160px;
+ float: right;
}
-.openerp .oe_mail ul.oe_header {
- position: absolute;
- right: 3px;
- top: -6px;
- z-index: 10;
-}
+/* a) THE FOLLOW BUTTON */
-.openerp .oe_mail ul.oe_header a {
- text-decoration: none;
+.openerp .oe_followers button.oe_follower{
+ display: block;
+ text-align: center;
+ width:100%;
}
-
-.openerp .oe_mail ul.oe_header>li {
- display: inline-block;
- height: 20px;
- text-align: right;
+.openerp .oe_followers button.oe_follower.oe_following{
+ background-color: #3465A4;
+ background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+ color: white;
}
-/* Message footer */
-.openerp .oe_mail_msg .oe_msg_footer {
- color: #888;
+.openerp .oe_followers button.oe_follower .oe_follow,
+.openerp .oe_followers button.oe_follower .oe_unfollow,
+.openerp .oe_followers button.oe_follower .oe_following{
+ display: none;
}
-.openerp .oe_mail_msg .oe_msg_footer li {
- float: left;
- margin-right: 3px;
+/* a.1) when following, show 'following' */
+.openerp .oe_followers button.oe_follower.oe_following .oe_following{
+ display: inline;
+}
+/* a.2) when following and hovering, show 'unfollow' */
+.openerp .oe_followers button.oe_follower.oe_following:hover .oe_following{
+ display: none;
}
-.openerp .oe_mail_msg .oe_msg_footer li:after {
- content: " · ";
+.openerp .oe_followers button.oe_follower.oe_following:hover .oe_unfollow{
+ display: inline;
}
-.openerp .oe_mail_msg .oe_msg_footer li:last-child:after {
- content: "";
+/* a.3) when not following show 'follow' */
+.openerp .oe_followers button.oe_follower.oe_notfollow .oe_follow{
+ display: inline;
}
-/* Attachments list */
-.openerp .oe_msg_content ul.oe_msg_attachments {
- width: 100%;
- margin: .5em 0 0 0;
- padding: .5em 0;
- list-style-position: inside;
+.openerp .oe_followers .oe_subtype_list{
+ margin-top: 4px;
}
-.openerp .oe_msg_content ul.oe_msg_attachments.oe_hidden {
- display: none;
+
+/* b) THE FOLLOWERS */
+.openerp .oe_followers .oe_follower_title{
+ display: inline;
}
-.openerp .oe_msg_content ul.oe_msg_attachments li {
- float: none;
- height: 20px;
- line-height: 20px;
- margin: 0;
- padding: 0;
- list-style-type: square;
+.openerp .oe_followers .oe_follower_title_box{
+ margin-top: 12px;
+ margin-bottom: 4px;
}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process {
+.openerp .oe_followers .oe_invite{
float: right;
- width: 200px;
- height: 16px;
-}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process div {
- float: left;
- width: 38px;
- height: 16px;
- margin-right: 2px;
- background: #66FF66;
-}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process span {
- color: #aaaaaa;
- position: absolute;
+}
+.openerp .oe_followers .oe_partner {
+ height: 32px;
+ overflow: hidden;
+}
+.openerp .oe_followers .oe_partner img{
+ width: 32px;
+ margin-right:4px;
+ border-radius: 2px;
}
-/* ------------------------------------------------------------ */
-/* Topbar button
-/* ------------------------------------------------------------ */
+/* ---------------- MESSAGES BODY ------------------ */
+.openerp .oe_mail .oe_msg_content .oe_blockquote,
+.openerp .oe_mail .oe_msg_content blockquote {
+ padding: 4px;
+ border-radius: 2px;
+ border: solid 1px rgba(124,123,173,0.14);
+}
-.openerp .oe_topbar .oe_topbar_compose_full_email {
- float: right;
- margin: 3px 25px 0 0;
-} \ No newline at end of file
+/* ----------- FORM INTEGRATION ------------ */
+
+.openerp .oe_record_thread{
+ display: block;
+ margin-right: 180px;
+}
+
+/* ----------- INBOX INTEGRATION ----------- */
+
+.openerp .oe_mail_wall .oe_mail{
+ margin: 16px;
+ width: 720px;
+}
diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css
deleted file mode 100644
index c706c55bcde..00000000000
--- a/addons/mail/static/src/css/mail_compose_message.css
+++ /dev/null
@@ -1,166 +0,0 @@
-/* ------------------------------ */
-/* Compose Message */
-/* ------------------------------ */
-
-.openerp .oe_msg_content .oe_mail_compose_message_footer {
- height: 24px;
-}
-
-.openerp .oe_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_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_msg_content div.oe_formview {
- background-color: transparent;
-}
-
-.openerp .oe_msg_content div.oe_form_nosheet {
- margin: 0px;
-}
-
-.openerp .oe_msg_content table.oe_form_group {
- margin: 0px;
-}
-
-.openerp .oe_msg_content table.oe_form_field,
-.openerp .oe_msg_content div.oe_form_field {
- padding: 0px;
-}
-
-.openerp .oe_msg_content td.oe_form_group_cell {
- vertical-align: bottom;
-}
-
-/* subject: change width */
-.openerp .oe_msg_content .oe_form .oe_form_field input[type='text'] {
- width: 472px;
-}
-
-/* body_html: cleditor */
-.openerp .oe_msg_content div.cleditorMain {
- border: 1px solid #cccccc;
-}
-
-/* destination_partner_ids */
-.openerp .oe_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 7501eabe104..9133e5de257 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -1,4 +1,4 @@
-openerp.mail = function(session) {
+openerp.mail = function (session) {
var _t = session.web._t,
_lt = session.web._lt;
@@ -16,15 +16,16 @@ openerp.mail = function(session) {
*/
session.web.FormView = session.web.FormView.extend({
- do_action: function(action) {
+ do_action: function (action) {
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
*/
var context_keys = ['default_template_id', 'default_composition_mode',
'default_use_template', 'default_partner_ids', 'default_model',
- 'default_res_id', 'default_content_subtype', 'active_id', 'lang',
- 'bin_raw', 'tz', 'active_model', 'edi_web_url_view', 'active_ids']
+ 'default_res_id', 'default_content_subtype', , 'default_subject',
+ 'default_body', 'active_id', 'lang', 'bin_raw', 'tz',
+ 'active_model', 'edi_web_url_view', 'active_ids']
for (var key in action.context) {
if (_.indexOf(context_keys, key) == -1) {
action.context[key] = null;
@@ -51,17 +52,18 @@ openerp.mail = function(session) {
mail.ChatterUtils = {
- /** Get an image in /web/binary/image?... */
- get_image: function(session, model, field, id) {
+ /* Get an image in /web/binary/image?... */
+ get_image: function (session, model, field, id) {
return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
},
- /** Get the url of an attachment {'id': id} */
+ /* Get the url of an attachment {'id': id} */
get_attachment_url: function (session, attachment) {
return session.origin + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
},
- /** Replaces some expressions
+ /**
+ * Replaces some expressions
* - :name - shortcut to an image
*/
do_replace_expressions: function (string) {
@@ -78,14 +80,36 @@ openerp.mail = function(session) {
return string;
},
- /* replace textarea text into html text
- * (add <p>, <a>)
- * TDE note : should not be here, but server-side I think ...
- */
- get_text2html: function(text){
+ /**
+ * Replaces textarea text into html text (add <p>, <a>)
+ * TDE note : should be done server-side, in Python -> use mail.compose.message ?
+ */
+ get_text2html: function (text) {
return text
.replace(/[\n\r]/g,'<br/>')
.replace(/((?:https?|ftp):\/\/[\S]+)/g,'<a href="$1">$1</a> ')
+ },
+
+ /* Returns the complete domain with "&"
+ * TDE note: please add some comments to explain how/why
+ */
+ expand_domain: function (domain) {
+ var new_domain = [];
+ var nb_and = -1;
+ // TDE note: smarted code maybe ?
+ for ( var k = domain.length-1; k >= 0 ; k-- ) {
+ if ( typeof domain[k] != 'array' && typeof domain[k] != 'object' ) {
+ nb_and -= 2;
+ continue;
+ }
+ nb_and += 1;
+ }
+
+ for (var k = 0; k < nb_and ; k++) {
+ domain.unshift('&');
+ }
+
+ return domain;
}
};
@@ -97,6 +121,8 @@ 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.
+ * On first time : display a compact textarea that is not the compose form.
+ * When the user focuses the textarea, the compose message is instantiated.
*/
mail.ThreadComposeMessage = session.web.Widget.extend({
@@ -108,60 +134,50 @@ openerp.mail = function(session) {
* @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) {
+
+ init: function (parent, datasets, options) {
var self = this;
this._super(parent);
this.context = options.context || {};
-
- this.datasets = {
- 'attachment_ids' : [],
- 'id': options.datasets.id,
- 'model': options.datasets.model,
- 'res_model': options.datasets.res_model,
- 'is_private': options.datasets.is_private || false,
- 'partner_ids': options.datasets.partner_ids || []
- };
- this.options={};
- this.options.thread={};
- this.options.thread.show_header_compose = options.options.thread.show_header_compose;
- this.options.thread.display_on_thread = options.options.thread.display_on_thread;
- this.options.thread.show_attachment_delete = true;
- this.options.thread.show_attachment_link = true;
-
+ this.options = options.options;
+
+ this.show_compact_message = false;
+
+ // data of this compose message
+ this.attachment_ids = [];
+ this.id = datasets.id;
+ this.model = datasets.model;
+ this.res_model = datasets.res_model;
+ this.is_private = datasets.is_private || false;
+ this.partner_ids = datasets.partner_ids || [];
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
+ this.thread_level = datasets.thread_level;
this.parent_thread= parent.messages!= undefined ? parent : false;
this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
+ this.show_delete_attachment = true;
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(self.fileupload_id, self.on_attachment_loaded);
},
- start: function(){
+ 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);
},
/* upload the file on the server, add in the attachments list and reload display
*/
- display_attachments: function(){
- var self = this;
- 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_msg_attachments");
-
+ display_attachments: function () {
+ this.$(".oe_msg_attachment_list").html(
+ session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
// event: delete an attachment
- this.$el.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete);
+ this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete);
},
+
+ /* when a user click on the upload button, send file read on_attachment_loaded
+ */
on_attachment_change: function (event) {
event.stopPropagation();
var self = this;
@@ -172,25 +188,24 @@ openerp.mail = function(session) {
// if the files exits for this answer, delete the file before upload
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if((this.datasets.attachment_ids[i].filename || this.datasets.attachment_ids[i].name) == filename){
- if(this.datasets.attachment_ids[i].upload){
+ 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.datasets.attachment_ids[i].id]);
+ this.ds_attachment.unlink([this.attachment_ids[i].id]);
} else {
- attachments.push(this.datasets.attachment_ids[i]);
+ attachments.push(this.attachment_ids[i]);
}
}
- this.datasets.attachment_ids = attachments;
+ this.attachment_ids = attachments;
// submit file
- //session.web.blockUI();
- self.$('form.oe_form_binary_form').submit();
+ this.$('form.oe_form_binary_form').submit();
this.$(".oe_attachment_file").hide();
- this.datasets.attachment_ids.push({
+ this.attachment_ids.push({
'id': 0,
'name': filename,
'filename': filename,
@@ -201,11 +216,12 @@ openerp.mail = function(session) {
}
},
+ /* when the file is uploaded
+ */
on_attachment_loaded: function (event, result) {
- //session.web.unblockUI();
- for(var i in this.datasets.attachment_ids){
- if(this.datasets.attachment_ids[i].filename == result.filename && this.datasets.attachment_ids[i].upload) {
- this.datasets.attachment_ids[i]={
+ 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,
@@ -219,6 +235,7 @@ openerp.mail = function(session) {
$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) {
@@ -226,61 +243,58 @@ openerp.mail = function(session) {
var attachment_id=$(event.target).data("id");
if (attachment_id) {
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if(attachment_id!=this.datasets.attachment_ids[i].id){
- attachments.push(this.datasets.attachment_ids[i]);
+ 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.datasets.attachment_ids = attachments;
+ this.attachment_ids = attachments;
this.display_attachments();
}
},
- /* to avoid having unsorted file on the server.
- we will show the users files of the first message post
- TDE note: unnecessary call to server I think
- */
- // 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: function() {
+ bind_events: function () {
var self = this;
+ this.$('textarea.oe_compact').on('focus', _.bind( this.on_compose_expandable, this));
+
// 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()} );
+ this.$el.on('change', 'input.oe_form_binary_file', _.bind( this.on_attachment_change, this) );
+
+ this.$el.on('click', '.oe_cancel', _.bind( this.on_cancel, this) );
+ this.$el.on('click', '.oe_post', _.bind( this.on_message_post, this) );
+ this.$el.on('click', '.oe_full', _.bind( this.on_compose_fullmail, this, 'reply') );
+
+ /* stack for don't close the compose form if the user click on a button */
+ this.$el.on('mousedown', '.oe_msg_footer', _.bind( function () { this.stay_open = true; }, this));
+ this.$('textarea:not(.oe_compact):first').on('focus, mouseup, keydown', _.bind( function () { this.stay_open = false; }, this));
+ this.$('textarea:not(.oe_compact):first').autosize();
+
+ // auto close
+ this.$el.on('blur', 'textarea:not(.oe_compact):first', _.bind( this.on_compose_expandable, this));
},
- on_compose_fullmail: function(){
- var attachments=[];
- for(var i in this.datasets.attachment_ids){
- attachments.push(this.datasets.attachment_ids[i].id);
- }
- /* TDE note: I think this is not necessary, because
- * 1/ post on a document: followers added server-side in _notify
- * 2/ reply to a message: mail.compose.message should add the previous partners
- */
- var partner_ids=[];
- for(var i in this.datasets.partner_ids){
- partner_ids.push(this.datasets.partner_ids[i][0]);
+ on_compose_fullmail: function (default_composition_mode) {
+ if (default_composition_mode == 'reply') {
+ var context = {
+ 'default_composition_mode': default_composition_mode,
+ 'default_parent_id': this.id,
+ 'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
+ 'default_attachment_ids': this.attachment_ids,
+ };
+ } else {
+ var context = {
+ 'default_model': this.context.default_model,
+ 'default_res_id': this.context.default_res_id,
+ 'default_content_subtype': 'html',
+ 'default_composition_mode': default_composition_mode,
+ 'default_parent_id': this.id,
+ 'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
+ 'default_attachment_ids': this.attachment_ids,
+ };
}
var action = {
type: 'ir.actions.act_window',
@@ -290,70 +304,108 @@ openerp.mail = function(session) {
action_from: 'mail.ThreadComposeMessage',
views: [[false, 'form']],
target: 'new',
- context: {
- 'default_model': this.context.default_model,
- 'default_res_id': this.context.default_res_id,
- 'default_content_subtype': 'html',
- 'default_parent_id': this.context.default_parent_id,
- 'default_body': mail.ChatterUtils.get_text2html(this.$('textarea').val() || ''),
- 'default_attachment_ids': attachments,
- 'default_partner_ids': partner_ids
- },
+ context: context,
};
+
this.do_action(action);
+ this.on_cancel();
},
- on_cancel: function(event){
- if(event) event.stopPropagation();
- this.$('textarea').val("");
- this.$('input[data-id]').remove();
- //this.attachment_ids=[];
+ reinit: function() {
+ var $render = $( session.web.qweb.render('mail.compose_message', {'widget': this}) );
+
+ $render.insertAfter(this.$el.last());
+ this.$el.remove();
+ this.$el = $render;
+
this.display_attachments();
- if(!this.options.thread.show_header_compose || !this.options.thread.display_on_thread[0]){
- this.$el.hide();
- }
+ this.bind_events();
+ },
+
+ on_cancel: function (event) {
+ if (event) event.stopPropagation();
+ this.attachment_ids=[];
+ this.stay_open = false;
+ this.show_composer = false;
+ this.reinit();
},
/*post a message and fetch the message*/
- on_message_post: function (body) {
+ on_message_post: function (event) {
var self = this;
- if (! body) {
- var comment_node = this.$('textarea');
- var body = comment_node.val();
- comment_node.val('');
- }
+ var comment_node = this.$('textarea');
+ var body = comment_node.val();
+ comment_node.val('');
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if(this.datasets.attachment_ids[i].upload){
+ 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.datasets.attachment_ids[i].id);
+ attachments.push(this.attachment_ids[i].id);
}
- if(body.match(/\S+/)) {
+ if (body.match(/\S+/)) {
+ //session.web.blockUI();
this.parent_thread.ds_thread.call('message_post_api', [
this.context.default_res_id,
mail.ChatterUtils.get_text2html(body),
false,
- 'comment',
- 'mail.mt_comment',
this.context.default_parent_id,
attachments,
this.parent_thread.context
- ]).then(function(records){
- self.parent_thread.switch_new_message(records);
- self.datasets.attachment_ids=[];
- self.on_cancel();
+ ]).then(function (record) {
+ var thread = self.parent_thread;
+ // create object and attach to the thread object
+ thread.message_fetch(false, false, [record], function (arg, data) {
+ var message = thread.create_message_object( data[0] );
+ // insert the message on dom
+ thread.insert_message( message, self.$el );
+ if (thread.parent_message) {
+ self.$el.remove();
+ self.parent_thread.compose_message = null;
+ } else {
+ self.on_cancel();
+ }
+ });
+ //session.web.unblockUI();
});
return true;
}
},
+
+ /* convert the compact mode into the compose message
+ */
+ on_compose_expandable: function (event) {
+
+ if (!this.stay_open && (!this.show_composer || !this.$('textarea:not(.oe_compact)').val().match(/\S+/))) {
+ this.show_composer = !this.show_composer || this.stay_open;
+ this.reinit();
+ }
+ if (!this.stay_open && this.show_composer) {
+ this.$('textarea:not(.oe_compact):first').focus();
+ }
+ return true;
+ },
+
+ do_hide_compact: function () {
+ this.show_compact_message = false;
+ if (!this.show_composer) {
+ this.reinit();
+ }
+ },
+
+ do_show_compact: function () {
+ this.show_compact_message = true;
+ if (!this.show_composer) {
+ this.reinit();
+ }
+ }
});
- /**
+ /**
* ------------------------------------------------------------
* Thread Message Expandable Widget
* ------------------------------------------------------------
@@ -369,71 +421,81 @@ openerp.mail = function(session) {
mail.ThreadExpandable = session.web.Widget.extend({
template: 'mail.thread.expandable',
- init: function(parent, options) {
+ init: function (parent, datasets, context) {
this._super(parent);
- this.domain = options.domain || [];
+ this.domain = datasets.domain || [];
+ this.options = datasets.options;
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
- default_parent_id: false }, options.context || {});
-
- this.datasets = {
- 'id' : options.datasets.id || -1,
- 'model' : options.datasets.model || false,
- 'parent_id' : options.datasets.parent_id || false,
- 'nb_messages' : options.datasets.nb_messages || 0,
- 'type' : 'expandable',
- 'max_limit' : options.datasets.max_limit || false,
- 'flag_used' : false,
- };
-
- // record options and data
- this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0] ;
+ default_parent_id: false }, context || {});
+
+ // data of this expandable message
+ this.id = datasets.id || -1,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.nb_messages = datasets.nb_messages || 0,
+ this.thread_level = datasets.thread_level || 0,
+ this.type = 'expandable',
+ this.max_limit = this.id < 0 || false,
+ this.flag_used = false,
+ this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
this.bind_events();
},
+ reinit: function () {
+ var $render = $(session.web.qweb.render('mail.thread.expandable', {'widget': this}));
+ this.$el.replaceWith( $render );
+ this.$el = $render;
+ this.bind_events();
+ },
+
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
- var self = this;
- this.$el.on('click', 'a.oe_mail_fetch_more', self.on_expandable);
+ bind_events: function () {
+ this.$el.on('click', 'a.oe_msg_fetch_more', this.on_expandable);
},
- animated_destroy: function(options) {
+ animated_destroy: function (fadeTime) {
var self=this;
- //graphic effects
- if(options && options.fadeTime) {
- self.$el.fadeOut(options.fadeTime, function(){
- self.destroy();
- });
- } else {
+ this.$el.fadeOut(fadeTime, function () {
self.destroy();
- }
+ });
},
/*The selected thread and all childs (messages/thread) became read
* @param {object} mouse envent
*/
on_expandable: function (event) {
- if(event)event.stopPropagation();
- if(this.datasets.flag_used) {
+ if (event)event.stopPropagation();
+ if (this.flag_used) {
return false
}
- this.datasets.flag_used = true;
+ this.flag_used = true;
- this.animated_destroy({'fadeTime':300});
- this.parent_thread.message_fetch(false, this.domain, this.context);
+ this.animated_destroy(200);
+ this.parent_thread.message_fetch(this.domain, this.context);
return false;
},
+
+ /**
+ * call on_message_delete on his parent thread
+ */
+ destroy: function () {
+
+ this._super();
+ this.parent_thread.on_message_detroy(this);
+
+ }
});
- /**
+ /**
* ------------------------------------------------------------
* Thread Message Widget
* ------------------------------------------------------------
@@ -463,80 +525,56 @@ 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_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_dd_delete]
- * @param {Array [A,B]} [show_reply] display the reply button on the
- * message for thread level between A and B. -1 for no begin or no end.
- * @param {Array [A,B]} [show_read_unread] display the read/unread button on the
- * message for thread level between A and B. -1 for no begin or no end.
+ *... @param {int} [show_reply_button] number thread level to display the reply button
+ *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
*/
- init: function(parent, options) {
+ init: function (parent, datasets, context) {
this._super(parent);
- // record datasets
- var param = options.datasets;
- this.datasets = _.extend({
- 'id' : -1,
- 'model' : false,
- 'parent_id': false,
- 'res_id' : false,
- 'type' : false,
- 'is_author' : false,
- 'is_private' : false,
- 'subject' : false,
- 'name' : false,
- 'record_name' : false,
- 'body' : false,
- 'vote_user_ids' :[],
- 'has_voted' : false,
- 'is_favorite' : false,
- 'thread_level' : 0,
- 'to_read' : true,
- 'author_id' : [],
- 'attachment_ids' : [],
- }, param || {});
- this.datasets._date = param.date;
-
// record domain and context
- this.domain = options.domain || [];
+ this.domain = datasets.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
- default_parent_id: false }, options.context || {});
+ default_parent_id: false }, 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,
- 'truncate_limit': options.options.message.truncate_limit || 250,
- 'show_reply': options.options.message.show_reply || [0,-1],
- 'show_read_unread': options.options.message.show_read_unread || [0,-1],
- }
- };
-
- this.datasets.show_reply = this.options.message.show_reply[0]>=0 &&
- this.options.message.show_reply[0]<=this.datasets.thread_level &&
- (this.options.message.show_reply[1]<0 || this.options.message.show_reply[1]>=this.datasets.thread_level);
-
- this.datasets.show_read_unread = this.options.message.show_read_unread[0]>=0 &&
- this.options.message.show_read_unread[0]<=this.datasets.thread_level &&
- (this.options.message.show_read_unread[1]<0 || this.options.message.show_read_unread[1]>=this.datasets.thread_level);
+ this.options = datasets.options;
+
+ // data of this message
+ this.id = datasets.id || -1,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.res_id = datasets.res_id || false,
+ this.type = datasets.type || false,
+ this.is_author = datasets.is_author || false,
+ this.is_private = datasets.is_private || false,
+ this.subject = datasets.subject || false,
+ this.name = datasets.name || false,
+ this.record_name = datasets.record_name || false,
+ this.body = datasets.body || false,
+ this.vote_nb = datasets.vote_nb || 0,
+ this.has_voted = datasets.has_voted || false,
+ this.is_favorite = datasets.is_favorite || false,
+ this.thread_level = datasets.thread_level || 0,
+ this.to_read = datasets.to_read || false,
+ this.author_id = datasets.author_id || [],
+ this.attachment_ids = datasets.attachment_ids || [],
+ this._date = datasets.date;
+
+
+ this.show_reply_button = this.options.show_compose_message && this.options.show_reply_button > this.thread_level;
+ this.show_read_unread_button = this.options.show_read_unread_button > this.thread_level;
// record options and data
- this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0];
+ this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
this.thread = false;
- if( this.datasets.id > 0 ) {
+ if ( this.id > 0 ) {
this.formating_data();
}
@@ -545,63 +583,93 @@ openerp.mail = function(session) {
this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers');
},
- formating_data: function(){
+ /* Convert date, timerelative and avatar in displayable data. */
+ formating_data: function () {
//formating and add some fields for render
- this.datasets.date = session.web.format_value(this.datasets._date, {type:"datetime"});
- this.datasets.timerelative = $.timeago(this.datasets.date);
- if (this.datasets.type == 'email') {
- this.datasets.avatar = ('/mail/static/src/img/email_icon.png');
+ this.date = session.web.format_value(this._date, {type:"datetime"});
+ this.timerelative = $.timeago(this.date);
+ if (this.type == 'email') {
+ this.avatar = ('/mail/static/src/img/email_icon.png');
} else {
- this.datasets.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.datasets.author_id[0]);
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
}
- for (var l in this.datasets.attachment_ids) {
- var attach = this.datasets.attachment_ids[l];
+ for (var l in this.attachment_ids) {
+ var attach = this.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
+
+ if ((attach.filename || attach.name).match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
+ attach.is_image = true;
+ attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id);
+ }
}
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
this.expender();
- this.$el.hide().fadeIn(750);
+ this.$el.hide().fadeIn(750, function () {$(this).css('display', '');});
+ this.resize_img();
this.bind_events();
- this.create_thread();
+ if(this.thread_level < this.options.display_indented_thread) {
+ this.create_thread();
+ }
+ this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
+ },
+
+ resize_img: function () {
+ var resize = function () {
+ var h = $(this).height();
+ var w = $(this).width();
+ if ( h > 100 || w >100 ) {
+ var ratio = 100 / (h > w ? h : w);
+ $(this).attr("width", parseInt( w*ratio )).attr("height", parseInt( h*ratio ));
+ }
+ };
+ this.$("img").load(resize).each(resize);
},
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
+ bind_events: function () {
var self = this;
// event: click on 'Attachment(s)' in msg
- this.$('a.oe_msg_view_attachments:first').on('click', function (event) {
- self.$('.oe_msg_attachments:first').toggle();
+ this.$('.oe_mail_msg_view_attachments').on('click', function (event) {
+ var attach = self.$('.oe_msg_attachments:first, .oe_msg_images:first');
+ if ( self.$('.oe_msg_attachments:first').hasClass("oe_hidden") ) {
+ attach.removeClass("oe_hidden");
+ } else {
+ attach.addClass("oe_hidden");
+ }
+ self.resize_img();
});
// event: click on icone 'Read' in header
- this.$el.on('click', 'a.oe_read', this.on_message_read_unread);
+ this.$el.on('click', '.oe_read', this.on_message_read_unread);
// event: click on icone 'UnRead' in header
- this.$el.on('click', 'a.oe_unread', this.on_message_read_unread);
+ this.$el.on('click', '.oe_unread', this.on_message_read_unread);
// event: click on 'Delete' in msg side menu
- this.$el.on('click', 'a.oe_msg_delete', this.on_message_delete);
+ this.$el.on('click', '.oe_msg_delete', this.on_message_delete);
// event: click on 'Reply' in msg
- this.$el.on('click', 'a.oe_reply', this.on_message_reply);
+ this.$el.on('click', '.oe_reply', this.on_message_reply);
// event: click on 'Vote' button
- this.$el.on('click', 'button.oe_msg_vote', this.on_vote);
- // event: click on 'Star' button
- this.$el.on('click', 'button.oe_mail_starbox', this.on_star);
+ this.$el.on('click', '.oe_msg_vote', this.on_vote);
+ // event: click on 'starred/favorite' button
+ this.$el.on('click', '.oe_star', this.on_star);
},
- on_message_reply:function(event){
+ /* Call the on_compose_message on the thread of this message. */
+ on_message_reply:function (event) {
event.stopPropagation();
+ this.create_thread();
this.thread.on_compose_message();
return false;
},
- expender: function(){
- this.$('div.oe_msg_body:first').expander({
+ expender: function () {
+ this.$('.oe_msg_body:first').expander({
slicePoint: this.options.truncate_limit,
expandText: 'read more',
userCollapseText: '[^]',
@@ -611,52 +679,55 @@ openerp.mail = function(session) {
});
},
- create_thread: function(){
- var self=this;
- if(this.thread){
+ /**
+ * Instantiate the thread object of this message.
+ * Each message have only one thread.
+ */
+ create_thread: function () {
+ if (this.thread) {
return false;
}
-
/*create thread*/
- self.thread = new mail.Thread(self, {
- 'domain': self.domain,
+ this.thread = new mail.Thread(this, this, {
+ 'domain': this.domain,
'context':{
- 'default_model': self.datasets.model,
- 'default_res_id': self.datasets.res_id,
- 'default_parent_id': self.datasets.id
+ 'default_model': this.model,
+ 'default_res_id': this.res_id,
+ 'default_parent_id': this.id
},
- 'options': {
- 'thread' : self.options.thread,
- 'message' : self.options.message
- },
- 'datasets': self.datasets
+ 'options': this.options
}
);
/*insert thread in parent message*/
- self.thread.appendTo(self.$el.find('div.oe_thread_placeholder'));
+ this.thread.insertAfter(this.$el);
},
- animated_destroy: function(options) {
+ /**
+ * Fade out the message and his child thread.
+ * Then this object is destroyed.
+ */
+ animated_destroy: function (fadeTime) {
var self=this;
- //graphic effects
- if(options && options.fadeTime) {
- self.$el.fadeOut(options.fadeTime, function(){
- self.destroy();
- });
- } else {
- self.destroy();
+ this.$el.fadeOut(fadeTime, function () {
+ self.parent_thread.message_to_expandable(self);
+ });
+ if (this.thread) {
+ this.thread.$el.fadeOut(fadeTime);
}
},
+ /**
+ * Wait a confirmation for delete the message on the DB.
+ * Make an animate destroy
+ */
on_message_delete: function (event) {
event.stopPropagation();
if (! confirm(_t("Do you really want to delete this message?"))) { return false; }
- this.animated_destroy({fadeTime:250});
+ this.animated_destroy(150);
// delete this message and his childs
- var ids = [this.datasets.id].concat( this.get_child_ids() );
+ var ids = [this.id].concat( this.get_child_ids() );
this.ds_message.unlink(ids);
- this.animated_destroy();
return false;
},
@@ -664,117 +735,125 @@ openerp.mail = function(session) {
* @param {object} mouse envent
*/
on_message_read_unread: function (event) {
- // TDE note: code here seems complicated... just check that current message is read (value coming from server)
- // and send its opposite to set_message_read
event.stopPropagation();
- // if this message is read, all childs message display is read
- var ids = [this.datasets.id].concat( this.get_child_ids() );
- var read = $(event.srcElement).hasClass("oe_read");
- this.$el.removeClass("oe_mail_" + (read?"un":"") + "read").addClass("oe_mail_" + (read?"":"un") + "read");
+ var self=this;
- if( (read && this.options.thread.typeof_thread == 'inbox') ||
- (!read && this.options.thread.typeof_thread == 'archives')) {
- this.animated_destroy({fadeTime:250});
+ if ( (this.to_read && this.options.typeof_thread == 'inbox') ||
+ (!this.to_read && this.options.typeof_thread == 'archives')) {
+ this.animated_destroy(150);
}
- // TDE note: should have a context here
- this.ds_notification.call('set_message_read', [ids, read]);
+
+ // if this message is read, all childs message display is read
+ this.ds_notification.call('set_message_read', [ [this.id].concat( this.get_child_ids() ) , this.to_read, this.context]).pipe(function () {
+ self.$el.removeClass(self.to_read ? 'oe_msg_unread':'oe_msg_read').addClass(self.to_read ? 'oe_msg_read':'oe_msg_unread');
+ self.to_read = !self.to_read;
+ });
return false;
},
- /** browse message
+ /**
+ * search a message in all thread and child thread.
+ * This method return an object message.
* @param {object}{int} option.id
* @param {object}{string} option.model
* @param {object}{boolean} option._go_thread_wall
* private for check the top thread
* @return thread object
*/
- browse_message: function(options){
+ browse_message: function (options) {
// goto the wall thread for launch browse
- if(!options._go_thread_wall) {
+ if (!options._go_thread_wall) {
options._go_thread_wall = true;
- for(var i in this.options.thread._parents[0].messages){
- var res=this.options.thread._parents[0].messages[i].browse_message(options);
- if(res) return res;
+ for (var i in this.options._parents[0].messages) {
+ var res=this.options._parents[0].messages[i].browse_message(options);
+ if (res) return res;
}
}
- if(this.datasets.id==options.id)
+ if (this.id==options.id)
return this;
- for(var i in this.thread.messages){
- if(this.thread.messages[i].thread){
+ for (var i in this.thread.messages) {
+ if (this.thread.messages[i].thread) {
var res=this.thread.messages[i].browse_message(options);
- if(res) return res;
+ if (res) return res;
}
}
return false;
},
- /* get all child message/thread id linked
+ /* get all child message id linked.
+ * @return array of id
*/
- get_child_ids: function(){
+ get_child_ids: function () {
var res=[]
- if(arguments[0]) res.push(this.datasets.id);
- if(this.thread){
+ if (arguments[0]) res.push(this.id);
+ if (this.thread) {
res = res.concat( this.thread.get_child_ids(true) );
}
return res;
},
+ /**
+ * add or remove a vote for a message and display the result
+ */
on_vote: function (event) {
event.stopPropagation();
var self=this;
- return this.ds_message.call('vote_toggle', [[self.datasets.id]]).pipe(function(vote){
- // TDE note: to update, because vote_user_ids is about to disappear to be replaced by vote_nb (number of votes)
- self.datasets.has_voted=vote;
- if (!self.datasets.has_voted) {
- var votes=[];
- for(var i in self.datasets.vote_user_ids){
- if(self.datasets.vote_user_ids[i][0]!=self.datasets.session.uid)
- vote.push(self.datasets.vote_user_ids[i]);
- }
- self.datasets.vote_user_ids=votes;
- }
- else {
- self.datasets.vote_user_ids.push([self.session.uid, 'You']);
- }
+ return this.ds_message.call('vote_toggle', [[self.id]]).pipe(function (vote) {
+ self.has_voted = vote;
+ self.vote_nb += self.has_voted ? 1 : -1;
self.display_vote();
});
return false;
},
- // Render vote Display template.
+ /**
+ * Display the render of this message's vote
+ */
display_vote: function () {
var self = this;
var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self});
- self.$(".placeholder-mail-vote:first").empty();
- self.$(".placeholder-mail-vote:first").html(vote_element);
+ self.$(".oe_msg_vote:first").remove();
+ self.$(".oe_mail_vote_count:first").replaceWith(vote_element);
},
- // Stared/unstared + Render star.
+ /**
+ * add or remove a favorite (or starred) for a message and change class on the DOM
+ */
on_star: function (event) {
event.stopPropagation();
var self=this;
- var button = self.$('button.oe_mail_starbox:first');
- return this.ds_message.call('favorite_toggle', [[self.datasets.id]]).pipe(function(star){
- self.datasets.is_favorite=star;
- if(self.datasets.is_favorite){
- button.addClass('oe_stared');
+ var button = self.$('.oe_star:first');
+ return this.ds_message.call('favorite_toggle', [[self.id]]).pipe(function (star) {
+ self.is_favorite=star;
+ if (self.is_favorite) {
+ button.addClass('oe_starred');
} else {
- button.removeClass('oe_stared');
- if( self.options.thread.typeof_thread == 'stared' ) {
- self.animated_destroy({fadeTime:250});
+ button.removeClass('oe_starred');
+ if ( self.options.typeof_thread == 'stared' ) {
+ self.animated_destroy(150);
}
}
});
return false;
},
+ /**
+ * call on_message_delete on his parent thread
+ */
+ destroy: function () {
+
+ this._super();
+ this.parent_thread.on_message_detroy(this);
+
+ }
+
});
- /**
- * ------------------------------------------------------------
+ /**
+ * ------------------------------------------------------------
* Thread Widget
* ------------------------------------------------------------
*
@@ -799,23 +878,15 @@ openerp.mail = function(session) {
* @param {Object} [options]
* @param {Object} [message] read about mail.ThreadMessage object
* @param {Object} [thread]
- * @param {Boolean} [use_composer] use the advanced composer, or
- * the default basic textarea if not set
- * @param {Number} [expandable_number=5] number message show
- * for each click on "show more message"
- * @param {Number} [expandable_default_number=5] number message show
- * on begin before the first click on "show more message"
- * @param {Array [A,B]} [display_on_thread] display the threads (hierarchy)
- * for the thread level between A and B. -1 for no begin or no end.
- * All thread before A are insert in the root thread.
- * All thread after B are insert in parent thread on B level.
+ * @param {int} [display_indented_thread] number thread level to indented threads.
+ * other are on flat mode
* @param {Select} [typeof_thread] inbox/archives/stared/sent
* type of thread and option for user application like animate
* destroy for read/unread
* @param {Array} [parents] liked with the parents thread
* use with browse, fetch... [O]= top parent
*/
- init: function(parent, options) {
+ init: function (parent, datasets, options) {
this._super(parent);
this.domain = options.domain || [];
this.context = _.extend({
@@ -823,154 +894,132 @@ openerp.mail = function(session) {
default_res_id: 0,
default_parent_id: false }, options.context || {});
- // options
- this.options={
- 'thread' : {
- '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,
- 'expandable_default_number': options.options.thread.expandable_default_number || 5,
- '_expandable_max': options.options.thread.expandable_default_number || 5,
- 'display_on_thread': options.options.thread.display_on_thread || [0,-1],
- 'typeof_thread': options.options.thread.typeof_thread || 'inbox',
- '_parents': (options.options.thread._parents != undefined ? options.options.thread._parents : []).concat( [this] )
- },
- 'message' : options.options.message
- };
+ this.options = options.options;
+ this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] );
// record options and data
this.parent_message= parent.thread!= undefined ? parent : false ;
- var param = options.datasets
- // datasets and internal vars
- this.datasets = {
- 'id' : param.id || false,
- 'model' : param.model || false,
- 'parent_id' : param.parent_id || false,
- 'is_private' : param.is_private || false,
- 'author_id' : param.author_id || false,
- 'thread_level' : (param.thread_level+1) || 0,
- 'partner_ids' : []
- };
-
- for(var i in param.partner_ids){
- if(param.partner_ids[i][0]!=(param.author_id ? param.author_id[0] : -1)){
- this.datasets.partner_ids.push(param.partner_ids[i]);
- }
- }
-
+ // data of this thread
+ this.id = datasets.id || false,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.is_private = datasets.is_private || false,
+ this.author_id = datasets.author_id || false,
+ this.thread_level = (datasets.thread_level+1) || 0,
+ this.partner_ids = _.filter(datasets.partner_ids, function (partner) { return partner[0]!=datasets.author_id[0]; } )
this.messages = [];
- this.ComposeMessage = false;
+ this.show_compose_message = this.options.show_compose_message && (this.options.show_reply_button > this.thread_level || !this.thread_level);
+
+ // object compose message
+ this.compose_message = false;
this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model || 'mail.thread');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
-
- this.list_ul = this.$('ul.oe_mail_thread_display:first');
- this.more_msg = this.$(">.oe_msg_more_message:first");
-
- this.display_user_avatar();
- var display_done = compose_done = false;
-
this.bind_events();
-
- if(this.options.thread._parents[0]==this){
- this.on_root_thread();
- }
-
- return display_done && compose_done;
},
- instantiate_ComposeMessage: function() {
- // add message composition form view
- this.ComposeMessage = new mail.ThreadComposeMessage(this,{
- 'context': this.context,
- 'datasets': this.datasets,
- 'options': this.options,
- 'show_attachment_delete': true,
- });
- this.ComposeMessage.appendTo(this.$(".oe_mail_thread_action:first"));
- },
-
- /* this method is runing for first parent thread
+ /* instantiate the compose message object and insert this on the DOM.
+ * The compose message is display in compact form.
*/
- on_root_thread: function(){
- var self=this;
- // fetch and display message, using message_ids if set
- this.message_fetch();
-
- $(document).scroll( self.on_scroll );
- $(window).resize( self.on_scroll );
- window.setTimeout( self.on_scroll, 500 );
-
- $(session.web.qweb.render('mail.wall_no_message', {})).appendTo(this.$('ul.oe_mail_thread_display'));
-
- this.instantiate_ComposeMessage();
- this.ComposeMessage.datasets.is_private=true;
-
- if(this.options.thread.show_header_compose){
- this.ComposeMessage.$el.show();
- //this.ComposeMessage.set_free_attachments();
+ instantiate_compose_message: function () {
+ // add message composition form view
+ if (!this.compose_message) {
+ this.compose_message = new mail.ThreadComposeMessage(this, this, {
+ 'context': this.context,
+ 'options': this.options,
+ });
+ if (!this.thread_level) {
+ // root view
+ this.compose_message.insertBefore(this.$el);
+ } else if (this.thread_level > this.options.display_indented_thread) {
+ this.compose_message.insertAfter(this.$el);
+ } else {
+ this.compose_message.appendTo(this.$el);
+ }
}
-
- this.$el.addClass("oe_mail_root_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 message = this.messages[0];
- if(message && message.datasets.type=="expandable" && message.datasets.max_limit){
+ on_scroll: function (event) {
+ if (event)event.stopPropagation();
+ this.$('.oe_msg_expandable:last');
+
+ var message = this.messages[this.messages.length-1];
+ if (message && message.type=="expandable" && message.max_limit) {
var pos = message.$el.position();
- if(pos.top){
+ if (pos.top) {
/* bottom of the screen */
var bottom = $(window).scrollTop()+$(window).height()+200;
- if(bottom - pos.top > 0){
+ if (bottom > pos.top) {
message.on_expandable();
}
}
-
}
},
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
+ bind_events: function () {
var self = this;
- 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(); });
+ self.$el.on('click', '.oe_mail_list_recipients .oe_more', self.on_show_recipients);
+ self.$el.on('click', '.oe_mail_compose_textarea .oe_more_hidden', self.on_hide_recipients);
},
- /* get all child message/thread id linked
+ /**
+ *show all the partner list of this parent message
*/
- get_child_ids: function(){
+ on_show_recipients: function () {
+ var p=$(this).parent();
+ p.find('.oe_more_hidden, .oe_hidden').show();
+ p.find('.oe_more').hide();
+ },
+
+ /**
+ *hide a part of the partner list of this parent message
+ */
+ on_hide_recipients: 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.
+ * @return array of id
+ */
+ get_child_ids: function () {
var res=[];
- _(this.get_childs()).each(function (val, key) { res.push(val.datasets.id); });
+ _(this.get_childs()).each(function (val, key) { res.push(val.id); });
return res;
},
- /* get all child message/thread linked
+ /* get all child message/thread linked.
+ * @param {int} nb_thread_level, number of traversed thread level for this search
+ * @return array of thread object
*/
- get_childs: function(nb_thread_level){
+ get_childs: function (nb_thread_level) {
var res=[];
- if(arguments[1]) res.push(this);
- if(isNaN(nb_thread_level) || nb_thread_level>0){
+ if (arguments[1]) res.push(this);
+ if (isNaN(nb_thread_level) || nb_thread_level>0) {
_(this.messages).each(function (val, key) {
- if(val.thread){
- res = res.concat( val.thread.get_childs((isNaN(nb_thread_level) ? null : nb_thread_level-1), true) )
+ if (val.thread) {
+ res = res.concat( val.thread.get_childs((isNaN(nb_thread_level) ? undefined : nb_thread_level-1), true) );
}
});
}
return res;
},
- /** browse thread
+ /**
+ *search a thread in all thread and child thread.
+ * This method return an object thread.
* @param {object}{int} option.id
* @param {object}{string} option.model
* @param {object}{boolean} option._go_thread_wall
@@ -979,252 +1028,469 @@ openerp.mail = function(session) {
* return the top thread (wall) if no thread found
* @return thread object
*/
- browse_thread: function(options){
+ browse_thread: function (options) {
// goto the wall thread for launch browse
- if(!options._go_thread_wall) {
+ if (!options._go_thread_wall) {
options._go_thread_wall = true;
- return this.options.thread._parents[0].browse_thread(options);
+ return this.options._parents[0].browse_thread(options);
}
- if(this.datasets.id==options.id){
+ if (this.id==options.id) {
return this;
}
- if(options.id)
- for(var i in this.messages){
- if(this.messages[i].thread){
- var res=this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
- if(res) return res;
+ if (options.id) {
+ for (var i in this.messages) {
+ if (this.messages[i].thread) {
+ var res=this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
+ if (res) return res;
+ }
}
}
//if option default_return_top_thread, return the top if no found thread
- if(options.default_return_top_thread){
+ if (options.default_return_top_thread) {
return this;
}
return false;
},
- /** browse message
+ /**
+ *search a message in all thread and child thread.
+ * This method return an object message.
* @param {object}{int} option.id
* @param {object}{string} option.model
* @param {object}{boolean} option._go_thread_wall
* private for check the top thread
- * @return thread object
+ * @return message object
*/
- browse_message: function(options){
- if(this.options.thread._parents[0].messages[0])
- return this.options.thread._parents[0].messages[0].browse_message(options);
+ browse_message: function (options) {
+ if (this.options._parents[0].messages[0])
+ return this.options._parents[0].messages[0].browse_message(options);
+ },
+
+ /**
+ *If compose_message doesn't exist, instantiate the compose message.
+ * Call the on_compose_expandable method to allow the user to write his message.
+ * (Is call when a user click on "Reply" button)
+ */
+ on_compose_message: function () {
+ this.instantiate_compose_message();
+ this.compose_message.on_compose_expandable();
},
- /* this function is launch when a user click on "Reply" button
+ /**
+ *display the message "there are no message" on the thread
*/
- on_compose_message: function(){
- if(!this.ComposeMessage){
- this.instantiate_ComposeMessage();
+ no_message: function () {
+ var no_message = $(session.web.qweb.render('mail.wall_no_message', {}));
+ if (this.options.no_message) {
+ no_message.html(this.options.no_message);
}
- this.ComposeMessage.$el.toggle();
- return false;
+ no_message.appendTo(this.$el);
},
- /** Fetch messages
- * @param {Bool} initial_mode: initial mode: try to use message_data or
- * message_ids, if nothing available perform a message_read; otherwise
- * directly perform a message_read
+ /**
+ *make a request to read the message (calling RPC to "message_read").
+ * The result of this method is send to the switch message for sending ach message to
+ * his parented object thread.
* @param {Array} replace_domain: added to this.domain
* @param {Object} replace_context: added to this.context
+ * @param {Array} ids read (if the are some ids, the method don't use the domain)
*/
- message_fetch: function (initial_mode, replace_domain, replace_context, ids, callback) {
+ message_fetch: function (replace_domain, replace_context, ids, callback) {
var self = this;
- // initial mode: try to use message_data or message_ids
- if (initial_mode && this.options.thread.message_data) {
- return this.create_message_object(this.options.message_data);
- }
// domain and context: options + additional
fetch_domain = replace_domain ? replace_domain : this.domain;
fetch_context = replace_context ? replace_context : this.context;
- var message_loaded = [this.datasets.id||0].concat( self.options.thread._parents[0].get_child_ids() );
+ var message_loaded_ids = this.id ? [this.id].concat( self.get_child_ids() ) : self.get_child_ids();
- return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded, fetch_context, this.context.default_parent_id || undefined]
- ).then(this.proxy('switch_new_message'));
+ // CHM note : option for sending in flat mode by server
+ var thread_level = this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0;
+
+ return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded_ids, thread_level, fetch_context, this.context.default_parent_id || undefined])
+ .then(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
},
- /* create record object and linked him
+ /**
+ *create the message object and attached on this thread.
+ * When the message object is create, this method call insert_message for,
+ * displaying this message on the DOM.
+ * @param : {object} data from calling RPC to "message_read"
*/
create_message_object: function (data) {
var self = this;
- if(data.type=='expandable'){
- var message = new mail.ThreadExpandable(self, {
- 'domain': data.domain,
- 'context': {
- 'default_model': data.model || self.context.default_model,
- 'default_res_id': data.res_id || self.context.default_res_id,
- 'default_parent_id': self.datasets.id },
- 'datasets': data
+ var data = _.extend(data, {'thread_level': data.thread_level ? data.thread_level : self.thread_level});
+ data.options = _.extend(self.options, data.options);
+
+ if (data.type=='expandable') {
+ var message = new mail.ThreadExpandable(self, data, {
+ 'default_model': data.model || self.context.default_model,
+ 'default_res_id': data.res_id || self.context.default_res_id,
+ 'default_parent_id': self.id,
});
} else {
- var message = new mail.ThreadMessage(self, {
- 'domain': data.domain,
- 'context': {
- 'default_model': data.model,
- 'default_res_id': data.res_id,
- 'default_parent_id': data.id },
- 'options':{
- 'thread': self.options.thread,
- 'message': self.options.message
- },
- 'datasets': _.extend(data, {'thread_level': self.datasets.thread_level})
+ var message = new mail.ThreadMessage(self, data, {
+ 'default_model': data.model,
+ 'default_res_id': data.res_id,
+ 'default_parent_id': data.id,
});
- var data = _.extend(data, {'thread_level': self.datasets.thread_level});
}
// check if the message is already create
- for(var i in self.messages){
- if(self.messages[i].datasets.id==message.datasets.id){
+ for (var i in self.messages) {
+ if (self.messages[i] && self.messages[i].id == message.id) {
self.messages[i].destroy();
- self.messages[i]=self.insert_message(message);
- return true;
}
}
- self.messages.push( self.insert_message(message) );
+ self.messages.push( message );
+
+ return message;
},
- /** Displays a message or an expandable message */
- insert_message: function (message) {
+ /**
+ *insert the message on the DOM.
+ * All message (and expandable message) are sorted. The method get the
+ * older and newer message to insert the message (before, after).
+ * If there are no older or newer, the message is prepend or append to
+ * the thread (parent object or on root thread for flat view).
+ * The sort is define by the thread_level (O for newer on top).
+ * @param : {object} ThreadMessage object
+ */
+ insert_message: function (message, dom_insert_after) {
var self=this;
- this.$("li.oe_wall_no_message").remove();
+ if (this.show_compose_message && this.options.show_compact_message) {
+ this.instantiate_compose_message();
+ this.compose_message.do_show_compact();
+ }
- // insert on hierarchy display => insert in self child
- var thread_messages = self.messages;
- var thread = self;
- var flat = false;
- var hierarchy = self.options.thread.display_on_thread;
- if( hierarchy[0] < 0 ||
- hierarchy[0] > self.datasets.thread_level ||
- (hierarchy[1]>0 && hierarchy[1] < self.datasets.thread_level) ) {
+ this.$('.oe_wall_no_message').remove();
- var flat = true;
- if(hierarchy[0]<0){
-
- // all is in flat mode
- thread = self.options.thread._parents[0];
- var nb_thread_level = null;
-
- } else if(hierarchy[0] > self.datasets.thread_level) {
-
- // list all childs messages for flat display before the hierarchy
- thread = self.options.thread._parents[0];
- var nb_thread_level = hierarchy[0];
-
- } else if(hierarchy[1] < self.datasets.thread_level) {
-
- // list all childs messages for flat display after the hierarchy
- thread = self.options.thread._parents[hierarchy[1]];
- var nb_thread_level = hierarchy[1]>0 ? hierarchy[1]-hierarchy[0] : null;
- } else {
+ if (dom_insert_after) {
+ message.insertAfter(dom_insert_after);
+ return message
+ }
- thread = self.options.thread._parents[0];
- var nb_thread_level = null;
+ // check older and newer message for insertion
+ var message_newer = false;
+ var message_older = false;
+ if (message.id > 0) {
+ for (var i in self.messages) {
+ if (self.messages[i].id > message.id) {
+ if (!message_newer || message_newer.id > self.messages[i].id) {
+ message_newer = self.messages[i];
+ }
+ } else if (self.messages[i].id > 0 && self.messages[i].id < message.id) {
+ if (!message_older || message_older.id < self.messages[i].id) {
+ message_older = self.messages[i];
+ }
+ }
}
-
- var thread_messages = [];
- _(thread.get_childs( nb_thread_level )).each(function (val, key) { thread_messages.push(val.parent_message); });
}
+ var sort = (!!self.thread_level || message.id<0);
- // check older and newer message for insert
- var parent_newer = false;
- var parent_older = false;
- if ( message.datasets.id > 0 ){
- for(var i in thread_messages){
- if(thread_messages[i].datasets.id > message.datasets.id){
- if(!parent_newer || parent_newer.datasets.id>=thread_messages[i].datasets.id)
- parent_newer = thread_messages[i];
- } else if(thread_messages[i].datasets.id>0 && thread_messages[i].datasets.id < message.datasets.id) {
- if(!parent_older || parent_older.id<thread_messages[i].datasets.id)
- parent_older = thread_messages[i];
- }
- }
- }
+ if (sort) {
+ if (message_older) {
- var sort = self.datasets.thread_level==0 || (flat && self.datasets.thread_level>=1);
+ message.insertAfter(message_older.thread ? (message_older.thread.compose_message ? message_older.thread.compose_message.$el : message_older.thread.$el) : message_older.$el);
+
+ } else if (message_newer) {
+
+ message.insertBefore(message_newer.$el);
+
+ } else if (message.id < 0) {
+
+ message.appendTo(self.$el);
- 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);
+
+ message.prependTo(self.$el);
}
} else {
- if(sort && message.id > 0){
- message.prependTo(thread.list_ul);
+ if (message_older) {
+
+ message.insertBefore(message_older.$el);
+
+ } else if (message_newer) {
+
+ message.insertAfter(message_newer.thread ? (message_newer.thread.compose_message ? message_newer.thread.compose_message.$el : message_newer.thread.$el) : message_newer.$el );
+
+ } else if (message.id < 0) {
+
+ message.prependTo(self.$el);
+
} else {
- message.appendTo(thread.list_ul);
+
+ message.appendTo(self.$el);
+
}
}
return message
},
-
- display_user_avatar: function () {
- var avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
- return this.$('img.oe_mail_icon').attr('src', avatar);
- },
- /* Send the records to his parent thread */
- switch_new_message: function(records) {
+ /**
+ *get the parent thread of the messages.
+ * Each message is send to his parent object (or parent thread flat mode) for creating the object message.
+ * @param : {Array} datas from calling RPC to "message_read"
+ */
+ switch_new_message: function (records) {
var self=this;
- _(records).each(function(record){
- self.browse_thread({
+ _(records).each(function (record) {
+ var thread = self.browse_thread({
'id': record.parent_id,
'default_return_top_thread':true
- }).create_message_object( record );
+ });
+ // create object and attach to the thread object
+ var message = thread.create_message_object( record );
+ // insert the message on dom
+ thread.insert_message( message );
});
},
- });
+ /**
+ * this method is call when the widget of a message or an expandable message is destroy
+ * in this thread. The this.messages array is filter to remove this message
+ */
+ on_message_detroy: function (message) {
+
+ this.messages = _.filter(this.messages, function (val) { return !val.isDestroyed(); });
+
+ },
+
+ /**
+ * Convert a destroyed message into a expandable message
+ */
+ message_to_expandable: function (message) {
+
+ if (!this.thread_level || message.isDestroyed()) {
+ message.destroy();
+ return false;
+ }
+
+ var messages = _.sortBy( this.messages, function (val) { return val.id; });
+ var it = _.indexOf( messages, message );
+
+ var msg_up = messages[it-1];
+ var msg_down = messages[it+1];
+
+ var message_dom = [ ["id", "=", message.id] ];
+
+ if ( msg_up && msg_up.type == "expandable" && msg_down && msg_down.type == "expandable") {
+ // concat two expandable message and add this message to this dom
+ msg_up.domain = mail.ChatterUtils.expand_domain( msg_up.domain );
+ msg_down.domain = mail.ChatterUtils.expand_domain( msg_down.domain );
- /**
+ msg_down.domain = ['|','|'].concat( msg_up.domain ).concat( message_dom ).concat( msg_down.domain );
+
+ if ( !msg_down.max_limit ) {
+ msg_down.nb_messages += 1 + msg_up.nb_messages;
+ }
+
+ msg_up.$el.remove();
+ msg_up.destroy();
+
+ msg_down.reinit();
+
+ } else if ( msg_up && msg_up.type == "expandable") {
+ // concat preview expandable message and this message to this dom
+ msg_up.domain = mail.ChatterUtils.expand_domain( msg_up.domain );
+ msg_up.domain = ['|'].concat( msg_up.domain ).concat( message_dom );
+
+ msg_up.nb_messages++;
+
+ msg_up.reinit();
+
+ } else if ( msg_down && msg_down.type == "expandable") {
+ // concat next expandable message and this message to this dom
+ msg_down.domain = mail.ChatterUtils.expand_domain( msg_down.domain );
+ msg_down.domain = ['|'].concat( msg_down.domain ).concat( message_dom );
+
+ // it's maybe a message expandable for the max limit read message
+ if ( !msg_down.max_limit ) {
+ msg_down.nb_messages++;
+ }
+
+ msg_down.reinit();
+
+ } else {
+ // create a expandable message
+ var expandable = new mail.ThreadExpandable(this, {
+ 'id': message.id,
+ 'model': message.model,
+ 'parent_id': message.parent_id,
+ 'nb_messages': 1,
+ 'thread_level': message.thread_level,
+ 'parent_id': message.parent_id,
+ 'domain': message_dom,
+ 'options': message.options,
+ }, {
+ 'default_model': message.model || this.context.default_model,
+ 'default_res_id': message.res_id || this.context.default_res_id,
+ 'default_parent_id': this.id,
+ });
+
+ // add object on array and DOM
+ this.messages.push(expandable);
+ expandable.insertAfter(message.$el);
+ }
+
+ // destroy message
+ message.destroy();
+
+ return true;
+ },
+ });
+
+ /**
+ * ------------------------------------------------------------
+ * mail : root Widget
* ------------------------------------------------------------
+ *
+ * This widget handles the display of messages with thread options. Its main
+ * use is to receive a context and a domain, and to delegate the message
+ * fetching and displaying to the Thread widget.
+ */
+ session.web.client_actions.add('mail.Widget', 'session.mail.Widget');
+ mail.Widget = session.web.Widget.extend({
+ template: 'mail.Widget',
+
+ /**
+ * @param {Object} parent parent
+ * @param {Array} [domain]
+ * @param {Object} [context] context of the thread. It should
+ * contain at least default_model, default_res_id. Please refer to
+ * the compose_message widget for more information about it.
+ * ... @param {Select} [typeof_thread=(mail|stared|archives|send|other)]
+ * options for destroy message when the user click on a button
+ * @param {Object} [options]
+ *... @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] display the name and link for do action
+ *... @param {int} [show_reply_button] number thread level to display the reply button
+ *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
+ *... @param {int} [display_indented_thread] number thread level to indented threads.
+ * other are on flat mode
+ *... @param {Boolean} [show_compose_message] allow to display the composer
+ *... @param {Boolean} [show_compact_message] display the compact message on the thread
+ * when the user clic on this compact mode, the composer is open
+ *... @param {Array} [message_ids] List of ids to fetch by the root thread.
+ * When you use this option, the domain is not used for the fetch root.
+ * @param {String} [no_message] Message to display when there are no message
+ */
+ init: function (parent, options) {
+ this._super(parent);
+ this.domain = options.domain || [];
+ this.context = options.context || {};
+ this.search_results = {'domain': [], 'context': {}, 'groupby': {}};
+
+ this.options = _.extend({
+ 'typeof_thread' : 'inbox',
+ 'display_indented_thread' : -1,
+ 'show_reply_button' : -1,
+ 'show_read_unread_button' : -1,
+ 'truncate_limit' : 250,
+ 'show_record_name' : false,
+ 'show_compose_message' : false,
+ 'show_compact_message' : false,
+ 'message_ids': undefined,
+ 'no_message': false
+ }, options);
+
+ if (this.display_indented_thread === false) {
+ this.display_indented_thread = -1;
+ }
+ if (this.show_reply_button === false) {
+ this.show_reply_button = -1;
+ }
+ if (this.show_read_unread_button === false) {
+ this.show_read_unread_button = -1;
+ }
+
+ },
+
+ start: function (options) {
+ this._super.apply(this, arguments);
+ this.message_render();
+ this.bind_events();
+ },
+
+
+ /**
+ *Create the root thread and display this object in the DOM.
+ * Call the no_message method then c all the message_fetch method
+ * of this root thread to display the messages.
+ */
+ message_render: function (search) {
+
+ this.thread = new mail.Thread(this, {}, {
+ 'domain' : this.domain,
+ 'context' : this.context,
+ 'options': this.options,
+ });
+
+ this.thread.appendTo( this.$el );
+ this.thread.no_message();
+ this.thread.message_fetch(null, null, this.options.message_ids);
+
+ if (this.options.show_compose_message) {
+ this.thread.instantiate_compose_message();
+ if (this.options.show_compact_message) {
+ this.thread.compose_message.do_show_compact();
+ } else {
+ this.thread.compose_message.do_hide_compact();
+ }
+ }
+ },
+
+ bind_events: function () {
+ if (this.context['typeof_thread']!='other') {
+ $(document).scroll( this.thread.on_scroll );
+ $(window).resize( this.thread.on_scroll );
+ window.setTimeout( this.thread.on_scroll, 500 );
+ }
+ }
+ });
+
+
+ /**
+ * ------------------------------------------------------------
* mail_thread Widget
* ------------------------------------------------------------
*
* This widget handles the display of messages on a document. Its main
* use is to receive a context and a domain, and to delegate the message
* fetching and displaying to the Thread widget.
+ * Use Help on the field to display a custom "no message loaded"
*/
session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread');
mail.RecordThread = session.web.form.AbstractField.extend({
template: 'mail.record_thread',
- init: function() {
+ init: function () {
this._super.apply(this, arguments);
this.options.domain = this.options.domain || [];
this.options.context = {'default_model': 'mail.thread', 'default_res_id': false};
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
// NB: check the actual_mode property on view to know if the view is in create mode anymore
this.view.on("change:actual_mode", this, this._check_visibility);
this._check_visibility();
},
- _check_visibility: function() {
+ _check_visibility: function () {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
- render_value: function() {
+ render_value: function () {
var self = this;
if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$('oe_mail_thread').hide();
@@ -1237,39 +1503,37 @@ openerp.mail = function(session) {
default_is_private: false });
// update domain
var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
- // create and render Thread widget
- // TDE note: replace message_is_follower by a check in message_follower_ids, as message_is_follower is not used in views anymore
- var show_header_compose = this.view.is_action_enabled('edit') ||
+
+ var show_compose_message = 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();
+ var message_ids = this.getParent().fields.message_ids && this.getParent().fields.message_ids.get_value();
+
+ if (this.root) {
+ this.root.destroy();
}
- this.thread = new mail.Thread(self, {
- 'domain': domain,
- 'context': this.options.context,
- 'options':{
- 'thread':{
- 'show_header_compose': show_header_compose,
- 'use_composer': show_header_compose,
- 'display_on_thread':[-1,-1]
- },
- 'message':{
- 'show_reply': [-1,-1],
- 'show_read_unread': [-1,-1],
- 'show_dd_delete': false
- }
- },
- 'datasets': {},
+ // create and render Thread widget
+ this.root = new mail.Widget(this, {
+ 'domain' : domain,
+ 'context' : this.options.context,
+ 'typeof_thread': this.options.context['typeof_thread'] || 'other',
+ 'display_indented_thread': -1,
+ 'show_reply_button': 0,
+ 'show_read_unread_button': -1,
+ 'show_compose_message': show_compose_message,
+ 'message_ids': message_ids,
+ 'show_compact_message': true,
+ 'no_message': this.node.attrs.help
}
);
- return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
+
+ return this.root.replace(this.$('.oe_mail-placeholder'));
},
});
- /**
- * ------------------------------------------------------------
+ /**
+ * ------------------------------------------------------------
* Wall Widget
* ------------------------------------------------------------
*
@@ -1287,8 +1551,6 @@ openerp.mail = function(session) {
* @param {Array} [options.domain] domain on the Wall
* @param {Object} [options.context] context, is an object. It should
* contain default_model, default_res_id, to give it to the threads.
- * @param {Number} [options.thread_level] number of thread levels to display
- * 0 being flat.
*/
init: function (parent, options) {
this._super(parent);
@@ -1305,7 +1567,7 @@ openerp.mail = function(session) {
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);
+ return $.when(searchview_ready, thread_displayed);
},
/**
@@ -1328,7 +1590,7 @@ openerp.mail = function(session) {
* @param {Array} contexts
* @param {Array} groupbys
*/
- do_searchview_search: function(domains, contexts, groupbys) {
+ do_searchview_search: function (domains, contexts, groupbys) {
var self = this;
this.rpc('/web/session/eval_domain_and_context', {
domains: domains || [],
@@ -1337,51 +1599,43 @@ openerp.mail = function(session) {
}).then(function (results) {
self.search_results['context'] = results.context;
self.search_results['domain'] = results.domain;
- self.thread.destroy();
+ self.root.destroy();
return self.message_render();
});
},
/**
- * Display the threads
+ *Create the root thread widget and display this object in the DOM
*/
message_render: function (search) {
var domain = this.options.domain.concat(this.search_results['domain']);
var context = _.extend(this.options.context, search&&search.search_results['context'] ? search.search_results['context'] : {});
- this.thread = new mail.Thread(this, {
- 'domain' : domain,
- 'context' : context,
- 'options': {
- 'thread' :{
- 'use_composer': true,
- 'show_header_compose': false,
- 'typeof_thread': context.typeof_thread || 'inbox',
- 'display_on_thread': [0,1]
- },
- 'message': {
- 'show_reply': [0,0],
- 'show_read_unread': [0,-1],
- 'show_dd_delete': false,
- },
- },
- 'datasets': {},
+ this.root = new mail.Widget(this, {
+ 'domain' : domain,
+ 'context' : context,
+ 'typeof_thread': context['typeof_thread'] || 'other',
+ 'display_indented_thread': 1,
+ 'show_reply_button': 10,
+ 'show_read_unread_button': 11,
+ 'show_compose_message': true,
+ 'show_compact_message': false,
}
);
- return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
+ return this.root.replace(this.$('.oe_mail-placeholder'));
},
- bind_events: function(){
+ 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(); });
+ this.$(".oe_write_full").click(function(){ self.root.thread.compose_message.on_compose_fullmail(); });
+ this.$(".oe_write_onwall").click(function(){ self.root.thread.on_compose_message(); });
}
});
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* UserMenu
* ------------------------------------------------------------
*
@@ -1401,13 +1655,13 @@ openerp.mail = function(session) {
};
},
- start: function(parent, params) {
+ start: function (parent, params) {
var self = this;
this.$el.on('click', 'button', self.on_compose_message );
this._super(parent, params);
},
- on_compose_message: function(event){
+ on_compose_message: function (event) {
event.stopPropagation();
var action = {
type: 'ir.actions.act_window',
@@ -1417,7 +1671,11 @@ openerp.mail = function(session) {
action_from: 'mail.ThreadComposeMessage',
views: [[false, 'form']],
target: 'new',
- context: this.options.context,
+ context: _.extend(this.options.context, {
+ 'default_model': this.context.default_model,
+ 'default_res_id': this.context.default_res_id,
+ 'default_content_subtype': 'html',
+ }),
};
session.client.action_manager.do_action(action);
},
@@ -1425,7 +1683,7 @@ openerp.mail = function(session) {
});
session.web.UserMenu = session.web.UserMenu.extend({
- start: function(parent, params) {
+ start: function (parent, params) {
var render = new session.web.ComposeMessageTopButton();
render.insertAfter(this.$el);
this._super(parent, params);
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index 806bf28e7b2..a13d2048bf3 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -24,10 +24,9 @@ openerp_mail_followers = function(session, mail) {
init: function() {
this._super.apply(this, arguments);
- this.options.image = this.node.attrs.image || 'image_small';
- this.options.title = this.node.attrs.title || 'Followers';
- this.options.comment = this.node.attrs.help || false;
- this.options.displayed_nb = this.node.attrs.displayed_nb || 10;
+ this.image = this.node.attrs.image || 'image_small';
+ this.comment = this.node.attrs.help || false;
+ this.displayed_nb = this.node.attrs.displayed_nb || 10;
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
@@ -61,7 +60,7 @@ openerp_mail_followers = function(session, mail) {
self.do_unfollow();
});
// event: click on a subtype, that (un)subscribe for this subtype
- this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription);
+ this.$el.on('click', '.oe_subtype_list input', self.do_update_subscription);
// event: click on 'invite' button, that opens the invite wizard
this.$('.oe_invite').on('click', function (event) {
action = {
@@ -115,20 +114,24 @@ openerp_mail_followers = function(session, mail) {
self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
}).pipe(self.proxy('display_generic'));
},
-
+ _format_followers: function(count){
+ // TDE note: why redefining _t ?
+ function _t(str) { return str; }
+ var str = '';
+ if(count <= 0){
+ str = _t('No followers');
+ }else if(count === 1){
+ str = _t('One follower');
+ }else{
+ str = ''+count+' '+_t('followers');
+ }
+ return str;
+ },
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function () {
var self = this;
- var node_user_list = this.$('ul.oe_mail_followers_display').empty();
- // format content: Followers (You and 0 other) // Followers (3)
- var content = this.options.title;
- if (this.message_is_follower) {
- content += ' (You and ' + (this.get('value').length-1) + ' other)';
- }
- else {
- content += ' (' + this.get('value').length + ')'
- }
- this.$('div.oe_mail_recthread_followers h4').html(content);
+ var node_user_list = this.$('.oe_follower_list').empty();
+ this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
},
/** Display the followers */
@@ -137,16 +140,17 @@ openerp_mail_followers = function(session, mail) {
records = records || [];
this.message_is_follower = this.set_is_follower(records);
// clean and display title
- var node_user_list = this.$('ul.oe_mail_followers_display').empty();
- this.$('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
+ var node_user_list = this.$('.oe_follower_list').empty();
+ this.$('.oe_follower_title').html(this._format_followers(records.length));
// truncate number of displayed followers
- truncated = records.splice(0, this.options.displayed_nb);
+ truncated = records.splice(0, this.displayed_nb);
_(truncated).each(function (record) {
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list);
});
+ // FVA note: be sure it is correctly translated
if (truncated.length < records.length) {
- $('<li>And ' + (records.length - truncated.length) + ' more.</li>').appendTo(node_user_list);
+ $('<div class="oe_partner">And ' + (records.length - truncated.length) + ' more.</div>').appendTo(node_user_list);
}
},
@@ -172,7 +176,7 @@ openerp_mail_followers = function(session, mail) {
/** Fetch subtypes, only if current user is follower */
fetch_subtypes: function () {
- var subtype_list_ul = this.$('.oe_subtypes').empty();
+ var subtype_list_ul = this.$('.oe_subtype_list').empty();
if (! this.message_is_follower) return;
var context = new session.web.CompoundContext(this.build_context(), {});
this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).pipe(this.proxy('display_subtypes'));
@@ -181,13 +185,12 @@ openerp_mail_followers = function(session, mail) {
/** Display subtypes: {'name': default, followed} */
display_subtypes:function (data) {
var self = this;
- var subtype_list_ul = this.$('.oe_subtypes');
- var records = data[this.view.datarecord.id].message_subtype_data;
-
+ var subtype_list_ul = this.$('.oe_subtype_list');
+ var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
_(records).each(function (record, record_name) {
record.name = record_name;
record.followed = record.followed || undefined;
- $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('ul.oe_subtypes') );
+ $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('.oe_subtype_list') );
});
},
@@ -210,7 +213,7 @@ openerp_mail_followers = function(session, mail) {
var self = this;
var checklist = new Array();
- _(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function (record) {
+ _(this.$('.oe_actions input[type="checkbox"]')).each(function (record) {
if ($(record).is(':checked')) {
checklist.push(parseInt($(record).data('id')));
}
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index fbb64cc6772..caa85cd3deb 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,33 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
+ <!-- this template contains the mail widget and is used to namespace the css -->
+ <t t-name="mail.Widget">
+ <div class="oe_mail">
+ </div>
+ </t>
+
<!--
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.
+
+ mail.compose_message.compact template
+ This template holds the composition form to write a message, this box is converted into
+ mail.compose_message when focus on textarea
-->
<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_msg_content">
- <!-- contains the composition form -->
- <!-- default content: old basic textarea -->
- <div class="oe_mail_post_header">
+ <div t-if="widget.show_composer" t-attf-class="oe_msg oe_msg_composer #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
+ <div class="oe_msg_left">
+ <img class="oe_msg_icon" alt="User img" t-attf-src="#{widget.avatar}"/>
+ </div>
+ <div class="oe_msg_center">
+ <div class="oe_msg_content">
<t t-call="mail.thread.list_recipients"/>
- <a class="oe_cancel oe_e">X</a>
+ <textarea class="field_text"></textarea>
</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>
+ <div class="oe_msg_footer">
+ <div class="oe_msg_attachment_list"></div>
+ <button class="oe_post">Post</button>
<t t-call="mail.compose_message.add_attachment"/>
+ <!--<a class="oe_cancel oe_e">X</a>-->
+ <button class="oe_full"><span class='oe_e'>&amp;ograve</span></button>
</div>
</div>
- <div class="oe_clear"/>
</div>
+ <div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
+ <textarea class="field_text oe_compact" placeholder="Write a reply..."/>
+ </div>
+ <span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
</t>
<!--
@@ -35,19 +48,20 @@
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">
+ <span class="oe_attachment_file">
+ <span class="oe_add">
<!-- uploader of file -->
- <button><span class="oe_e">p</span></button>
+ <button class="oe_attach"><span class="oe_e">'</span></button>
+ <span class='oe_attach_label'>File</span>
<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="mail.message"/>
+ <input type="hidden" name="model" value="mail.compose.message"/>
<input type="hidden" name="id" value="0"/>
<input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
</t>
- </div>
- </div>
+ </span>
+ </span>
</t>
<!--
@@ -55,25 +69,33 @@
Template used to display attachments in a mail.message
-->
<t t-name="mail.thread.message.attachments">
- <ul t-attf-class="oe_msg_attachments #{widget.datasets.attachment_ids[0] and widget.options.thread.show_attachment_link?'':'oe_hidden'}">
- <t t-foreach="widget.datasets.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}">
+ <span class="oe_msg_attachments">
+ <t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image">
+ <div class="oe_attachment">
+ <span t-if="(attachment.upload and 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 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 t-if="(!attachment.upload or attachment.percent_loaded&gt;=100)">
+ <a 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>
-
- </li>
+ <t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded&gt;=100))">
+ <a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a>
+ </t>
+ </div>
+ </t>
+ </span>
+ <span class="oe_msg_images">
+ <t t-foreach="widget.attachment_ids" t-as="attachment" t-if="attachment.is_image">
+ <a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
+ <img t-if="attachment.is_image" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-att-src="attachment.url"/>
+ </a>
</t>
- </ul>
+ </span>
</t>
<t t-name="mail.thread.message.private">
@@ -89,14 +111,14 @@
-->
<t t-name="mail.thread.list_recipients">
<div class="oe_mail_list_recipients">
- Post to:
- <span t-if="!widget.datasets.is_private" class="oe_all_follower">All Followers</span>
- <t t-if="!widget.datasets.is_private and widget.datasets.partner_ids.length"> and </t>
+ To:
+ <span t-if="!widget.is_private" class="oe_all_follower">Everyone</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.datasets.partner_ids.length" t-foreach="widget.datasets.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-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.datasets.partner_ids.length>=3">
- <span class="oe_more">, <a><t t-raw="widget.datasets.partner_ids.length-3"/> others...</a></span>
+ <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>
@@ -131,122 +153,95 @@
<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>
+ <span class='oe_alternative'>
+ or
+ <a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to your followers</a>
+ </span>
</td>
</tr>
</tbody>
</table>
- <!-- placeholder for the wall threads -->
- <div class="oe_mail_wall_threads"/>
- <div class="oe_mail_wall_aside">
- <!-- contains currently nothing -->
- </div>
+ <div class="oe_mail-placeholder"></div>
</div>
<!--
display message on the wall when there are no message
-->
- <li t-name="mail.wall_no_message" class="oe_wall_no_message">
- You have no messages
- </li>
+ <t t-name="mail.wall_no_message">
+ <div class="oe_wall_no_message">You have no messages</div>
+ </t>
<!--
record_thread main template
Template used to display the communication history in documents
form view.
-->
- <div t-name="mail.record_thread" class="oe_mail_record_wall">
- <!-- <h4>History and Comments</h4> -->
- <ul class="oe_mail_wall_threads">
- <!-- contains the document thread -->
- </ul>
- </div>
-
- <!--
- 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.
- -->
- <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) -->
+ <div t-name="mail.record_thread" class="oe_record_thread">
+ <div class="oe_mail-placeholder">
</div>
- <ul class="oe_mail_thread_display">
- <!-- contains the threads -->
- </ul>
</div>
+ <t t-name="mail.thread">
+ <div t-attf-class="oe_thread #{widget.root?'oe_root_thread':''}"/>
+ </t>
+
<!-- default layout -->
- <li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.datasets.to_read ?'oe_mail_unread':'oe_mail_read'}">
- <div t-attf-class="oe_msg_#{widget.datasets.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 class="placeholder-mail-star"><t t-call="mail.thread.message.star"/></li>
- <li t-if="widget.datasets.show_read_unread" title="Read" class="oe_read"><a class="oe_read oe_e">W</a></li>
- <li t-if="widget.datasets.show_read_unread" title="Set back to unread" class="oe_unread"><a class="oe_unread oe_e">h</a></li>
- <li title="Quick reply" t-if="widget.datasets.show_reply"><a class="oe_reply oe_e">)</a></li>
- <t t-if="(widget.datasets.is_author and widget.options.message.show_dd_delete) or widget.datasets.type == 'email'">
- <li>
- <span class="oe_dropdown_toggle">
- <a class="oe_e" title="More options">í</a>
- <ul class="oe_dropdown_menu">
- <li t-if="widget.datasets.is_author and widget.options.message.show_dd_delete"><a class="oe_msg_delete">Delete</a></li>
- <li t-if="widget.datasets.type == 'email'"><a class="oe_msg_details" t-attf-href="#model=mail.message&amp;id=#{widget.datasets.id}" >Details</a></li>
- </ul>
- </span>
- </li>
- </t>
- </ul>
+ <t t-name="mail.thread.message">
+ <div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type} oe_msg_#{widget.to_read?'unread':'read'}">
- <a t-attf-href="#model=res.partner&amp;id=#{widget.datasets.author_id[0]}" t-att-title="widget.datasets.author_id[1]">
- <img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="widget.datasets.avatar"/>
- </a>
+ <div class='oe_msg_left'>
+ <a t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
+ <img class="oe_msg_icon" t-att-src="widget.avatar"/>
+ </a>
+ </div>
- <div class="oe_msg_content">
+ <div class="oe_msg_center">
+ <div class='oe_msg_icons'>
+ <span class='oe_read' t-if="widget.show_read_unread_button"><a title="Read" class="oe_e">X</a></span>
+ <span class='oe_unread' t-if="widget.show_read_unread_button"><a title="Set back to unread" class="oe_e">v</a></span>
+ <span class='oe_reply' t-if="widget.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
+ <span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Add To Favorites" class="oe_e">7</a></span>
+ </div>
<!-- message itself -->
- <div class="oe_mail_msg">
- <h1 t-if="widget.datasets.subject" class="oe_msg_title">
- <t t-raw="widget.datasets.subject"/>
+ <div class="oe_msg_content">
+ <h1 t-if="widget.subject and !widget.thread_level" class="oe_msg_title">
+ <t t-raw="widget.subject"/>
</h1>
- <ul class="oe_msg_footer">
- <li t-if="widget.datasets.author_id"><a t-attf-href="#model=res.partner&amp;id=#{widget.datasets.author_id[0]}"><t t-raw="widget.datasets.author_id[1]"/></a></li>
- <li><span t-att-title="widget.datasets.date"><t t-raw="widget.datasets.timerelative"/></span></li>
- <li t-if="widget.datasets.attachment_ids.length > 0">
- <a class="oe_msg_view_attachments">
- <t t-if="widget.datasets.attachment_ids.length == 1">1 Attachment</t>
- <t t-if="widget.datasets.attachment_ids.length > 1"><t t-raw="widget.datasets.attachment_ids.length"/> Attachments</t>
- </a>
- </li>
- </ul>
- <div class="oe_clear"/>
<div class="oe_msg_body">
- <t t-if="widget.options.message.show_record_name and widget.datasets.record_name and (!widget.datasets.subject) and !widget.options.thread.thread_level and !widget.options.thread.display_on_thread[0] and widget.datasets.model!='res.partner'">
- <a class="oe_mail_action_model" t-attf-href="#model=#{widget.datasets.model}&amp;id=#{widget.res_id}"><t t-raw="widget.datasets.record_name"/></a>
+ <t t-if="widget.options.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread_level and !widget.options.display_on_thread[0] 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.datasets.body"/>
+ <t t-raw="widget.body"/>
</div>
- <t t-if="widget.datasets.attachment_ids.length > 0">
- <div class="oe_clear"></div>
+ <t t-if="widget.attachment_ids.length > 0">
<t t-call="mail.thread.message.attachments"/>
</t>
</div>
+ <div class="oe_msg_footer">
+ <a t-if="widget.author_id" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a>
+ <span class='oe_subtle'>•</span>
+ <span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span>
+ <a t-if="widget.attachment_ids.length > 0" class="oe_mail_msg_view_attachments">
+ <t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
+ <t t-if="widget.attachment_ids.length > 1"><t t-raw="widget.attachment_ids.length"/> Attachments</t>
+ </a>
+ <span class='oe_subtle'>•</span>
+ <t t-call="mail.thread.message.vote"/>
+ </div>
</div>
</div>
- <div class="oe_thread_placeholder"></div>
- </li>
+ </t>
<!-- expandable message layout -->
- <li t-name="mail.thread.expandable" class="oe_mail oe_mail_thread_msg oe_mail_unread">
- <div t-attf-class="oe_msg_#{widget.datasets.type} oe_semantic_html_override">
+ <t t-name="mail.thread.expandable">
+ <div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_unread">
<div class="oe_msg_content oe_msg_more_message">
- <a class="oe_mail_fetch_more">Load more messages <span t-if="widget.datasets.nb_messages>0">(<t t-raw="widget.datasets.nb_messages"/> messages not display)</span>...</a>
+ <div class='oe_separator'></div>
+ <a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
+ <a t-if="widget.nb_messages !== 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
</div>
</div>
- </li>
+ </t>
<!--
mail.compose_message.button_top_bar
@@ -254,7 +249,7 @@
-->
<t t-name="mail.compose_message.button_top_bar">
<div class="oe_topbar_compose_full_email">
- <button class="oe_button oe_highlight">Write an email</button>
+ <!-- <button class="oe_button oe_highlight">Write an email</button> -->
</div>
</t>
@@ -262,33 +257,18 @@
Template used to display Like/Unlike in a mail.message
-->
<span t-name="mail.thread.message.vote">
- <span class="oe_left oe_mail_vote_count">
- <t t-if='widget.datasets.has_voted'>
- You
- </t>
- <t t-if='(widget.datasets.vote_user_ids.length-(widget.datasets.has_voted?1:0)) > 0'>
- <t t-if='widget.datasets.has_voted'> and </t>
- <t t-esc="widget.datasets.vote_user_ids.length"/> people
- </t>
- <t t-if='widget.datasets.vote_user_ids.length > 0'>
- agree
- </t>
+
+ <span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
+ <t t-raw='widget.vote_nb' />
+ <span class='oe_e'>8</span>
</span>
- <button t-attf-class="oe_msg_vote oe_tag">
- <span>
- <t t-if="!widget.has_voted">Agree</t>
- <t t-if="widget.has_voted">Undo</t>
- </span>
- </button>
+ <a href='#' t-attf-class="oe_msg_vote">
+ <t t-if="!widget.has_voted">like</t>
+ <t t-if="widget.has_voted">unlike</t>
+ </a>
</span>
<!-- mail.thread.message.star
Template used to display stared/unstared message in a mail.message
-->
- <span t-name="mail.thread.message.star">
- <span class="oe_left">
- <button t-attf-class="oe_mail_starbox oe_tag #{widget.datasets.is_favorite?'oe_stared':''}">*</button>
- </span>
- </span>
-
</template>
diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml
index 87b49579100..3dec4ef1dd2 100644
--- a/addons/mail/static/src/xml/mail_followers.xml
+++ b/addons/mail/static/src/xml/mail_followers.xml
@@ -5,51 +5,45 @@
followers main template
Template used to display the followers, the actions and the subtypes in a record.
-->
- <div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
- <div class="oe_mail_recthread_actions">
- <div class="oe_mail_subtypes">
- <button type="button" class="oe_follower oe_notfollow">
- <span class="oe_follow">Follow</span>
- <span class="oe_unfollow">Unfollow</span>
- <span class="oe_following">Following</span>
- </button>
- </div>
- <div class="oe_recthread_subtypes">
- <ul class="oe_subtypes"></ul>
- </div>
+ <div t-name="mail.followers" class="oe_followers">
+ <div class="oe_actions">
+ <button type="button" class="oe_follower oe_notfollow">
+ <span class="oe_follow">Follow</span>
+ <span class="oe_unfollow">Unfollow</span>
+ <span class="oe_following">Following</span>
+ </button>
+ <div class="oe_subtype_list"></div>
</div>
- <t t-if="widget.options.comment">
- <h5 class="oe_grey"><t t-raw="widget.options.comment"/></h5>
+ <t t-if="widget.comment">
+ <h5 class="oe_comment"><t t-raw="widget.comment"/></h5>
</t>
- <div class="oe_mail_recthread_followers">
- <button type="button" class="oe_invite"><span>Invite</span></button>
- <t t-if="widget.options.title">
- <h4><t t-raw="widget.options.title"/></h4>
- </t>
- <ul class="oe_mail_followers_display"></ul>
+ <div class='oe_follower_title_box'>
+ <h4 class='oe_follower_title'>Followers</h4>
+ <a href='#' class="oe_invite">Invite others</a>
</div>
+ <div class="oe_follower_list"></div>
</div>
<!--
followers.partner template
Template used to display a partner following the record
-->
- <li t-name="mail.followers.partner">
+ <div t-name="mail.followers.partner" class='oe_partner'>
<img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
<a t-attf-href="#model=res.partner&amp;id=#{record.id}"><t t-raw="record.name"/></a>
- </li>\
+ </div>
<!--
followers.subtype template
Template used to display message subtypes of a follower subscription
-->
- <li t-name="mail.followers.subtype">
- <table width="50%">
+ <t t-name="mail.followers.subtype">
+ <table class='oe_subtype'>
<tr>
- <td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
+ <td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
</tr>
</table>
- </li>
+ </t>
</template>