diff options
| author | Randhir Mayatra (OpenERP) <rma@tinyerp.com> | 2012-11-19 16:38:24 +0530 |
|---|---|---|
| committer | Randhir Mayatra (OpenERP) <rma@tinyerp.com> | 2012-11-19 16:38:24 +0530 |
| commit | d23c5c8dca985dd0fde280c9237dee85b7bb4d7a (patch) | |
| tree | cf03bc2a74a1d9e93b5ef571fb78070c09c9857b /addons/mail/static/src | |
| parent | f4ab81afb8285a8157258232c5fb2a6745ae7f72 (diff) | |
| parent | 66afff1b52b7ab6ab8c340e18960fb6367cfb714 (diff) | |
[MERGE]merge with trunk
bzr revid: rma@tinyerp.com-20121119110824-rj4y32rfbszqln7g
Diffstat (limited to 'addons/mail/static/src')
25 files changed, 917 insertions, 678 deletions
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index af3cc3ed359..b0ce6d499b5 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -1,7 +1,36 @@ +/* ------------ TOPBAR MAIL BUTTON --------------- */ + +/* FIXME this css is not very pretty because it uses a + * 'button' element wich comes with a lot of inappropriate + * styling. Entypo is also a headache to center properly + * */ + +.openerp .oe_topbar_item.oe_topbar_compose_full_email{ + padding: 0px; + width: 32px; + height: 32px; +} +.openerp .oe_topbar_item.oe_topbar_compose_full_email button{ + position: relative; + top: -3px; /* centering entypo ... urgh */ + box-sizing: border-box; + border: none; + box-shadow: none; + color: white; + background: none; + text-shadow: 0px 1px 2px black; + width: 32px; + height: 32px; + padding: 0px; + margin: 0px + border-radius: 0px; +} /* ------------ MAIL WIDGET --------------- */ .openerp .oe_mail, .openerp .oe_mail *{ - box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } .openerp .oe_mail { display: block; @@ -42,9 +71,10 @@ } .openerp .oe_mail .oe_msg .oe_msg_footer{ padding-left: 4px; + padding-top: 3px; overflow: hidden; - opacity:0.8; - -webkit-transition: opacity 0.2s linear; + margin-bottom: 4px; + font-size: 11px; } .openerp .oe_mail .oe_msg .oe_msg_content{ display: block; @@ -57,6 +87,13 @@ margin-bottom: 0px; margin-top: 2px; } +.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p:first-of-type { + margin-top: 0px; +} + +.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p { + margin-bottom: 0px; +} /* a) Indented Messages */ @@ -80,18 +117,24 @@ .openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{ padding-top:2px; } +.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_footer{ + margin-bottom: 5px; +} + /* b) Votes (likes) */ + .openerp .oe_mail .oe_mail_vote_count{ display: inline; position: relative; - background: #7C7BAD; - color: white; + background: white; + box-shadow: 0px 0px 0px 1px rgba(124, 123, 173, 0.36) inset; + color: #7c7bad; text-shadow: none; border-radius: 3px; margin: 0px; padding-left: 3px; - padding-right: 18px; - margin-right: 3px; + padding-right: 15px; + margin-right: 5px; } .openerp .oe_mail .oe_mail_vote_count .oe_e{ position: absolute; @@ -102,12 +145,6 @@ /* c) Message action icons */ -.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{ - display:none; -} -.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{ - display:none; -} .openerp .oe_mail .oe_msg .oe_msg_icons{ float: right; margin-top: 4px; @@ -115,6 +152,9 @@ margin-left: 8px; height: 24px; -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .openerp .oe_mail .oe_msg .oe_msg_icons span{ float:right; @@ -128,10 +168,16 @@ 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; + -moz-transition: all 0.2s linear; + -o-transition: all 0.2s linear; + transition: all 0.2s linear; } .openerp .oe_mail .oe_msg:hover .oe_msg_icons a{ opacity: 1; -webkit-transition: all 0.1s linear; + -moz-transition: all 0.1s linear; + -o-transition: all 0.1s linear; + transition: all 0.1s linear; } .openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{ color: #FFF6C0; @@ -155,7 +201,7 @@ } .openerp .oe_mail .oe_msg .oe_msg_content textarea{ width: 100%; - height: 32px; + height: 64px; margin: 0px; padding: 0px; resize: vertical; @@ -171,43 +217,156 @@ width: 100%; } -/* ---------------- MESSAGE QUICK COMPOSER --------------- */ +/* --------------------- ATTACHMENTS --------------------- */ -.openerp .oe_mail .oe_msg_composer .oe_msg_footer{ - padding-right:4px; - padding-top: 2px; - padding-bottom:6px; +.openerp .oe_mail .oe_msg_attachment_list{ + display: none; + margin-top: 12px; + margin-bottom: 12px; } -.openerp .oe_mail .oe_msg_attachments.oe_hidden, -.openerp .oe_mail .oe_msg_images.oe_hidden{ - margin:0px; - border: none; +.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{ + display: block; +} +.openerp .oe_mail .oe_attachment{ + display: inline-block; + width: 100px; + margin: 2px; + min-height: 80px; + position: relative; + border-radius: 3px; + text-align: center; + vertical-align: top; +} +.openerp .oe_mail .oe_attachment .oe_name{ + display: inline-block; + max-width: 100%; + padding: 1px 3px; + margin-top: 50px; + margin-bottom: 5px; + background: rgba(124, 123, 173, 0.13); + overflow: hidden; + color: #4c4c4c; + text-shadow: none; + border-radius: 3px; +} + +.openerp .oe_mail .oe_attachment.oe_preview{ + background: url( data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAJ0lEQVQYV2MsLS39z4AGLCws0IUYGIeCwrVr12J45sSJE5ieGQIKAbuZKf/EMCs7AAAAAElFTkSuQmCC ); +} +.openerp .oe_mail .oe_attachment .oe_progress_bar{ display: none; + position: absolute; + top: 18px; + left: 16px; + right: 16px; + height: 17px; + line-height: 13px; + padding: 0px; + background: #4BBD00; + color: white; + text-align: center; + border-radius: 3px; + border: solid 1px rgba(0,0,0,0.2); + box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.34); + -webkit-animation: oe_mail_attach_loading_anim 0.75s infinite linear; + -moz-animation: oe_mail_attach_loading_anim 0.75s infinite linear; + -o-animation: oe_mail_attach_loading_anim 0.75s infinite linear; + animation: oe_mail_attach_loading_anim 0.75s infinite linear; +} +.openerp .oe_mail .oe_attachment.oe_uploading .oe_progress_bar{ + display: block; } -.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); +@-webkit-keyframes oe_mail_attach_loading_anim{ + 0% { background: #4BBD00 } + 50% { background: #009123 } + 100% { background: #4BBD00 } } -.openerp .oe_mail .oe_msg_attachments .oe_attachment{ - padding: 2px; - padding-left: 4px; - padding-right: 4px; +@-moz-keyframes oe_mail_attach_loading_anim{ + 0% { background: #4BBD00 } + 50% { background: #009123 } + 100% { background: #4BBD00 } } -.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{ - font-size: 23px; - margin-top: -5px; +@-o-keyframes oe_mail_attach_loading_anim{ + 0% { background: #4BBD00 } + 50% { background: #009123 } + 100% { background: #4BBD00 } } -.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{ - text-decoration: none; +@keyframes oe_mail_attach_loading_anim{ + 0% { background: #4BBD00 } + 50% { background: #009123 } + 100% { background: #4BBD00 } } -.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){ - background:white; +.openerp .oe_mail .oe_attachment.oe_preview .oe_name{ + position: absolute; + bottom: 0px; + margin: 0px; + left: 0px; + right: 0px; + max-height: 64px; + background: rgba(0,0,0,0.8); + color: white; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} +.openerp .oe_mail .oe_attachment.oe_preview:hover .oe_name{ + opacity: 1; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; } -.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){ - background: #F4F5FA; +.openerp .oe_mail .oe_attachment img{ + position: absolute; + width: 48px; + height: 48px; + top: 0px; + left: 50%; + margin-left: -24px; +} +.openerp .oe_mail .oe_attachment.oe_preview img{ + display: block; + position: relative; + margin:0px; + width: 100px; + height: 100px; + border-radius: 3px; + margin-left: -50px; +} +.openerp .oe_mail .oe_attachment .oe_delete{ + display: none; +} +.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_delete{ + display: block; + position: absolute; + top: -7px; + right: 0px; + color: black; + text-shadow: 1px 0px white, -1px 0px white, 0px 1px white, 0px -1px white; + cursor: pointer; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} +.openerp .oe_mail .oe_msg_composer .oe_attachment:hover .oe_delete{ + opacity: 1; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} +/* ---------------- MESSAGE QUICK COMPOSER --------------- */ + +.openerp .oe_mail .oe_msg_composer .oe_msg_footer{ + padding-right:4px; + padding-top: 2px; + padding-bottom:6px; } .openerp .oe_mail .oe_msg_images { display: block; @@ -222,11 +381,13 @@ .openerp .oe_mail .oe_msg_footer button.oe_attach{ width: 24px; overflow: hidden; + filter:none; } .openerp .oe_mail .oe_msg_footer button.oe_attach .oe_e{ position: relative; top: -1px; left: -9px; + filter:none; } .openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{ display:inline; @@ -235,11 +396,13 @@ width:24px; overflow:hidden; float: right; + filter:none; } .openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{ position: relative; top: -1px; left: -9px; + filter:none; } .openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{ background: transparent; @@ -247,6 +410,7 @@ box-shadow: none; border: none; text-shadow: none; + filter:none; } .openerp .oe_mail .oe_attach_label{ color: #7C7BAD; @@ -258,6 +422,7 @@ height: 28px; width: 52px; margin-top: -6px; + filter:none; } .openerp .oe_mail .oe_mail_list_recipients{ font-size: 12px; @@ -269,6 +434,7 @@ .openerp .oe_mail .oe_msg_content.oe_msg_more_message{ text-align: right; + cursor: pointer; } .openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{ height: 0; @@ -280,7 +446,7 @@ } .openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more { background: white; - margin-right: 280px; + margin-right: 210px; padding-left: 8px; padding-right: 8px; text-decoration: none; @@ -298,6 +464,7 @@ padding-top: 5px; width: 160px; float: right; + margin-right: 16px; } /* a) THE FOLLOW BUTTON */ @@ -308,9 +475,23 @@ width:100%; } .openerp .oe_followers button.oe_follower.oe_following{ + color: white; background-color: #3465A4; background-image: -webkit-linear-gradient(top, #729FCF, #3465A4); + background-image: -moz-linear-gradient(top, #729FCF, #3465A4); + background-image: -ms-linear-gradient(top, #729FCF, #3465A4); + background-image: -o-linear-gradient(top, #729FCF, #3465A4); + background-image: linear-gradient(to bottom, #729FCF, #3465A4); +} +.openerp .oe_followers button.oe_follower.oe_following:hover{ color: white; + filter:none; + background-color: #A21A1A; + background-image: -webkit-linear-gradient(top, #DF3F3F, #A21A1A); + background-image: -moz-linear-gradient(top, #DF3F3F, #A21A1A); + background-image: -ms-linear-gradient(top, #DF3F3F, #A21A1A); + background-image: -o-linear-gradient(top, #DF3F3F, #A21A1A); + background-image: linear-gradient(to bottom, #DF3F3F, #A21A1A); } .openerp .oe_followers button.oe_follower .oe_follow, @@ -371,12 +552,17 @@ .openerp .oe_record_thread{ display: block; - margin-right: 180px; + margin-left: 16px; + margin-right: 212px; } /* ----------- INBOX INTEGRATION ----------- */ .openerp .oe_mail_wall .oe_mail{ margin: 16px; - width: 720px; + width: 600px; +} + +.openerp .oe_mail .oe_view_nocontent > p { + padding-left: 15px; } diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css index b700cb8c80f..c1cefee9070 100644 --- a/addons/mail/static/src/css/mail_group.css +++ b/addons/mail/static/src/css/mail_group.css @@ -44,20 +44,23 @@ text-align: center; overflow: hidden; -moz-border-radius: 3px; + border-collapse: separate; -webkit-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px; + border-collapse: separate; -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + 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); } .oe_group_photo { width: 100px; height: 100px; clip: rect(0px, 100px, 100px, 0px); + border:none; } .oe_group_details { @@ -68,30 +71,31 @@ min-height: 120px; } -.oe_group_details a, .oe_group_details a:hover { - font-weight: bold; - color: #4c4c4c; -} - .oe_group_details h4 { margin: 0; font-size: 13px; } -.oe_group_details h4 a { - color: #4c4c4c; -} - -.oe_group_details h4 a:hover { - text-decoration: underline; -} - .oe_group_details ul { margin: 3px 0 5px; padding: 0; list-style: none; } -.oe_group_details li { +.openerp .oe_group_details li { margin: 2px 0; } + +.openerp .oe_group_button { + padding-top: 7px; +} + +.openerp .oe_group_button .oe_group_join { + color: white; + background-color: #3465A4; + background-image: -webkit-linear-gradient(top, #729FCF, #3465A4); + background-image: -moz-linear-gradient(top, #729FCF, #3465A4); + background-image: -ms-linear-gradient(top, #729FCF, #3465A4); + background-image: -o-linear-gradient(top, #729FCF, #3465A4); + background-image: linear-gradient(to bottom, #729FCF, #3465A4); +} diff --git a/addons/mail/static/src/img/mimetypes/addresses.png b/addons/mail/static/src/img/mimetypes/addresses.png Binary files differnew file mode 100644 index 00000000000..586f1257196 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/addresses.png diff --git a/addons/mail/static/src/img/mimetypes/archive.png b/addons/mail/static/src/img/mimetypes/archive.png Binary files differnew file mode 100644 index 00000000000..704d962d426 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/archive.png diff --git a/addons/mail/static/src/img/mimetypes/audio.png b/addons/mail/static/src/img/mimetypes/audio.png Binary files differnew file mode 100644 index 00000000000..6dd4c635fe7 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/audio.png diff --git a/addons/mail/static/src/img/mimetypes/binary.png b/addons/mail/static/src/img/mimetypes/binary.png Binary files differnew file mode 100644 index 00000000000..5708c9fc2a5 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/binary.png diff --git a/addons/mail/static/src/img/mimetypes/calendar.png b/addons/mail/static/src/img/mimetypes/calendar.png Binary files differnew file mode 100644 index 00000000000..81791af05e0 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/calendar.png diff --git a/addons/mail/static/src/img/mimetypes/certificate.png b/addons/mail/static/src/img/mimetypes/certificate.png Binary files differnew file mode 100644 index 00000000000..e48cb1de432 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/certificate.png diff --git a/addons/mail/static/src/img/mimetypes/disk.png b/addons/mail/static/src/img/mimetypes/disk.png Binary files differnew file mode 100644 index 00000000000..9c241cdf896 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/disk.png diff --git a/addons/mail/static/src/img/mimetypes/document.png b/addons/mail/static/src/img/mimetypes/document.png Binary files differnew file mode 100644 index 00000000000..fee2f1619b6 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/document.png diff --git a/addons/mail/static/src/img/mimetypes/font.png b/addons/mail/static/src/img/mimetypes/font.png Binary files differnew file mode 100644 index 00000000000..3b04787b9f9 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/font.png diff --git a/addons/mail/static/src/img/mimetypes/html.png b/addons/mail/static/src/img/mimetypes/html.png Binary files differnew file mode 100644 index 00000000000..440c5bc9972 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/html.png diff --git a/addons/mail/static/src/img/mimetypes/image.png b/addons/mail/static/src/img/mimetypes/image.png Binary files differnew file mode 100644 index 00000000000..ab2f84a1a54 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/image.png diff --git a/addons/mail/static/src/img/mimetypes/presentation.png b/addons/mail/static/src/img/mimetypes/presentation.png Binary files differnew file mode 100644 index 00000000000..8642a8e9298 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/presentation.png diff --git a/addons/mail/static/src/img/mimetypes/print.png b/addons/mail/static/src/img/mimetypes/print.png Binary files differnew file mode 100644 index 00000000000..fee2f1619b6 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/print.png diff --git a/addons/mail/static/src/img/mimetypes/script.png b/addons/mail/static/src/img/mimetypes/script.png Binary files differnew file mode 100644 index 00000000000..10c58c0bcc3 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/script.png diff --git a/addons/mail/static/src/img/mimetypes/spreadsheet.png b/addons/mail/static/src/img/mimetypes/spreadsheet.png Binary files differnew file mode 100644 index 00000000000..77a834697f1 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/spreadsheet.png diff --git a/addons/mail/static/src/img/mimetypes/text.png b/addons/mail/static/src/img/mimetypes/text.png Binary files differnew file mode 100644 index 00000000000..e65d2ec5a67 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/text.png diff --git a/addons/mail/static/src/img/mimetypes/unknown.png b/addons/mail/static/src/img/mimetypes/unknown.png Binary files differnew file mode 100644 index 00000000000..e65d2ec5a67 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/unknown.png diff --git a/addons/mail/static/src/img/mimetypes/vector.png b/addons/mail/static/src/img/mimetypes/vector.png Binary files differnew file mode 100644 index 00000000000..c4595ec1b24 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/vector.png diff --git a/addons/mail/static/src/img/mimetypes/video.png b/addons/mail/static/src/img/mimetypes/video.png Binary files differnew file mode 100644 index 00000000000..e3a87b5bde6 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/video.png diff --git a/addons/mail/static/src/img/mimetypes/webimage.png b/addons/mail/static/src/img/mimetypes/webimage.png Binary files differnew file mode 100644 index 00000000000..ab2f84a1a54 --- /dev/null +++ b/addons/mail/static/src/img/mimetypes/webimage.png diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 7acebe9b1df..dc265a343d5 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -24,9 +24,10 @@ openerp.mail = function (session) { */ var context_keys = ['default_template_id', 'default_composition_mode', 'default_use_template', 'default_partner_ids', 'default_model', - 'default_res_id', 'default_content_subtype', , 'default_subject', + 'default_res_id', 'default_content_subtype', 'default_subject', 'default_body', 'active_id', 'lang', 'bin_raw', 'tz', - 'active_model', 'edi_web_url_view', 'active_ids'] + 'active_model', 'edi_web_url_view', 'active_ids', + 'default_attachment_ids'] for (var key in action.context) { if (_.indexOf(context_keys, key) == -1) { action.context[key] = null; @@ -54,13 +55,15 @@ openerp.mail = function (session) { mail.ChatterUtils = { /* 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_image: function (session, model, field, id, resize) { + var r = resize ? encodeURIComponent(resize) : ''; + id = id || ''; + return session.url('/web/binary/image', {model: model, field: field, id: id, resize: r}); }, /* 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']; + return session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: attachment['id']}); }, /** @@ -111,12 +114,267 @@ openerp.mail = function (session) { } return domain; - } + }, + + // inserts zero width space between each letter of a string so that + // the word will correctly wrap in html boxes smaller than the text + breakword: function(str){ + var out = ''; + if (!str) { + return str; + } + for(var i = 0, len = str.length; i < len; i++){ + out += _.str.escapeHTML(str[i]) + '​'; + } + return out; + }, + + // returns the file type of a file based on its extension + // As it only looks at the extension it is quite approximative. + filetype: function(url){ + url = url.filename || url; + var tokens = url.split('.'); + if(tokens.length <= 1){ + return 'unknown'; + } + var extension = tokens[tokens.length -1]; + if(extension.length === 0){ + return 'unknown'; + }else{ + extension = extension.toLowerCase(); + } + var filetypes = { + 'webimage': ['png','jpg','jpeg','jpe','gif'], // those have browser preview + 'image': ['tif','tiff','tga', + 'bmp','xcf','psd','ppm','pbm','pgm','pnm','mng', + 'xbm','ico','icon','exr','webp','psp','pgf','xcf', + 'jp2','jpx','dng','djvu','dds'], + 'vector': ['ai','svg','eps','vml','cdr','xar','cgm','odg','sxd'], + 'print': ['dvi','pdf','ps'], + 'document': ['doc','docx','odm','odt'], + 'presentation': ['key','keynote','odp','pps','ppt'], + 'font': ['otf','ttf','woff','eot'], + 'archive': ['zip','7z','ace','apk','bzip2','cab','deb','dmg','gzip','jar', + 'rar','tar','gz','pak','pk3','pk4','lzip','lz','rpm'], + 'certificate': ['cer','key','pfx','p12','pem','crl','der','crt','csr'], + 'audio': ['aiff','wav','mp3','ogg','flac','wma','mp2','aac', + 'm4a','ra','mid','midi'], + 'video': ['asf','avi','flv','mkv','m4v','mpeg','mpg','mpe','wmv','mp4','ogm'], + 'text': ['txt','rtf','ass'], + 'html': ['html','xhtml','xml','htm','css'], + 'disk': ['iso','nrg','img','ccd','sub','cdi','cue','mds','mdx'], + 'script': ['py','js','c','cc','cpp','cs','h','java','bat','sh', + 'd','rb','pl','as','cmd','coffee','m','r','vbs','lisp'], + 'spreadsheet': ['123','csv','ods','numbers','sxc','xls','vc','xlsx'], + 'binary': ['exe','com','bin','app'], + }; + for(filetype in filetypes){ + var ext_list = filetypes[filetype]; + for(var i = 0, len = ext_list.length; i < len; i++){ + if(extension === ext_list[i]){ + return filetype; + } + } + } + return 'unknown'; + }, + }; /** * ------------------------------------------------------------ + * MessageCommon + * ------------------------------------------------------------ + * + * Common base for expandables, chatter messages and composer. It manages + * the various variables common to those models. + */ + + mail.MessageCommon = session.web.Widget.extend({ + + /** + * ------------------------------------------------------------ + * FIXME: this comment was moved as is from the ThreadMessage Init as + * part of a refactoring. Check that it is still correct + * ------------------------------------------------------------ + * This widget handles the display of a messages in a thread. + * Displays a record and performs some formatting on the record : + * - record.date: formatting according to the user timezone + * - record.timerelative: relative time givein by timeago lib + * - record.avatar: image url + * - record.attachment_ids[].url: url of each attachmentThe + * thread view : + * - root thread + * - - sub message (parent_id = root message) + * - - - sub thread + * - - - - sub sub message (parent id = sub thread) + * - - sub message (parent_id = root message) + * - - - sub thread + */ + + init: function (parent, datasets, options) { + this._super(parent, options); + + // record options + this.options = datasets.options || options || {}; + // record domain and context + this.domain = datasets.domain || options.domain || []; + this.context = _.extend({ + default_model: false, + default_res_id: 0, + default_parent_id: false }, options.context || {}); + + // data of this message + this.id = datasets.id || false, + this.last_id = this.id, + this.model = datasets.model || this.context.default_model || false, + this.res_id = datasets.res_id || this.context.default_res_id || false, + this.parent_id = datasets.parent_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 || false, + this.attachment_ids = datasets.attachment_ids || [], + this.partner_ids = datasets.partner_ids || []; + this._date = datasets.date; + + this.format_data(); + + // record options and data + this.show_record_name = this.record_name && !this.thread_level && this.model != 'res.partner'; + this.options.show_read = false; + this.options.show_unread = false; + if (this.options.show_read_unread_button) { + if (this.options.read_action == 'read') this.options.show_read = true; + else if (this.options.read_action == 'unread') this.options.show_unread = true; + else { + this.options.show_read = this.to_read; + this.options.show_unread = !this.to_read; + this.options.rerender = true; + this.options.toggle_read = true; + } + } + this.parent_thread = parent.messages != undefined ? parent : this.options.root_thread; + this.thread = false; + }, + + /* Convert date, timerelative and avatar in displayable data. */ + format_data: function () { + + //formating and add some fields for render + if (this._date) { + this.date = session.web.format_value(this._date, {type:"datetime"}); + this.timerelative = $.timeago(this.date); + } + if (this.type == 'email' && (!this.author_id || !this.author_id[0])) { + this.avatar = ('/mail/static/src/img/email_icon.png'); + } else if (this.author_id && this.template != 'mail.compose_message') { + this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]); + } else { + this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid); + } + + }, + + + /* upload the file on the server, add in the attachments list and reload display + */ + display_attachments: function () { + for (var l in this.attachment_ids) { + var attach = this.attachment_ids[l]; + if (!attach.formating) { + attach.url = mail.ChatterUtils.get_attachment_url(this.session, attach); + attach.filetype = mail.ChatterUtils.filetype(attach.filename); + attach.name = mail.ChatterUtils.breakword(attach.name || attach.filename); + attach.formating = true; + } + } + this.$(".oe_msg_attachment_list").html( session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) ); + }, + + /* return the link to resized image + */ + attachments_resize_image: function (id, resize) { + return mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', id, resize); + }, + + /* get all child message id linked. + * @return array of id + */ + get_child_ids: function () { + return _.map(this.get_childs(), function (val) { return val.id; }); + }, + + /* get all child message linked. + * @return array of message object + */ + get_childs: function (nb_thread_level) { + var res=[]; + if (arguments[1] && this.id) res.push(this); + if ((isNaN(nb_thread_level) || nb_thread_level>0) && this.thread) { + _(this.thread.messages).each(function (val, key) { + res = res.concat( val.get_childs((isNaN(nb_thread_level) ? undefined : nb_thread_level-1), true) ); + }); + } + return res; + }, + + /** + * 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) { + // goto the wall thread for launch browse + if (!options._go_thread_wall) { + options._go_thread_wall = true; + for (var i in this.options.root_thread.messages) { + var res=this.options.root_thread.messages[i].browse_message(options); + if (res) return res; + } + } + + if (this.id==options.id) + return this; + + 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; + } + } + + return false; + }, + + /** + * call on_message_delete on his parent thread + */ + destroy: function () { + + this._super(); + this.parent_thread.on_message_detroy(this); + + } + + }); + + /** + * ------------------------------------------------------------ * ComposeMessage widget * ------------------------------------------------------------ * @@ -126,7 +384,7 @@ openerp.mail = function (session) { * When the user focuses the textarea, the compose message is instantiated. */ - mail.ThreadComposeMessage = session.web.Widget.extend({ + mail.ThreadComposeMessage = mail.MessageCommon.extend({ template: 'mail.compose_message', /** @@ -138,45 +396,22 @@ openerp.mail = function (session) { */ init: function (parent, datasets, options) { - var self = this; - this._super(parent); - this.context = options.context || {}; - this.options = options.options; - + this._super(parent, datasets, options); this.show_compact_message = false; + this.show_delete_attachment = true; + }, - // 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; + start: function () { + this._super.apply(this, arguments); 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); - }, + $(window).on(this.fileupload_id, this.on_attachment_loaded); - start: function () { this.display_attachments(); this.bind_events(); }, - /* upload the file on the server, add in the attachments list and reload display - */ - display_attachments: function () { - this.$(".oe_msg_attachment_list").html( - session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) ); - // event: delete an attachment - 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) { @@ -263,19 +498,24 @@ openerp.mail = function (session) { 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', _.bind( this.on_attachment_change, this) ); + this.$('input.oe_form_binary_file').on('change', _.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') ); + this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) ); + this.$('.oe_post').on('click', _.bind( this.on_message_post, this) ); + this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') ); /* 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(); + this.$('.oe_msg_footer').on('mousedown', _.bind( function () { this.stay_open = true; }, this)); + var ev_stay = {}; + ev_stay.mouseup = ev_stay.keydown = ev_stay.focus = function () { self.stay_open = false; }; + this.$('textarea:not(.oe_compact)').on(ev_stay); + this.$('textarea:not(.oe_compact)').autosize(); // auto close - this.$el.on('blur', 'textarea:not(.oe_compact):first', _.bind( this.on_compose_expandable, this)); + this.$('textarea:not(.oe_compact)').on('blur', _.bind( this.on_compose_expandable, this)); + + // event: delete child attachments off the oe_msg_attachment_list box + this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete); }, on_compose_fullmail: function (default_composition_mode) { @@ -290,7 +530,6 @@ openerp.mail = function (session) { 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() || '') : ''), @@ -350,7 +589,7 @@ openerp.mail = function (session) { if (body.match(/\S+/)) { //session.web.blockUI(); - this.parent_thread.ds_thread.call('message_post_api', [ + this.parent_thread.ds_thread.call('message_post_user_api', [ this.context.default_res_id, mail.ChatterUtils.get_text2html(body), false, @@ -359,11 +598,15 @@ openerp.mail = function (session) { this.parent_thread.context ]).done(function (record) { var thread = self.parent_thread; + if (self.options.display_indented_thread < self.thread_level && thread.parent_message) { + var thread = thread.parent_message.parent_thread; + } + var root = thread == self.options.root_thread; // create object and attach to the thread object - thread.message_fetch(false, false, [record], function (arg, data) { + thread.message_fetch([['id', 'child_of', [self.id]]], false, [record], function (arg, data) { var message = thread.create_message_object( data[0] ); // insert the message on dom - thread.insert_message( message, self.$el ); + thread.insert_message( message, root ? undefined : self.$el, root ); if (thread.parent_message) { self.$el.remove(); self.parent_thread.compose_message = null; @@ -419,30 +662,16 @@ openerp.mail = function (session) { * - - visible message * - - expandable */ - mail.ThreadExpandable = session.web.Widget.extend({ + mail.ThreadExpandable = mail.MessageCommon.extend({ template: 'mail.thread.expandable', - init: function (parent, datasets, context) { - this._super(parent); - this.domain = datasets.domain || []; - this.options = datasets.options; - this.context = _.extend({ - default_model: 'mail.thread', - default_res_id: 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]; + init: function (parent, datasets, options) { + this._super(parent, datasets, options); + this.type = 'expandable'; + this.max_limit = datasets.max_limit; + this.nb_messages = datasets.nb_messages; + this.flag_used = false; }, - start: function () { this._super.apply(this, arguments); @@ -460,7 +689,7 @@ openerp.mail = function (session) { * Bind events in the widget. Each event is slightly described * in the function. */ bind_events: function () { - this.$el.on('click', 'a.oe_msg_fetch_more', this.on_expandable); + this.$('.oe_msg_more_message').on('click', this.on_expandable); }, animated_destroy: function (fadeTime) { @@ -480,154 +709,35 @@ openerp.mail = function (session) { } this.flag_used = true; - this.animated_destroy(200); - this.parent_thread.message_fetch(this.domain, this.context); - return false; - }, + var self = this; - /** - * call on_message_delete on his parent thread - */ - destroy: function () { + // read messages + self.parent_thread.message_fetch(this.domain, this.context, false, function (arg, data) { + // insert the message on dom after this message + self.id = false; + self.parent_thread.switch_new_message( data, self.$el ); + self.animated_destroy(200); + }); - this._super(); - this.parent_thread.on_message_detroy(this); + return false; + }, - } }); - /** - * ------------------------------------------------------------ - * Thread Message Widget - * ------------------------------------------------------------ - * This widget handles the display of a messages in a thread. - * Displays a record and performs some formatting on the record : - * - record.date: formatting according to the user timezone - * - record.timerelative: relative time givein by timeago lib - * - record.avatar: image url - * - record.attachment_ids[].url: url of each attachmentThe - * thread view : - * - root thread - * - - sub message (parent_id = root message) - * - - - sub thread - * - - - - sub sub message (parent id = sub thread) - * - - sub message (parent_id = root message) - * - - - sub thread - */ - mail.ThreadMessage = session.web.Widget.extend({ + mail.ThreadMessage = mail.MessageCommon.extend({ template: 'mail.thread.message', - - /** - * @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 ComposeMessage widget for more information about it. - * @param {Object} [options] - * @param {Object} [thread] read obout mail.Thread object - * @param {Object} [message] - * @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 {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, datasets, context) { - this._super(parent); - - // record domain and context - this.domain = datasets.domain || []; - this.context = _.extend({ - default_model: 'mail.thread', - default_res_id: 0, - default_parent_id: false }, context || {}); - - // record options - 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 : this.options._parents[0]; - this.thread = false; - - if ( this.id > 0 ) { - this.formating_data(); - } - - this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification'); - this.ds_message = new session.web.DataSetSearch(this, 'mail.message'); - this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers'); - }, - - /* Convert date, timerelative and avatar in displayable data. */ - formating_data: function () { - - //formating and add some fields for render - 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.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]); - } - 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 () { this._super.apply(this, arguments); this.expender(); - this.$el.hide().fadeIn(750, function () {$(this).css('display', '');}); - this.resize_img(); this.bind_events(); 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); + this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification'); + this.ds_message = new session.web.DataSetSearch(this, 'mail.message'); }, /** @@ -635,30 +745,15 @@ openerp.mail = function (session) { * in the function. */ bind_events: function () { var self = this; + // header icons bindings + this.$('.oe_read').on('click', this.on_message_read); + this.$('.oe_unread').on('click', this.on_message_unread); + this.$('.oe_msg_delete').on('click', this.on_message_delete); + this.$('.oe_reply').on('click', this.on_message_reply); + this.$('.oe_star').on('click', this.on_star); + this.$('.oe_msg_vote').on('click', this.on_vote); + this.$('.oe_view_attachments').on('click', this.on_view_attachments); - // event: click on 'Attachment(s)' in msg - 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', '.oe_read', this.on_message_read_unread); - // event: click on icone 'UnRead' in header - this.$el.on('click', '.oe_unread', this.on_message_read_unread); - // event: click on 'Delete' in msg side menu - this.$el.on('click', '.oe_msg_delete', this.on_message_delete); - - // event: click on 'Reply' in msg - this.$el.on('click', '.oe_reply', this.on_message_reply); - // event: click on 'Vote' button - 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); }, /* Call the on_compose_message on the thread of this message. */ @@ -673,7 +768,7 @@ openerp.mail = function (session) { this.$('.oe_msg_body:first').expander({ slicePoint: this.options.truncate_limit, expandText: 'read more', - userCollapseText: '[^]', + userCollapseText: 'read less', detailClass: 'oe_msg_tail', moreClass: 'oe_mail_expand', lessClass: 'oe_mail_reduce', @@ -717,6 +812,17 @@ openerp.mail = function (session) { } }, + /* Call the on_compose_message on the thread of this message. */ + on_view_attachments:function (event) { + event.stopPropagation(); + var self = this; + if (!this.toggle_attachment) { + self.display_attachments(); + this.toggle_attachment = true; + } + this.$('.oe_msg_attachment_list').toggle(200); + }, + /** * Wait a confirmation for delete the message on the DB. * Make an animate destroy @@ -732,68 +838,76 @@ openerp.mail = function (session) { return false; }, - /*The selected thread and all childs (messages/thread) became read - * @param {object} mouse envent + /* Check if the message must be destroy and detroy it or check for re render widget + * @param {callback} apply function */ - on_message_read_unread: function (event) { - event.stopPropagation(); - var self=this; + check_for_rerender: function () { + var self = this; - if ( (this.to_read && this.options.typeof_thread == 'inbox') || - (!this.to_read && this.options.typeof_thread == 'archives')) { - this.animated_destroy(150); - } + var messages = [this].concat(this.get_childs()); + var message_ids = _.map(messages, function (msg) { return msg.id;}); + var domain = mail.ChatterUtils.expand_domain( this.options.root_thread.domain ) + .concat([["id", "in", message_ids ]]); + + return this.parent_thread.ds_message.call('message_read', [undefined, domain, [], !!this.parent_thread.options.display_indented_thread, this.context, this.parent_thread.id]) + .then( function (records) { + // remove message not loaded + _.map(messages, function (msg) { + if(!_.find(records, function (record) { return record.id == msg.id; })) { + msg.animated_destroy(150); + } else { + msg.renderElement(); + msg.start() + } + }); - // 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]).then(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; + }); }, - /** - * 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) { - // goto the wall thread for launch browse - if (!options._go_thread_wall) { - options._go_thread_wall = true; - 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.id==options.id) - return this; - - 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; - } - } + on_message_read: function (event) { + event.stopPropagation(); + this.on_message_read_unread(true); + return false; + }, + on_message_unread: function (event) { + event.stopPropagation(); + this.on_message_read_unread(false); return false; }, - /* get all child message id linked. - * @return array of id + /*The selected thread and all childs (messages/thread) became read + * @param {object} mouse envent */ - get_child_ids: function () { - var res=[] - if (arguments[0]) res.push(this.id); - if (this.thread) { - res = res.concat( this.thread.get_child_ids(true) ); + on_message_read_unread: function (read_value) { + var self = this; + var messages = [this].concat(this.get_childs()); + + // inside the inbox, when the user mark a message as read/done, don't apply this value + // for the stared/favorite message + if (this.options.view_inbox && read_value) { + var messages = _.filter(messages, function (val) { return !val.is_favorite && val.id; }); + if (!messages.length) { + this.check_for_rerender(); + return false; + } } - return res; + var message_ids = _.map(messages, function (val) { return val.id; }); + + this.ds_notification.call('set_message_read', [message_ids, read_value, this.context]) + .then(function () { + // apply modification + _.each(messages, function (msg) { + msg.to_read = !read_value; + if (msg.options.toggle_read) { + msg.options.show_read = msg.to_read; + msg.options.show_unread = !msg.to_read; + } + }); + // check if the message must be display, destroy or rerender + self.check_for_rerender(); + }); + return false; }, /** @@ -801,12 +915,13 @@ openerp.mail = function (session) { */ on_vote: function (event) { event.stopPropagation(); - var self=this; - return this.ds_message.call('vote_toggle', [[self.id]]).then(function (vote) { - self.has_voted = vote; - self.vote_nb += self.has_voted ? 1 : -1; - self.display_vote(); - }); + this.ds_message.call('vote_toggle', [[this.id]]) + .then( + _.bind(function (vote) { + this.has_voted = vote; + this.vote_nb += this.has_voted ? 1 : -1; + this.display_vote(); + }, this)); return false; }, @@ -814,10 +929,10 @@ openerp.mail = function (session) { * 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.$(".oe_msg_vote:first").remove(); - self.$(".oe_mail_vote_count:first").replaceWith(vote_element); + var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': this}); + this.$(".oe_msg_footer:first .oe_mail_vote_count").remove(); + this.$(".oe_msg_footer:first .oe_msg_vote").replaceWith(vote_element); + this.$('.oe_msg_vote').on('click', this.on_vote); }, /** @@ -827,34 +942,29 @@ openerp.mail = function (session) { event.stopPropagation(); var self=this; var button = self.$('.oe_star:first'); - return this.ds_message.call('favorite_toggle', [[self.id]]).then(function (star) { - self.is_favorite=star; - if (self.is_favorite) { - button.addClass('oe_starred'); - } else { - button.removeClass('oe_starred'); - if ( self.options.typeof_thread == 'stared' ) { - self.animated_destroy(150); + + this.ds_message.call('favorite_toggle', [[self.id]]) + .then(function (star) { + self.is_favorite=star; + if (self.is_favorite) { + button.addClass('oe_starred'); + } else { + button.removeClass('oe_starred'); } - } - }); + + if (self.options.view_inbox && self.is_favorite) { + self.on_message_read_unread(true); + } else { + self.check_for_rerender(); + } + }); return false; }, - /** - * call on_message_delete on his parent thread - */ - destroy: function () { - - this._super(); - this.parent_thread.on_message_detroy(this); - - } - }); /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * Thread Widget * ------------------------------------------------------------ * @@ -881,14 +991,11 @@ openerp.mail = function (session) { * @param {Object} [thread] * @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, datasets, options) { - this._super(parent); + this._super(parent, options); this.domain = options.domain || []; this.context = _.extend({ default_model: 'mail.thread', @@ -896,21 +1003,24 @@ openerp.mail = function (session) { default_parent_id: false }, options.context || {}); this.options = options.options; - this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] ); - + this.options.root_thread = (options.options.root_thread != undefined ? options.options.root_thread : this); + this.options.show_compose_message = this.options.show_compose_message && (this.options.display_indented_thread >= this.thread_level || !this.thread_level); + // record options and data this.parent_message= parent.thread!= undefined ? parent : false ; // data of this thread this.id = datasets.id || false, - this.model = datasets.model || false, + this.last_id = datasets.last_id || 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.show_compose_message = this.options.show_compose_message && (this.options.show_reply_button > this.thread_level || !this.thread_level); + + this.options.flat_mode = !!(this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0); // object compose message this.compose_message = false; @@ -934,13 +1044,10 @@ openerp.mail = function (session) { 'context': this.context, 'options': this.options, }); - if (!this.thread_level) { - // root view + if (!this.thread_level || this.thread_level > this.options.display_indented_thread) { 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.compose_message.prependTo(this.$el); } } }, @@ -948,21 +1055,20 @@ openerp.mail = function (session) { /* 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(); - 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(); + on_scroll: function () { + var expandables = + _.each( _.filter(this.messages, function (val) {return val.max_limit && !val.parent_id;}), function (val) { + var pos = val.$el.position(); if (pos.top) { /* bottom of the screen */ var bottom = $(window).scrollTop()+$(window).height()+200; if (bottom > pos.top) { - message.on_expandable(); + val.on_expandable(); + // load only one time + val.loading = true; } } - } + }); }, /** @@ -970,8 +1076,8 @@ openerp.mail = function (session) { * in the function. */ bind_events: function () { var self = this; - 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); + self.$('.oe_mail_list_recipients .oe_more').on('click', self.on_show_recipients); + self.$('.oe_mail_compose_textarea .oe_more_hidden').on('click', self.on_hide_recipients); }, /** @@ -981,6 +1087,7 @@ openerp.mail = function (session) { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').show(); p.find('.oe_more').hide(); + return false; }, /** @@ -990,15 +1097,14 @@ openerp.mail = function (session) { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').hide(); p.find('.oe_more').show(); + return false; }, /* 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.id); }); - return res; + return _.map(this.get_childs(), function (val) { return val.id; }); }, /* get all child message/thread linked. @@ -1033,17 +1139,17 @@ openerp.mail = function (session) { // goto the wall thread for launch browse if (!options._go_thread_wall) { options._go_thread_wall = true; - return this.options._parents[0].browse_thread(options); + return this.options.root_thread.browse_thread(options); } - if (this.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}); + var res = this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true}); if (res) return res; } } @@ -1067,8 +1173,8 @@ openerp.mail = function (session) { * @return message object */ browse_message: function (options) { - if (this.options._parents[0].messages[0]) - return this.options._parents[0].messages[0].browse_message(options); + if (this.options.root_thread.messages[0]) + return this.options.root_thread.messages[0].browse_message(options); }, /** @@ -1079,6 +1185,7 @@ openerp.mail = function (session) { on_compose_message: function () { this.instantiate_compose_message(); this.compose_message.on_compose_expandable(); + return false; }, /** @@ -1086,8 +1193,8 @@ openerp.mail = function (session) { */ 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); + if (this.options.help) { + no_message.html(this.options.help); } no_message.appendTo(this.$el); }, @@ -1101,18 +1208,20 @@ openerp.mail = function (session) { * @param {Array} ids read (if the are some ids, the method don't use the domain) */ message_fetch: function (replace_domain, replace_context, ids, callback) { - var self = this; - - // domain and context: options + additional - fetch_domain = replace_domain ? replace_domain : this.domain; - fetch_context = replace_context ? replace_context : this.context; - var message_loaded_ids = this.id ? [this.id].concat( self.get_child_ids() ) : self.get_child_ids(); - - // 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]) - .done(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message')); + return this.ds_message.call('message_read', [ + // ids force to read + ids == false ? undefined : ids, + // domain + additional + (replace_domain ? replace_domain : this.domain), + // ids allready loaded + (this.id ? [this.id].concat( this.get_child_ids() ) : this.get_child_ids()), + // option for sending in flat mode by server + this.options.flat_mode, + // context + additional + (replace_context ? replace_context : this.context), + // parent_id + this.context.default_parent_id || undefined + ]).done(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message')); }, /** @@ -1128,22 +1237,23 @@ openerp.mail = function (session) { data.options = _.extend(self.options, data.options); if (data.type=='expandable') { - var message = new mail.ThreadExpandable(self, data, { + var message = new mail.ThreadExpandable(self, data, {'context':{ '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, data, { + var message = new mail.ThreadMessage(self, data, {'context':{ 'default_model': data.model, 'default_res_id': data.res_id, 'default_parent_id': data.id, - }); + }}); } // check if the message is already create for (var i in self.messages) { if (self.messages[i] && self.messages[i].id == message.id) { + console.log('Reload message', message.id); self.messages[i].destroy(); } } @@ -1161,76 +1271,22 @@ openerp.mail = function (session) { * The sort is define by the thread_level (O for newer on top). * @param : {object} ThreadMessage object */ - insert_message: function (message, dom_insert_after) { + insert_message: function (message, dom_insert_after, prepend) { var self=this; - - if (this.show_compose_message && this.options.show_compact_message) { + if (this.options.show_compact_message > this.thread_level) { this.instantiate_compose_message(); this.compose_message.do_show_compact(); } - this.$('.oe_wall_no_message').remove(); + this.$('.oe_view_nocontent').remove(); if (dom_insert_after) { message.insertAfter(dom_insert_after); - return message - } - - // 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 sort = (!!self.thread_level || message.id<0); - - if (sort) { - if (message_older) { - - 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); - - } else { - - message.prependTo(self.$el); - } + }if (prepend) { + message.prependTo(self.$el); } else { - 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(self.$el); - - } + message.appendTo(self.$el); } return message @@ -1241,7 +1297,7 @@ openerp.mail = function (session) { * 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) { + switch_new_message: function (records, dom_insert_after) { var self=this; _(records).each(function (record) { var thread = self.browse_thread({ @@ -1251,7 +1307,7 @@ openerp.mail = function (session) { // create object and attach to the thread object var message = thread.create_message_object( record ); // insert the message on dom - thread.insert_message( message ); + thread.insert_message( message, typeof dom_insert_after == 'object' ? dom_insert_after : false); }); }, @@ -1262,6 +1318,10 @@ openerp.mail = function (session) { on_message_detroy: function (message) { this.messages = _.filter(this.messages, function (val) { return !val.isDestroyed(); }); + if (this.options.root_thread == this && !this.messages.length) { + this.no_message(); + } + return false; }, @@ -1323,7 +1383,6 @@ openerp.mail = function (session) { } 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, @@ -1332,9 +1391,11 @@ openerp.mail = function (session) { '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, + 'context':{ + '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 @@ -1350,7 +1411,7 @@ openerp.mail = function (session) { }); /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * mail : root Widget * ------------------------------------------------------------ * @@ -1360,7 +1421,7 @@ openerp.mail = function (session) { */ session.web.client_actions.add('mail.Widget', 'session.mail.Widget'); mail.Widget = session.web.Widget.extend({ - template: 'mail.Widget', + template: 'mail.Root', /** * @param {Object} parent parent @@ -1368,15 +1429,13 @@ openerp.mail = function (session) { * @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 {boolean} [show_reply_button] display the reply button + *... @param {boolean} [show_read_unread_button] 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 @@ -1387,35 +1446,25 @@ openerp.mail = function (session) { * @param {String} [no_message] Message to display when there are no message */ init: function (parent, action) { - var options = action.params || {}; - this._super(parent); - this.domain = options.domain || []; - this.context = options.context || {}; - this.search_results = {'domain': [], 'context': {}, 'groupby': {}}; + this._super(parent, action); + var self = this; + this.action = _.clone(action); + this.domain = this.action.domain || this.action.params.domain || []; + this.context = this.action.context || this.action.params.context || {}; - this.options = _.extend({ - 'typeof_thread' : 'inbox', + this.action.params = _.extend({ 'display_indented_thread' : -1, - 'show_reply_button' : -1, - 'show_read_unread_button' : -1, + 'show_reply_button' : false, + 'show_read_unread_button' : false, 'truncate_limit' : 250, 'show_record_name' : false, 'show_compose_message' : false, 'show_compact_message' : false, + 'view_inbox': false, 'message_ids': undefined, - 'no_message': false - }, options); + }, this.action.params); - 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; - } - + this.action.params.help = this.action.help || false; }, start: function (options) { @@ -1423,7 +1472,6 @@ openerp.mail = function (session) { this.message_render(); this.bind_events(); }, - /** *Create the root thread and display this object in the DOM. @@ -1435,35 +1483,32 @@ openerp.mail = function (session) { this.thread = new mail.Thread(this, {}, { 'domain' : this.domain, 'context' : this.context, - 'options': this.options, + 'options': this.action.params, }); 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) { + if (this.action.params.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(); - } + this.thread.compose_message.do_show_compact(); } + + this.thread.message_fetch(null, null, this.action.params.message_ids); + }, 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 ); - } + $(document).scroll( _.bind(this.thread.on_scroll, this.thread) ); + $(window).resize( _.bind(this.thread.on_scroll, this.thread) ); + this.$el.resize( _.bind(this.thread.on_scroll, this.thread) ); + window.setTimeout( _.bind(this.thread.on_scroll, this.thread), 500 ); } }); /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * mail_thread Widget * ------------------------------------------------------------ * @@ -1476,10 +1521,18 @@ openerp.mail = function (session) { mail.RecordThread = session.web.form.AbstractField.extend({ template: 'mail.record_thread', - init: function () { + init: function (parent, node) { this._super.apply(this, arguments); - this.options.domain = this.options.domain || []; - this.options.context = {'default_model': 'mail.thread', 'default_res_id': false}; + this.node = _.clone(node); + + this.node.params = _.extend({ + 'display_indented_thread': -1, + 'show_reply_button': false, + 'show_read_unread_button': false, + 'show_compact_message': 1, + }, this.node.params); + + this.domain = this.node.params && this.node.params.domain || []; }, start: function () { @@ -1492,42 +1545,32 @@ openerp.mail = function (session) { _check_visibility: function () { this.$el.toggle(this.view.get("actual_mode") !== "create"); }, + 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(); return; } - // update context - _.extend(this.options.context, { - default_res_id: this.view.datarecord.id, - default_model: this.view.model, - default_is_private: false }); - // update domain - var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]); - - var show_compose_message = this.view.is_action_enabled('edit') || - (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value()); - var message_ids = this.getParent().fields.message_ids && this.getParent().fields.message_ids.get_value(); + this.node.params = _.extend(this.node.params, { + 'message_ids': this.get_value(), + 'show_compose_message': this.view.is_action_enabled('edit'), + }); + this.node.context = { + 'default_res_id': this.view.datarecord.id || false, + 'default_model': this.view.model || false, + }; if (this.root) { + $('<span class="oe_mail-placeholder"/>').insertAfter(this.root.$el); this.root.destroy(); } // create and render Thread widget - this.root = new mail.Widget(this, { params: { - '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 - }} - ); + this.root = new mail.Widget(this, _.extend(this.node, { + 'domain' : (this.domain || []).concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]), + })); return this.root.replace(this.$('.oe_mail-placeholder')); }, @@ -1535,7 +1578,7 @@ openerp.mail = function (session) { /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * Wall Widget * ------------------------------------------------------------ * @@ -1543,6 +1586,7 @@ openerp.mail = function (session) { * 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.wall', 'session.mail.Wall'); mail.Wall = session.web.Widget.extend({ template: 'mail.wall', @@ -1555,35 +1599,53 @@ openerp.mail = function (session) { * contain default_model, default_res_id, to give it to the threads. */ init: function (parent, action) { - this._super(parent); - var options = action.params || {}; - this.options = options; - this.options.domain = options.domain || []; - this.options.context = options.context || {}; - this.search_results = {'domain': [], 'context': {}, 'groupby': {}} - this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); + this._super(parent, action); + + this.action = _.clone(action); + this.domain = this.action.params.domain || this.action.domain || []; + this.context = this.action.params.context || this.action.context || {}; + + this.defaults = {}; + for (var key in this.context) { + if (key.match(/^search_default_/)) { + this.defaults[key.replace(/^search_default_/, '')] = this.context[key]; + } + } + + this.action.params = _.extend({ + 'display_indented_thread': 1, + 'show_reply_button': true, + 'show_read_unread_button': true, + 'show_compose_message': true, + 'show_compact_message': this.action.params.view_mailbox ? false : 1, + 'view_inbox': false, + }, this.action.params); }, start: function () { - this._super.apply(this, arguments); - var searchview_ready = this.load_searchview({}, false); - var thread_displayed = this.message_render(); - this.options.domain = this.options.domain.concat(this.search_results['domain']); + this._super.apply(this); this.bind_events(); - return $.when(searchview_ready, thread_displayed); + var searchview_loaded = this.load_searchview(this.defaults); + if (! this.searchview.has_defaults) { + this.message_render(); + } + }, /** * Load the mail.message search view * @param {Object} defaults ?? - * @param {Boolean} hidden some kind of trick we do not care here */ - load_searchview: function (defaults, hidden) { + load_searchview: function (defaults) { var self = this; - this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false); - return this.searchview.appendTo(this.$('.oe_view_manager_view_search')).done(function () { - self.searchview.on('search_data', self, self.do_searchview_search); - }); + var ds_msg = new session.web.DataSetSearch(this, 'mail.message'); + this.searchview = new session.web.SearchView(this, ds_msg, false, defaults || {}, false); + this.searchview.appendTo(this.$('.oe_view_manager_view_search')) + .then(function () { self.searchview.on('search_data', self, self.do_searchview_search); }); + if (this.searchview.has_defaults) { + this.searchview.ready.then(this.searchview.do_search); + } + return this.searchview }, /** @@ -1599,33 +1661,26 @@ openerp.mail = function (session) { domains: domains || [], contexts: contexts || [], group_by_seq: groupbys || [] - }).done(function (results) { - self.search_results['context'] = results.context; - self.search_results['domain'] = results.domain; - self.root.destroy(); - return self.message_render(); + }).then(function (results) { + if(self.root) { + $('<span class="oe_mail-placeholder"/>').insertAfter(self.root.$el); + self.root.destroy(); + } + return self.message_render(results); }); }, - /** - *Create the root thread widget and display this object in the DOM - */ + * 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.root = new mail.Widget(this, { params: { + var domain = this.domain.concat(search && search['domain'] ? search['domain'] : []); + var context = _.extend(this.context, search && search['context'] ? search['context'] : {}); + + this.root = new mail.Widget(this, _.extend(this.action, { '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.root.replace(this.$('.oe_mail-placeholder')); }, @@ -1642,7 +1697,6 @@ openerp.mail = function (session) { views: [[false, 'form']], target: 'new', context: { - 'default_content_subtype': 'html', }, }; session.client.action_manager.do_action(action); @@ -1653,19 +1707,18 @@ openerp.mail = function (session) { /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * UserMenu * ------------------------------------------------------------ * * Add a link on the top user bar for write a full mail */ session.web.ComposeMessageTopButton = session.web.Widget.extend({ - template:'mail.compose_message.button_top_bar', + template:'mail.ComposeMessageTopButton', - start: function (parent, params) { - var self = this; - this.$el.on('click', 'button', self.on_compose_message ); - this._super(parent, params); + start: function () { + this.$('button').on('click', this.on_compose_message ); + this._super(); }, on_compose_message: function (event) { @@ -1675,26 +1728,22 @@ openerp.mail = function (session) { res_model: 'mail.compose.message', view_mode: 'form', view_type: 'form', - action_from: 'mail.ThreadComposeMessage', views: [[false, 'form']], target: 'new', - context: { - 'default_model': '', - 'default_res_id': false, - 'default_content_subtype': 'html', - }, + context: {}, }; session.client.action_manager.do_action(action); }, - }); - session.web.UserMenu = session.web.UserMenu.extend({ - start: function (parent, params) { - var render = new session.web.ComposeMessageTopButton(); - render.insertAfter(this.$el); - this._super(parent, params); - } + session.web.UserMenu.include({ + do_update: function(){ + var self = this; + this._super.apply(this, arguments); + this.update_promise.then(function() { + var mail_button = new session.web.ComposeMessageTopButton(); + mail_button.appendTo(session.webclient.$el.find('.oe_systray')); + }); + }, }); - }; diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index a5a97f48d26..7bb019cde4f 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -30,6 +30,8 @@ openerp_mail_followers = function(session, mail) { this.ds_model = new session.web.DataSetSearch(this, this.view.model); this.ds_follow = new session.web.DataSetSearch(this, this.field.relation); this.ds_users = new session.web.DataSetSearch(this, 'res.users'); + + this.value = []; }, start: function() { @@ -41,6 +43,11 @@ openerp_mail_followers = function(session, mail) { this._super(); }, + set_value: function(_value) { + this.value = _value; + this._super(_value); + }, + _check_visibility: function() { this.$el.toggle(this.view.get("actual_mode") !== "create"); }, @@ -86,13 +93,14 @@ openerp_mail_followers = function(session, mail) { read_value: function () { var self = this; return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).then(function (results) { - self.set_value(results[0].message_follower_ids); + self.value = results[0].message_follower_ids; + self.render_value(); }); }, render_value: function () { this.reinit(); - return this.fetch_followers(this.get("value")); + return this.fetch_followers(this.value); }, fetch_followers: function (value_) { @@ -111,7 +119,7 @@ openerp_mail_followers = function(session, mail) { event.preventDefault(); return this.ds_users.call('read', [this.session.uid, ['partner_id']]).then(function (results) { var pid = results['partner_id'][0]; - self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1); + self.message_is_follower = (_.indexOf(self.value, pid) != -1); }).then(self.proxy('display_generic')); }, _format_followers: function(count){ @@ -131,7 +139,7 @@ openerp_mail_followers = function(session, mail) { display_generic: function () { var self = this; var node_user_list = this.$('.oe_follower_list').empty(); - this.$('.oe_follower_title').html(this._format_followers(this.get('value').length)); + this.$('.oe_follower_title').html(this._format_followers(this.value.length)); }, /** Display the followers */ @@ -206,7 +214,8 @@ openerp_mail_followers = function(session, mail) { $(record).attr('checked',false); }); var context = new session.web.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]).then(this.proxy('read_value')); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]) + .then(this.proxy('read_value')); }, do_update_subscription: function (event) { @@ -220,7 +229,7 @@ openerp_mail_followers = function(session, mail) { }); var context = new session.web.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist:undefined, context]) + return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist : undefined, context]) .then(this.proxy('read_value')); }, }); diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index bfd0e4cd10f..89fbe45dc36 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -1,8 +1,9 @@ <?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"> + <!-- + mail.Widget template used to namespace the css --> + <t t-name="mail.Root"> <div class="oe_mail"> </div> </t> @@ -38,7 +39,8 @@ </div> </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..."/> + <textarea t-if="!widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to the followers of this docmuent..."/> + <textarea t-if="widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to my followers..."/> </div> <span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span> </t> @@ -69,41 +71,28 @@ Template used to display attachments in a mail.message --> <t t-name="mail.thread.message.attachments"> - <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<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> - <t t-if="(!attachment.upload or attachment.percent_loaded>=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> - <t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded>=100))"> - <a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a> - </t> + <t t-foreach='widget.attachment_ids' t-as='attachment'> + <t t-if="attachment.filetype !== 'webimage'"> + <div t-attf-class="oe_attachment #{attachment.upload ? 'oe_uploading' : ''}"> + <a t-att-href='attachment.url'><img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.filetype + '.png'"></img></a> + <div class='oe_delete oe_e' title="Delete this attachment" t-attf-data-id="{attachment.id}">[</div> + <div class='oe_name'><t t-raw='attachment.name' /></div> + <div class='oe_progress_bar'> + uploading + </div> </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 t-if="attachment.filetype === 'webimage'"> + <div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}"> + <a t-att-href='attachment.url'><img t-att-src="widget.attachments_resize_image(attachment.id, [100,100])"></img></a> + <div class='oe_delete oe_e'>[</div> + <div class='oe_name'><t t-raw='attachment.name' /></div> + <div class='oe_progress_bar'> + uploading + </div> + </div> </t> - </span> - </t> - - <t t-name="mail.thread.message.private"> - <div> - <span class="oe_placeholder_checkbox_private"/> - <span class="oe_send_private">This email is private.</span> - <span class="oe_send_public">I wrote for contacts and all my followers.</span> - </div> + </t> </t> <!-- @@ -112,7 +101,7 @@ <t t-name="mail.thread.list_recipients"> <div class="oe_mail_list_recipients"> To: - <span t-if="!widget.is_private" class="oe_all_follower">Everyone</span> + <span t-if="!widget.is_private" class="oe_all_follower">Followers <t t-raw="' of ' + (widget.parent_thread.parent_message.record_name ? '"' + widget.parent_thread.parent_message.record_name + '"' : 'this document')"/></span> <t t-if="!widget.is_private and widget.partner_ids.length"> and </t> <t t-set="inc" t-value="0"/> <t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/> @@ -140,22 +129,20 @@ <td colspan="2"> <h2 class="oe_view_title"> <span class="oe_view_title_text"> - News Feed - <t t-if="widget.options.res_model"> / </t> - <t t-if="widget.options.res_model" t-esc="widget.options.res_model"/> + Email box </span> </h2> </td> <td><div class="oe_view_manager_view_search" t-opentag="true"/></td> </tr> - <tr class="oe_header_row"> + <tr class="oe_header_row" t-if="widget.action.params.view_inbox" > <td colspan="2"> <button type="button" class="oe_write_full oe_highlight"> Compose a new message </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> + <a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to my followers</a> </span> </td> </tr> @@ -168,7 +155,7 @@ display message on the wall when there are no message --> <t t-name="mail.wall_no_message"> - <div class="oe_wall_no_message">You have no messages</div> + <div class="oe_view_nocontent">No messages.</div> </t> <!-- @@ -187,7 +174,7 @@ <!-- default layout --> <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'}"> + <div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type}"> <div class='oe_msg_left'> <a t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]"> @@ -197,10 +184,11 @@ <div class="oe_msg_center"> <div class='oe_msg_icons'> - <span class='oe_read' t-if="widget.show_read_unread_button"><a title="Mark as read" class="oe_e">X</a></span> - <span class='oe_unread' t-if="widget.show_read_unread_button"><a title="Mark as 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> + <span class='oe_read' t-if="widget.options.show_read"><a title="Done" class="oe_e">X</a></span> + <span class='oe_unread' t-if="widget.is_favorite and widget.options.show_unread"><a title="Set back to Todo" class="oe_e">v</a></span> + <span class='oe_unread' t-if="!widget.is_favorite and widget.options.show_unread"><a title="Set back to Inbox" class="oe_e">v</a></span> + <span class='oe_reply' t-if="widget.options.show_reply_button"><a title="Reply" class="oe_e">(</a></span> + <span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Mark as Todo" class="oe_e">7</a></span> </div> <!-- message itself --> <div class="oe_msg_content"> @@ -208,23 +196,26 @@ <t t-raw="widget.subject"/> </h1> <div class="oe_msg_body"> - <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'"> + <t t-if="widget.show_record_name"> <a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}"><t t-raw="widget.record_name"/></a> </t> <t t-raw="widget.body"/> </div> - <t t-if="widget.attachment_ids.length > 0"> - <t t-call="mail.thread.message.attachments"/> - </t> </div> <div class="oe_msg_footer"> + <t t-if="widget.attachment_ids.length > 0"> + <div class="oe_msg_attachment_list"></div> + </t> <a t-if="widget.author_id" t-attf-href="#model=res.partner&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 > 0'> + <span class='oe_subtle'>•</span> + <a class="oe_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> + </t> <span class='oe_subtle'>•</span> <t t-call="mail.thread.message.vote"/> </div> @@ -234,11 +225,12 @@ <!-- expandable message layout --> <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 t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.max_limit ? 'oe_max_limit' : ''} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}"> <div class="oe_msg_content oe_msg_more_message"> <div class='oe_separator'></div> + <a t-if="widget.nb_messages <= 0" class="oe_msg_fetch_more">show more message</a> <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> + <a t-if="widget.nb_messages > 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a> </div> </div> </t> @@ -247,9 +239,9 @@ mail.compose_message.button_top_bar render of the button on the user bar for open wizard compose message --> - <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> --> + <t t-name="mail.ComposeMessageTopButton"> + <div title='Compose new Message' class="oe_topbar_item oe_topbar_compose_full_email"> + <button class="oe_e">%</button> </div> </t> @@ -257,12 +249,11 @@ Template used to display Like/Unlike in a mail.message --> <span t-name="mail.thread.message.vote"> - <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> - <a href='#' t-attf-class="oe_msg_vote"> + <a href='#' class="oe_msg_vote"> <t t-if="!widget.has_voted">like</t> <t t-if="widget.has_voted">unlike</t> </a> |
