From 2dfb242689ec41d21ceac7f7e1690deac3d85316 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Thu, 7 Jun 2018 15:55:30 +0500 Subject: [PATCH] [11.0][PORT] mail_move_message (#146) --- mail_move_message/README.rst | 49 +++++- mail_move_message/__init__.py | 2 + mail_move_message/__manifest__.py | 30 ++++ mail_move_message/__openerp__.py | 21 --- mail_move_message/controllers/__init__.py | 2 + mail_move_message/controllers/main.py | 57 +----- .../data/mail_move_message_data.xml | 4 + mail_move_message/doc/index.rst | 35 ++++ mail_move_message/mail_move_message_models.py | 162 +++++++++++------- mail_move_message/mail_move_message_views.xml | 95 +++++----- .../static/description/delete-message.png | Bin 40214 -> 32244 bytes mail_move_message/static/description/icon.png | Bin 1531 -> 2140 bytes .../static/description/inbox-move.png | Bin 58044 -> 32686 bytes .../static/description/index.html | 72 ++++++-- .../static/description/record-move-back.png | Bin 43479 -> 44557 bytes .../static/src/js/mail_move_message.js | 145 +++++++--------- .../static/src/xml/mail_move_message_main.xml | 8 +- 17 files changed, 397 insertions(+), 285 deletions(-) create mode 100644 mail_move_message/__manifest__.py delete mode 100644 mail_move_message/__openerp__.py create mode 100644 mail_move_message/doc/index.rst diff --git a/mail_move_message/README.rst b/mail_move_message/README.rst index 5957d56..f3e420a 100644 --- a/mail_move_message/README.rst +++ b/mail_move_message/README.rst @@ -1,10 +1,49 @@ -Mail relocation -=============== +.. image:: https://img.shields.io/badge/license-LGPL--3-blue.png + :target: https://www.gnu.org/licenses/lgpl + :alt: License: LGPL-3 -Demo: http://runbot.it-projects.info/demo/mail-addons/9.0 +================= + Mail Relocation +================= -Description: https://www.odoo.com/apps/modules/9.0/mail_move_message/ +The module allows to relocate messages between models + +Credits +======= + +Contributors +------------ +* `Ivan Yelizariev `__ + +Sponsors +-------- +* `IT-Projects LLC `__ + +Maintainers +----------- +* `IT-Projects LLC `__ + + To get a guaranteed support + you are kindly requested to purchase the module + at `odoo apps store `__. + + Thank you for understanding! + + `IT-Projects Team `__ + +Further information +=================== + +Demo: http://runbot.it-projects.info/demo/mail-addons/11.0 + +HTML Description: https://apps.odoo.com/apps/modules/11.0/mail_move_message/ + +Usage instructions: ``_ + +Changelog: ``_ + +Notifications on updates: `via Atom `_, `by Email `_ Further information and discussion: http://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html -Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625 +Tested on Odoo 11.0 e9454e79e27d0b85546132cbe00b391e974c66bf diff --git a/mail_move_message/__init__.py b/mail_move_message/__init__.py index 45edf0d..da664f4 100644 --- a/mail_move_message/__init__.py +++ b/mail_move_message/__init__.py @@ -1,2 +1,4 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + from . import controllers from . import mail_move_message_models diff --git a/mail_move_message/__manifest__.py b/mail_move_message/__manifest__.py new file mode 100644 index 0000000..ebc20bc --- /dev/null +++ b/mail_move_message/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright 2016 Ildar Nasyrov +# Copyright 2017 Ilmir Karamov +# Copyright 2017 Lilia Salihova +# Copyright 2016-2018 Ivan Yelizariev +# Copyright 2018 Kolushov Alexandr +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +{ + 'name': 'Mail Relocation', + 'version': '11.0.1.0.5', + 'author': 'IT-Projects LLC, Ivan Yelizariev, Pavel Romanchenko', + 'license': 'LGPL-3', + 'category': 'Discuss', + 'images': ['images/m1.png'], + "support": "apps@it-projects.info", + 'website': 'https://twitter.com/yelizariev', + 'price': 100.00, + 'currency': 'EUR', + 'depends': [ + 'mail_all', + ], + 'data': [ + 'mail_move_message_views.xml', + 'data/mail_move_message_data.xml', + ], + 'qweb': [ + 'static/src/xml/mail_move_message_main.xml', + ], + 'installable': True, +} diff --git a/mail_move_message/__openerp__.py b/mail_move_message/__openerp__.py deleted file mode 100644 index bdb65ad..0000000 --- a/mail_move_message/__openerp__.py +++ /dev/null @@ -1,21 +0,0 @@ -{ - 'name': 'Mail relocation', - 'version': '1.0.5', - 'author': 'IT-Projects LLC, Ivan Yelizariev, Pavel Romanchenko', - 'license': 'LGPL-3', - 'category': 'Discuss', - 'images': ['images/m1.png'], - "support": "apps@it-projects.info", - 'website': 'https://twitter.com/yelizariev', - 'price': 100.00, - 'currency': 'EUR', - 'depends': ['mail_all', 'web_polymorphic_field'], - 'data': [ - 'mail_move_message_views.xml', - 'data/mail_move_message_data.xml', - ], - 'qweb': [ - 'static/src/xml/mail_move_message_main.xml', - ], - 'installable': False, -} diff --git a/mail_move_message/controllers/__init__.py b/mail_move_message/controllers/__init__.py index 12a7e52..373d38d 100644 --- a/mail_move_message/controllers/__init__.py +++ b/mail_move_message/controllers/__init__.py @@ -1 +1,3 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + from . import main diff --git a/mail_move_message/controllers/main.py b/mail_move_message/controllers/main.py index 69b061f..e0974bc 100644 --- a/mail_move_message/controllers/main.py +++ b/mail_move_message/controllers/main.py @@ -1,6 +1,8 @@ -from odoo.addons.web.controllers.main import DataSet -from odoo.tools.translate import _ -from odoo import http +# Copyright 2016 Ildar Nasyrov +# Copyright 2018 Ivan Yelizariev +# Copyright 2018 Kolushov Alexandr +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + from odoo.http import request from odoo.addons.bus.controllers.main import BusController @@ -15,52 +17,3 @@ class MailChatController(BusController): channels.append((request.db, 'mail_move_message')) channels.append((request.db, 'mail_move_message.delete_message')) return super(MailChatController, self)._poll(dbname, channels, last, options) - - -class DataSetCustom(DataSet): - - def _extend_name(self, model, records): - Model = request.env[model] - fields = Model.fields_get() - contact_field = False - for n, f in fields.iteritems(): - if f['type'] == 'many2one' and f['relation'] == 'res.partner': - contact_field = n - break - partner_info = {} - if contact_field: - partner_info = Model.browse([r[0] for r in records]).read([contact_field]) - partner_info = dict([(p['id'], p[contact_field]) for p in partner_info]) - res = [] - for r in records: - if partner_info.get(r[0]): - res.append((r[0], _('%s [%s] ID %s') % (r[1], partner_info.get(r[0])[1], r[0]))) - else: - res.append((r[0], _('%s ID %s') % (r[1], r[0]))) - return res - - @http.route('/web/dataset/call_kw//name_search', type='json', auth="user") - def name_search(self, model, method, args, kwargs): - context = kwargs.get('context') - if context and context.get('extended_name_with_contact'): - # add order by ID desc - Model = request.env[model] - search_args = list(kwargs.get('args') or []) - limit = int(kwargs.get('limit') or 100) - operator = kwargs.get('operator') - name = kwargs.get('name') - if Model._rec_name and (not name == '' and operator == 'ilike'): - search_args += [(Model._rec_name, operator, name)] - records = Model.search(search_args, limit=limit, order='id desc') - res = records.name_get() - return self._extend_name(model, res) - - return self._call_kw(model, method, args, kwargs) - - @http.route('/web/dataset/call_kw//name_get', type='json', auth="user") - def name_get(self, model, method, args, kwargs): - res = self._call_kw(model, method, args, kwargs) - context = kwargs.get('context') - if context and context.get('extended_name_with_contact'): - res = self._extend_name(model, res) - return res diff --git a/mail_move_message/data/mail_move_message_data.xml b/mail_move_message/data/mail_move_message_data.xml index f63eaae..9131606 100644 --- a/mail_move_message/data/mail_move_message_data.xml +++ b/mail_move_message/data/mail_move_message_data.xml @@ -1,4 +1,8 @@ + + diff --git a/mail_move_message/doc/index.rst b/mail_move_message/doc/index.rst new file mode 100644 index 0000000..8d3d134 --- /dev/null +++ b/mail_move_message/doc/index.rst @@ -0,0 +1,35 @@ +================= + Mail Relocation +================= + +Installation +============ + +* `Install `__ this module in a usual way + +Configuration +============= + +* Open ``[[ Settings ]] >> Mail Relocation`` menu +* In **Model** field add models to be used for message relocation +* Check the box **[x] Move Followers** to move followers by default when relocation + +Usage +===== + +Move message +------------ + +* Open ``[[ Discuss ]] >> Inbox`` menu +* Click on icon of two cross arrows +* Select a record you need +* Click **Move** +RESULT: The message has been moved to the record selected. + +Move to origin +-------------- + +* Open the record where the message was moved to +* Click on the two cross arrows icon highlighted as red +* Check the box **[x] Move to origin** +RESULT: The message has been returned back to the original record. diff --git a/mail_move_message/mail_move_message_models.py b/mail_move_message/mail_move_message_models.py index d5e7076..21bde78 100644 --- a/mail_move_message/mail_move_message_models.py +++ b/mail_move_message/mail_move_message_models.py @@ -1,19 +1,27 @@ -from openerp import api -from openerp import fields -from openerp import models -from openerp.tools import email_split -from openerp.tools.translate import _ +# Copyright 2016 Ildar Nasyrov +# Copyright 2016-2018 Ivan Yelizariev +# Copyright 2016 intero-chz +# Copyright 2016 manawi +# Copyright 2018 Kolushov Alexandr +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import api +from odoo import fields +from odoo import models +from odoo.tools import email_split +from odoo.tools.translate import _ +from odoo import exceptions class Wizard(models.TransientModel): _name = 'mail_move_message.wizard' + @api.model def _model_selection(self): selection = [] config_parameters = self.env['ir.config_parameter'] - model_names = config_parameters.get_param('mail_relocation_models') + model_names = config_parameters.sudo().get_param('mail_relocation_models') model_names = model_names.split(',') if model_names else [] - if 'default_message_id' in self.env.context: message = self.env['mail.message'].browse(self.env.context['default_message_id']) if message.model and message.model not in model_names: @@ -22,16 +30,16 @@ class Wizard(models.TransientModel): model_names.append(message.moved_from_model) if model_names: selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])] - return selection @api.model def default_get(self, fields_list): res = super(Wizard, self).default_get(fields_list) - model_fields = self.fields_get() - if model_fields['model']['selection']: - res['model'] = model_fields['model']['selection'] and model_fields['model']['selection'][0][0] + available_models = self._model_selection() + if len(available_models): + record = self.env[available_models[0][0]].search([], limit=1) + res['model_record'] = len(record) and (available_models[0][0] + ',' + str(record.id)) or False if 'message_id' in res: message = self.env['mail.message'].browse(res['message_id']) @@ -54,10 +62,9 @@ class Wizard(models.TransientModel): res['res_id'] = res_id[0].id config_parameters = self.env['ir.config_parameter'] - res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers') + res['move_followers'] = config_parameters.sudo().get_param('mail_relocation_move_followers') res['uid'] = self.env.uid - return res message_id = fields.Many2one('mail.message', string='Message') @@ -68,16 +75,19 @@ class Wizard(models.TransientModel): message_moved_by_user_id = fields.Many2one('res.users', related='message_id.moved_by_user_id', string='Moved by', readonly=True) message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True) parent_id = fields.Many2one('mail.message', string='Search by name', ) - model = fields.Selection(_model_selection, string='Model') - res_id = fields.Integer(string='Record') - can_move = fields.Boolean('Can move', compute='get_can_move') + model_record = fields.Reference(selection="_model_selection", string='Record') + model = fields.Char(compute="_compute_model_res_id", string='Model') + res_id = fields.Integer(compute="_compute_model_res_id", string='Record') + + can_move = fields.Boolean('Can move', compute='_compute_get_can_move') move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place') partner_id = fields.Many2one('res.partner', string='Author') filter_by_partner = fields.Boolean('Filter Records by partner') message_email_from = fields.Char() message_name_from = fields.Char() # FIXME message_to_read should be True even if current message or any his childs are unread - message_to_read = fields.Boolean(related='message_id.needaction') + message_to_read = fields.Boolean(compute='_compute_is_read', string="Unread message", + help="Service field shows that this message were unread when moved") uid = fields.Integer() move_followers = fields.Boolean( 'Move Followers', @@ -85,12 +95,24 @@ class Wizard(models.TransientModel): "You must use this option, if new record has restricted access.\n" "You can change default value for this option at Settings/System Parameters") + @api.multi + @api.depends('model_record') + def _compute_model_res_id(self): + for rec in self: + rec.model = rec.model_record and rec.model_record._name or False + rec.res_id = rec.model_record and rec.model_record.id or False + @api.depends('message_id') @api.multi - def get_can_move(self): + def _compute_get_can_move(self): for r in self: r.get_can_move_one() + @api.multi + def _compute_is_read(self): + messages = self.env['mail.message'].sudo().browse(self.message_id.all_child_ids.ids + [self.message_id.id]) + self.message_to_read = True in [m.needaction for m in messages] + @api.multi def get_can_move_one(self): self.ensure_one() @@ -102,12 +124,11 @@ class Wizard(models.TransientModel): if not self.move_back: return self.parent_id = self.message_id.moved_from_parent_id - model = self.message_id.moved_from_model - if self.message_id.is_moved: - self.model = model - self.res_id = self.message_id.moved_from_res_id + message = self.message_id + if message.is_moved: + self.model_record = self.env[message.moved_from_model].browse(message.moved_from_res_id) - @api.onchange('parent_id', 'res_id', 'model') + @api.onchange('parent_id', 'model_record') def update_move_back(self): model = self.message_id.moved_from_model self.move_back = self.parent_id == self.message_id.moved_from_parent_id \ @@ -129,7 +150,7 @@ class Wizard(models.TransientModel): if self.model and self.filter_by_partner and self.partner_id: fields = self.env[self.model].fields_get(False) contact_field = False - for n, f in fields.iteritems(): + for n, f in fields.items(): if f['type'] == 'many2one' and f['relation'] == 'res.partner': contact_field = n break @@ -178,6 +199,10 @@ class Wizard(models.TransientModel): @api.multi def move(self): + for r in self: + if not r.model: + raise exceptions.except_orm(_('Record field is empty!'), _('Select a record for relocation first')) + for r in self: r.check_access() if not r.parent_id or not (r.parent_id.model == r.model and @@ -185,15 +210,14 @@ class Wizard(models.TransientModel): # link with the first message of record parent = self.env['mail.message'].search([('model', '=', r.model), ('res_id', '=', r.res_id)], order='id', limit=1) r.parent_id = parent.id or None + r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers, r.message_to_read) - r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers) - - if not (r.model and r.res_id): - r.message_id.needaction = False + if r.model in ['mail.message', 'mail.channel', False]: return { - 'type': 'ir.actions.client', - 'name': 'All messages', - 'tag': 'reload', + 'name': 'Chess game page', + 'type': 'ir.actions.act_url', + 'url': '/web', + 'target': 'self', } return { 'name': _('Record'), @@ -238,7 +262,7 @@ class Wizard(models.TransientModel): fields = model.fields_get() contact_field = False - for n, f in fields.iteritems(): + for n, f in fields.items(): if f['type'] == 'many2one' and f['relation'] == 'res.partner': contact_field = n break @@ -268,10 +292,11 @@ class MailMessage(models.Model): moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null') moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message') moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null') - all_child_ids = fields.One2many('mail.message', string='All childs', compute='_get_all_childs', help='all childs, including subchilds') + all_child_ids = fields.One2many('mail.message', string='All childs', compute='_compute_get_all_childs', help='all childs, including subchilds') + moved_as_unread = fields.Boolean('Was Unread', default=False) @api.multi - def _get_all_childs(self, include_myself=True): + def _compute_get_all_childs(self, include_myself=True): for r in self: r._get_all_childs_one(include_myself=include_myself) @@ -300,12 +325,12 @@ class MailMessage(models.Model): self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids]) @api.multi - def move(self, parent_id, res_id, model, move_back, move_followers=False): + def move(self, parent_id, res_id, model, move_back, move_followers=False, message_to_read=False): for r in self: - r.move_one(parent_id, res_id, model, move_back, move_followers=move_followers) + r.move_one(parent_id, res_id, model, move_back, move_followers=move_followers, message_to_read=message_to_read) @api.multi - def move_one(self, parent_id, res_id, model, move_back, move_followers=False): + def move_one(self, parent_id, res_id, model, move_back, move_followers=False, message_to_read=False): self.ensure_one() if parent_id == self.id: # if for any reason method is called to move message with parent @@ -322,6 +347,7 @@ class MailMessage(models.Model): vals['moved_from_res_id'] = None vals['moved_from_model'] = None vals['moved_from_parent_id'] = None + vals['moved_as_unread'] = None else: vals['parent_id'] = parent_id vals['res_id'] = res_id @@ -330,22 +356,34 @@ class MailMessage(models.Model): vals['is_moved'] = True vals['moved_by_user_id'] = self.env.user.id vals['moved_by_message_id'] = self.id - - # Update record_name in message - vals['record_name'] = self._get_record_name(vals) + vals['moved_as_unread'] = message_to_read + # Update record_name in message + vals['record_name'] = self._get_record_name(vals) + + # unread message remains unread after moving back to origin + if self.moved_as_unread and move_back: + notification = { + 'mail_message_id': self.id, + 'res_partner_id': self.env.user.partner_id.id, + 'is_read': False, + } + self.write({ + 'notification_ids': [(0, 0, notification)], + }) for r in self.all_child_ids: r_vals = vals.copy() if not r.is_moved: # moved_from_* variables contain not last, but original # reference - r_vals['moved_from_parent_id'] = r.parent_id.id - r_vals['moved_from_res_id'] = r.res_id - r_vals['moved_from_model'] = r.model + r_vals['moved_from_parent_id'] = r.parent_id.id or r.env.context.get('uid') + r_vals['moved_from_res_id'] = r.res_id or r.id + r_vals['moved_from_model'] = r.model or r._name elif move_back: r_vals['parent_id'] = r.moved_from_parent_id.id r_vals['res_id'] = r.moved_from_res_id - r_vals['model'] = r.moved_from_model + r_vals['model'] = (r.moved_from_model and r.moved_from_model not in ['mail.message', 'mail.channel', False]) and r.moved_from_model + r_vals['record_name'] = r_vals['model'] and self.env[r.moved_from_model].browse(r.moved_from_res_id).name if move_followers: r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) @@ -361,7 +399,7 @@ class MailMessage(models.Model): 'res_id': vals.get('res_id'), 'model': vals.get('model'), 'is_moved': vals['is_moved'], - 'record_name': vals['record_name'] + 'record_name': 'record_name' in vals and vals['record_name'], } self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message'), notification) @@ -390,34 +428,32 @@ class MailMessage(models.Model): class MailMoveMessageConfiguration(models.TransientModel): - _name = 'mail_move_message.config.settings' _inherit = 'res.config.settings' model_ids = fields.Many2many(comodel_name='ir.model', string='Models') move_followers = fields.Boolean('Move Followers') @api.model - def get_default_move_message_configs(self, fields): - config_parameters = self.env['ir.config_parameter'] - model_obj = self.env['ir.model'] - model_names = config_parameters.get_param('mail_relocation_models') - if not model_names: - return {} + def get_values(self): + res = super(MailMoveMessageConfiguration, self).get_values() + config_parameters = self.env["ir.config_parameter"].sudo() + model_names = config_parameters.sudo().get_param('mail_relocation_models') model_names = model_names.split(',') - model_ids = model_obj.search([('model', 'in', model_names)]) - return { - 'model_ids': [m.id for m in model_ids], - 'move_followers': config_parameters.get_param('mail_relocation_move_followers') - } + model_ids = self.env['ir.model'].sudo().search([('model', 'in', model_names)]) + res.update( + model_ids=[m.id for m in model_ids], + move_followers=config_parameters.sudo().get_param('mail_relocation_move_followers'), + ) + return res @api.multi - def set_move_message_configs(self): - config_parameters = self.env['ir.config_parameter'] - model_names = '' + def set_values(self): + super(MailMoveMessageConfiguration, self).set_values() + config_parameters = self.env["ir.config_parameter"].sudo() for record in self: - model_names = ','.join([m.model for m in record.model_ids]) - config_parameters.set_param('mail_relocation_models', model_names) - config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '') + model_names = ','.join([x.model for x in record.model_ids]) + config_parameters.set_param("mail_relocation_models", model_names or '') + config_parameters.set_param("mail_relocation_move_followers", record.move_followers or '') class ResPartner(models.Model): diff --git a/mail_move_message/mail_move_message_views.xml b/mail_move_message/mail_move_message_views.xml index 7695d8c..5e90c43 100644 --- a/mail_move_message/mail_move_message_views.xml +++ b/mail_move_message/mail_move_message_views.xml @@ -1,5 +1,10 @@ - + + +