You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1170 lines
48 KiB

[LINT] ************* Module mail_archives mail_archives/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_archives/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_to mail_to/static/src/js/mail_to.js:11: [W7903(javascript-lint), ] Missing semicolon. mail_to/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_to/doc/changelog.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message mail_move_message/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_move_message/data/mail_move_message_data.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message.mail_move_message_models mail_move_message/mail_move_message_models.py:74: [C8108(method-compute), Wizard] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:91: [W8104(api-one-deprecated), Wizard.get_can_move] api.one deprecated mail_move_message/mail_move_message_models.py:141: [W8104(api-one-deprecated), Wizard.check_access] api.one deprecated mail_move_message/mail_move_message_models.py:198: [W8104(api-one-deprecated), Wizard.delete] api.one deprecated mail_move_message/mail_move_message_models.py:233: [W8104(api-one-deprecated), Wizard.read_close] api.one deprecated mail_move_message/mail_move_message_models.py:248: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:251: [W8104(api-one-deprecated), MailMessage._get_all_childs] api.one deprecated mail_move_message/mail_move_message_models.py:274: [W8104(api-one-deprecated), MailMessage.move] api.one deprecated ************* Module mail_base mail_base/static/src/js/base.js:245: [W7903(javascript-lint), ] Misleading line break before '||'; readers may interpret this as an expression boundary. mail_base/static/src/js/base.js:1119: [W7903(javascript-lint), ] Missing semicolon. mail_base/static/src/js/base.js:1121: [W7903(javascript-lint), ] Missing semicolon. mail_base/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent mail_sent/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_sent/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent.models mail_sent/models.py:8: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_sent/models.py:12: [W8104(api-one-deprecated), MailMessage._get_sent] api.one deprecated ************* Module mail_reply mail_reply/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count res_partner_mails_count/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count.models res_partner_mails_count/models.py:8: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:9: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:12: [W8104(api-one-deprecated), ResPartner._mails_to] api.one deprecated res_partner_mails_count/models.py:17: [W8104(api-one-deprecated), ResPartner._mails_from] api.one deprecated
8 years ago
[LINT] ************* Module mail_archives mail_archives/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_archives/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_to mail_to/static/src/js/mail_to.js:11: [W7903(javascript-lint), ] Missing semicolon. mail_to/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_to/doc/changelog.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message mail_move_message/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_move_message/data/mail_move_message_data.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message.mail_move_message_models mail_move_message/mail_move_message_models.py:74: [C8108(method-compute), Wizard] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:91: [W8104(api-one-deprecated), Wizard.get_can_move] api.one deprecated mail_move_message/mail_move_message_models.py:141: [W8104(api-one-deprecated), Wizard.check_access] api.one deprecated mail_move_message/mail_move_message_models.py:198: [W8104(api-one-deprecated), Wizard.delete] api.one deprecated mail_move_message/mail_move_message_models.py:233: [W8104(api-one-deprecated), Wizard.read_close] api.one deprecated mail_move_message/mail_move_message_models.py:248: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:251: [W8104(api-one-deprecated), MailMessage._get_all_childs] api.one deprecated mail_move_message/mail_move_message_models.py:274: [W8104(api-one-deprecated), MailMessage.move] api.one deprecated ************* Module mail_base mail_base/static/src/js/base.js:245: [W7903(javascript-lint), ] Misleading line break before '||'; readers may interpret this as an expression boundary. mail_base/static/src/js/base.js:1119: [W7903(javascript-lint), ] Missing semicolon. mail_base/static/src/js/base.js:1121: [W7903(javascript-lint), ] Missing semicolon. mail_base/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent mail_sent/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_sent/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent.models mail_sent/models.py:8: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_sent/models.py:12: [W8104(api-one-deprecated), MailMessage._get_sent] api.one deprecated ************* Module mail_reply mail_reply/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count res_partner_mails_count/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count.models res_partner_mails_count/models.py:8: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:9: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:12: [W8104(api-one-deprecated), ResPartner._mails_to] api.one deprecated res_partner_mails_count/models.py:17: [W8104(api-one-deprecated), ResPartner._mails_from] api.one deprecated
8 years ago
[LINT] ************* Module mail_archives mail_archives/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_archives/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_to mail_to/static/src/js/mail_to.js:11: [W7903(javascript-lint), ] Missing semicolon. mail_to/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline mail_to/doc/changelog.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message mail_move_message/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_move_message/data/mail_move_message_data.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_move_message.mail_move_message_models mail_move_message/mail_move_message_models.py:74: [C8108(method-compute), Wizard] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:91: [W8104(api-one-deprecated), Wizard.get_can_move] api.one deprecated mail_move_message/mail_move_message_models.py:141: [W8104(api-one-deprecated), Wizard.check_access] api.one deprecated mail_move_message/mail_move_message_models.py:198: [W8104(api-one-deprecated), Wizard.delete] api.one deprecated mail_move_message/mail_move_message_models.py:233: [W8104(api-one-deprecated), Wizard.read_close] api.one deprecated mail_move_message/mail_move_message_models.py:248: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_move_message/mail_move_message_models.py:251: [W8104(api-one-deprecated), MailMessage._get_all_childs] api.one deprecated mail_move_message/mail_move_message_models.py:274: [W8104(api-one-deprecated), MailMessage.move] api.one deprecated ************* Module mail_base mail_base/static/src/js/base.js:245: [W7903(javascript-lint), ] Misleading line break before '||'; readers may interpret this as an expression boundary. mail_base/static/src/js/base.js:1119: [W7903(javascript-lint), ] Missing semicolon. mail_base/static/src/js/base.js:1121: [W7903(javascript-lint), ] Missing semicolon. mail_base/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent mail_sent/doc/changelog.rst:1: [E7901(rst-syntax-error), ] Hyperlink target "changelog" is not referenced. mail_sent/views/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module mail_sent.models mail_sent/models.py:8: [C8108(method-compute), MailMessage] Name of compute method should starts with "_compute_" mail_sent/models.py:12: [W8104(api-one-deprecated), MailMessage._get_sent] api.one deprecated ************* Module mail_reply mail_reply/templates.xml:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count res_partner_mails_count/README.rst:1: [W7908(missing-newline-extrafiles), ] Missing newline ************* Module res_partner_mails_count.models res_partner_mails_count/models.py:8: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:9: [C8108(method-compute), ResPartner] Name of compute method should starts with "_compute_" res_partner_mails_count/models.py:12: [W8104(api-one-deprecated), ResPartner._mails_to] api.one deprecated res_partner_mails_count/models.py:17: [W8104(api-one-deprecated), ResPartner._mails_from] api.one deprecated
8 years ago
  1. odoo.define('mail_base.base', function (require) {
  2. "use strict";
  3. var bus = require('bus.bus').bus;
  4. var utils = require('mail.utils');
  5. var config = require('web.config');
  6. var core = require('web.core');
  7. var data = require('web.data');
  8. var Model = require('web.Model');
  9. var session = require('web.session');
  10. var time = require('web.time');
  11. var web_client = require('web.web_client');
  12. var _t = core._t;
  13. var _lt = core._lt;
  14. var LIMIT = 100;
  15. var preview_msg_max_size = 350; // optimal for native english speakers
  16. var ODOOBOT_ID = "ODOOBOT";
  17. var MessageModel = new Model('mail.message', session.context);
  18. var ChannelModel = new Model('mail.channel', session.context);
  19. var UserModel = new Model('res.users', session.context);
  20. var PartnerModel = new Model('res.partner', session.context);
  21. var chat_manager = require('mail.chat_manager');
  22. // Private model
  23. //----------------------------------------------------------------------------------
  24. var messages = [];
  25. var channels = [];
  26. var channels_preview_def;
  27. var channel_defs = {};
  28. var chat_unread_counter = 0;
  29. var unread_conversation_counter = 0;
  30. var emojis = [];
  31. var emoji_substitutions = {};
  32. var needaction_counter = 0;
  33. var starred_counter = 0;
  34. var mention_partner_suggestions = [];
  35. var canned_responses = [];
  36. var commands = [];
  37. var discuss_menu_id;
  38. var global_unread_counter = 0;
  39. var pinned_dm_partners = []; // partner_ids we have a pinned DM with
  40. var client_action_open = false;
  41. bus.on("window_focus", null, function() {
  42. global_unread_counter = 0;
  43. web_client.set_title_part("_chat");
  44. });
  45. var channel_seen = _.throttle(function (channel) {
  46. return ChannelModel.call('channel_seen', [[channel.id]], {}, {shadow: true});
  47. }, 3000);
  48. var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
  49. ChatAction.include({
  50. init: function(parent, action, options) {
  51. this._super.apply(this, arguments);
  52. this.channels_show_send_button = ['channel_inbox'];
  53. this.channels_display_subject = [];
  54. },
  55. start: function() {
  56. var result = this._super.apply(this, arguments);
  57. var search_defaults = {};
  58. var context = this.action ? this.action.context : [];
  59. _.each(context, function (value, key) {
  60. var match = /^search_default_(.*)$/.exec(key);
  61. if (match) {
  62. search_defaults[match[1]] = value;
  63. }
  64. });
  65. this.searchview.defaults = search_defaults;
  66. var self = this;
  67. return $.when(result).done(function() {
  68. $('.oe_leftbar').toggle(false);
  69. self.searchview.do_search();
  70. });
  71. },
  72. destroy: function() {
  73. var result = this._super.apply(this, arguments);
  74. $('.oe_leftbar .oe_secondary_menu').each(function(){
  75. if ($(this).css('display') == 'block'){
  76. if ($(this).children().length > 0) {
  77. $('.oe_leftbar').toggle(true);
  78. }
  79. return false;
  80. }
  81. });
  82. return result;
  83. },
  84. set_channel: function(channel){
  85. var result = this._super.apply(this, arguments);
  86. var self = this;
  87. return $.when(result).done(function() {
  88. self.$buttons
  89. .find('.o_mail_chat_button_new_message')
  90. .toggle(self.channels_show_send_button.indexOf(channel.id) != -1);
  91. });
  92. },
  93. get_thread_rendering_options: function (messages) {
  94. var options = this._super.apply(this, arguments);
  95. options.display_subject = options.display_subject || this.channels_display_subject.indexOf(this.channel.id) != -1;
  96. return options;
  97. },
  98. update_message_on_current_channel: function(current_channel_id, message){
  99. var starred = current_channel_id === "channel_starred" && !message.is_starred;
  100. var inbox = current_channel_id === "channel_inbox" && !message.is_needaction;
  101. return starred || inbox;
  102. },
  103. on_update_message: function (message) {
  104. var self = this;
  105. var current_channel_id = this.channel.id;
  106. if (this.update_message_on_current_channel(current_channel_id, message)) {
  107. chat_manager.get_messages({channel_id: this.channel.id, domain: this.domain}).then(function (messages) {
  108. var options = self.get_thread_rendering_options(messages);
  109. self.thread.remove_message_and_render(message.id, messages, options).then(function () {
  110. self.update_button_status(messages.length === 0);
  111. });
  112. });
  113. } else if (_.contains(message.channel_ids, current_channel_id)) {
  114. this.fetch_and_render_thread();
  115. }
  116. }
  117. });
  118. var MailTools = core.Class.extend({
  119. notify_incoming_message: function (msg, options) {
  120. if (bus.is_odoo_focused() && options.is_displayed) {
  121. // no need to notify
  122. return;
  123. }
  124. var title = _t('New message');
  125. if (msg.author_id[1]) {
  126. title = _.escape(msg.author_id[1]);
  127. }
  128. var content = utils.parse_and_transform(msg.body, utils.strip_html).substr(0, preview_msg_max_size);
  129. if (!bus.is_odoo_focused()) {
  130. global_unread_counter++;
  131. var tab_title = _.str.sprintf(_t("%d Messages"), global_unread_counter);
  132. web_client.set_title_part("_chat", tab_title);
  133. }
  134. utils.send_notification(title, content);
  135. },
  136. // Message and channel manipulation helpers
  137. //----------------------------------------------------------------------------------
  138. // options: channel_id, silent
  139. add_message: function (data, options) {
  140. options = options || {};
  141. var msg = _.findWhere(messages, { id: data.id });
  142. if (!msg) {
  143. msg = chat_manager.mail_tools.make_message(data);
  144. // Keep the array ordered by id when inserting the new message
  145. messages.splice(_.sortedIndex(messages, msg, 'id'), 0, msg);
  146. _.each(msg.channel_ids, function (channel_id) {
  147. var channel = chat_manager.get_channel(channel_id);
  148. if (channel) {
  149. chat_manager.mail_tools.add_to_cache(msg, []);
  150. if (options.domain && options.domain !== []) {
  151. chat_manager.mail_tools.add_to_cache(msg, options.domain);
  152. }
  153. if (channel.hidden) {
  154. channel.hidden = false;
  155. chat_manager.bus.trigger('new_channel', channel);
  156. }
  157. if (channel.type !== 'static' && !msg.is_author && !msg.is_system_notification) {
  158. if (options.increment_unread) {
  159. chat_manager.mail_tools.update_channel_unread_counter(channel, channel.unread_counter+1);
  160. }
  161. if (channel.is_chat && options.show_notification) {
  162. if (!client_action_open && config.device.size_class !== config.device.SIZES.XS) {
  163. // automatically open chat window
  164. chat_manager.bus.trigger('open_chat', channel, { passively: true });
  165. }
  166. var query = {is_displayed: false};
  167. chat_manager.bus.trigger('anyone_listening', channel, query);
  168. chat_manager.mail_tools.notify_incoming_message(msg, query);
  169. }
  170. }
  171. }
  172. });
  173. if (!options.silent) {
  174. chat_manager.bus.trigger('new_message', msg);
  175. }
  176. } else if (options.domain && options.domain !== []) {
  177. chat_manager.mail_tools.add_to_cache(msg, options.domain);
  178. }
  179. return msg;
  180. },
  181. property_descr: function (channel, msg, self) {
  182. return {
  183. enumerable: true,
  184. get: function () {
  185. return _.contains(msg.channel_ids, channel);
  186. },
  187. set: function (bool) {
  188. if (bool) {
  189. self.add_channel_to_message(msg, channel);
  190. } else {
  191. msg.channel_ids = _.without(msg.channel_ids, channel);
  192. }
  193. }
  194. };
  195. },
  196. get_properties: function(msg){
  197. return {
  198. is_starred: chat_manager.mail_tools.property_descr("channel_starred", msg, chat_manager.mail_tools),
  199. is_needaction: chat_manager.mail_tools.property_descr("channel_inbox", msg, chat_manager.mail_tools)
  200. };
  201. },
  202. set_channel_flags: function(data, msg){
  203. if (_.contains(data.needaction_partner_ids, session.partner_id)) {
  204. msg.is_needaction = true;
  205. }
  206. if (_.contains(data.starred_partner_ids, session.partner_id)) {
  207. msg.is_starred = true;
  208. }
  209. return msg;
  210. },
  211. get_channel_array: function(msg){
  212. return [ msg.channel_ids, 'channel_inbox', 'channel_starred' ];
  213. },
  214. make_message: function (data) {
  215. var msg = {
  216. id: data.id,
  217. author_id: data.author_id,
  218. body: data.body || "",
  219. date: moment(time.str_to_datetime(data.date)),
  220. message_type: data.message_type,
  221. subtype_description: data.subtype_description,
  222. is_author: data.author_id && data.author_id[0] === session.partner_id,
  223. is_note: data.is_note,
  224. is_system_notification: data.message_type === 'notification' && data.model === 'mail.channel' || data.info === 'transient_message',
  225. attachment_ids: data.attachment_ids || [],
  226. subject: data.subject,
  227. email_from: data.email_from,
  228. customer_email_status: data.customer_email_status,
  229. customer_email_data: data.customer_email_data,
  230. record_name: data.record_name,
  231. tracking_value_ids: data.tracking_value_ids,
  232. channel_ids: data.channel_ids,
  233. model: data.model,
  234. res_id: data.res_id,
  235. url: session.url("/mail/view?message_id=" + data.id)
  236. };
  237. _.each(_.keys(emoji_substitutions), function (key) {
  238. var escaped_key = String(key).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1');
  239. var regexp = new RegExp("(?:^|\\s|<[a-z]*>)(" + escaped_key + ")(?=\\s|$|</[a-z]*>)", "g");
  240. msg.body = msg.body.replace(regexp, ' <span class="o_mail_emoji">'+emoji_substitutions[key]+'</span> ');
  241. });
  242. Object.defineProperties(msg, chat_manager.mail_tools.get_properties(msg));
  243. msg = chat_manager.mail_tools.set_channel_flags(data, msg);
  244. if (msg.model === 'mail.channel') {
  245. var real_channels = _.without(chat_manager.mail_tools.get_channel_array(msg));
  246. var origin = real_channels.length === 1 ? real_channels[0] : undefined;
  247. var channel = origin && chat_manager.get_channel(origin);
  248. if (channel) {
  249. msg.origin_id = origin;
  250. msg.origin_name = channel.name;
  251. }
  252. }
  253. // Compute displayed author name or email
  254. if ((!msg.author_id || !msg.author_id[0]) && msg.email_from) {
  255. msg.mailto = msg.email_from;
  256. } else {
  257. msg.displayed_author = (msg.author_id === ODOOBOT_ID) && "OdooBot" ||
  258. msg.author_id && msg.author_id[1] ||
  259. msg.email_from || _t('Anonymous');
  260. }
  261. // Don't redirect on author clicked of self-posted or OdooBot messages
  262. msg.author_redirect = !msg.is_author && msg.author_id !== ODOOBOT_ID;
  263. // Compute the avatar_url
  264. if (msg.author_id === ODOOBOT_ID) {
  265. msg.avatar_src = "/mail/static/src/img/odoo_o.png";
  266. } else if (msg.author_id && msg.author_id[0]) {
  267. msg.avatar_src = "/web/image/res.partner/" + msg.author_id[0] + "/image_small";
  268. } else if (msg.message_type === 'email') {
  269. msg.avatar_src = "/mail/static/src/img/email_icon.png";
  270. } else {
  271. msg.avatar_src = "/mail/static/src/img/smiley/avatar.jpg";
  272. }
  273. // add anchor tags to urls
  274. msg.body = utils.parse_and_transform(msg.body, utils.add_link);
  275. // Compute url of attachments
  276. _.each(msg.attachment_ids, function(a) {
  277. a.url = '/web/content/' + a.id + '?download=true';
  278. });
  279. return msg;
  280. },
  281. add_channel_to_message: function (message, channel_id) {
  282. message.channel_ids.push(channel_id);
  283. message.channel_ids = _.uniq(message.channel_ids);
  284. },
  285. add_channel: function (data, options) {
  286. options = typeof options === "object" ? options : {};
  287. var channel = chat_manager.get_channel(data.id);
  288. if (channel) {
  289. if (channel.is_folded !== (data.state === "folded")) {
  290. channel.is_folded = (data.state === "folded");
  291. chat_manager.bus.trigger("channel_toggle_fold", channel);
  292. }
  293. } else {
  294. channel = chat_manager.mail_tools.make_channel(data, options);
  295. channels.push(channel);
  296. channels = _.sortBy(channels, function (channel) { return _.isString(channel.name) ? channel.name.toLowerCase() : ''; });
  297. if (!options.silent) {
  298. chat_manager.bus.trigger("new_channel", channel);
  299. }
  300. if (channel.is_detached) {
  301. chat_manager.bus.trigger("open_chat", channel);
  302. }
  303. }
  304. return channel;
  305. },
  306. make_channel: function (data, options) {
  307. var channel = {
  308. id: data.id,
  309. name: data.name,
  310. server_type: data.channel_type,
  311. type: data.type || data.channel_type,
  312. all_history_loaded: false,
  313. uuid: data.uuid,
  314. is_detached: data.is_minimized,
  315. is_folded: data.state === "folded",
  316. autoswitch: 'autoswitch' in options ? options.autoswitch : true,
  317. hidden: options.hidden,
  318. display_needactions: options.display_needactions,
  319. mass_mailing: data.mass_mailing,
  320. needaction_counter: data.message_needaction_counter || 0,
  321. unread_counter: 0,
  322. last_seen_message_id: data.seen_message_id,
  323. cache: {'[]': {
  324. all_history_loaded: false,
  325. loaded: false,
  326. messages: []
  327. }}
  328. };
  329. if (channel.type === "channel" && data.public !== "private") {
  330. channel.type = "public";
  331. } else if (data.public === "private") {
  332. channel.type = "private";
  333. }
  334. if (_.size(data.direct_partner) > 0) {
  335. channel.type = "dm";
  336. channel.name = data.direct_partner[0].name;
  337. channel.direct_partner_id = data.direct_partner[0].id;
  338. channel.status = data.direct_partner[0].im_status;
  339. pinned_dm_partners.push(channel.direct_partner_id);
  340. bus.update_option('bus_presence_partner_ids', pinned_dm_partners);
  341. } else if ('anonymous_name' in data) {
  342. channel.name = data.anonymous_name;
  343. }
  344. if (data.last_message_date) {
  345. channel.last_message_date = moment(time.str_to_datetime(data.last_message_date));
  346. }
  347. channel.is_chat = !channel.type.match(/^(public|private|static)$/);
  348. if (data.message_unread_counter) {
  349. chat_manager.mail_tools.update_channel_unread_counter(channel, data.message_unread_counter);
  350. }
  351. return channel;
  352. },
  353. remove_channel: function (channel) {
  354. if (!channel) { return; }
  355. if (channel.type === 'dm') {
  356. var index = pinned_dm_partners.indexOf(channel.direct_partner_id);
  357. if (index > -1) {
  358. pinned_dm_partners.splice(index, 1);
  359. bus.update_option('bus_presence_partner_ids', pinned_dm_partners);
  360. }
  361. }
  362. channels = _.without(channels, channel);
  363. delete channel_defs[channel.id];
  364. },
  365. get_channel_cache: function (channel, domain) {
  366. var stringified_domain = JSON.stringify(domain || []);
  367. if (!channel.cache[stringified_domain]) {
  368. channel.cache[stringified_domain] = {
  369. all_history_loaded: false,
  370. loaded: false,
  371. messages: []
  372. };
  373. }
  374. return channel.cache[stringified_domain];
  375. },
  376. invalidate_caches: function (channel_ids) {
  377. _.each(channel_ids, function (channel_id) {
  378. var channel = chat_manager.get_channel(channel_id);
  379. if (channel) {
  380. channel.cache = { '[]': channel.cache['[]']};
  381. }
  382. });
  383. },
  384. clear_cache_all_channels: function(){
  385. _.each(channels, function(channel){
  386. channel.cache = {};
  387. });
  388. },
  389. add_to_cache: function (message, domain) {
  390. _.each(message.channel_ids, function (channel_id) {
  391. var channel = chat_manager.get_channel(channel_id);
  392. if (channel) {
  393. var channel_cache = chat_manager.mail_tools.get_channel_cache(channel, domain);
  394. var index = _.sortedIndex(channel_cache.messages, message, 'id');
  395. if (channel_cache.messages[index] !== message) {
  396. channel_cache.messages.splice(index, 0, message);
  397. }
  398. }
  399. });
  400. },
  401. remove_from_cache: function(message, domain){
  402. var self = this;
  403. _.each(message.channel_ids, function (channel_id) {
  404. var channel = chat_manager.get_channel(channel_id);
  405. if (channel) {
  406. var channel_cache = self.get_channel_cache(channel, domain);
  407. var index = _.sortedIndex(channel_cache.messages, message, 'id');
  408. if (channel_cache.messages[index] === message) {
  409. channel_cache.messages.splice(index, 1);
  410. }
  411. }
  412. });
  413. },
  414. remove_message_from_channel: function (channel_id, message) {
  415. message.channel_ids = _.without(message.channel_ids, channel_id);
  416. var channel = _.findWhere(channels, { id: channel_id });
  417. _.each(channel.cache, function (cache) {
  418. cache.messages = _.without(cache.messages, message);
  419. });
  420. },
  421. get_domain: function(channel){
  422. return (channel.id === "channel_inbox") ? [['needaction', '=', true]] :
  423. (channel.id === "channel_starred") ? [['starred', '=', true]] : false;
  424. },
  425. // options: domain, load_more
  426. fetch_from_channel: function (channel, options) {
  427. options = options || {};
  428. var domain = chat_manager.mail_tools.get_domain(channel) || [['channel_ids', 'in', channel.id]];
  429. var cache = chat_manager.mail_tools.get_channel_cache(channel, options.domain);
  430. if (options.domain) {
  431. domain = new data.CompoundDomain(domain, options.domain || []);
  432. }
  433. if (options.load_more) {
  434. var min_message_id = cache.messages[0].id;
  435. domain = new data.CompoundDomain([['id', '<', min_message_id]], domain);
  436. }
  437. return MessageModel.call('message_fetch', [domain], {limit: LIMIT}).then(function (msgs) {
  438. if (!cache.all_history_loaded) {
  439. cache.all_history_loaded = msgs.length < LIMIT;
  440. }
  441. cache.loaded = true;
  442. _.each(msgs, function (msg) {
  443. chat_manager.mail_tools.add_message(msg, {channel_id: channel.id, silent: true, domain: options.domain});
  444. });
  445. var channel_cache = chat_manager.mail_tools.get_channel_cache(channel, options.domain || []);
  446. return channel_cache.messages;
  447. });
  448. },
  449. // options: force_fetch
  450. fetch_document_messages: function (ids, options) {
  451. var loaded_msgs = _.filter(messages, function (message) {
  452. return _.contains(ids, message.id);
  453. });
  454. var loaded_msg_ids = _.pluck(loaded_msgs, 'id');
  455. options = options || {};
  456. if (options.force_fetch || _.difference(ids.slice(0, LIMIT), loaded_msg_ids).length) {
  457. var ids_to_load = _.difference(ids, loaded_msg_ids).slice(0, LIMIT);
  458. return MessageModel.call('message_format', [ids_to_load]).then(function (msgs) {
  459. var processed_msgs = [];
  460. _.each(msgs, function (msg) {
  461. processed_msgs.push(chat_manager.mail_tools.add_message(msg, {silent: true}));
  462. });
  463. return _.sortBy(loaded_msgs.concat(processed_msgs), function (msg) {
  464. return msg.date;
  465. });
  466. });
  467. } else {
  468. return $.when(loaded_msgs);
  469. }
  470. },
  471. update_channel_unread_counter: function (channel, counter) {
  472. if (channel.unread_counter > 0 && counter === 0) {
  473. unread_conversation_counter = Math.max(0, unread_conversation_counter-1);
  474. } else if (channel.unread_counter === 0 && counter > 0) {
  475. unread_conversation_counter++;
  476. }
  477. if (channel.is_chat) {
  478. chat_unread_counter = Math.max(0, chat_unread_counter - channel.unread_counter + counter);
  479. }
  480. channel.unread_counter = counter;
  481. chat_manager.bus.trigger("update_channel_unread_counter", channel);
  482. },
  483. // Notification handlers
  484. // ---------------------------------------------------------------------------------
  485. on_notification: function (notifications) {
  486. // sometimes, the web client receives unsubscribe notification and an extra
  487. // notification on that channel. This is then followed by an attempt to
  488. // rejoin the channel that we just left. The next few lines remove the
  489. // extra notification to prevent that situation to occur.
  490. var unsubscribed_notif = _.find(notifications, function (notif) {
  491. return notif[1].info === "unsubscribe";
  492. });
  493. if (unsubscribed_notif) {
  494. notifications = _.reject(notifications, function (notif) {
  495. return notif[0][1] === "mail.channel" && notif[0][2] === unsubscribed_notif[1].id;
  496. });
  497. }
  498. _.each(notifications, function (notification) {
  499. var model = notification[0][1];
  500. if (model === 'ir.needaction') {
  501. // new message in the inbox
  502. chat_manager.mail_tools.on_needaction_notification(notification[1]);
  503. } else if (model === 'mail.channel') {
  504. // new message in a channel
  505. chat_manager.mail_tools.on_channel_notification(notification[1]);
  506. } else if (model === 'res.partner') {
  507. // channel joined/left, message marked as read/(un)starred, chat open/closed
  508. chat_manager.mail_tools.on_partner_notification(notification[1]);
  509. } else if (model === 'bus.presence') {
  510. // update presence of users
  511. chat_manager.mail_tools.on_presence_notification(notification[1]);
  512. } else if (model === 'mail_base.mail_sent') {
  513. // Delete cache in order to fetch new message
  514. // TODO find a solution without deleting cache. Currently
  515. // problem is that on inheriting send_mail in
  516. // mail.compose.message it's not possible to get id of new
  517. // message
  518. chat_manager.mail_tools.clear_cache_all_channels();
  519. }
  520. });
  521. },
  522. on_needaction_notification: function (message) {
  523. message = chat_manager.mail_tools.add_message(message, {
  524. channel_id: 'channel_inbox',
  525. show_notification: true,
  526. increment_unread: true
  527. });
  528. chat_manager.mail_tools.invalidate_caches(message.channel_ids);
  529. needaction_counter++;
  530. _.each(message.channel_ids, function (channel_id) {
  531. var channel = chat_manager.get_channel(channel_id);
  532. if (channel) {
  533. channel.needaction_counter++;
  534. }
  535. });
  536. chat_manager.bus.trigger('update_needaction', needaction_counter);
  537. },
  538. on_channel_notification: function (message) {
  539. var def;
  540. var channel_already_in_cache = true;
  541. if (message.channel_ids.length === 1) {
  542. channel_already_in_cache = !!chat_manager.get_channel(message.channel_ids[0]);
  543. def = chat_manager.join_channel(message.channel_ids[0], {autoswitch: false});
  544. } else {
  545. def = $.when();
  546. }
  547. def.then(function () {
  548. // don't increment unread if channel wasn't in cache yet as its unread counter has just been fetched
  549. chat_manager.mail_tools.add_message(message, { show_notification: true, increment_unread: channel_already_in_cache });
  550. chat_manager.mail_tools.invalidate_caches(message.channel_ids);
  551. });
  552. },
  553. on_partner_notification: function (data) {
  554. if (data.info === "unsubscribe") {
  555. var channel = chat_manager.get_channel(data.id);
  556. if (channel) {
  557. var msg;
  558. if (_.contains(['public', 'private'], channel.type)) {
  559. msg = _.str.sprintf(_t('You unsubscribed from <b>%s</b>.'), channel.name);
  560. } else {
  561. msg = _.str.sprintf(_t('You unpinned your conversation with <b>%s</b>.'), channel.name);
  562. }
  563. this.remove_channel(channel);
  564. chat_manager.bus.trigger("unsubscribe_from_channel", data.id);
  565. web_client.do_notify(_("Unsubscribed"), msg);
  566. }
  567. } else if (data.type === 'toggle_star') {
  568. chat_manager.mail_tools.on_toggle_star_notification(data);
  569. } else if (data.type === 'mark_as_read') {
  570. chat_manager.mail_tools.on_mark_as_read_notification(data);
  571. } else if (data.type === 'mark_as_unread') {
  572. chat_manager.mail_tools.on_mark_as_unread_notification(data);
  573. } else if (data.info === 'channel_seen') {
  574. chat_manager.mail_tools.on_channel_seen_notification(data);
  575. } else if (data.info === 'transient_message') {
  576. chat_manager.mail_tools.on_transient_message_notification(data);
  577. } else {
  578. chat_manager.mail_tools.on_chat_session_notification(data);
  579. }
  580. },
  581. on_toggle_star_notification: function (data) {
  582. _.each(data.message_ids, function (msg_id) {
  583. var message = _.findWhere(messages, { id: msg_id });
  584. if (message) {
  585. chat_manager.mail_tools.invalidate_caches(message.channel_ids);
  586. message.is_starred = data.starred;
  587. if (!message.is_starred) {
  588. chat_manager.mail_tools.remove_message_from_channel("channel_starred", message);
  589. starred_counter--;
  590. } else {
  591. chat_manager.mail_tools.add_to_cache(message, []);
  592. var channel_starred = chat_manager.get_channel('channel_starred');
  593. channel_starred.cache = _.pick(channel_starred.cache, "[]");
  594. starred_counter++;
  595. }
  596. chat_manager.bus.trigger('update_message', message);
  597. }
  598. });
  599. chat_manager.bus.trigger('update_starred', starred_counter);
  600. },
  601. on_mark_as_read_notification: function (data) {
  602. _.each(data.message_ids, function (msg_id) {
  603. var message = _.findWhere(messages, { id: msg_id });
  604. if (message) {
  605. chat_manager.mail_tools.invalidate_caches(message.channel_ids);
  606. chat_manager.mail_tools.remove_message_from_channel("channel_inbox", message);
  607. chat_manager.bus.trigger('update_message', message);
  608. }
  609. });
  610. if (data.channel_ids) {
  611. _.each(data.channel_ids, function (channel_id) {
  612. var channel = chat_manager.get_channel(channel_id);
  613. if (channel) {
  614. channel.needaction_counter = Math.max(channel.needaction_counter - data.message_ids.length, 0);
  615. }
  616. });
  617. } else { // if no channel_ids specified, this is a 'mark all read' in the inbox
  618. _.each(channels, function (channel) {
  619. channel.needaction_counter = 0;
  620. });
  621. }
  622. needaction_counter = Math.max(needaction_counter - data.message_ids.length, 0);
  623. chat_manager.bus.trigger('update_needaction', needaction_counter);
  624. },
  625. on_mark_as_unread_notification: function (data) {
  626. _.each(data.message_ids, function (message_id) {
  627. var message = _.findWhere(messages, { id: message_id });
  628. if (message) {
  629. chat_manager.mail_tools.invalidate_caches(message.channel_ids);
  630. chat_manager.mail_tools.add_channel_to_message(message, 'channel_inbox');
  631. chat_manager.mail_tools.add_to_cache(message, []);
  632. }
  633. });
  634. var channel_inbox = chat_manager.get_channel('channel_inbox');
  635. channel_inbox.cache = _.pick(channel_inbox.cache, "[]");
  636. _.each(data.channel_ids, function (channel_id) {
  637. var channel = chat_manager.get_channel(channel_id);
  638. if (channel) {
  639. channel.needaction_counter += data.message_ids.length;
  640. }
  641. });
  642. needaction_counter += data.message_ids.length;
  643. chat_manager.bus.trigger('update_needaction', needaction_counter);
  644. },
  645. on_channel_seen_notification: function (data) {
  646. var channel = chat_manager.get_channel(data.id);
  647. if (channel) {
  648. channel.last_seen_message_id = data.last_message_id;
  649. if (channel.unread_counter) {
  650. chat_manager.mail_tools.update_channel_unread_counter(channel, 0);
  651. }
  652. }
  653. },
  654. on_chat_session_notification: function (chat_session) {
  655. var channel;
  656. if ((chat_session.channel_type === "channel") && (chat_session.state === "open")) {
  657. chat_manager.mail_tools.add_channel(chat_session, {autoswitch: false});
  658. if (!chat_session.is_minimized && chat_session.info !== 'creation') {
  659. web_client.do_notify(_t("Invitation"), _t("You have been invited to: ") + chat_session.name);
  660. }
  661. }
  662. // partner specific change (open a detached window for example)
  663. if ((chat_session.state === "open") || (chat_session.state === "folded")) {
  664. channel = chat_session.is_minimized && chat_manager.get_channel(chat_session.id);
  665. if (channel) {
  666. channel.is_detached = true;
  667. channel.is_folded = (chat_session.state === "folded");
  668. chat_manager.bus.trigger("open_chat", channel);
  669. }
  670. } else if (chat_session.state === "closed") {
  671. channel = chat_manager.get_channel(chat_session.id);
  672. if (channel) {
  673. channel.is_detached = false;
  674. chat_manager.bus.trigger("close_chat", channel, {keep_open_if_unread: true});
  675. }
  676. }
  677. },
  678. on_presence_notification: function (data) {
  679. var dm = chat_manager.get_dm_from_partner_id(data.id);
  680. if (dm) {
  681. dm.status = data.im_status;
  682. chat_manager.bus.trigger('update_dm_presence', dm);
  683. }
  684. },
  685. on_transient_message_notification: function (data) {
  686. var last_message = _.last(messages);
  687. data.id = (last_message ? last_message.id : 0) + 0.01;
  688. data.author_id = data.author_id || ODOOBOT_ID;
  689. chat_manager.mail_tools.add_message(data);
  690. }
  691. });
  692. var cls = new MailTools();
  693. // Public interface
  694. //----------------------------------------------------------------------------------
  695. chat_manager.mail_tools = cls;
  696. // we add this function this way in order to make them extendable via MailTools.include({...})
  697. chat_manager.make_message = function(){
  698. return chat_manager.mail_tools.make_message.apply(chat_manager.mail_tools, arguments);
  699. };
  700. chat_manager.make_channel = function(){
  701. return chat_manager.mail_tools.make_channel.apply(chat_manager.mail_tools, arguments);
  702. };
  703. chat_manager.post_message = function (data, options) {
  704. options = options || {};
  705. var msg = {
  706. partner_ids: data.partner_ids,
  707. body: _.str.trim(data.content),
  708. attachment_ids: data.attachment_ids
  709. };
  710. if ('subject' in data) {
  711. msg.subject = data.subject;
  712. }
  713. if ('channel_id' in options) {
  714. // post a message in a channel or execute a command
  715. return ChannelModel.call(data.command ? 'execute_command' : 'message_post', [options.channel_id], _.extend(msg, {
  716. message_type: 'comment',
  717. content_subtype: 'html',
  718. subtype: 'mail.mt_comment',
  719. command: data.command,
  720. }));
  721. }
  722. if ('model' in options && 'res_id' in options) {
  723. // post a message in a chatter
  724. _.extend(msg, {
  725. content_subtype: data.content_subtype,
  726. context: data.context,
  727. message_type: data.message_type,
  728. subtype: data.subtype,
  729. subtype_id: data.subtype_id
  730. });
  731. if (options.model && options.res_id){
  732. var model = new Model(options.model);
  733. return model.call('message_post', [options.res_id], msg).then(function (msg_id) {
  734. return MessageModel.call('message_format', [msg_id]).then(function (msgs) {
  735. msgs[0].model = options.model;
  736. msgs[0].res_id = options.res_id;
  737. chat_manager.mail_tools.add_message(msgs[0]);
  738. });
  739. });
  740. } else {
  741. options.model = 'mail.compose.message';
  742. var compose_model = new Model(options.model);
  743. return compose_model.call('create', [msg, {default_parent_id: options.parent_id}]).then(function(id){
  744. return compose_model.call('send_mail_action', [id, {}]);
  745. });
  746. }
  747. }
  748. };
  749. chat_manager.get_message = function (id) {
  750. return _.findWhere(messages, {id: id});
  751. };
  752. chat_manager.get_messages = function (options) {
  753. var channel;
  754. if ('channel_id' in options && options.load_more) {
  755. // get channel messages, force load_more
  756. channel = this.get_channel(options.channel_id);
  757. return chat_manager.mail_tools.fetch_from_channel(channel, {domain: options.domain || {}, load_more: true});
  758. }
  759. if ('channel_id' in options) {
  760. // channel message, check in cache first
  761. channel = this.get_channel(options.channel_id);
  762. var channel_cache = chat_manager.mail_tools.get_channel_cache(channel, options.domain);
  763. if (channel_cache.loaded) {
  764. return $.when(channel_cache.messages);
  765. } else {
  766. return chat_manager.mail_tools.fetch_from_channel(channel, {domain: options.domain});
  767. }
  768. }
  769. if ('ids' in options) {
  770. // get messages from their ids (chatter is the main use case)
  771. return chat_manager.mail_tools.fetch_document_messages(options.ids, options).then(function(result) {
  772. chat_manager.mark_as_read(options.ids);
  773. return result;
  774. });
  775. }
  776. if ('model' in options && 'res_id' in options) {
  777. // get messages for a chatter, when it doesn't know the ids (use
  778. // case is when using the full composer)
  779. var domain = [['model', '=', options.model], ['res_id', '=', options.res_id]];
  780. MessageModel.call('message_fetch', [domain], {limit: 30}).then(function (msgs) {
  781. return _.map(msgs, chat_manager.mail_tools.add_message);
  782. });
  783. }
  784. };
  785. chat_manager.toggle_star_status = function (message_id) {
  786. return MessageModel.call('toggle_message_starred', [[message_id]]);
  787. };
  788. chat_manager.unstar_all = function () {
  789. return MessageModel.call('unstar_all', [[]], {});
  790. };
  791. chat_manager.mark_as_read = function (message_ids) {
  792. var ids = _.filter(message_ids, function (id) {
  793. var message = _.findWhere(messages, {id: id});
  794. // If too many messages, not all are fetched, and some might not be found
  795. return !message || message.is_needaction;
  796. });
  797. if (ids.length) {
  798. return MessageModel.call('set_message_done', [ids]);
  799. } else {
  800. return $.when();
  801. }
  802. };
  803. chat_manager.mark_all_as_read = function (channel, domain) {
  804. if ((channel.id === "channel_inbox" && needaction_counter) || (channel && channel.needaction_counter)) {
  805. return MessageModel.call('mark_all_as_read', [], {channel_ids: channel.id !== "channel_inbox" ? [channel.id] : [], domain: domain});
  806. }
  807. return $.when();
  808. };
  809. chat_manager.undo_mark_as_read = function (message_ids, channel) {
  810. return MessageModel.call('mark_as_unread', [message_ids, [channel.id]]);
  811. };
  812. chat_manager.mark_channel_as_seen = function (channel) {
  813. if (channel.unread_counter > 0 && channel.type !== 'static') {
  814. chat_manager.mail_tools.update_channel_unread_counter(channel, 0);
  815. channel_seen(channel);
  816. }
  817. };
  818. chat_manager.get_channels = function () {
  819. return _.clone(channels);
  820. };
  821. chat_manager.get_channel = function (id) {
  822. return _.findWhere(channels, {id: id});
  823. };
  824. chat_manager.get_dm_from_partner_id = function (partner_id) {
  825. return _.findWhere(channels, {direct_partner_id: partner_id});
  826. };
  827. chat_manager.all_history_loaded = function (channel, domain) {
  828. return chat_manager.mail_tools.get_channel_cache(channel, domain).all_history_loaded;
  829. };
  830. chat_manager.get_mention_partner_suggestions = function (channel) {
  831. if (!channel) {
  832. return mention_partner_suggestions;
  833. }
  834. if (!channel.members_deferred) {
  835. channel.members_deferred = ChannelModel
  836. .call("channel_fetch_listeners", [channel.uuid], {}, {shadow: true})
  837. .then(function (members) {
  838. var suggestions = [];
  839. _.each(mention_partner_suggestions, function (partners) {
  840. suggestions.push(_.filter(partners, function (partner) {
  841. return !_.findWhere(members, { id: partner.id });
  842. }));
  843. });
  844. return [members];
  845. });
  846. }
  847. return channel.members_deferred;
  848. };
  849. chat_manager.get_commands = function (channel) {
  850. return _.filter(commands, function (command) {
  851. return !command.channel_types || _.contains(command.channel_types, channel.server_type);
  852. });
  853. };
  854. chat_manager.get_canned_responses = function () {
  855. return canned_responses;
  856. };
  857. chat_manager.get_needaction_counter = function () {
  858. return needaction_counter;
  859. };
  860. chat_manager.get_starred_counter = function () {
  861. return starred_counter;
  862. };
  863. chat_manager.get_chat_unread_counter = function () {
  864. return chat_unread_counter;
  865. };
  866. chat_manager.get_unread_conversation_counter = function () {
  867. return unread_conversation_counter;
  868. };
  869. chat_manager.get_last_seen_message = function (channel) {
  870. if (channel.last_seen_message_id) {
  871. var messages = channel.cache['[]'].messages;
  872. var msg = _.findWhere(messages, {id: channel.last_seen_message_id});
  873. if (msg) {
  874. var i = _.sortedIndex(messages, msg, 'id') + 1;
  875. while (i < messages.length && (messages[i].is_author || messages[i].is_system_notification)) {
  876. msg = messages[i];
  877. i++;
  878. }
  879. return msg;
  880. }
  881. }
  882. };
  883. chat_manager.get_discuss_menu_id = function () {
  884. return discuss_menu_id;
  885. };
  886. chat_manager.detach_channel = function (channel) {
  887. return ChannelModel.call("channel_minimize", [channel.uuid, true], {}, {shadow: true});
  888. };
  889. chat_manager.remove_chatter_messages = function (model) {
  890. messages = _.reject(messages, function (message) {
  891. return message.channel_ids.length === 0 && message.model === model;
  892. });
  893. };
  894. chat_manager.create_channel = function (name, type) {
  895. var method = type === "dm" ? "channel_get" : "channel_create";
  896. var args = type === "dm" ? [[name]] : [name, type];
  897. return ChannelModel
  898. .call(method, args)
  899. .then(chat_manager.mail_tools.add_channel);
  900. };
  901. chat_manager.join_channel = function (channel_id, options) {
  902. if (channel_id in channel_defs) {
  903. // prevents concurrent calls to channel_join_and_get_info
  904. return channel_defs[channel_id];
  905. }
  906. var channel = this.get_channel(channel_id);
  907. if (channel) {
  908. // channel already joined
  909. channel_defs[channel_id] = $.when(channel);
  910. } else {
  911. channel_defs[channel_id] = ChannelModel
  912. .call('channel_join_and_get_info', [[channel_id]])
  913. .then(function (result) {
  914. return chat_manager.mail_tools.add_channel(result, options);
  915. });
  916. }
  917. return channel_defs[channel_id];
  918. };
  919. chat_manager.open_and_detach_dm = function (partner_id) {
  920. return ChannelModel.call('channel_get_and_minimize', [[partner_id]]).then(chat_manager.mail_tools.add_channel);
  921. };
  922. chat_manager.open_channel = function (channel) {
  923. chat_manager.bus.trigger(client_action_open ? 'open_channel' : 'detach_channel', channel);
  924. };
  925. chat_manager.unsubscribe = function (channel) {
  926. var def;
  927. if (_.contains(['public', 'private'], channel.type)) {
  928. return ChannelModel.call('action_unfollow', [[channel.id]]);
  929. } else {
  930. return ChannelModel.call('channel_pin', [channel.uuid, false]);
  931. }
  932. };
  933. chat_manager.close_chat_session = function (channel_id) {
  934. var channel = this.get_channel(channel_id);
  935. ChannelModel.call("channel_fold", [], {uuid : channel.uuid, state : "closed"}, {shadow: true});
  936. };
  937. chat_manager.fold_channel = function (channel_id, folded) {
  938. var args = {
  939. uuid: this.get_channel(channel_id).uuid
  940. };
  941. if (_.isBoolean(folded)) {
  942. args.state = folded ? 'folded' : 'open';
  943. }
  944. return ChannelModel.call("channel_fold", [], args, {shadow: true});
  945. };
  946. /**
  947. * Special redirection handling for given model and id
  948. *
  949. * If the model is res.partner, and there is a user associated with this
  950. * partner which isn't the current user, open the DM with this user.
  951. * Otherwhise, open the record's form view, if this is not the current user's.
  952. */
  953. chat_manager.redirect = function (res_model, res_id, dm_redirection_callback) {
  954. var self = this;
  955. var redirect_to_document = function (res_model, res_id, view_id) {
  956. web_client.do_action({
  957. type:'ir.actions.act_window',
  958. view_type: 'form',
  959. view_mode: 'form',
  960. res_model: res_model,
  961. views: [[view_id || false, 'form']],
  962. res_id: res_id
  963. });
  964. };
  965. if (res_model === "res.partner") {
  966. var domain = [["partner_id", "=", res_id]];
  967. UserModel.call("search", [domain]).then(function (user_ids) {
  968. if (user_ids.length && user_ids[0] !== session.uid && dm_redirection_callback) {
  969. self.create_channel(res_id, 'dm').then(dm_redirection_callback);
  970. } else {
  971. redirect_to_document(res_model, res_id);
  972. }
  973. });
  974. } else {
  975. new Model(res_model).call('get_formview_id', [[res_id], session.context]).then(function (view_id) {
  976. redirect_to_document(res_model, res_id, view_id);
  977. });
  978. }
  979. };
  980. chat_manager.get_channels_preview = function (channels) {
  981. var channels_preview = _.map(channels, function (channel) {
  982. var info = _.pick(channel, 'id', 'is_chat', 'name', 'status', 'unread_counter');
  983. info.last_message = _.last(channel.cache['[]'].messages);
  984. if (!info.is_chat) {
  985. info.image_src = '/web/image/mail.channel/'+channel.id+'/image_small';
  986. } else if (channel.direct_partner_id) {
  987. info.image_src = '/web/image/res.partner/'+channel.direct_partner_id+'/image_small';
  988. } else {
  989. info.image_src = '/mail/static/src/img/smiley/avatar.jpg';
  990. }
  991. return info;
  992. });
  993. var missing_channels = _.where(channels_preview, {last_message: undefined});
  994. if (!channels_preview_def) {
  995. if (missing_channels.length) {
  996. var missing_channel_ids = _.pluck(missing_channels, 'id');
  997. channels_preview_def = ChannelModel.call('channel_fetch_preview', [missing_channel_ids], {}, {shadow: true});
  998. } else {
  999. channels_preview_def = $.when();
  1000. }
  1001. }
  1002. return channels_preview_def.then(function (channels) {
  1003. _.each(missing_channels, function (channel_preview) {
  1004. var channel = _.findWhere(channels, {id: channel_preview.id});
  1005. if (channel) {
  1006. channel_preview.last_message = chat_manager.mail_tools.add_message(channel.last_message);
  1007. }
  1008. });
  1009. return _.filter(channels_preview, function (channel) {
  1010. return channel.last_message; // remove empty channels
  1011. });
  1012. });
  1013. };
  1014. chat_manager.get_message_body_preview = function (message_body) {
  1015. return utils.parse_and_transform(message_body, utils.inline);
  1016. };
  1017. chat_manager.search_partner = function (search_val, limit) {
  1018. var def = $.Deferred();
  1019. var values = [];
  1020. // search among prefetched partners
  1021. var search_regexp = new RegExp(_.str.escapeRegExp(utils.unaccent(search_val)), 'i');
  1022. _.each(mention_partner_suggestions, function (partners) {
  1023. if (values.length < limit) {
  1024. values = values.concat(_.filter(partners, function (partner) {
  1025. return session.partner_id !== partner.id && search_regexp.test(partner.name);
  1026. })).splice(0, limit);
  1027. }
  1028. });
  1029. if (!values.length) {
  1030. // extend the research to all users
  1031. def = PartnerModel.call('im_search', [search_val, limit || 20], {}, {shadow: true});
  1032. } else {
  1033. def = $.when(values);
  1034. }
  1035. return def.then(function (values) {
  1036. var autocomplete_data = _.map(values, function (value) {
  1037. return { id: value.id, value: value.name, label: value.name };
  1038. });
  1039. return _.sortBy(autocomplete_data, 'label');
  1040. });
  1041. };
  1042. chat_manager.bus.on('client_action_open', null, function (open) {
  1043. client_action_open = open;
  1044. });
  1045. // In order to extend init use chat_manager.is_ready Derrered object. See example in mail_arhive module
  1046. function init () {
  1047. chat_manager.mail_tools.add_channel({
  1048. id: "channel_inbox",
  1049. name: _lt("Inbox"),
  1050. type: "static",
  1051. }, { display_needactions: true });
  1052. chat_manager.mail_tools.add_channel({
  1053. id: "channel_starred",
  1054. name: _lt("Starred"),
  1055. type: "static"
  1056. });
  1057. // unsubscribe and then subscribe to the event, to avoid duplication of new messages
  1058. bus.off('notification');
  1059. bus.on('notification', null, function(){
  1060. chat_manager.mail_tools.on_notification.apply(chat_manager.mail_tools, arguments);
  1061. });
  1062. return session.rpc('/mail/client_action').then(function (result) {
  1063. _.each(result.channel_slots, function (channels) {
  1064. _.each(channels, chat_manager.mail_tools.add_channel);
  1065. });
  1066. needaction_counter = result.needaction_inbox_counter;
  1067. starred_counter = result.starred_counter;
  1068. commands = _.map(result.commands, function (command) {
  1069. return _.extend({ id: command.name }, command);
  1070. });
  1071. mention_partner_suggestions = result.mention_partner_suggestions;
  1072. discuss_menu_id = result.menu_id;
  1073. // Shortcodes: canned responses and emojis
  1074. _.each(result.shortcodes, function (s) {
  1075. if (s.shortcode_type === 'text') {
  1076. canned_responses.push(_.pick(s, ['id', 'source', 'substitution']));
  1077. } else {
  1078. emojis.push(_.pick(s, ['id', 'source', 'substitution', 'description']));
  1079. emoji_substitutions[_.escape(s.source)] = s.substitution;
  1080. }
  1081. });
  1082. bus.start_polling();
  1083. });
  1084. }
  1085. chat_manager.is_ready = init();
  1086. return {
  1087. ODOOBOT_ID: ODOOBOT_ID,
  1088. chat_manager: chat_manager,
  1089. MailTools: MailTools
  1090. };
  1091. });