Browse Source

[11.0][PORT] mail_move_message (#146)

pull/153/head
Aleksandr 6 years ago
committed by Ivan Yelizariev
parent
commit
2dfb242689
  1. 49
      mail_move_message/README.rst
  2. 2
      mail_move_message/__init__.py
  3. 30
      mail_move_message/__manifest__.py
  4. 21
      mail_move_message/__openerp__.py
  5. 2
      mail_move_message/controllers/__init__.py
  6. 57
      mail_move_message/controllers/main.py
  7. 4
      mail_move_message/data/mail_move_message_data.xml
  8. 35
      mail_move_message/doc/index.rst
  9. 162
      mail_move_message/mail_move_message_models.py
  10. 95
      mail_move_message/mail_move_message_views.xml
  11. BIN
      mail_move_message/static/description/delete-message.png
  12. BIN
      mail_move_message/static/description/icon.png
  13. BIN
      mail_move_message/static/description/inbox-move.png
  14. 72
      mail_move_message/static/description/index.html
  15. BIN
      mail_move_message/static/description/record-move-back.png
  16. 145
      mail_move_message/static/src/js/mail_move_message.js
  17. 8
      mail_move_message/static/src/xml/mail_move_message_main.xml

49
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 <https://it-projects.info/team/yelizariev>`__
Sponsors
--------
* `IT-Projects LLC <https://it-projects.info>`__
Maintainers
-----------
* `IT-Projects LLC <https://it-projects.info>`__
To get a guaranteed support
you are kindly requested to purchase the module
at `odoo apps store <https://apps.odoo.com/apps/modules/11.0/mail_move_message/>`__.
Thank you for understanding!
`IT-Projects Team <https://www.it-projects.info/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: `<doc/index.rst>`_
Changelog: `<doc/changelog.rst>`_
Notifications on updates: `via Atom <https://github.com/it-projects-llc/mail-addons/commits/11.0/mail_move_message.atom>`_, `by Email <https://blogtrottr.com/?subscribe=https://github.com/it-projects-llc/mail-addons/commits/11.0/mail_move_message.atom>`_
Further information and discussion: http://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html 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

2
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 controllers
from . import mail_move_message_models from . import mail_move_message_models

30
mail_move_message/__manifest__.py

@ -0,0 +1,30 @@
# Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2017 Ilmir Karamov <https://it-projects.info/team/ilmir-k>
# Copyright 2017 Lilia Salihova
# Copyright 2016-2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# 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,
}

21
mail_move_message/__openerp__.py

@ -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,
}

2
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 from . import main

57
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 <https://it-projects.info/team/iledarn>
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo.http import request from odoo.http import request
from odoo.addons.bus.controllers.main import BusController 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'))
channels.append((request.db, 'mail_move_message.delete_message')) channels.append((request.db, 'mail_move_message.delete_message'))
return super(MailChatController, self)._poll(dbname, channels, last, options) 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/<model>/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/<model>/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

4
mail_move_message/data/mail_move_message_data.xml

@ -1,4 +1,8 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!--# Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2017 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).-->
<openerp> <openerp>
<data noupdate="1"> <data noupdate="1">
<record id="mail_relocation_models" model="ir.config_parameter"> <record id="mail_relocation_models" model="ir.config_parameter">

35
mail_move_message/doc/index.rst

@ -0,0 +1,35 @@
=================
Mail Relocation
=================
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ 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.

162
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 <https://it-projects.info/team/iledarn>
# Copyright 2016-2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2016 intero-chz <https://github.com/intero-chz>
# Copyright 2016 manawi <https://github.com/manawi>
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# 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): class Wizard(models.TransientModel):
_name = 'mail_move_message.wizard' _name = 'mail_move_message.wizard'
@api.model
def _model_selection(self): def _model_selection(self):
selection = [] selection = []
config_parameters = self.env['ir.config_parameter'] 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 [] model_names = model_names.split(',') if model_names else []
if 'default_message_id' in self.env.context: if 'default_message_id' in self.env.context:
message = self.env['mail.message'].browse(self.env.context['default_message_id']) message = self.env['mail.message'].browse(self.env.context['default_message_id'])
if message.model and message.model not in model_names: 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) model_names.append(message.moved_from_model)
if model_names: if model_names:
selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])] selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])]
return selection return selection
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
res = super(Wizard, self).default_get(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: if 'message_id' in res:
message = self.env['mail.message'].browse(res['message_id']) message = self.env['mail.message'].browse(res['message_id'])
@ -54,10 +62,9 @@ class Wizard(models.TransientModel):
res['res_id'] = res_id[0].id res['res_id'] = res_id[0].id
config_parameters = self.env['ir.config_parameter'] 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 res['uid'] = self.env.uid
return res return res
message_id = fields.Many2one('mail.message', string='Message') 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_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) 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', ) 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') move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place')
partner_id = fields.Many2one('res.partner', string='Author') partner_id = fields.Many2one('res.partner', string='Author')
filter_by_partner = fields.Boolean('Filter Records by partner') filter_by_partner = fields.Boolean('Filter Records by partner')
message_email_from = fields.Char() message_email_from = fields.Char()
message_name_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 # 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() uid = fields.Integer()
move_followers = fields.Boolean( move_followers = fields.Boolean(
'Move Followers', 'Move Followers',
@ -85,12 +95,24 @@ class Wizard(models.TransientModel):
"You must use this option, if new record has restricted access.\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") "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.depends('message_id')
@api.multi @api.multi
def get_can_move(self):
def _compute_get_can_move(self):
for r in self: for r in self:
r.get_can_move_one() 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 @api.multi
def get_can_move_one(self): def get_can_move_one(self):
self.ensure_one() self.ensure_one()
@ -102,12 +124,11 @@ class Wizard(models.TransientModel):
if not self.move_back: if not self.move_back:
return return
self.parent_id = self.message_id.moved_from_parent_id 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): def update_move_back(self):
model = self.message_id.moved_from_model model = self.message_id.moved_from_model
self.move_back = self.parent_id == self.message_id.moved_from_parent_id \ 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: if self.model and self.filter_by_partner and self.partner_id:
fields = self.env[self.model].fields_get(False) fields = self.env[self.model].fields_get(False)
contact_field = 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': if f['type'] == 'many2one' and f['relation'] == 'res.partner':
contact_field = n contact_field = n
break break
@ -178,6 +199,10 @@ class Wizard(models.TransientModel):
@api.multi @api.multi
def move(self): 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: for r in self:
r.check_access() r.check_access()
if not r.parent_id or not (r.parent_id.model == r.model and 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 # 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) 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.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 { return {
'type': 'ir.actions.client',
'name': 'All messages',
'tag': 'reload',
'name': 'Chess game page',
'type': 'ir.actions.act_url',
'url': '/web',
'target': 'self',
} }
return { return {
'name': _('Record'), 'name': _('Record'),
@ -238,7 +262,7 @@ class Wizard(models.TransientModel):
fields = model.fields_get() fields = model.fields_get()
contact_field = 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': if f['type'] == 'many2one' and f['relation'] == 'res.partner':
contact_field = n contact_field = n
break break
@ -268,10 +292,11 @@ class MailMessage(models.Model):
moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null') 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_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') 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 @api.multi
def _get_all_childs(self, include_myself=True):
def _compute_get_all_childs(self, include_myself=True):
for r in self: for r in self:
r._get_all_childs_one(include_myself=include_myself) 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]) self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids])
@api.multi @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: 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 @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() self.ensure_one()
if parent_id == self.id: if parent_id == self.id:
# if for any reason method is called to move message with parent # 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_res_id'] = None
vals['moved_from_model'] = None vals['moved_from_model'] = None
vals['moved_from_parent_id'] = None vals['moved_from_parent_id'] = None
vals['moved_as_unread'] = None
else: else:
vals['parent_id'] = parent_id vals['parent_id'] = parent_id
vals['res_id'] = res_id vals['res_id'] = res_id
@ -330,22 +356,34 @@ class MailMessage(models.Model):
vals['is_moved'] = True vals['is_moved'] = True
vals['moved_by_user_id'] = self.env.user.id vals['moved_by_user_id'] = self.env.user.id
vals['moved_by_message_id'] = self.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: for r in self.all_child_ids:
r_vals = vals.copy() r_vals = vals.copy()
if not r.is_moved: if not r.is_moved:
# moved_from_* variables contain not last, but original # moved_from_* variables contain not last, but original
# reference # 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: elif move_back:
r_vals['parent_id'] = r.moved_from_parent_id.id r_vals['parent_id'] = r.moved_from_parent_id.id
r_vals['res_id'] = r.moved_from_res_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: if move_followers:
r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) 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'), 'res_id': vals.get('res_id'),
'model': vals.get('model'), 'model': vals.get('model'),
'is_moved': vals['is_moved'], '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) self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message'), notification)
@ -390,34 +428,32 @@ class MailMessage(models.Model):
class MailMoveMessageConfiguration(models.TransientModel): class MailMoveMessageConfiguration(models.TransientModel):
_name = 'mail_move_message.config.settings'
_inherit = 'res.config.settings' _inherit = 'res.config.settings'
model_ids = fields.Many2many(comodel_name='ir.model', string='Models') model_ids = fields.Many2many(comodel_name='ir.model', string='Models')
move_followers = fields.Boolean('Move Followers') move_followers = fields.Boolean('Move Followers')
@api.model @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_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 @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: 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): class ResPartner(models.Model):

95
mail_move_message/mail_move_message_views.xml

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp><data>
<!--# Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2016 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).-->
<odoo>
<template id="assets_backend" name="custom bar assets" inherit_id="web.assets_backend"> <template id="assets_backend" name="custom bar assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
@ -29,9 +34,11 @@
</group> </group>
<group attrs="{'invisible':[('can_move', '=', False)]}" colspan="2"> <group attrs="{'invisible':[('can_move', '=', False)]}" colspan="2">
<label for="model"/>
<label for="model_record"/>
<div> <div>
<field name="model" widget="polymorphic" polymorphic="res_id" class="oe_inline"/>
<field name="model_record" class="oe_inline"/>
<field name="model" invisible="1"/>
<field name="res_id" invisible="1"/>
</div> </div>
<label for="filter_by_partner"/> <label for="filter_by_partner"/>
<div> <div>
@ -41,11 +48,6 @@
class="oe_highlight oe_inline ml32" class="oe_highlight oe_inline ml32"
special="quick_create" field="partner_id" context="{'force_email':True,'default_email':message_email_from,'default_name':message_name_from, 'update_message_author':True}" /> special="quick_create" field="partner_id" context="{'force_email':True,'default_email':message_email_from,'default_name':message_name_from, 'update_message_author':True}" />
</div> </div>
<label for="res_id"/>
<div>
<field name="res_id" context="{'extended_name_with_contact':1}" widget="many2one" attrs="{'readonly': [('model','=',False)]}" class="oe_inline"/>
<button string="Create new record" name="create_record" type="object" class="oe_highlight oe_inline ml32" attrs="{'invisible':['|',('model','=',False)]}" special="quick_create" field="res_id" use_for_mail_move_message="True"/>
</div>
<label for="move_back" attrs="{'invisible':[('message_is_moved','=',False)]}"/> <label for="move_back" attrs="{'invisible':[('message_is_moved','=',False)]}"/>
<div attrs="{'invisible':[('message_is_moved','=',False)]}"> <div attrs="{'invisible':[('message_is_moved','=',False)]}">
<field name="move_back"/> <field name="move_back"/>
@ -86,39 +88,50 @@
</field> </field>
</record> </record>
<!-- Relocation config wizard -->
<record id="view_mail_move_message_config_settings" model="ir.ui.view">
<field name="name">relocation settings</field>
<field name="model">mail_move_message.config.settings</field>
<field name="arch" type="xml">
<form string="Configure Mail Relocation" class="oe_form_configuration">
<header>
<button string="Apply" type="object" name="execute" class="oe_highlight"/>
or
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</header>
<div name="general">
<separator string="Models"/>
<field name="model_ids" widget="many2many_tags"/>
<separator string="Options"/>
<label for="move_followers"/>
<field name="move_followers"/>
<!-- Relocation config wizard -->
<record id="view_mail_move_message_config_settings" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="55"/>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div class="app_settings_block" data-string="Mail Relocation" string="Mail Relocation" data-key="mail_move_message">
<h2>Mail Relocation</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" id="crm_lead">
<div class="o_setting_left_pane">
</div>
<div class="o_setting_right_pane">
<label for="model_ids"/>
<div class="text-muted">
Add models to be used for message relocation
</div>
<field name="model_ids" widget="many2many_tags"/>
</div>
</div> </div>
</form>
</field>
</record>
<record id="action_mail_move_message_config" model="ir.actions.act_window">
<field name="name">Mail Relocation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mail_move_message.config.settings</field>
<field name="view_id" ref="view_mail_move_message_config_settings"/>
<field name="view_mode">form</field>
<field name="target">inline</field>
</record>
<div class="col-xs-12 col-md-6 o_setting_box" id="crm_lead">
<div class="o_setting_left_pane">
<field name="move_followers"/>
</div>
<div class="o_setting_right_pane">
<label for="move_followers"/>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
<!-- Add menu entry in Settings/Email -->
<menuitem name="Mail Relocation" id="menu_mail_move_message" parent="base.menu_email" sequence="99" action="action_mail_move_message_config"/>
<record id="action_mail_move_message_config" model="ir.actions.act_window">
<field name="name">Mail Relocation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_id" ref="view_mail_move_message_config_settings"/>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'mail_move_message'}</field>
</record>
</data>
</openerp>
</odoo>

BIN
mail_move_message/static/description/delete-message.png

Before

Width: 1012  |  Height: 546  |  Size: 39 KiB

After

Width: 667  |  Height: 446  |  Size: 32 KiB

BIN
mail_move_message/static/description/icon.png

Before

Width: 149  |  Height: 149  |  Size: 1.5 KiB

After

Width: 100  |  Height: 100  |  Size: 2.1 KiB

BIN
mail_move_message/static/description/inbox-move.png

Before

Width: 1054  |  Height: 562  |  Size: 57 KiB

After

Width: 667  |  Height: 446  |  Size: 32 KiB

72
mail_move_message/static/description/index.html

@ -1,26 +1,27 @@
<section class="oe_container"> <section class="oe_container">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12"> <div class="oe_span12">
<h2 class="oe_slogan">Mail relocation</h2>
<h2 class="oe_slogan">Mail Relocation</h2>
<h3 class="oe_slogan">Relocate customer's mails to a correct place (lead, task etc.)</h3> <h3 class="oe_slogan">Relocate customer's mails to a correct place (lead, task etc.)</h3>
</div> </div>
<div class="oe_span12"> <div class="oe_span12">
<p class="oe_mt32">
<div class="alert alert-success" style="padding:0.6em 0.6em; font-size: 120%;">
<p>
The module helps you to organise mails from customers. The module helps you to organise mails from customers.
</p> </p>
<p> <p>
If customer send mail to user's personal alias (e.g. admin@yourcompany.example.com), a user would be able to relocate such mail to corresponding record. If customer send mail to user's personal alias (e.g. admin@yourcompany.example.com), a user would be able to relocate such mail to corresponding record.
</p> </p>
</div>
</div> </div>
</div> </div>
</section> </section>
<section class="oe_container oe_dark"> <section class="oe_container oe_dark">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span12 text-center">
<h2>Usage</h2> <h2>Usage</h2>
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Click on an icon of two cross arrows. Click on an icon of two cross arrows.
</p> </p>
</div> </div>
@ -36,7 +37,7 @@
<section class="oe_container"> <section class="oe_container">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12"> <div class="oe_span12">
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Find a record you need and click on "Move" button. Find a record you need and click on "Move" button.
</p> </p>
</div> </div>
@ -52,7 +53,7 @@
<section class="oe_container oe_dark"> <section class="oe_container oe_dark">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12"> <div class="oe_span12">
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Done! You've attached mail and its child mails to the record. Done! You've attached mail and its child mails to the record.
</p> </p>
</div> </div>
@ -61,7 +62,7 @@
<div class="oe_demo oe_picture oe_screenshot"> <div class="oe_demo oe_picture oe_screenshot">
<img src="record1.png?3"/> <img src="record1.png?3"/>
</div> </div>
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Scroll down... Moved mails are marked by highlighted icon. Scroll down... Moved mails are marked by highlighted icon.
</p> </p>
<div class="oe_demo oe_picture oe_screenshot"> <div class="oe_demo oe_picture oe_screenshot">
@ -74,16 +75,16 @@
<section class="oe_container"> <section class="oe_container">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12"> <div class="oe_span12">
<p class="oe_mt32">
<p class="oe_mt32 text-center">
You could easily return mails back if you change your mind. You could easily return mails back if you change your mind.
</p> </p>
</div> </div>
<div class="oe_span12"> <div class="oe_span12">
<div class="oe_demo oe_picture oe_screenshot">
<div class="oe_demo oe_picture" style="margin-bottom: 4em;">
<img src="record-move-back.png?1"/> <img src="record-move-back.png?1"/>
</div> </div>
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Further information and discussion: <a href="https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html">https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html</a> Further information and discussion: <a href="https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html">https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html</a>
</p> </p>
</div> </div>
@ -93,30 +94,69 @@
<section class="oe_container oe_dark"> <section class="oe_container oe_dark">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span12 text-center">
<h2>Tips and tricks</h2> <h2>Tips and tricks</h2>
</div> </div>
<div class="oe_span12"> <div class="oe_span12">
<p class="oe_mt32"> <p class="oe_mt32">
The module can be used to delete a message (it's available for superuser only) The module can be used to delete a message (it's available for superuser only)
</p> </p>
<div class="oe_demo oe_picture oe_screenshot">
<div class="oe_demo oe_picture">
<img src="delete-message.png"/> <img src="delete-message.png"/>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<br/>
<section class="oe_container"> <section class="oe_container">
<div class="oe_row oe_spaced"> <div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span8">
<h2>Need our service?</h2> <h2>Need our service?</h2>
<p class="oe_mt32">Contact us by <a href="mailto:apps@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> <p class="oe_mt32">Contact us by <a href="mailto:apps@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p>
<ul> <ul>
<li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li> <li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
</ul> </ul>
</div> </div>
<div class="oe_span4">
<div class="stamp" style="width:200px;">
<div style="margin-top: 15px;
position: relative;
font-family:'Vollkorn', serif;
font-size: 16px;
line-height: 25px;
text-transform: uppercase;
font-weight: bold;
color: #75526b;
border: 3px dashed #75526b;
float: left;
padding: 4px 12px;
-webkit-transform: rotate(6deg);
-o-transform: rotate(6deg);
-moz-transform: rotate(6deg);
-ms-transform: rotate(6deg);">
Tested on Odoo<br/>11.0 community
</div>
<div style="margin-top: 15px;
position: relative;
font-family:'Vollkorn', serif;
font-size: 16px;
line-height: 25px;
text-transform: uppercase;
font-weight: bold;
color: #75526b;
border: 3px dashed #75526b;
float: left;
padding: 4px 12px;
-webkit-transform: rotate(-7deg);
-o-transform: rotate(-7deg);
-moz-transform: rotate(-7deg);
-ms-transform: rotate(-7deg);">
Tested on Odoo<br/>11.0 enterprise
</div>
</div>
</div>
</div> </div>
</section> </section>

BIN
mail_move_message/static/description/record-move-back.png

Before

Width: 904  |  Height: 482  |  Size: 42 KiB

After

Width: 658  |  Height: 465  |  Size: 44 KiB

145
mail_move_message/static/src/js/mail_move_message.js

@ -1,15 +1,27 @@
/*Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2016 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2016 Pavel Romanchenko
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
odoo.define('mail_move_message.relocate', function (require) { odoo.define('mail_move_message.relocate', function (require) {
"use strict"; "use strict";
var bus = require('bus.bus').bus; var bus = require('bus.bus').bus;
var chat_manager = require('mail.chat_manager');
var base_obj = require('mail_base.base');
var chat_manager = require('mail_base.base').chat_manager;
var thread = require('mail.ChatThread'); var thread = require('mail.ChatThread');
var chatter = require('mail.Chatter'); var chatter = require('mail.Chatter');
var Model = require('web.Model');
var form_common = require('web.form_common');
var widgets = require('web.form_widgets');
var rpc = require('web.rpc');
var Basicmodel = require('web.BasicModel');
var view_dialogs = require('web.view_dialogs');
var field_utils_format = require('web.field_utils').format;
var BasicRenderer = require('web.BasicRenderer');
var core = require('web.core'); var core = require('web.core');
var form_widget = require('web.FormRenderer');
var session = require('web.Session');
var FormController = require('web.FormController');
var FormView = require('web.FormView');
var registry = require('web.field_registry');
var _t = core._t; var _t = core._t;
@ -33,7 +45,6 @@ odoo.define('mail_move_message.relocate', function (require) {
target: 'new', target: 'new',
context: {'default_message_id': message_id} context: {'default_message_id': message_id}
}; };
this.do_action(action, { this.do_action(action, {
'on_close': function(){} 'on_close': function(){}
}); });
@ -44,12 +55,14 @@ odoo.define('mail_move_message.relocate', function (require) {
start: function() { start: function() {
var result = this._super.apply(this, arguments); var result = this._super.apply(this, arguments);
// For show wizard in the form // For show wizard in the form
this.thread.on('move_message', this, this.thread.on_move_message);
if (this.fields.thread && this.fields.thread.thread) {
var thread = this.fields.thread.thread;
thread.on('move_message', this, thread.on_move_message);
}
return $.when(result).done(function() {}); return $.when(result).done(function() {});
} }
}); });
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
ChatAction.include({ ChatAction.include({
start: function() { start: function() {
@ -60,85 +73,45 @@ odoo.define('mail_move_message.relocate', function (require) {
} }
}); });
base_obj.MailTools.include({
make_message: function(data){
var msg = this._super(data);
// Mark msg as moved after reload
msg.is_moved = data.is_moved || false;
return msg;
},
on_notification: function(notifications){
this._super(notifications);
var self = this;
_.each(notifications, function (notification) {
var model = notification[0][1];
var message_id = notification[1].id;
var message = base_obj.chat_manager.get_message(message_id);
if (model === 'mail_move_message' && message) {
message.res_id = notification[1].res_id;
message.model = notification[1].model;
message.record_name = notification[1].record_name;
// Mark message as moved after move
message.is_moved = notification[1].is_moved;
// Update cache and accordingly message in the thread
self.add_to_cache(message, []);
// Call thread.on_update_message(message)
chat_manager.bus.trigger('update_message', message);
} else if (model === 'mail_move_message.delete_message') {
self.remove_from_cache(message, []);
chat_manager.bus.trigger('update_message', message);
}
});
}
});
widgets.WidgetButton.include({
on_click: function(){
if(this.node.attrs.special == 'quick_create'){
var self = this;
var related_field = this.field_manager.fields[this.node.attrs.field];
var context_built = $.Deferred();
if(this.node.attrs.use_for_mail_move_message) {
var model = new Model(this.view.dataset.model);
var partner_id = self.field_manager.fields.partner_id.get_value();
var message_name_from = self.field_manager.fields.message_name_from.get_value();
var message_email_from = self.field_manager.fields.message_email_from.get_value();
context_built = model.call('create_partner', [
self.view.dataset.context.default_message_id,
related_field.field.relation,
partner_id,
message_name_from,
message_email_from
]);
}
else {
context_built.resolve(this.build_context());
}
$.when(context_built).pipe(function (context) {
if(self.node.attrs.use_for_mail_move_message) {
self.field_manager.fields.partner_id.set_value(context.partner_id);
}
var dialog = new form_common.FormViewDialog(self, {
res_model: related_field.field.relation,
res_id: false,
context: context,
title: _t("Create new record")
}).open();
dialog.on('closed', self, function () {
self.force_disabled = false;
self.check_disable();
});
dialog.on('create_completed', self, function(id) {
related_field.set_value(id);
if(self.field_manager.fields.filter_by_partner) {
self.field_manager.fields.filter_by_partner.set_value(true);
}
});
});
}
else {
this._super.apply(this, arguments);
// override methods of chat manager
var chat_manager_super_make_message = chat_manager.make_message;
chat_manager.make_message = function(data){
var msg = chat_manager_super_make_message(data);
// Mark msg as moved after reload
msg.is_moved = data.is_moved || false;
return msg;
};
var chat_manager_super_on_notification = chat_manager.on_notification;
chat_manager.on_notification = function(notifications){
chat_manager_super_on_notification(notifications);
var self = this;
_.each(notifications, function (notification) {
var model = notification[0][1];
var message_id = notification[1].id;
var message = chat_manager.get_message(message_id);
if (model === 'mail_move_message' && message) {
message.res_id = notification[1].res_id;
message.model = notification[1].model;
message.record_name = notification[1].record_name;
// Mark message as moved after move
message.is_moved = notification[1].is_moved;
// Update cache and accordingly message in the thread
self.add_to_cache(message, []);
// Call thread.on_update_message(message)
chat_manager.bus.trigger('update_message', message);
} else if (model === 'mail_move_message.delete_message') {
_.each(message.channel_ids, function(ch){
self.remove_message_from_channel(ch, message);
})
chat_manager.bus.trigger('update_message', message);
} }
});
};
Basicmodel.include({
applyDefaultValues: function (recordID, values, options) {
delete values.model
return this._super(recordID, values, options)
} }
}); });
}); });

8
mail_move_message/static/src/xml/mail_move_message_main.xml

@ -1,7 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--# Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2016 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2016 Pavel Romanchenko
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).-->
<template> <template>
<t t-extend="mail.ChatThread.Message"> <t t-extend="mail.ChatThread.Message">
<t t-jquery='p.o_mail_info>span>i:first-child' t-operation="before">
<t t-jquery='p.o_mail_info span:last-child i:first-child' t-operation="before">
<i t-if="!message.is_system_notification" t-att-class="'fa fa-exchange oe_move' + (message.is_moved ? ' oe_moved' : '')" <i t-if="!message.is_system_notification" t-att-class="'fa fa-exchange oe_move' + (message.is_moved ? ' oe_moved' : '')"
t-att-data-message-id="message.id" title="Move to thread"/> t-att-data-message-id="message.id" title="Move to thread"/>
</t> </t>

Loading…
Cancel
Save