From 644a20058f30caaf99c714493de16fb523411c04 Mon Sep 17 00:00:00 2001 From: ommo73 Date: Wed, 24 Apr 2019 12:43:00 +0500 Subject: [PATCH] :arrow_up::one::two: mail_private Port --- mail_private/README.rst | 10 +- mail_private/__init__.py | 1 + mail_private/__manifest__.py | 33 +++-- mail_private/full_composer_wizard.xml | 3 + mail_private/models.py | 77 +++-------- mail_private/static/src/js/mail_private.js | 137 ++++++++++--------- mail_private/static/src/js/test_private.js | 29 ++-- mail_private/static/src/xml/mail_private.xml | 11 +- mail_private/template.xml | 3 + mail_private/tests/__init__.py | 1 - mail_private/tests/test_js.py | 14 +- 11 files changed, 154 insertions(+), 165 deletions(-) diff --git a/mail_private/README.rst b/mail_private/README.rst index ea60ffe..f280cae 100644 --- a/mail_private/README.rst +++ b/mail_private/README.rst @@ -25,7 +25,7 @@ Maintainers To get a guaranteed support you are kindly requested to purchase the module - at `odoo apps store `__. + at `odoo apps store `__. Thank you for understanding! @@ -34,14 +34,14 @@ Maintainers Further information =================== -Demo: http://runbot.it-projects.info/demo/mail-addons/11.0 +Demo: http://runbot.it-projects.info/demo/mail-addons/12.0 -HTML Description: https://apps.odoo.com/apps/modules/11.0/mail_private/ +HTML Description: https://apps.odoo.com/apps/modules/12.0/mail_private/ Usage instructions: ``_ Changelog: ``_ -Notifications on updates: `via Atom `_, `by Email `_ +Notifications on updates: `via Atom `_, `by Email `_ -Tested on Odoo 11.0 3d09560ffc779e169ed9488e4e07928204dd234d +Tested on Odoo 12.0 5240bc2303816348837425b88fc7ee3ff7de2336 diff --git a/mail_private/__init__.py b/mail_private/__init__.py index a9e3372..ea29fd5 100644 --- a/mail_private/__init__.py +++ b/mail_private/__init__.py @@ -1,2 +1,3 @@ +# License LGPL-3.0 (https://www.gnu.org/licenses/lgpl.html). from . import models diff --git a/mail_private/__manifest__.py b/mail_private/__manifest__.py index 9ae93b5..a97ec25 100644 --- a/mail_private/__manifest__.py +++ b/mail_private/__manifest__.py @@ -1,37 +1,52 @@ +# Copyright 2018 Kolushov Alexandr +# Copyright 2019 Artem Rafailov +# License LGPL-3.0 (https://www.gnu.org/licenses/lgpl.html). { "name": """Internal Messaging""", "summary": """Send private messages to specified recipients, regardless of who are in followers list.""", "category": "Discuss", - "images": ['images/mail_private_image.png'], - "version": "11.0.1.0.2", + # "live_test_url": "http://apps.it-projects.info/shop/product/DEMO-URL?version=12.0", + "images": [], + "version": "12.0.1.0.2", "application": False, "author": "IT-Projects LLC, Pavel Romanchenko", "support": "apps@it-projects.info", - "website": "https://it-projects.info", - "license": "GPL-3", + "website": "https://it-projects.info/", + "license": "LGPL-3", "price": 50.00, "currency": "EUR", "depends": [ - "mail", - "base", - "mail_base" + "mail" ], "external_dependencies": {"python": [], "bin": []}, "data": [ 'template.xml', 'full_composer_wizard.xml', ], + "demo": [ + ], "qweb": [ 'static/src/xml/mail_private.xml', ], - "demo": [], "post_load": None, "pre_init_hook": None, "post_init_hook": None, + "uninstall_hook": None, "auto_install": False, - "installable": False, + "installable": True, + + # "demo_title": "{MODULE_NAME}", + # "demo_addons": [ + # ], + # "demo_addons_hidden": [ + # ], + # "demo_url": "DEMO-URL", + # "demo_summary": "{SHORT_DESCRIPTION_OF_THE_MODULE}", + # "demo_images": [ + # "images/MAIN_IMAGE", + # ] } diff --git a/mail_private/full_composer_wizard.xml b/mail_private/full_composer_wizard.xml index 9f57a20..1410548 100644 --- a/mail_private/full_composer_wizard.xml +++ b/mail_private/full_composer_wizard.xml @@ -1,4 +1,7 @@ + diff --git a/mail_private/models.py b/mail_private/models.py index dd6b60d..4ae1b91 100644 --- a/mail_private/models.py +++ b/mail_private/models.py @@ -1,3 +1,8 @@ +# Copyright 2016 x620 +# Copyright 2017 Artyom Losev +# Copyright 2018 Ivan Yelizariev +# Copyright 2019 Artem Rafailov +# License LGPL-3.0 (https://www.gnu.org/licenses/lgpl.html). from odoo import models, fields, api @@ -41,68 +46,18 @@ class MailMessage(models.Model): return result @api.multi - def _notify(self, force_send=False, send_after_commit=True, user_signature=True): + def _notify(self, record, msg_vals, force_send=False, send_after_commit=True, model_description=False, mail_auto_delete=True): self_sudo = self.sudo() - if not self_sudo.is_private: - super(MailMessage, self)._notify(force_send, send_after_commit, user_signature) + msg_vals = msg_vals if msg_vals else {} + if 'is_private' not in self_sudo._context or not self_sudo._context['is_private']: + return super(MailMessage, self)._notify(record, msg_vals, force_send, send_after_commit, model_description, mail_auto_delete) else: - self._notify_mail_private(force_send, send_after_commit, user_signature) + rdata = self._notify_compute_internal_recipients(record, msg_vals) + return self._notify_recipients(rdata, record, msg_vals, force_send, send_after_commit, model_description, mail_auto_delete) @api.multi - def _notify_mail_private(self, force_send=False, send_after_commit=True, user_signature=True): - """ Compute recipients to notify based on specified recipients and document - followers. Delegate notification to partners to send emails and bus notifications - and to channels to broadcast messages on channels """ - group_user = self.env.ref('base.group_user') - # have a sudoed copy to manipulate partners (public can go here with website modules like forum / blog / ... ) - self_sudo = self.sudo() - - self.ensure_one() - partners_sudo = self_sudo.partner_ids - channels_sudo = self_sudo.channel_ids - - if self_sudo.subtype_id and self.model and self.res_id: - followers = self_sudo.env['mail.followers'].search([ - ('res_model', '=', self.model), - ('res_id', '=', self.res_id), - ('subtype_ids', 'in', self_sudo.subtype_id.id), - ]) - if self_sudo.subtype_id.internal: - followers = followers.filtered(lambda fol: fol.channel_id or (fol.partner_id.user_ids and group_user in fol.partner_id.user_ids[0].mapped('groups_id'))) - channels_sudo |= followers.mapped('channel_id') - - # remove author from notified partners - if not self._context.get('mail_notify_author', False) and self_sudo.author_id: - partners_sudo = partners_sudo - self_sudo.author_id - - # update message, with maybe custom valuesz - message_values = {} - if channels_sudo: - message_values['channel_ids'] = [(6, 0, channels_sudo.ids)] - if partners_sudo: - message_values['needaction_partner_ids'] = [(6, 0, partners_sudo.ids)] - if self.model and self.res_id and hasattr(self.env[self.model], 'message_get_message_notify_values'): - message_values.update(self.env[self.model].browse(self.res_id).message_get_message_notify_values(self, message_values)) - if message_values: - self.write(message_values) - - # notify partners and channels - # those methods are called as SUPERUSER because portal users posting messages - # have no access to partner model. Maybe propagating a real uid could be necessary. - email_channels = channels_sudo.filtered(lambda channel: channel.email_send) - notif_partners = partners_sudo.filtered(lambda partner: 'inbox' in partner.mapped('user_ids.notification_type')) - if email_channels or partners_sudo - notif_partners: - partners_sudo.search([ - '|', - ('id', 'in', (partners_sudo - notif_partners).ids), - ('channel_ids', 'in', email_channels.ids), - ('email', '!=', self_sudo.author_id.email or self_sudo.email_from), - ])._notify(self, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature) - channels_sudo._notify(self) - - # Discard cache, because child / parent allow reading and therefore - # change access rights. - if self.parent_id: - self.parent_id.invalidate_cache() - - return True + def _notify_compute_internal_recipients(self, record, msg_vals): + recipient_data = super(MailMessage, self)._notify_compute_recipients(record, msg_vals) + pids = [x[1] for x in msg_vals.get('partner_ids')] if 'partner_ids' in msg_vals else self.sudo().partner_ids.ids + recipient_data['partners'] = [i for i in recipient_data['partners'] if i['id'] in pids] + return recipient_data diff --git a/mail_private/static/src/js/mail_private.js b/mail_private/static/src/js/mail_private.js index 4b3a7d0..8432807 100644 --- a/mail_private/static/src/js/mail_private.js +++ b/mail_private/static/src/js/mail_private.js @@ -2,19 +2,19 @@ Copyright 2016 manavi Copyright 2017-2018 Artyom Losev Copyright 2018 Kolushov Alexandr + Copyright 2019 Artem Rafailov License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). */ odoo.define('mail_private', function (require) { 'use strict'; var core = require('web.core'); var Chatter = require('mail.Chatter'); -var ChatterComposer = require('mail.ChatterComposer'); -var chat_manager = require('mail_base.base').chat_manager; +var ChatterComposer = require('mail.composer.Chatter'); var session = require('web.session'); var rpc = require('web.rpc'); var config = require('web.config'); -var utils = require('mail.utils'); +var mailUtils = require('mail.utils'); Chatter.include({ @@ -36,51 +36,55 @@ Chatter.include({ _openComposer: function (options) { var self = this; - var old_composer = this.composer; + var old_composer = this._composer; // create the new composer - this.composer = new ChatterComposer(this, this.record.model, options.suggested_partners || [], { - commands_enabled: false, + this._composer = new ChatterComposer(this, this.record.model, options.suggested_partners || [], { + commandsEnabled: false, context: this.context, - input_min_height: 50, - input_max_height: Number.MAX_VALUE, - input_baseline: 14, - is_log: options && options.is_log, - record_name: this.record_name, - default_body: old_composer && old_composer.$input && old_composer.$input.val(), - default_mention_selections: old_composer && old_composer.mention_get_listener_selections(), + inputMinHeight: 50, + isLog: options && options.isLog, + recordName: this.recordName, + defaultBody: old_composer && old_composer.$input && old_composer.$input.val(), + defaultMentionSelections: old_composer && old_composer.getMentionListenerSelections(), + attachmentIds: (old_composer && old_composer.get('attachment_ids')) || [], is_private: options.is_private }); - this.composer.on('input_focused', this, function () { - this.composer.mention_set_prefetched_partners(this.mentionSuggestions || []); + this._composer.on('input_focused', this, function () { + this._composer.mentionSetPrefetchedPartners(this._mentionSuggestions || []); }); - this.composer.insertAfter(this.$('.o_chatter_topbar')).then(function () { + this._composer.insertAfter(this.$('.o_chatter_topbar')).then(function () { // destroy existing composer if (old_composer) { old_composer.destroy(); } - if (!config.device.touch) { - self.composer.focus(); - } - self.composer.on('post_message', self, function (message) { + self._composer.focus(); + self._composer.on('post_message', self, function (messageData) { if (options.is_private) { - self.composer.options.is_log = true; + self._composer.options.isLog = true; } - self.fields.thread.postMessage(message).then(function () { - - self._closeComposer(true); - if (self.postRefresh === 'always' || (self.postRefresh === 'recipients' && message.partner_ids.length)) { - self.trigger_up('reload'); - } + self._discardOnReload(messageData).then(function () { + self._disableComposer(); + self.fields.thread.postMessage(messageData).then(function () { + self._closeComposer(true); + if (self._reloadAfterPost(messageData)) { + self.trigger_up('reload'); + } else if (messageData.attachment_ids.length) { + self._reloadAttachmentBox(); + self.trigger_up('reload', {fieldNames: ['message_attachment_count'], keepChanges: true}); + } + }).fail(function () { + self._enableComposer(); + }); }); }); - var toggle_post_private = self.composer.options.is_private || false; - self.composer.on('need_refresh', self, self.trigger_up.bind(self, 'reload')); - self.composer.on('close_composer', null, self._closeComposer.bind(self, true)); + var toggle_post_private = self._composer.options.is_private || false; + self._composer.on('need_refresh', self, self.trigger_up.bind(self, 'reload')); + self._composer.on('close_composer', null, self._closeComposer.bind(self, true)); self.$el.addClass('o_chatter_composer_active'); self.$('.o_chatter_button_new_message, .o_chatter_button_log_note, .oe_compose_post_private').removeClass('o_active'); - self.$('.o_chatter_button_new_message').toggleClass('o_active', !self.composer.options.is_log && !self.composer.options.is_private); - self.$('.o_chatter_button_log_note').toggleClass('o_active', (self.composer.options.is_log && !options.is_private)); + self.$('.o_chatter_button_new_message').toggleClass('o_active', !self._composer.options.isLog && !self._composer.options.is_private); + self.$('.o_chatter_button_log_note').toggleClass('o_active', self._composer.options.isLog && !options.is_private); self.$('.oe_compose_post_private').toggleClass('o_active', toggle_post_private); }); }, @@ -102,8 +106,8 @@ Chatter.include({ }); ChatterComposer.include({ - init: function (parent, model, suggested_partners, options) { - this._super(parent, model, suggested_partners, options); + init: function (parent, model, suggestedPartners, options) { + this._super(parent, model, suggestedPartners, options); this.events['click .oe_composer_uncheck'] = 'on_uncheck_recipients'; if (typeof options.is_private === 'undefined') { // otherwise it causes an error in context creating function @@ -111,40 +115,39 @@ ChatterComposer.include({ } }, - preprocess_message: function () { + _preprocessMessage: function () { var self = this; var def = $.Deferred(); this._super().then(function (message) { message = _.extend(message, { subtype: 'mail.mt_comment', message_type: 'comment', - content_subtype: 'html', - context: self.context, + context: _.defaults({}, self.context, session.user_context), }); // Subtype - if (self.options.is_log) { + if (self.options.isLog) { message.subtype = 'mail.mt_note'; } if (self.options.is_private) { - message.is_private = true; + message.context.is_private = true; message.channel_ids = self.get_checked_channel_ids(); } // Partner_ids - if (!self.options.is_log) { - var checked_suggested_partners = self.get_checked_suggested_partners(); - self.check_suggested_partners(checked_suggested_partners).done(function (partner_ids) { - message.partner_ids = (message.partner_ids || []).concat(partner_ids); + if (self.options.isLog) { + def.resolve(message); + } else { + var check_suggested_partners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(check_suggested_partners).done(function (partnerIDs) { + message.partner_ids = (message.partner_ids || []).concat(partnerIDs); // update context message.context = _.defaults({}, message.context, { mail_post_autofollow: true, }); def.resolve(message); }); - } else { - def.resolve(message); } }); return def; @@ -156,25 +159,30 @@ ChatterComposer.include({ }); }, - on_open_full_composer: function() { - if (!this.do_check_attachment_upload()){ + _onOpenFullComposer: function () { + if (!this._doCheckAttachmentUpload()){ return false; } var self = this; - var recipient_done = $.Deferred(); - if (this.options.is_log) { - recipient_done.resolve([]); - } else { - var checked_suggested_partners = this.get_checked_suggested_partners(); - recipient_done = this.check_suggested_partners(checked_suggested_partners); - } - recipient_done.then(function (partner_ids) { + var recipientDoneDef = $.Deferred(); + this.trigger_up('discard_record_changes', { + proceed: function () { + if (self.options.isLog) { + recipientDoneDef.resolve([]); + } else { + var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(checkedSuggestedPartners) + .then(recipientDoneDef.resolve.bind(recipientDoneDef)); + } + }, + }); + recipientDoneDef.then(function (partnerIDs) { var context = { default_parent_id: self.id, - default_body: utils.get_text2html(self.$input.val()), + default_body: mailUtils.getTextToHTML(self.$input.val()), default_attachment_ids: _.pluck(self.get('attachment_ids'), 'id'), - default_partner_ids: partner_ids, - default_is_log: self.options.is_log, + default_partner_ids: partnerIDs, + default_is_log: self.options.isLog, mail_post_autofollow: true, is_private: self.options.is_private, }; @@ -187,7 +195,7 @@ ChatterComposer.include({ context.default_model = self.context.default_model; context.default_res_id = self.context.default_res_id; } - self.do_action({ + var action = { type: 'ir.actions.act_window', res_model: 'mail.compose.message', view_mode: 'form', @@ -195,17 +203,14 @@ ChatterComposer.include({ views: [[false, 'form']], target: 'new', context: context, - }, { - on_close: function() { - self.trigger('need_refresh'); - var parent = self.getParent(); - chat_manager.get_messages({model: parent.model, res_id: parent.res_id}); - }, + }; + self.do_action(action, { + on_close: self.trigger.bind(self, 'need_refresh'), }).then(self.trigger.bind(self, 'close_composer')); }); }, - get_checked_suggested_partners: function () { + _getCheckedSuggestedPartners: function () { var checked_partners = this._super(this, arguments); // workaround: odoo code works only when all partners are checked intially, // while may select only some of them (internal recepients) diff --git a/mail_private/static/src/js/test_private.js b/mail_private/static/src/js/test_private.js index 4b6efda..2b30b4d 100644 --- a/mail_private/static/src/js/test_private.js +++ b/mail_private/static/src/js/test_private.js @@ -1,17 +1,30 @@ -/* Copyright 2018 Kolushov Alexandr +/* Copyright 2018-2019 Kolushov Alexandr + Copyright 2019 Artem Rafailov License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).*/ -odoo.define('mail_private.tour', function (require) { - "use strict"; +odoo.define('mail_private.tour', function(require) { +"use strict"; - var tour = require("web_tour.tour"); var core = require('web.core'); + var tour = require('web_tour.tour'); var _t = core._t; var email = 'mail_private test email'; - var steps = [{ - trigger: '.o_thread_message strong.o_mail_redirect:contains("Agrolait")', - content: _t("Open Partners Form"), + var steps = [tour.STEPS.SHOW_APPS_MENU_ITEM, { + trigger: '.fa.fa-cog.o_mail_channel_settings', + content: _t('Select channel settings'), position: 'bottom', + }, { + trigger: '.nav-link:contains("Members")', + content: _t('Go to the list of subscribers'), + position: 'bottom', + }, { + trigger: '.o_data_cell:contains("YourCompany, Marc Demo")', + content: _t("Select a user"), + position: "bottom", + }, { + trigger: '.o_form_uri.o_field_widget:contains("YourCompany, Marc Demo")', + content: _t("Go to user page"), + position: "bottom" }, { trigger: "button.oe_compose_post_private", content: _t("Click on Private mail creating button"), @@ -26,7 +39,7 @@ odoo.define('mail_private.tour', function (require) { content: _t("Uncheck all Followers"), timeout: 10000, }, { - trigger: "div.o_composer_suggested_partners input:first", + trigger: "div.o_composer_suggested_partners", content: _t("Check the first one"), }, { trigger: "textarea.o_composer_text_field:first", diff --git a/mail_private/static/src/xml/mail_private.xml b/mail_private/static/src/xml/mail_private.xml index baadbb5..f04b9db 100644 --- a/mail_private/static/src/xml/mail_private.xml +++ b/mail_private/static/src/xml/mail_private.xml @@ -1,13 +1,16 @@ +