diff --git a/mail_move_message/README.rst b/mail_move_message/README.rst new file mode 100644 index 0000000..ef727fc --- /dev/null +++ b/mail_move_message/README.rst @@ -0,0 +1,8 @@ +Mail relocation +=============== + +Description: https://www.odoo.com/apps/modules/8.0/mail_move_message/ + +Further information and discussion: http://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html + +Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625 diff --git a/mail_move_message/__init__.py b/mail_move_message/__init__.py new file mode 100644 index 0000000..f247113 --- /dev/null +++ b/mail_move_message/__init__.py @@ -0,0 +1,2 @@ +import controllers +import mail_move_message_models diff --git a/mail_move_message/__openerp__.py b/mail_move_message/__openerp__.py new file mode 100644 index 0000000..9b3a0bb --- /dev/null +++ b/mail_move_message/__openerp__.py @@ -0,0 +1,20 @@ +{ + 'name' : 'Mail relocation', + 'version' : '1.0.4', + 'author' : 'IT-Projects LLC, Ivan Yelizariev', + 'license': 'GPL-3', + 'category' : 'Social Network', + 'website' : 'https://twitter.com/yelizariev', + 'price': 9.00, + 'currency': 'EUR', + 'depends' : ['mail', 'web_polymorphic_field'], + 'images': ['images/inbox.png'], + '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/controllers/__init__.py b/mail_move_message/controllers/__init__.py new file mode 100644 index 0000000..039d971 --- /dev/null +++ b/mail_move_message/controllers/__init__.py @@ -0,0 +1 @@ +import main \ No newline at end of file diff --git a/mail_move_message/controllers/main.py b/mail_move_message/controllers/main.py new file mode 100644 index 0000000..bef9074 --- /dev/null +++ b/mail_move_message/controllers/main.py @@ -0,0 +1,55 @@ +from openerp.addons.web.controllers.main import DataSet +from openerp.tools.translate import _ +from openerp import http +from openerp.http import request + +class DataSetCustom(DataSet): + + def _extend_name(self, model, records): + cr, uid, context = request.cr, request.uid, request.context + Model = request.registry[model] + fields = Model.fields_get(cr, uid, False, context) + 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.read(cr, uid, [r[0] for r in records], [contact_field], context) + 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 + cr, uid = request.cr, request.uid + Model = request.registry[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)] + ids = Model.search(cr, uid, search_args, limit=limit, order='id desc', context=context) + res = Model.name_get(cr, uid, ids, context) + 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 new file mode 100644 index 0000000..2b13fe2 --- /dev/null +++ b/mail_move_message/data/mail_move_message_data.xml @@ -0,0 +1,9 @@ + + + + + mail_relocation_models + crm.lead,project.task + + + \ No newline at end of file diff --git a/mail_move_message/doc/changelog.rst b/mail_move_message/doc/changelog.rst new file mode 100644 index 0000000..4704b1f --- /dev/null +++ b/mail_move_message/doc/changelog.rst @@ -0,0 +1,26 @@ +.. _changelog: + +Changelog +========= + +`1.0.4` +------- + +- FIX: don't allow to relocate message to itself as it cause infinitive loop +- ADD: 'Move Followers' option -- Add followers of current record to a new record. + +`1.0.3` +------- + +- FIX email_from parsing. There was an error with specific email_from value (e.g. '"name @ example" ') + +`1.0.2` +------- + +- big improvements in interface + +`1.0.1` +------- + +- fix bug "some messages are not shown in inbox after relocation" +- improve "Move back" tool diff --git a/mail_move_message/i18n/mail_move_message.pot b/mail_move_message/i18n/mail_move_message.pot new file mode 100644 index 0000000..2854cd5 --- /dev/null +++ b/mail_move_message/i18n/mail_move_message.pot @@ -0,0 +1,173 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_move_message +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-11 06:53+0000\n" +"PO-Revision-Date: 2015-08-11 06:53+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Cancel" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,create_date:0 +msgid "Created on" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,id:0 +msgid "ID" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,is_moved:0 +msgid "Is moved" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,record_url:0 +msgid "Link to record" +msgstr "" + +#. module: mail_move_message +#: model:ir.model,name:mail_move_message.model_mail_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +#: field:mail_move_message.wizard,message_id:0 +msgid "Message" +msgstr "" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Move" +msgstr "" + +#. module: mail_move_message +#: help:mail_move_message.wizard,move_back:0 +msgid "Move message and submessages to original place" +msgstr "" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Move Message" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,move_back:0 +msgid "Move to origin" +msgstr "" + +#. module: mail_move_message +#. openerp-web +#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 +#, python-format +msgid "Move to thread" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,moved_by_message_id:0 +msgid "Moved by message" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,moved_by_user_id:0 +msgid "Moved by user" +msgstr "" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Open message" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,moved_from_parent_id:0 +msgid "Parent Message (Original)" +msgstr "" + +#. module: mail_move_message +#: code:addons/mail_move_message/mail_move_message_models.py:107 +#, python-format +msgid "Record" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,res_id:0 +msgid "Record ID" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,model_id:0 +msgid "Record type" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,moved_from_res_id:0 +msgid "Related Document ID (Original)" +msgstr "" + +#. module: mail_move_message +#: field:mail.message,moved_from_model:0 +msgid "Related Document Model (Original)" +msgstr "" + +#. module: mail_move_message +#. openerp-web +#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 +#, python-format +msgid "Relocate Message" +msgstr "" + +#. module: mail_move_message +#: field:mail_move_message.wizard,parent_id:0 +msgid "Search by name" +msgstr "" + +#. module: mail_move_message +#: help:mail.message,moved_by_message_id:0 +msgid "Top message, that initate moving this message" +msgstr "" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." +msgstr "" + +#. module: mail_move_message +#: help:mail_move_message.wizard,model_id:0 +msgid "List available Models is configured at Settings\Technical\Emails\Mail Relocation. Empty for unassigned email" +msgstr "" + +#. module: mail_move_message +#: help:mail_move_message.wizard,filter_by_partner:0 +msgid "Show only records with the same partner as email author" +msgstr "" + +#. module: mail_move_message +#: help:mail_move_message.wizard,move_followers:0 +msgid "Add followers of current record to a new record.\nYou must use this option, if new record has restricted access.\nYou can change default value for this option at Settings/System Parameters" +msgstr "" \ No newline at end of file diff --git a/mail_move_message/i18n/sl.po b/mail_move_message/i18n/sl.po new file mode 100644 index 0000000..971ffeb --- /dev/null +++ b/mail_move_message/i18n/sl.po @@ -0,0 +1,160 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_move_message +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-11 06:53+0000\n" +"PO-Revision-Date: 2015-08-11 08:58+0200\n" +"Last-Translator: Matjaz Mozetic \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: sl\n" +"X-Generator: Poedit 1.8.2\n" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Cancel" +msgstr "Preklic" + +#. module: mail_move_message +#: field:mail_move_message.wizard,create_uid:0 +msgid "Created by" +msgstr "Ustvaril" + +#. module: mail_move_message +#: field:mail_move_message.wizard,create_date:0 +msgid "Created on" +msgstr "Ustvarjeno" + +#. module: mail_move_message +#: field:mail_move_message.wizard,id:0 +msgid "ID" +msgstr "ID" + +#. module: mail_move_message +#: field:mail.message,is_moved:0 +msgid "Is moved" +msgstr "Je premaknjeno" + +#. module: mail_move_message +#: field:mail_move_message.wizard,write_uid:0 +msgid "Last Updated by" +msgstr "Zadnjič posodobil" + +#. module: mail_move_message +#: field:mail_move_message.wizard,write_date:0 +msgid "Last Updated on" +msgstr "Zadnjič posodobljeno" + +#. module: mail_move_message +#: field:mail_move_message.wizard,record_url:0 +msgid "Link to record" +msgstr "Povezava do zapisa" + +#. module: mail_move_message +#: model:ir.model,name:mail_move_message.model_mail_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +#: field:mail_move_message.wizard,message_id:0 +msgid "Message" +msgstr "Sporočilo" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Move" +msgstr "Premik" + +#. module: mail_move_message +#: help:mail_move_message.wizard,move_back:0 +msgid "Move message and submessages to original place" +msgstr "Premik sporočila in podrejenih sporočil na izvorno mesto" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Move Message" +msgstr "Premik sporočila" + +#. module: mail_move_message +#: field:mail_move_message.wizard,move_back:0 +msgid "Move to origin" +msgstr "Premik na izvor" + +#. module: mail_move_message +#. openerp-web +#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 +#, python-format +msgid "Move to thread" +msgstr "Premik v nit" + +#. module: mail_move_message +#: field:mail.message,moved_by_message_id:0 +msgid "Moved by message" +msgstr "Premaknjeno s sporočilom" + +#. module: mail_move_message +#: field:mail.message,moved_by_user_id:0 +msgid "Moved by user" +msgstr "Premaknil uporabnik" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "Open message" +msgstr "Odpri sporočilo" + +#. module: mail_move_message +#: field:mail.message,moved_from_parent_id:0 +msgid "Parent Message (Original)" +msgstr "Nadrejeno sporočilo (original)" + +#. module: mail_move_message +#: code:addons/mail_move_message/mail_move_message_models.py:107 +#, python-format +msgid "Record" +msgstr "Zapis" + +#. module: mail_move_message +#: field:mail_move_message.wizard,res_id:0 +msgid "Record ID" +msgstr "ID zapisa" + +#. module: mail_move_message +#: field:mail_move_message.wizard,model_id:0 +msgid "Record type" +msgstr "Tip zapisa" + +#. module: mail_move_message +#: field:mail.message,moved_from_res_id:0 +msgid "Related Document ID (Original)" +msgstr "ID povezanega dokumenta (original)" + +#. module: mail_move_message +#: field:mail.message,moved_from_model:0 +msgid "Related Document Model (Original)" +msgstr "Model povezanega dokumenta (original)" + +#. module: mail_move_message +#. openerp-web +#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 +#, python-format +msgid "Relocate Message" +msgstr "Premik sporočila" + +#. module: mail_move_message +#: field:mail_move_message.wizard,parent_id:0 +msgid "Search by name" +msgstr "Iskanje po nazivu" + +#. module: mail_move_message +#: help:mail.message,moved_by_message_id:0 +msgid "Top message, that initate moving this message" +msgstr "Zgornje sporočilo, ki je sprožilo premik tega sporočila" + +#. module: mail_move_message +#: view:mail_move_message.wizard:mail_move_message.view_wizard +msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." +msgstr "Tega sporočila ne morete premakniti, ker je bilo že premaknjeno s spodnjim sporočilom. Tam lahko uveljavljate spremembe." diff --git a/mail_move_message/images/inbox.png b/mail_move_message/images/inbox.png new file mode 100644 index 0000000..f9657b3 Binary files /dev/null and b/mail_move_message/images/inbox.png differ diff --git a/mail_move_message/mail_move_message_models.py b/mail_move_message/mail_move_message_models.py new file mode 100644 index 0000000..0013e9e --- /dev/null +++ b/mail_move_message/mail_move_message_models.py @@ -0,0 +1,382 @@ +from lxml import etree +from openerp import api, models, fields, SUPERUSER_ID +from openerp.tools import email_split +from openerp.tools.translate import _ + +class wizard(models.TransientModel): + _name = 'mail_move_message.wizard' + + def _model_selection(self): + selection = [] + config_parameters = self.env['ir.config_parameter'] + model_names = config_parameters.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: + model_names.append(message.model) + if message.moved_from_model and message.moved_from_model not in model_names: + 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] + + if 'message_id' in res: + message = self.env['mail.message'].browse(res['message_id']) + email_from = message.email_from + parts = email_split(email_from.replace(' ',',')) + if parts: + email = parts[0] + name = email_from.find(email) != -1 and email_from[:email_from.index(email)].replace('"', '').replace('<', '').strip() or email_from + else: + name, email = email_from + res['message_name_from'] = name + res['message_email_from'] = email + + res['partner_id'] = message.author_id.id + if message.author_id and self.env.uid not in [u.id for u in message.author_id.user_ids]: + res['filter_by_partner'] = True + if message.author_id and res.get('model'): + res_id = self.env[res['model']].search([], order='id desc', limit=1) + res['res_id'] = res_id and res_id[0].id + + config_parameters = self.env['ir.config_parameter'] + res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers') + + res['uid'] = self.env.uid + + return res + + message_id = fields.Many2one('mail.message', string='Message') + message_body = fields.Html(related='message_id.body', string='Message to move', readonly=True) + message_from = fields.Char(related='message_id.email_from', string='From', readonly=True) + message_subject = fields.Char(related='message_id.subject', string='Subject', readonly=True) + message_moved_by_message_id = fields.Many2one('mail.message', related='message_id.moved_by_message_id', string='Moved with', readonly=True) + 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') + 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.to_read') + uid = fields.Integer() + move_followers = fields.Boolean( + 'Move Followers', + help="Add followers of current record to a new record.\n" + "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.depends('message_id') + @api.one + def get_can_move(self): + # message was not moved before OR message is a top message of previous move + self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id + + @api.onchange('move_back') + def on_change_move_back(self): + 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 + + @api.onchange('parent_id', 'res_id', 'model') + 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 \ + and self.res_id == self.message_id.moved_from_res_id \ + and (self.model == model or (not self.model and not model)) + + @api.onchange('parent_id') + def on_change_parent_id(self): + if self.parent_id and self.parent_id.model: + self.model = self.parent_id.model + self.res_id = self.parent_id.res_id + else: + self.model = None + self.res_id = None + + @api.onchange('model', 'filter_by_partner', 'partner_id') + def on_change_partner(self): + domain = {'res_id': [('id', '!=', self.message_id.res_id)]} + 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(): + if f['type'] == 'many2one' and f['relation'] == 'res.partner': + contact_field = n + break + if contact_field: + domain['res_id'].append((contact_field, '=', self.partner_id.id)) + if self.model: + res_id = self.env[self.model].search(domain['res_id'], order='id desc', limit=1) + self.res_id = res_id and res_id[0].id + else: + self.res_id = None + return {'domain': domain} + + @api.one + def check_access(self): + cr = self._cr + uid = self.env.user.id + operation = 'write' + context = self._context + + if not ( self.model and self.res_id ): + return True + model_obj = self.pool[self.model] + mids = model_obj.exists(cr, uid, [self.res_id]) + if hasattr(model_obj, 'check_mail_message_access'): + model_obj.check_mail_message_access(cr, uid, mids, operation, context=context) + else: + self.pool['mail.thread'].check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context) + + @api.multi + def open_moved_by_message_id(self): + message_id = None + for r in self: + message_id = r.message_moved_by_message_id.id + return { + 'type': 'ir.actions.act_window', + 'res_model': 'mail_move_message.wizard', + 'view_mode': 'form', + 'view_type': 'form', + 'views': [[False, 'form']], + 'target': 'new', + 'context': {'default_message_id': message_id}, + } + + @api.multi + def move(self): + for r in self: + r.check_access() + if not r.parent_id or not (r.parent_id.model == r.model and + r.parent_id.res_id == r.res_id): + #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) + + if not ( r.model and r.res_id ): + obj = self.pool.get('ir.model.data').get_object_reference(self._cr, SUPERUSER_ID, 'mail', 'mail_archivesfeeds')[1] + return { + 'type' : 'ir.actions.client', + 'name' : 'Archive', + 'tag' : 'reload', + 'params' : {'menu_id': obj}, + } + return { + 'name': _('Record'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': r.model, + 'res_id': r.res_id, + 'views': [(False, 'form')], + 'type': 'ir.actions.act_window', + } + + @api.one + def delete(self): + self.message_id.unlink() + return {} + + @api.model + def create_partner(self, message_id, relation, partner_id, message_name_from, message_email_from): + model = self.env[relation] + message = self.env['mail.message'].browse(message_id) + if not partner_id and message_name_from: + partner_id = self.env['res.partner'].with_context({'update_message_author': True}).create({ + 'name': message_name_from, + 'email': message_email_from + }).id + + context = {'partner_id': partner_id} + if model._rec_name: + context.update({'default_%s' % model._rec_name: message.subject}) + + 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 + if contact_field: + context.update({'default_%s' % contact_field: partner_id}) + return context + + @api.one + def read_close(self): + self.message_id.set_message_read(True) + self.message_id.child_ids.set_message_read(True) + return {'type': 'ir.actions.act_window_close'} + + +class mail_message(models.Model): + _inherit = 'mail.message' + + is_moved = fields.Boolean('Is moved') + moved_from_res_id = fields.Integer('Related Document ID (Original)') + moved_from_model = fields.Char('Related Document Model (Original)') + 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') + + @api.one + def _get_all_childs(self, include_myself=True): + ids = [] + if include_myself: + ids.append(self.id) + while True: + new_ids = self.search([('parent_id', 'in', ids), ('id', 'not in', ids)]).ids + if new_ids: + ids = ids + new_ids + continue + break + moved_childs = self.search([('moved_by_message_id', '=', self.id)]).ids + self.all_child_ids = ids + moved_childs + + @api.multi + def move_followers(self, model, ids): + fol_obj = self.env['mail.followers'] + for message in self: + followers = fol_obj.sudo().search([('res_model', '=', message.model), + ('res_id', '=', message.res_id)]) + for f in followers: + self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids]) + + @api.one + def move(self, parent_id, res_id, model, move_back, move_followers=False): + if parent_id == res_id: + return + vals = {} + if move_back: + # clear variables if we move everything back + vals['is_moved'] = False + vals['moved_by_user_id'] = None + vals['moved_by_message_id'] = None + + vals['moved_from_res_id'] = None + vals['moved_from_model'] = None + vals['moved_from_parent_id'] = None + else: + vals['parent_id'] = parent_id + vals['res_id'] = res_id + vals['model'] = model + + vals['is_moved'] = True + vals['moved_by_user_id'] = self.env.user.id + vals['moved_by_message_id'] = self.id + + 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 + 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 + print 'update message', r, r_vals + if move_followers: + r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) + r.sudo().write(r_vals) + r.attachment_ids.sudo().write({ + 'res_id': r_vals.get('res_id'), + 'res_model': r_vals.get('model') + }) + + def name_get(self, cr, uid, ids, context=None): + if not (context or {}).get('extended_name'): + return super(mail_message, self).name_get(cr, uid, ids, context=context) + if isinstance(ids, (list, tuple)) and not len(ids): + return [] + if isinstance(ids, (long, int)): + ids = [ids] + reads = self.read(cr, uid, ids, ['record_name','model', 'res_id'], context=context) + res = [] + for record in reads: + name = record['record_name'] or '' + extended_name = ' [%s] ID %s' % (record.get('model', 'UNDEF'), record.get('res_id', 'UNDEF')) + res.append((record['id'], name + extended_name)) + return res + + def _message_read_dict(self, cr, uid, message, parent_id=False, context=None): + res = super(mail_message, self)._message_read_dict(cr, uid, message, parent_id, context) + res['is_moved'] = message.is_moved + return res + + +class mail_move_message_configuration(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 {} + 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') + } + + @api.multi + def set_move_message_configs(self): + config_parameters = self.env['ir.config_parameter'] + model_names = '' + 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 '') + + +class res_partner(models.Model): + _inherit = 'res.partner' + + @api.model + def create(self, vals): + res = super(res_partner, self).create(vals) + if 'update_message_author' in self.env.context and 'email' in vals: + mail_message_obj = self.env['mail.message'] + # Escape special SQL characters in email_address to avoid invalid matches + email_address = (vals['email'].replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')) + email_brackets = "<%s>" % email_address + messages = mail_message_obj.search([ + '|', + ('email_from', '=ilike', email_address), + ('email_from', 'ilike', email_brackets), + ('author_id', '=', False) + ]) + if messages: + messages.sudo().write({'author_id': res.id}) + return res diff --git a/mail_move_message/mail_move_message_views.xml b/mail_move_message/mail_move_message_views.xml new file mode 100644 index 0000000..475f7d7 --- /dev/null +++ b/mail_move_message/mail_move_message_views.xml @@ -0,0 +1,122 @@ + + + + + + + mail_move_message.wizard.view + mail_move_message.wizard + +
+ + + + + + + + +

You cannot move this message. It was already moved with a message bellow. Open one and apply changes there.

+ + + +