Browse Source

Merge pull request #153 from yelizariev/master-merge-11

Master merge 11
pull/162/head
Ivan Yelizariev 6 years ago
committed by GitHub
parent
commit
8a5ff0f1c4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      mail_all/__init__.py
  2. 3
      mail_all/__openerp__.py
  3. 34
      mail_all/i18n/es.po
  4. 1
      mail_all/tests/__init__.py
  5. 1
      mail_all/tests/test_js.py
  6. 1
      mail_archives/__init__.py
  7. 3
      mail_archives/__openerp__.py
  8. 34
      mail_archives/i18n/es.po
  9. 1
      mail_archives/tests/__init__.py
  10. 1
      mail_archives/tests/test_js.py
  11. 1
      mail_base/__init__.py
  12. 3
      mail_base/__openerp__.py
  13. 1
      mail_base/controllers/main.py
  14. 29
      mail_base/i18n/es.po
  15. 29
      mail_base/i18n/pt.po
  16. 29
      mail_base/i18n/pt_BR.po
  17. 1
      mail_base/models.py
  18. 6
      mail_base/static/lib/base.js
  19. 1
      mail_base/tests/__init__.py
  20. 1
      mail_base/tests/test_default.py
  21. 1
      mail_check_immediately/__init__.py
  22. 1
      mail_check_immediately/__openerp__.py
  23. 1
      mail_check_immediately/models.py
  24. 1
      mail_fix_553/__init__.py
  25. 1
      mail_fix_553/__openerp__.py
  26. 1
      mail_fix_553/mail_fix_553.py
  27. 49
      mail_move_message/README.rst
  28. 3
      mail_move_message/__init__.py
  29. 30
      mail_move_message/__manifest__.py
  30. 22
      mail_move_message/__openerp__.py
  31. 3
      mail_move_message/controllers/__init__.py
  32. 58
      mail_move_message/controllers/main.py
  33. 4
      mail_move_message/data/mail_move_message_data.xml
  34. 35
      mail_move_message/doc/index.rst
  35. 163
      mail_move_message/mail_move_message_models.py
  36. 95
      mail_move_message/mail_move_message_views.xml
  37. BIN
      mail_move_message/static/description/delete-message.png
  38. BIN
      mail_move_message/static/description/icon.png
  39. BIN
      mail_move_message/static/description/inbox-move.png
  40. 72
      mail_move_message/static/description/index.html
  41. BIN
      mail_move_message/static/description/record-move-back.png
  42. 145
      mail_move_message/static/src/js/mail_move_message.js
  43. 8
      mail_move_message/static/src/xml/mail_move_message_main.xml
  44. 1
      mail_private/__init__.py
  45. 3
      mail_private/__openerp__.py
  46. 104
      mail_private/models.py
  47. BIN
      mail_private/static/description/check_recipients.png
  48. 123
      mail_private/static/description/index.html
  49. BIN
      mail_private/static/description/result_message.png
  50. 203
      mail_private/static/src/js/mail_private.js
  51. 10
      mail_private/static/src/xml/mail_private.xml
  52. 1
      mail_recovery/__init__.py
  53. 1
      mail_recovery/__openerp__.py
  54. 1
      mail_reply/__init__.py
  55. 1
      mail_reply/__openerp__.py
  56. 1
      mail_reply/tests/__init__.py
  57. 1
      mail_reply/tests/test_default.py
  58. 1
      mail_sent/__init__.py
  59. 3
      mail_sent/__openerp__.py
  60. 65
      mail_sent/i18n/es.po
  61. 68
      mail_sent/i18n/pt.po
  62. 68
      mail_sent/i18n/pt_BR.po
  63. 1
      mail_sent/models.py
  64. 1
      mail_sent/tests/__init__.py
  65. 1
      mail_sent/tests/test_js.py
  66. 1
      mail_to/__init__.py
  67. 3
      mail_to/__openerp__.py
  68. 40
      mail_to/i18n/es.po
  69. 31
      mailgun/README.rst
  70. 1
      mailgun/__init__.py
  71. 48
      mailgun/__manifest__.py
  72. 15
      mailgun/__openerp__.py
  73. 1
      mailgun/controllers/__init__.py
  74. 1
      mailgun/controllers/main.py
  75. 14
      mailgun/data/cron.xml
  76. 15
      mailgun/data/ir_cron_data.xml
  77. 3
      mailgun/doc/changelog.rst
  78. 111
      mailgun/doc/index.rst
  79. BIN
      mailgun/images/mailgun_main.png
  80. 36
      mailgun/models.py
  81. 4
      mailgun/models/__init__.py
  82. 25
      mailgun/models/ir_config_parameter.py
  83. 18
      mailgun/models/mail_thread.py
  84. BIN
      mailgun/static/description/icon.png
  85. 332
      mailgun/static/description/index.html
  86. BIN
      mailgun/static/description/mailgun_add_domain.png
  87. BIN
      mailgun/static/description/mailgun_auth_recipients.png
  88. BIN
      mailgun/static/description/mailgun_auth_recipients_form.png
  89. BIN
      mailgun/static/description/mailgun_domain_details.png
  90. BIN
      mailgun/static/description/mailgun_odoo_general.png
  91. BIN
      mailgun/static/description/mailgun_odoo_mail_alias.png
  92. BIN
      mailgun/static/description/mailgun_odoo_outgoing.png
  93. BIN
      mailgun/static/description/mailgun_odoo_partner_email.png
  94. BIN
      mailgun/static/description/mailgun_odoo_sys_param.png
  95. BIN
      mailgun/static/description/mailgun_route.png
  96. BIN
      mailgun/static/description/mailgun_route_form.png
  97. 1
      res_partner_company_messages/__init__.py
  98. 1
      res_partner_company_messages/__openerp__.py
  99. 1
      res_partner_company_messages/models.py
  100. 1
      res_partner_mails_count/__init__.py

1
mail_all/__init__.py

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

3
mail_all/__openerp__.py

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
{
"name": "Show all messages",
"summary": """Checkout all messages where you have access""",
"category": "Discuss",
# "live_test_url": "",
"images": ['images/1.jpg'],
"version": "1.0.0",
"version": "11.0.1.0.0",
"application": False,
"author": "IT-Projects LLC, Pavel Romanchenko",

34
mail_all/i18n/es.po

@ -0,0 +1,34 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_all
#
# Translators:
# Randall Castro <rcastro@treintaycinco.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 23:07+0000\n"
"PO-Revision-Date: 2018-04-21 23:07+0000\n"
"Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_all
#. openerp-web
#: code:addons/mail_all/static/src/js/mail_all.js:49
#: code:addons/mail_all/static/src/xml/menu.xml:7
#, python-format
msgid "All messages"
msgstr "Todos los mensajes"
#. module: mail_all
#. openerp-web
#: code:addons/mail_all/static/src/xml/menu.xml:15
#, python-format
msgid "No messages"
msgstr "Sin mensajes"

1
mail_all/tests/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_js

1
mail_all/tests/test_js.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import odoo.tests

1
mail_archives/__init__.py

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

3
mail_archives/__openerp__.py

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
{
"name": "Mail archives",
"summary": """Adds menu to find old messages""",
"category": "Discuss",
"images": ['images/1.jpg'],
"version": "1.0.0",
"version": "11.0.1.0.0",
"author": "IT-Projects LLC, Pavel Romanchenko",
"support": "apps@it-projects.info",

34
mail_archives/i18n/es.po

@ -0,0 +1,34 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_archives
#
# Translators:
# Randall Castro <rcastro@treintaycinco.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 23:07+0000\n"
"PO-Revision-Date: 2018-04-21 23:07+0000\n"
"Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_archives
#. openerp-web
#: code:addons/mail_archives/static/src/js/archives.js:68
#: code:addons/mail_archives/static/src/xml/menu.xml:7
#, python-format
msgid "Archive"
msgstr "Archivo"
#. module: mail_archives
#. openerp-web
#: code:addons/mail_archives/static/src/xml/menu.xml:15
#, python-format
msgid "Archive is empty"
msgstr "Archivo está vacío"

1
mail_archives/tests/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_js

1
mail_archives/tests/test_js.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import odoo.tests

1
mail_base/__init__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from . import models
from . import controllers

3
mail_base/__openerp__.py

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
{
"name": "Mail Base",
"summary": """Makes Mail extendable""",
"category": "Discuss",
"images": [],
"version": "1.0.2",
"version": "11.0.1.0.2",
"author": "IT-Projects LLC, Pavel Romanchenko",
"support": "apps@it-projects.info",

1
mail_base/controllers/main.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from openerp.http import request
from openerp.addons.bus.controllers.main import BusController

29
mail_base/i18n/es.po

@ -0,0 +1,29 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_base
#
# Translators:
# Randall Castro <rcastro@treintaycinco.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 23:07+0000\n"
"PO-Revision-Date: 2018-04-21 23:07+0000\n"
"Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Asistente para composición de correo"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_message
msgid "Message"
msgstr "Mensaje"

29
mail_base/i18n/pt.po

@ -0,0 +1,29 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_base
#
# Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 00:05+0000\n"
"PO-Revision-Date: 2018-04-21 00:05+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2018\n"
"Language-Team: Portuguese (https://www.transifex.com/it-projects-llc/teams/76080/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Assistente de composição de Email"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_message
msgid "Message"
msgstr "Mensagem"

29
mail_base/i18n/pt_BR.po

@ -0,0 +1,29 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_base
#
# Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 00:05+0000\n"
"PO-Revision-Date: 2018-04-21 00:05+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2018\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/it-projects-llc/teams/76080/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Assistente de Composição de Email"
#. module: mail_base
#: model:ir.model,name:mail_base.model_mail_message
msgid "Message"
msgstr "Mensagem"

1
mail_base/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from openerp import api, models

6
mail_base/static/lib/base.js

@ -877,6 +877,12 @@ chat_manager.post_message = function (data, options) {
attachment_ids: data.attachment_ids,
};
// for module mail_private
if (data.is_private) {
msg.is_private = data.is_private;
msg.channel_ids = data.channel_ids;
}
// Replace emojis by their unicode character
_.each(_.keys(emoji_unicodes), function (key) {
var escaped_key = String(key).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1');

1
mail_base/tests/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_default

1
mail_base/tests/test_default.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import odoo.tests

1
mail_check_immediately/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import models

1
mail_check_immediately/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': 'Check mail immediately',
'version': '1.0.1',

1
mail_check_immediately/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import datetime
from openerp.tools.translate import _

1
mail_fix_553/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import mail_fix_553

1
mail_fix_553/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
"name": "Fix mail error 553",
"version": "0.3",

1
mail_fix_553/mail_fix_553.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import base64
import logging

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
Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625
Tested on Odoo 11.0 e9454e79e27d0b85546132cbe00b391e974c66bf

3
mail_move_message/__init__.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from . import controllers
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,
}

22
mail_move_message/__openerp__.py

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
{
'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,
}

3
mail_move_message/controllers/__init__.py

@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from . import main

58
mail_move_message/controllers/main.py

@ -1,7 +1,8 @@
# -*- coding: utf-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.addons.bus.controllers.main import BusController
@ -16,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/<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"?>
<!--# 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>
<data noupdate="1">
<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.

163
mail_move_message/mail_move_message_models.py

@ -1,20 +1,27 @@
# -*- coding: utf-8 -*-
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):
_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:
@ -23,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'])
@ -55,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')
@ -69,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',
@ -86,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()
@ -103,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 \
@ -130,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
@ -179,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
@ -186,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'),
@ -239,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
@ -269,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)
@ -301,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
@ -323,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
@ -331,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'))
@ -362,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)
@ -391,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):

95
mail_move_message/mail_move_message_views.xml

@ -1,5 +1,10 @@
<?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">
<xpath expr="." position="inside">
@ -29,9 +34,11 @@
</group>
<group attrs="{'invisible':[('can_move', '=', False)]}" colspan="2">
<label for="model"/>
<label for="model_record"/>
<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>
<label for="filter_by_partner"/>
<div>
@ -41,11 +48,6 @@
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}" />
</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)]}"/>
<div attrs="{'invisible':[('message_is_moved','=',False)]}">
<field name="move_back"/>
@ -86,39 +88,50 @@
</field>
</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>
</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">
<div class="oe_row oe_spaced">
<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>
</div>
<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.
</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.
</p>
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span12 text-center">
<h2>Usage</h2>
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Click on an icon of two cross arrows.
</p>
</div>
@ -36,7 +37,7 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Find a record you need and click on "Move" button.
</p>
</div>
@ -52,7 +53,7 @@
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<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.
</p>
</div>
@ -61,7 +62,7 @@
<div class="oe_demo oe_picture oe_screenshot">
<img src="record1.png?3"/>
</div>
<p class="oe_mt32">
<p class="oe_mt32 text-center">
Scroll down... Moved mails are marked by highlighted icon.
</p>
<div class="oe_demo oe_picture oe_screenshot">
@ -74,16 +75,16 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<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.
</p>
</div>
<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"/>
</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>
</p>
</div>
@ -93,30 +94,69 @@
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span12 text-center">
<h2>Tips and tricks</h2>
</div>
<div class="oe_span12">
<p class="oe_mt32">
The module can be used to delete a message (it's available for superuser only)
</p>
<div class="oe_demo oe_picture oe_screenshot">
<div class="oe_demo oe_picture">
<img src="delete-message.png"/>
</div>
</div>
</div>
</section>
<br/>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="oe_span8">
<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>
<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="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>
</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>
</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) {
"use strict";
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 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 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;
@ -33,7 +45,6 @@ odoo.define('mail_move_message.relocate', function (require) {
target: 'new',
context: {'default_message_id': message_id}
};
this.do_action(action, {
'on_close': function(){}
});
@ -44,12 +55,14 @@ odoo.define('mail_move_message.relocate', function (require) {
start: function() {
var result = this._super.apply(this, arguments);
// 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() {});
}
});
var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
ChatAction.include({
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"?>
<!--# 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>
<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' : '')"
t-att-data-message-id="message.id" title="Move to thread"/>
</t>

1
mail_private/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import models

3
mail_private/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
"name": """Internal Messaging""",
"summary": """Send private messages to specified recipients, regardless of who are in followers list.""",
@ -34,5 +33,5 @@
"post_init_hook": None,
"auto_install": False,
"installable": False,
"installable": True,
}

104
mail_private/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
@ -7,8 +6,103 @@ class MailComposeMessage(models.TransientModel):
is_private = fields.Boolean(string='Send Internal Message')
class MailMessage(models.Model):
_inherit = 'mail.message'
is_private = fields.Boolean(string='Send Internal Message')
def send_recepients_for_internal_message(self, model, domain):
result = []
default_resource = self.env[model].search(domain)
follower_ids = default_resource.message_follower_ids
recipient_ids = [r.partner_id for r in follower_ids if r.partner_id]
# channel_ids = [c.channel_id for c in follower_ids if c.channel_id]
for recipient in recipient_ids:
result.append({
'checked': len(recipient.user_ids) > 0,
'partner_id': recipient.id,
'full_name': recipient.name,
'name': recipient.name,
'email_address': recipient.email,
'reason': 'Recipient'
})
# for channel in channel_ids:
# result.append({
# 'checked': True,
# 'channel_id': channel.id,
# 'full_name': channel,
# 'name': '# '+channel.name,
# 'reason': 'Channel',
# })
return result
@api.multi
def send_mail(self, auto_commit=False):
for w in self:
w.is_log = True if w.is_private else w.is_log
super(MailComposeMessage, self).send_mail(auto_commit=False)
def _notify(self, force_send=False, send_after_commit=True, user_signature=True):
self_sudo = self.sudo()
if not self_sudo.is_private:
super(MailMessage, self)._notify(force_send, send_after_commit, user_signature)
else:
self._notify_mail_private(force_send, send_after_commit, user_signature)
@api.multi
def _notify_mail_private(self, force_send=False, send_after_commit=True, user_signature=True):
""" Compute recipients to notify based on specified recipients and document
followers. Delegate notification to partners to send emails and bus notifications
and to channels to broadcast messages on channels """
group_user = self.env.ref('base.group_user')
# have a sudoed copy to manipulate partners (public can go here with website modules like forum / blog / ... )
self_sudo = self.sudo()
self.ensure_one()
partners_sudo = self_sudo.partner_ids
channels_sudo = self_sudo.channel_ids
if self_sudo.subtype_id and self.model and self.res_id:
followers = self_sudo.env['mail.followers'].search([
('res_model', '=', self.model),
('res_id', '=', self.res_id),
('subtype_ids', 'in', self_sudo.subtype_id.id),
])
if self_sudo.subtype_id.internal:
followers = followers.filtered(lambda fol: fol.channel_id or (fol.partner_id.user_ids and group_user in fol.partner_id.user_ids[0].mapped('groups_id')))
channels_sudo |= followers.mapped('channel_id')
# remove author from notified partners
if not self._context.get('mail_notify_author', False) and self_sudo.author_id:
partners_sudo = partners_sudo - self_sudo.author_id
# update message, with maybe custom valuesz
message_values = {}
if channels_sudo:
message_values['channel_ids'] = [(6, 0, channels_sudo.ids)]
if partners_sudo:
message_values['needaction_partner_ids'] = [(6, 0, partners_sudo.ids)]
if self.model and self.res_id and hasattr(self.env[self.model], 'message_get_message_notify_values'):
message_values.update(self.env[self.model].browse(self.res_id).message_get_message_notify_values(self, message_values))
if message_values:
self.write(message_values)
# notify partners and channels
# those methods are called as SUPERUSER because portal users posting messages
# have no access to partner model. Maybe propagating a real uid could be necessary.
email_channels = channels_sudo.filtered(lambda channel: channel.email_send)
notif_partners = partners_sudo.filtered(lambda partner: 'inbox' in partner.mapped('user_ids.notification_type'))
if email_channels or partners_sudo - notif_partners:
partners_sudo.search([
'|',
('id', 'in', (partners_sudo - notif_partners).ids),
('channel_ids', 'in', email_channels.ids),
('email', '!=', self_sudo.author_id.email or self_sudo.email_from),
])._notify(self, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature)
channels_sudo._notify(self)
# Discard cache, because child / parent allow reading and therefore
# change access rights.
if self.parent_id:
self.parent_id.invalidate_cache()
return True

BIN
mail_private/static/description/check_recipients.png

Before

Width: 751  |  Height: 352  |  Size: 73 KiB

After

Width: 1092  |  Height: 692  |  Size: 108 KiB

123
mail_private/static/description/index.html

@ -8,60 +8,109 @@
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<blockquote>
The module will be available for Odoo 9.0 soon. You have time to buy Internal Messaging before price increase! (When you purchase 8.0 version, you get a right to download 9.0 and any further module versions).
</blockquote>
<p class="oe_mt32">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
<div class="alert alert-warning" style="padding:0.6em 0.6em; font-size: 120%;">
By default, to send a private message to specific recipient(s) you need to delete other followers included to the same document. The module allows to send private messages to recipients you specified, regardless of who are in the followers list.
</p>
<p>
It simplify internal communication in leads, when you need to send some private messages to your colleagues before reply to customer.
It helps in records like project tasks too. In a task with many participants, you can easily send message and only specified colleagues will be notified. It allows to have clean inbox for everybody. It's very essential, because people often neglect important message in Inbox full of useless messages.
</p>
</div>
</div>
</p>
<div class="alert alert-success" style="padding:0.3em 0.6em; font-size: 120%;">
<ul style="padding-left: 1.2em;">
<li style="margin-top: 0.3em;">
It simplify internal communication in leads, when you need to send some private messages to your colleagues before reply to a customer.
</li>
<li style="margin-top: 0.3em;">
It helps in records like project tasks: in a task with many participants, you can easily send message and only specified colleagues will be notified.
</li>
<li style="margin-top: 0.3em;">
It allows to have clean inbox for everybody. It's very essential, because people often neglect important message in Inbox full of useless messages.
</li>
</ul>
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h3 class="oe_slogan">How it works</h3>
<p class="oe_mt32">
Click on the "Send internal message" and choose the recipient(s). Then type any message and click on the "Send" button.
</p>
<div class="oe_demo oe_picture oe_screenshot">
<img src="check_recipients.png"/>
<div class="oe_span12 text-center">
<h3 class="oe_slogan">How it works</h3>
<p class="oe_mt32">
<font style="font-size: 120%;">
Click <b>Send internal message</b>, choose the recipient(s) and send a message.
</font>
</p>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="check_recipients.png?1"/>
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
As a result you can see the message with a dark background, which is sent to the corresponding recipient(s). Other followers of the document will not receive your message.
</p>
<div class="oe_demo oe_picture oe_screenshot">
<img src="result_message.png"/>
</div>
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
As a result, the message is sent to the corresponding recipient(s). Other followers will not receive the notification.
</font>
</p>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="result_message.png?2"/>
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<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>
<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="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>
<div class="oe_row oe_spaced">
<div class="oe_span8">
<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>
<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="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>
</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>

BIN
mail_private/static/description/result_message.png

Before

Width: 751  |  Height: 352  |  Size: 92 KiB

After

Width: 1154  |  Height: 838  |  Size: 94 KiB

203
mail_private/static/src/js/mail_private.js

@ -3,10 +3,11 @@ odoo.define('mail_private', function (require) {
var core = require('web.core');
var Chatter = require('mail.Chatter');
var MailComposer = require('mail_base.base').MailComposer;
var chat_manager = require('mail.chat_manager');
var ChatterComposer = require('mail.ChatterComposer');
var chat_manager = require('mail_base.base').chat_manager;
var session = require('web.session');
var Model = require('web.Model');
var rpc = require('web.rpc');
var config = require('web.config');
var utils = require('mail.utils');
@ -18,97 +19,126 @@ Chatter.include({
this.events['click .oe_compose_post_private'] = 'on_open_composer_private_message';
},
on_post_message: function (message) {
var self = this;
if (this.private) {
message.subtype = false;
}
var options = {model: this.model, res_id: this.res_id};
chat_manager.post_message(message, options).then(
function () {
self.close_composer();
if (message.partner_ids.length) {
self.refresh_followers();
}
}).fail(function () {
// todo: display notification
});
},
on_open_composer_private_message: function (event) {
var self = this;
this.private = true;
this.get_recipients_for_internal_message().then(function (data) {
self.recipients_for_internal_message = data;
self.open_composer({is_private: true});
this.fetch_recipients_for_internal_message().then(function (data) {
self._openComposer({
is_private: true,
suggested_partners: data
});
});
},
on_open_composer_new_message: function () {
this._super.apply(this, arguments);
this.private = false;
},
open_composer: function (options) {
_openComposer: function (options) {
var self = this;
this._super.apply(this, arguments);
if (options && options.is_private) {
this.composer.options.is_private = options.is_private;
_.each(self.recipients_for_internal_message, function (partner) {
self.composer.suggested_partners.push({
checked: (partner.user_ids.length > 0),
partner_id: partner.id,
full_name: partner.name,
name: partner.name,
email_address: partner.email,
reason: _.include(partner.user_ids, self.session.uid)
?'Partner'
:'Follower'
var old_composer = this.composer;
// create the new composer
this.composer = new ChatterComposer(this, this.record.model, options.suggested_partners || [], {
commands_enabled: false,
context: this.context,
input_min_height: 50,
input_max_height: Number.MAX_VALUE,
input_baseline: 14,
is_log: options && options.is_log,
record_name: this.record_name,
default_body: old_composer && old_composer.$input && old_composer.$input.val(),
default_mention_selections: old_composer && old_composer.mention_get_listener_selections(),
is_private: options.is_private
});
this.composer.on('input_focused', this, function () {
this.composer.mention_set_prefetched_partners(this.mentionSuggestions || []);
});
this.composer.insertAfter(this.$('.o_chatter_topbar')).then(function () {
// destroy existing composer
if (old_composer) {
old_composer.destroy();
}
if (!config.device.touch) {
self.composer.focus();
}
self.composer.on('post_message', self, function (message) {
if (options.is_private) {
self.composer.options.is_log = true;
}
self.fields.thread.postMessage(message).then(function () {
self._closeComposer(true);
if (self.postRefresh === 'always' || (self.postRefresh === 'recipients' && message.partner_ids.length)) {
self.trigger_up('reload');
}
});
});
}
var toggle_post_private = self.composer.options.is_private || false;
self.composer.on('need_refresh', self, self.trigger_up.bind(self, 'reload'));
self.composer.on('close_composer', null, self._closeComposer.bind(self, true));
self.$el.addClass('o_chatter_composer_active');
self.$('.o_chatter_button_new_message, .o_chatter_button_log_note, .oe_compose_post_private').removeClass('o_active');
self.$('.o_chatter_button_new_message').toggleClass('o_active', !self.composer.options.is_log && !self.composer.options.is_private);
self.$('.o_chatter_button_log_note').toggleClass('o_active', (self.composer.options.is_log && !options.is_private));
self.$('.oe_compose_post_private').toggleClass('o_active', toggle_post_private);
});
},
get_recipients_for_internal_message: function () {
fetch_recipients_for_internal_message: function () {
var self = this;
self.result = {};
return new Model(this.context.default_model).query(
['message_follower_ids', 'partner_id']).filter(
[['id', '=', self.context.default_res_id]]).all().
then(function (thread) {
var follower_ids = thread[0].message_follower_ids;
self.result[self.context.default_res_id] = [];
self.customer = thread[0].partner_id;
// Fetch partner ids
return new Model('mail.followers').call(
'read', [follower_ids, ['partner_id']]).then(function (res_partners) {
// Filter result and push to array
var res_partners_filtered = _.map(res_partners, function (partner) {
if (partner.partner_id[0] && partner.partner_id[0] !== session.partner_id ) {
return partner.partner_id[0];
}
}).filter(function (partner) {
return typeof partner !== 'undefined';
});
return new Model('res.partner').call(
'read', [res_partners_filtered, ['name', 'email', 'user_ids']]
).then(function (recipients) {
return recipients;
});
});
var follower_ids_domain = [['id', '=', self.context.default_res_id]];
return rpc.query({
model: 'mail.message',
method: 'send_recepients_for_internal_message',
args: [[], self.context.default_model, follower_ids_domain]
}).then(function (res) {
return _.filter(res, function (obj) {
return obj.partner_id !== session.partner_id;
});
});
}
});
MailComposer.include({
init: function (parent, dataset, options) {
this._super(parent, dataset, options);
ChatterComposer.include({
init: function (parent, model, suggested_partners, options) {
this._super(parent, model, suggested_partners, options);
this.events['click .oe_composer_uncheck'] = 'on_uncheck_recipients';
},
preprocess_message: function () {
var self = this;
var def = $.Deferred();
this._super().then(function (message) {
message = _.extend(message, {
subtype: 'mail.mt_comment',
message_type: 'comment',
content_subtype: 'html',
context: self.context,
});
// Subtype
if (self.options.is_log) {
message.subtype = 'mail.mt_note';
}
if (self.options.is_private) {
message.is_private = true;
message.channel_ids = self.get_checked_channel_ids();
}
// Partner_ids
if (!self.options.is_log) {
var checked_suggested_partners = self.get_checked_suggested_partners();
self.check_suggested_partners(checked_suggested_partners).done(function (partner_ids) {
message.partner_ids = (message.partner_ids || []).concat(partner_ids);
// update context
message.context = _.defaults({}, message.context, {
mail_post_autofollow: true,
});
def.resolve(message);
});
} else {
def.resolve(message);
}
});
return def;
},
on_uncheck_recipients: function () {
@ -121,7 +151,6 @@ MailComposer.include({
if (!this.do_check_attachment_upload()){
return false;
}
var self = this;
var recipient_done = $.Deferred();
if (this.options.is_log) {
@ -138,6 +167,7 @@ MailComposer.include({
default_partner_ids: partner_ids,
default_is_log: self.options.is_log,
mail_post_autofollow: true,
is_private: self.options.is_private,
};
if (self.options && self.options.is_private) {
@ -148,7 +178,6 @@ MailComposer.include({
context.default_model = self.context.default_model;
context.default_res_id = self.context.default_res_id;
}
self.do_action({
type: 'ir.actions.act_window',
res_model: 'mail.compose.message',
@ -174,9 +203,27 @@ MailComposer.include({
_.each(checked_partners, function (partner) {
partner.checked = true;
});
checked_partners = _.uniq(_.filter(checked_partners, function (obj) {
return obj.reason !== 'Channel';
}));
this.get_checked_channel_ids();
return checked_partners;
},
get_checked_channel_ids: function () {
var self = this;
var checked_channels = [];
this.$('.o_composer_suggested_partners input:checked').each(function() {
var full_name = $(this).data('fullname');
checked_channels = checked_channels.concat(_.filter(self.suggested_partners, function(item) {
return full_name === item.full_name;
}));
});
checked_channels = _.uniq(_.filter(checked_channels, function (obj) {
return obj.reason === 'Channel';
}));
return _.pluck(checked_channels, 'channel_id');
}
});
});

10
mail_private/static/src/xml/mail_private.xml

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="mail.Chatter">
<t t-extend="mail.Chatter.Buttons">
<t t-jquery="button[title='Send a message']" t-operation="after">
<button class="btn btn-sm btn-link oe_compose_post_private" t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" title="Send a message to specified recipients only">Send internal message</button>
<button class="btn btn-sm btn-link oe_compose_post_private" title="Send a message to specified recipients only">Send internal message</button>
</t>
</t>
<t t-extend="mail.chatter.ChatComposer">
<t t-jquery="span[class='o_chatter_composer_info']" t-operation="replace">
<span class="o_chatter_composer_info" t-if="!widget.options.is_private">
<t t-jquery="small[class='o_chatter_composer_info']" t-operation="replace">
<small class="o_chatter_composer_info" t-if="!widget.options.is_private">
To: Followers of
<t t-if="widget.options.record_name">
&quot;<t t-esc="widget.options.record_name"/>&quot;
@ -17,7 +17,7 @@
<t t-if="!widget.options.record_name">
this document
</t>
</span>
</small>
</t>
<t t-jquery="div[class='o_composer_suggested_partners']" t-operation="after">
<button class="btn btn-sm btn-link oe_composer_uncheck" t-if="widget.options.is_private">Uncheck all</button>

1
mail_recovery/__init__.py

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

1
mail_recovery/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Mail Recovery",
'summary': """Backup and recover unsent message""",

1
mail_reply/__init__.py

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

1
mail_reply/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
"name": """Always show reply button""",
"summary": """Got a message out of a Record? Now you can reply to it too!""",

1
mail_reply/tests/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_default

1
mail_reply/tests/test_default.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import odoo.tests

1
mail_sent/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import models

3
mail_sent/__openerp__.py

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
{
"name": "Sentbox",
"summary": """Quick way to find sent messages""",
"category": "Discuss",
"images": ['images/menu.png'],
"version": "1.0.4",
"version": "11.0.1.0.4",
"author": "IT-Projects LLC, Ivan Yelizariev, Pavel Romanchenko",
"support": "apps@it-projects.info",

65
mail_sent/i18n/es.po

@ -0,0 +1,65 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_sent
#
# Translators:
# Randall Castro <rcastro@treintaycinco.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 23:07+0000\n"
"PO-Revision-Date: 2018-04-21 23:07+0000\n"
"Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Asistente para composición de correo"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_message
msgid "Message"
msgstr "Mensaje"
#. module: mail_sent
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:15
#, python-format
msgid "No sent messages"
msgstr "Sin mensajes enviados"
#. module: mail_sent
#. openerp-web
#: code:addons/mail_sent/static/src/js/sent.js:61
#: code:addons/mail_sent/static/src/xml/menu.xml:7
#: model:ir.model.fields,field_description:mail_sent.field_mail_compose_message_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_message_sent
#, python-format
msgid "Sent"
msgstr "Enviado"
#. module: mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_message_sent
msgid "Was message sent to someone"
msgstr "Mensaje fué enviado a alguien"
#. module: mail_sent
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:16
#, python-format
msgid "You can send messages and then these messages will appear here."
msgstr "Puede enviar mensajes y estos mensajes aparecerán aquí."
#. module: mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_compose_message_sent
msgid "dummy field to fix inherit error"
msgstr "campo ficticio para corregir el error heredado"

68
mail_sent/i18n/pt.po

@ -1,36 +1,22 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_sent
#
# * mail_sent
#
# Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-29 18:57+0000\n"
"PO-Revision-Date: 2017-03-29 18:57+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"POT-Creation-Date: 2018-04-21 00:05+0000\n"
"PO-Revision-Date: 2018-04-21 00:05+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2018\n"
"Language-Team: Portuguese (https://www.transifex.com/it-projects-llc/teams/76080/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: mail_sent
#: model:ir.actions.client,help:mail_sent.action_mail_sent_feeds
msgid "<p>\n"
" No message found and no message sent yet.\n"
" </p><p>\n"
" Click on the top-right icon to compose a message. This\n"
" message will be sent by email if it's an internal contact.\n"
" </p>\n"
" "
msgstr "<p>\n"
" Nenhuma mensagem encontrada e nenhuma enviada ainda.\n"
" </p><p>\n"
" Clique no ícone no canto superior direito para compor uma nova mensagem.\n"
" Esta mensagem será enviada por e-mail se não for um contato interno.\n"
" </p>\n"
" "
"Language: pt\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_compose_message
@ -43,25 +29,37 @@ msgid "Message"
msgstr "Mensagem"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_notification
msgid "Notifications"
msgstr "Notificações"
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:15
#, python-format
msgid "No sent messages"
msgstr ""
#. module: mail_sent
#: model:ir.actions.client,name:mail_sent.action_mail_sent_feeds
#: model:ir.ui.menu,name:mail_sent.mail_sentfeeds
#: field:mail.compose.message,sent:0
#: field:mail.message,sent:0
#. openerp-web
#: code:addons/mail_sent/static/src/js/sent.js:61
#: code:addons/mail_sent/static/src/xml/menu.xml:7
#: model:ir.model.fields,field_description:mail_sent.field_mail_compose_message_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_message_sent
#, python-format
msgid "Sent"
msgstr "Enviados"
#. module: mail_sent
#: help:mail.message,sent:0
#: model:ir.model.fields,help:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_message_sent
msgid "Was message sent to someone"
msgstr "Foi enviada mensagem para alguém"
#. module: mail_sent
#: help:mail.compose.message,sent:0
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:16
#, python-format
msgid "You can send messages and then these messages will appear here."
msgstr ""
#. module: mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_compose_message_sent
msgid "dummy field to fix inherit error"
msgstr "campo fictício para corrigir o erro da herança"

68
mail_sent/i18n/pt_BR.po

@ -1,36 +1,22 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_sent
#
# * mail_sent
#
# Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-29 18:57+0000\n"
"PO-Revision-Date: 2017-03-29 18:57+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"POT-Creation-Date: 2018-04-21 00:05+0000\n"
"PO-Revision-Date: 2018-04-21 00:05+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2018\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/it-projects-llc/teams/76080/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: mail_sent
#: model:ir.actions.client,help:mail_sent.action_mail_sent_feeds
msgid "<p>\n"
" No message found and no message sent yet.\n"
" </p><p>\n"
" Click on the top-right icon to compose a message. This\n"
" message will be sent by email if it's an internal contact.\n"
" </p>\n"
" "
msgstr "<p>\n"
" Nenhuma mensagem encontrada e nenhuma enviada ainda.\n"
" </p><p>\n"
" Clique no ícone no canto superior direito para compor uma nova mensagem.\n"
" Esta mensagem será enviada por e-mail se não for um contato interno.\n"
" </p>\n"
" "
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_compose_message
@ -43,25 +29,37 @@ msgid "Message"
msgstr "Mensagem"
#. module: mail_sent
#: model:ir.model,name:mail_sent.model_mail_notification
msgid "Notifications"
msgstr "Notificações"
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:15
#, python-format
msgid "No sent messages"
msgstr ""
#. module: mail_sent
#: model:ir.actions.client,name:mail_sent.action_mail_sent_feeds
#: model:ir.ui.menu,name:mail_sent.mail_sentfeeds
#: field:mail.compose.message,sent:0
#: field:mail.message,sent:0
#. openerp-web
#: code:addons/mail_sent/static/src/js/sent.js:61
#: code:addons/mail_sent/static/src/xml/menu.xml:7
#: model:ir.model.fields,field_description:mail_sent.field_mail_compose_message_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,field_description:mail_sent.field_mail_message_sent
#, python-format
msgid "Sent"
msgstr "Enviados"
#. module: mail_sent
#: help:mail.message,sent:0
#: model:ir.model.fields,help:mail_sent.field_mail_mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_message_sent
msgid "Was message sent to someone"
msgstr "Foi enviada mensagem para alguém"
#. module: mail_sent
#: help:mail.compose.message,sent:0
#. openerp-web
#: code:addons/mail_sent/static/src/xml/menu.xml:16
#, python-format
msgid "You can send messages and then these messages will appear here."
msgstr ""
#. module: mail_sent
#: model:ir.model.fields,help:mail_sent.field_mail_compose_message_sent
msgid "dummy field to fix inherit error"
msgstr "campo fictício para corrigir o erro da herança"

1
mail_sent/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from odoo import api, models, fields

1
mail_sent/tests/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_js

1
mail_sent/tests/test_js.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import odoo.tests

1
mail_to/__init__.py

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

3
mail_to/__openerp__.py

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
{
"name": """Show message recipients""",
"summary": """Allows you be sure, that all discussion participants were notified""",
"category": "Discuss",
"images": ['images/1.png'],
"version": "1.0.1",
"version": "11.0.1.0.1",
"author": "IT-Projects LLC, Pavel Romanchenko",
"support": "apps@it-projects.info",

40
mail_to/i18n/es.po

@ -0,0 +1,40 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_to
#
# Translators:
# Randall Castro <rcastro@treintaycinco.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 23:07+0000\n"
"PO-Revision-Date: 2018-04-21 23:07+0000\n"
"Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mail_to
#. openerp-web
#: code:addons/mail_to/static/src/xml/recipient.xml:6
#, python-format
msgid "To:"
msgstr "Para:"
#. module: mail_to
#. openerp-web
#: code:addons/mail_to/static/src/xml/recipient.xml:15
#, python-format
msgid "and"
msgstr "y"
#. module: mail_to
#. openerp-web
#: code:addons/mail_to/static/src/xml/recipient.xml:16
#, python-format
msgid "more"
msgstr "más"

31
mailgun/README.rst

@ -1,11 +1,14 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: https://www.gnu.org/licenses/lgpl
:alt: License: LGPL-3
=========
Mailgun
=========
With this module you can receive incoming messages from mailgun.
The module allows to receive incoming messages or send them to clients who uses external mail services (e.g. gmail.com) by using Mailgun.
There is no IMAP or POP3 servers on mailgun that is to be used with odoo.
That is why we need this module. It fetches messages from mailgun using their API
and stores them in odoo.
That is why we need this module. It fetches messages from mailgun using their API and stores them in odoo.
TODO
====
@ -24,13 +27,27 @@ 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/mailgun/>`__.
Thank you for understanding!
`IT-Projects Team <https://www.it-projects.info/team>`__
Further information
===================
HTML Description: https://apps.odoo.com/apps/modules/9.0/mailgun/
Demo: http://runbot.it-projects.info/demo/mail-addons/11.0
HTML Description: https://apps.odoo.com/apps/modules/11.0/mailgun/
Usage instructions: `<doc/index.rst>`_
Usage instructions: `<doc/index.rst>`__
Changelog: `<doc/changelog.rst>`_
Changelog: `<doc/changelog.rst>`__
Notifications on updates: `via Atom <https://github.com/it-projects-llc/mail-addons/commits/11.0/mailgun.atom>`_, `by Email <https://blogtrottr.com/?subscribe=https://github.com/it-projects-llc/mail-addons/commits/11.0/malgun.atom>`_
Tested on Odoo 9.0 c8cd67c5d98b410cabe0a6efb3347a8a4de731d8
Tested on Odoo 11.0 dc61861f90d15797b19f8ebddfb0c8a66d0afa88

1
mailgun/__init__.py

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models

48
mailgun/__manifest__.py

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": """Mailgun""",
"summary": """Setup the outgoing and incoming mail flow easily by using Mailgun""",
"category": "Discuss",
"live_test_url": "http://apps.it-projects.info/shop/product/mailgun?version=11.0",
"images": ["images/mailgun_main.png"],
"version": "11.0.1.1.0",
"application": False,
"author": "IT-Projects LLC, Ildar Nasyrov",
"support": "apps@it-projects.info",
"website": "https://it-projects.info/team/iledarn",
"license": "LGPL-3",
"price": 389.00,
"currency": "EUR",
"depends": [
"mail",
],
"external_dependencies": {"python": [], "bin": []},
"data": [
'data/ir_cron_data.xml',
],
"demo": [
],
"qweb": [
],
"post_load": None,
"pre_init_hook": None,
"post_init_hook": None,
"uninstall_hook": None,
"auto_install": False,
"installable": True,
"demo_title": "Mailgun",
"demo_addons": [],
"demo_addons_hidden": [],
"demo_url": "mailgun",
"demo_summary": "Easy to send outgoing and fetch incoming messages by using Mailgun",
"demo_images": [
"images/mailgun_main.png",
]
}

15
mailgun/__openerp__.py

@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
{
'name': "Mailgun",
'author': "IT-Projects LLC, Ildar Nasyrov",
'license': 'LGPL-3',
"support": "apps@it-projects.info",
'website': "https://twitter.com/nasyrov_ildar",
'category': 'Discuss',
'version': '1.1.0',
'depends': ['mail'],
'data': [
'data/cron.xml',
],
'installable': False
}

1
mailgun/controllers/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import main

1
mailgun/controllers/main.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from odoo import http
from odoo.http import request
import re

14
mailgun/data/cron.xml

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="mailgun_domain_verification" model="ir.cron">
<field name="name">Mailgun - domain verification</field>
<field name='interval_number'>10</field>
<field name='interval_type'>minutes</field>
<field name="numbercall">288</field>
<field name="active">True</field>
<field name="model">ir.config_parameter</field>
<field name="function">mailgun_verify</field>
</record>
</data>
</openerp>

15
mailgun/data/ir_cron_data.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="mailgun_domain_verification" model="ir.cron">
<field name="name">Mailgun - domain verification request</field>
<field name="model_id" ref="model_ir_config_parameter"/>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">10</field>
<field name="state">code</field>
<field name="code">model.mailgun_verify()</field>
<field name="doall" eval="True"/>
</record>
</data>
</openerp>

3
mailgun/doc/changelog.rst

@ -1,6 +1,3 @@
Updates
=======
`1.1.0`
-------

111
mailgun/doc/index.rst

@ -2,55 +2,92 @@
Mailgun
=========
Usage
=====
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Configuration
=============
Mailgun-side
------------
* register or log in http://mailgun.com
* On https://mailgun.com/app/domains click on you domain, e.g. sandbox123...mailgun.org domain. Here you can see all information needed to configure odoo outgoing mail feature
* Open menu ``[[ Domains ]]`` and click on your domain, e.g. ``sandbox123*****.mailgun.org`` domain. Here you can see all the information needed to configure odoo outgoing mail feature
* Please note that state of your domain should be ``Active`` before you can use it. If it is ``Unverified``, verify it first using Mailgun FAQ - `How do I verify my domain <https://help.mailgun.com/hc/en-us/articles/202052074-How-do-I-verify-my-domain->`__
* if you are using your sandbox domain, add Authorized Recipient first (Sandbox domains are restricted to `authorized recipients <https://help.mailgun.com/hc/en-us/articles/217531258>`__ only)
* create new Route
* if you in sandbox domain, add Authorized Recepient
* Copy API Key value into odoo
* Open menu ``Settings / Parameters / System Parameters``
* Create new parameter
* key: ``mailgun.apikey``
* Value: API Key from mailgun (``key-...``)
* click Save
* Copy smtp credentials into odoo
* Open menu ``[[ Routes ]]``
* Click ``[Create Route]`` button
* **Expression Type** - ``Custom``
* **Raw Expression** - ``match_recipient('.*@<your mail domain>')``
* **Actions** - ``Store and notify``, ``http://<your odoo domain>/mailgun/notify``
* open ``Settings / Technical / Email / Outgoing Mail Servers``
Odoo-side
---------
* delete localhost
* create new server
* `Activate Developer Mode <https://odoo-development.readthedocs.io/en/latest/odoo/usage/debug-mode.html>`__
* Configure **Outgoung mail server**
* Description: ``mailgun``
* SMTP Server: ``smtp.mailgun.org``
* Connection Security: ``SSL/TLS``
* Username: e.g. ``postmaster@sandbox123....mailgun.org``
* Password: ``...`` (copy ``Default Password`` from mailgun)
* Open menu ``[[ Settings ]] >> Technical >> Email >> Outgoing Mail Servers``
* Edit ``localhost`` record or create new one with the following:
* From odoo menu ``Settings / General Settings`` edit Alias Domain
* Put your mailgun domain here. E.g. sandbox123...mailgun.org
* Click 'Apply' button
* **Description** - ``Mailgun``
* **SMTP Server** - take from Mailgun **SMTP Hostname** (usually, it is ``smtp.mailgun.org``)
* **Connection Security** - ``SSL/TLS``
* **Username** - take from Mailgun **Default SMTP Login**
* **Password** - take from Mailgun **Default Password**
* Click ``[Test Connection]`` button to check the connection and then ``[Save]``
* From https://mailgun.com/cp/routes create new route
* Configure **Incoming mail feature**
* Priority: ``0``
* Filter expression: ``catch_all()``
* Actions: ``store(notify="http://<your odoo domain>/mailgun/notify")``
* Configure catchall domain
* Set admin's email alias. Open menu ``Settings / Users / Users``
* Open menu ``Settings / General Settings``, check **External Email Servers** and edit **Alias Domain** - set it from Mailgun **Domain Name**
* Click ``[Save]`` button
* choose your user and click ``[Edit]``
* On Preference tab put alias into Messaging Alias field and click ``[Save]``. E.g. ``admin@sandbox...mailgun.org``
* Set Mailgun API credentials
* Open menu ``[[ Settings ]] >> Parameters >> System Parameters``
* Create new parameter
* key: ``mailgun.apikey``
* Value: API Key from mailgun (``key-...``)
* Click ``[Save]`` button
* Configure mail aliases and emails for users
* Open menu ``[[ Settings ]] >> Users >> Users``
* Select the ``Administrator`` user (for example, you should configure all your users the same way but using different aliases) and click ``[Edit]``
* On Preference tab edit **Alias** field - create new mail alias, e.g. ``admin@<you mail domain>`` with the following settings
* **Alias Name** - ``admin``
* **Aliased Model** - ``Users``
* **Record Thread ID** - ``1``
* **Default Values** - ``{}``
* **Alias Contact** - ``Everyone``
* **Security Owner** - ``Administrator``
* **Parent Model** - Not set
* **Parent Record Thread ID** - ``0``
* Open user's **Related Partner** and edit **Email** field - usually it should be the same as mail alias name (``admin@<you mailgun domain`` for ``Administrator``) - this would be an address for replying user's messages
Usage
=====
* Via your favorite mail client (e.g. gmail.com) send email to ``admin@sandox...mailgun.org``
* Open ``Discuss`` in odoo
* See your message there
* Reply to the message and check it in your mail client (e.g. gmail.com)
Outgoing
--------
* Open menu ``[[ Settings ]]>> Email >> Emails`` to create a message
* Click ``[Send Now]`` button
* RESULT: receive the message in your mail client (e.g. on gmail.com)
Incoming
--------
* Create new message from your mail client to e.g. ``admin@<you mailgun domain>``
* Open menu ``[[ Discuss ]]`` in Odoo
* RESULT: See your message there

BIN
mailgun/images/mailgun_main.png

After

Width: 750  |  Height: 371  |  Size: 384 KiB

36
mailgun/models.py

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
import requests
import simplejson
from openerp import models, api
import logging
_logger = logging.getLogger(__name__)
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
@api.model
def mailgun_fetch_message(self, message_url):
api_key = self.env['ir.config_parameter'].sudo().get_param('mailgun.apikey')
res = requests.get(message_url, headers={'Accept': 'message/rfc2822'}, auth=('api', api_key), verify=False)
self.message_process(False, res.json().get('body-mime'))
class IrConfigParameter(models.Model):
_inherit = ['ir.config_parameter']
@api.model
def mailgun_verify(self):
verified = self.get_param('mailgun.verified')
if verified:
return
api_key = self.get_param('mailgun.apikey')
mail_domain = self.get_param('mail.catchall.domain')
if api_key and mail_domain:
url = "https://api.mailgun.net/v3/domains/%s/verify" % mail_domain
res = requests.put(url, auth=("api", api_key))
if res.status_code == 200 and simplejson.loads(res.text)["domain"]["state"] == "active":
self.set_param('mailgun.verified', '1')

4
mailgun/models/__init__.py

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import mail_thread
from . import ir_config_parameter

25
mailgun/models/ir_config_parameter.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
import requests
import simplejson
from openerp import models, api
import logging
_logger = logging.getLogger(__name__)
class IrConfigParameter(models.Model):
_inherit = ['ir.config_parameter']
@api.model
def mailgun_verify(self):
verified = self.sudo().get_param('mailgun.verified')
if verified:
return
api_key = self.sudo().get_param('mailgun.apikey')
mail_domain = self.sudo().get_param('mail.catchall.domain')
if api_key and mail_domain:
url = "https://api.mailgun.net/v3/domains/%s/verify" % mail_domain
res = requests.put(url, auth=("api", api_key))
if res.status_code == 200 and simplejson.loads(res.text)["domain"]["state"] == "active":
self.sudo().set_param('mailgun.verified', '1')

18
mailgun/models/mail_thread.py

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
import requests
from odoo import models, api
import logging
_logger = logging.getLogger(__name__)
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
@api.model
def mailgun_fetch_message(self, message_url):
api_key = self.env['ir.config_parameter'].sudo().get_param('mailgun.apikey')
res = requests.get(message_url, headers={'Accept': 'message/rfc2822'}, auth=('api', api_key), verify=False)
self.message_process(False, res.json().get('body-mime'))

BIN
mailgun/static/description/icon.png

After

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

332
mailgun/static/description/index.html

@ -0,0 +1,332 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan" style="color:#875A7B;">Mailgun</h2>
<h3 class="oe_slogan">Easy to setup the outgoing and incoming mail flow</h3>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
<div class="alert alert-success" style="padding:0.3em 0.6em; font-size: 150%;">
The module provides Odoo and Mailgun integration to be able to easily configure <em>Outgoing</em> and <em>Incoming</em> mail flow.
<div style="margin-top:2%;">
From Mailgun side you get:
<ul style="padding-left: 1.6em;">
<li style="margin-top: 0.3em;">
Powerful Sending Infrastructure
</li>
<li>
Intelligent Inbound Routing &amp; Storage
</li>
<li>
Tracking and Analytics
</li>
<li>
Email Validation
</li>
</ul>
</div>
</div>
</p>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h3 class="oe_slogan">Mailgun Configuration</h3>
<h4 class="oe_slogan"><b>Manage Domains</b></h4>
<div class="oe_span12">
<p class="oe_mt32">
<font style="font-size: 120%;">
<ul style="padding-left: 1.4em;">
<li>
Create account on <a href="https://mailgun.com" target="_blank">Mailgun</a> service
</li>
<li>
Add domain in <b>Domains</b> menu
</li>
</ul>
Note that the state of your domain should be <b>Active</b> to be able to use it from Odoo side. To verify it follow the instruction <a href="https://help.mailgun.com/hc/en-us/articles/202052074-How-do-I-verify-my-domain-" target="_blank">How do I verify my domain</a>
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_add_domain.png"/>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
Click on added domain name and see all the information needed to configure your <em>Incoming</em> and <em>Outgoing</em> mail feature.
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_domain_details.png"/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
If you use sandbox domain, add <b>Authorized Recipient</b> first (sandbox domains are restricted to <a href="https://help.mailgun.com/hc/en-us/articles/217531258" target="_blank">authorized recipients</a>)
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_auth_recipients.png"/>
</div>
<br/>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_auth_recipients_form.png"/>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced" style="margin-top: 5em;">
<h4 class="oe_slogan"><b>Routes</b></h4>
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
Navigate to <b>Routes</b> menu and create a new route
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_route.png"/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
<font style="font-size: 120%;">
Set the following fields and values:
<ul>
<li>
<b>Expression Type</b>: Custom
</li>
<li>
<b>Raw Expression</b>: match_recipient('.*@your_mail_domain')
</li>
<li>
<b>Actions</b>:
<ul>
<li>
Check the box <em>Store and notify</em>
</li>
<li>
Put there http://your_odoo_domain/mailgun/notify
</li>
</li>
</ul>
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_route_form.png"/>
</div>
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
<b>Now everything is ready to move to Odoo configuration.</b>
</font>
</p>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced" style="margin-top: 6em;">
<h3 class="oe_slogan">Odoo Configuration</h3>
<h4 class="oe_slogan"><b>Outgoing Mail Feature</b></h4>
<div class="oe_span12">
<p class="oe_mt32">
<font style="font-size: 120%;">
At first, activate <a href="https://odoo-development.readthedocs.io/en/latest/odoo/usage/debug-mode.html" target="_blank">the developer mode</a>. Then go to <em>Settings &rarr; Technical &rarr; Email &rarr; Outgoing Mail Servers</em> menu and populate the following fields:
<ul>
<li>
<b>Description</b> - e.g. Mailgun
</li>
<li>
<b>SMTP Server</b> - smtp.mailgun.org
</li>
<li>
<b>Connection Security</b> - SSL/TLS
</li>
<li>
<b>Username</b> - use <em>Default SMTP Login</em> from mailgun
</li>
<li>
<b>Password</b> - use <em>Default Password</em> from mailgun
</li>
</ul>
Once the fields are populated, click <b>Test Connection</b> button to check the connection to smtp server. If everything is ok, you can see popup message <em>Connection Test Succeeded! Everything seems properly set up!</em> and you are able to send messages now.
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_odoo_outgoing.png"/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h4 class="oe_slogan"><b>Incoming Mail Feature</b></h4>
<div class="oe_span12">
<p class="oe_mt32">
<font style="font-size: 120%;">
Note that to setup incoming mailing, it is not required to setup <em>Incoming Mail Server</em>.
<br/>
Open <em>Settings &rarr; General Settings</em> menu:
<ul>
<li>
Check the box <b>External Email Servers</b>
</li>
<li>
Set as <b>Alias Domain</b> the domain name from Mailgun
</li>
</ul>
Save the changes.
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_odoo_general.png"/>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
<font style="font-size: 120%;">
Setup mailgun API credentials in <em>Settings &rarr; Parameters &rarr; System Parameters</em> menu. Create new parameter:
<ul>
<li>
<b>Key</b> - mailgun.apikey
</li>
<li>
<b>Value</b> - key-12345...
</li>
</ul>
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_odoo_sys_param.png"/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
Now you can configure mail aliases for users. Go to <em>Settings &rarr; Users</em> menu and open user form (e.g. for Administrator). Then switch to <em>Preferences</em> tab and select an existing alias or create new one in the <b>Alias</b> field.
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_odoo_mail_alias.png"/>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
Open the user's related partner and set <b>Email</b> - the email is used for replying on user messages
</font>
</p>
</div>
<div class="oe_screenshot" align="center">
<img style="max-width: 80%" src="mailgun_odoo_partner_email.png"/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12 text-center">
<p class="oe_mt32">
<font style="font-size: 120%;">
<b>
Everything is done! It's time to send/receive email messages now!
</b>
</font>
</p>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span8">
<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>
<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="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>
</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>
</section>

BIN
mailgun/static/description/mailgun_add_domain.png

After

Width: 1196  |  Height: 509  |  Size: 133 KiB

BIN
mailgun/static/description/mailgun_auth_recipients.png

After

Width: 1135  |  Height: 916  |  Size: 150 KiB

BIN
mailgun/static/description/mailgun_auth_recipients_form.png

After

Width: 1079  |  Height: 443  |  Size: 68 KiB

BIN
mailgun/static/description/mailgun_domain_details.png

After

Width: 1209  |  Height: 916  |  Size: 135 KiB

BIN
mailgun/static/description/mailgun_odoo_general.png

After

Width: 1257  |  Height: 681  |  Size: 112 KiB

BIN
mailgun/static/description/mailgun_odoo_mail_alias.png

After

Width: 1192  |  Height: 647  |  Size: 113 KiB

BIN
mailgun/static/description/mailgun_odoo_outgoing.png

After

Width: 1186  |  Height: 671  |  Size: 114 KiB

BIN
mailgun/static/description/mailgun_odoo_partner_email.png

After

Width: 969  |  Height: 653  |  Size: 83 KiB

BIN
mailgun/static/description/mailgun_odoo_sys_param.png

After

Width: 1192  |  Height: 647  |  Size: 108 KiB

BIN
mailgun/static/description/mailgun_route.png

After

Width: 1179  |  Height: 921  |  Size: 287 KiB

BIN
mailgun/static/description/mailgun_route_form.png

After

Width: 1179  |  Height: 921  |  Size: 201 KiB

1
res_partner_company_messages/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import models

1
res_partner_company_messages/__openerp__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Aggregate messages from company's contacts",
'version': '1.0.0',

1
res_partner_company_messages/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from openerp import api
from openerp import models

1
res_partner_mails_count/__init__.py

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import models

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save