Browse Source

Merge pull request #1 from KolushovAlexandr/11.0-merge_commit_conflict_resolving

11.0 merge commit conflict resolving
pull/170/head
Ruslan Ronzhin 6 years ago
committed by GitHub
parent
commit
dd5bb64837
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .travis.yml
  2. 3
      README.md
  3. 35
      mail_all/README.rst
  4. 3
      mail_all/__init__.py
  5. 11
      mail_all/__manifest__.py
  6. 3
      mail_all/doc/changelog.rst
  7. 10
      mail_all/doc/index.rst
  8. 8
      mail_all/i18n/es.po
  9. 1
      mail_all/models/__init__.py
  10. 2
      mail_all/static/description/index.html
  11. 3
      mail_all/static/src/css/mail_all.css
  12. 62
      mail_all/static/src/js/mail_all.js
  13. 2
      mail_all/static/src/xml/menu.xml
  14. 1
      mail_all/tests/__init__.py
  15. 1
      mail_all/tests/test_js.py
  16. 1
      mail_all/views/templates.xml
  17. 40
      mail_archives/README.rst
  18. 1
      mail_archives/__init__.py
  19. 3
      mail_archives/__manifest__.py
  20. 0
      mail_archives/doc/changelog.rst
  21. 20
      mail_archives/doc/index.rst
  22. 10
      mail_archives/i18n/es.po
  23. 6
      mail_archives/static/description/index.html
  24. 3
      mail_archives/static/src/css/archives.css
  25. 97
      mail_archives/static/src/js/archives.js
  26. 2
      mail_archives/static/src/xml/menu.xml
  27. 1
      mail_archives/tests/__init__.py
  28. 1
      mail_archives/tests/test_js.py
  29. 1
      mail_archives/views/templates.xml
  30. 35
      mail_attachment_popup/README.rst
  31. 0
      mail_attachment_popup/__init__.py
  32. 16
      mail_attachment_popup/doc/index.rst
  33. 26
      mail_attachment_popup/i18n/ru.po
  34. BIN
      mail_attachment_popup/images/popup_image.png
  35. BIN
      mail_attachment_popup/static/description/attach_image.png
  36. BIN
      mail_attachment_popup/static/description/download.png
  37. 84
      mail_attachment_popup/static/description/index.html
  38. BIN
      mail_attachment_popup/static/description/popup.png
  39. 429
      mail_attachment_popup/static/lib/js/jquery.arcticmodal.js
  40. 8
      mail_attachment_popup/static/src/css/jquery.arcticmodal.css
  41. 11
      mail_attachment_popup/static/src/css/simple.css
  42. 16
      mail_attachment_popup/static/src/css/styles.css
  43. BIN
      mail_attachment_popup/static/src/img/loading.gif
  44. 29
      mail_attachment_popup/static/src/xml/mail_attachment_popup.xml
  45. 13
      mail_attachment_popup/views/mail_attachment_popup_template.xml
  46. 32
      mail_base/README.rst
  47. 1
      mail_base/__init__.py
  48. 3
      mail_base/__manifest__.py
  49. 1
      mail_base/controllers/main.py
  50. 20
      mail_base/doc/index.rst
  51. 8
      mail_base/i18n/es.po
  52. 10
      mail_base/i18n/pt.po
  53. 10
      mail_base/i18n/pt_BR.po
  54. 1
      mail_base/models.py
  55. 2264
      mail_base/static/lib/base.js
  56. 2
      mail_base/tests/__init__.py
  57. 16
      mail_base/tests/test_default.py
  58. 1
      mail_check_immediately/__init__.py
  59. 1
      mail_check_immediately/__manifest__.py
  60. 1
      mail_check_immediately/models.py
  61. 2
      mail_check_immediately/static/description/index.html
  62. 1
      mail_fix_553/__init__.py
  63. 1
      mail_fix_553/__manifest__.py
  64. 1
      mail_fix_553/mail_fix_553.py
  65. 49
      mail_move_message/README.rst
  66. 3
      mail_move_message/__init__.py
  67. 3
      mail_move_message/controllers/__init__.py
  68. 58
      mail_move_message/controllers/main.py
  69. 10
      mail_move_message/data/mail_move_message_data.xml
  70. 5
      mail_move_message/doc/changelog.rst
  71. 35
      mail_move_message/doc/index.rst
  72. 199
      mail_move_message/mail_move_message_models.py
  73. 97
      mail_move_message/mail_move_message_views.xml
  74. BIN
      mail_move_message/static/description/delete-message.png
  75. BIN
      mail_move_message/static/description/icon.png
  76. BIN
      mail_move_message/static/description/inbox-move.png
  77. 74
      mail_move_message/static/description/index.html
  78. BIN
      mail_move_message/static/description/record-move-back.png
  79. 174
      mail_move_message/static/src/js/mail_move_message.js
  80. 8
      mail_move_message/static/src/xml/mail_move_message_main.xml
  81. 3
      mail_move_message/tests/__init__.py
  82. 52
      mail_move_message/tests/test_mail_move.py
  83. 62
      mail_multi_website/README.rst
  84. 40
      mail_multi_website/__init__.py
  85. 51
      mail_multi_website/__manifest__.py
  86. 4
      mail_multi_website/doc/changelog.rst
  87. 73
      mail_multi_website/doc/index.rst
  88. BIN
      mail_multi_website/images/main.jpg
  89. 7
      mail_multi_website/models/__init__.py
  90. 20
      mail_multi_website/models/ir_property.py
  91. 16
      mail_multi_website/models/mail_message.py
  92. 151
      mail_multi_website/models/mail_template.py
  93. 26
      mail_multi_website/models/mail_thread.py
  94. 45
      mail_multi_website/models/res_users.py
  95. 10
      mail_multi_website/models/website.py
  96. 0
      mail_multi_website/static/description/icon.png
  97. 97
      mail_multi_website/static/description/index.html
  98. 4
      mail_multi_website/tests/__init__.py
  99. 41
      mail_multi_website/tests/test_fetch.py
  100. 10
      mail_multi_website/tests/test_mail_model.py

7
.travis.yml

@ -1,7 +1,7 @@
language: python language: python
python: python:
- "2.7"
- "3.5"
#dist: trusty #dist: trusty
sudo: false sudo: false
@ -15,7 +15,7 @@ addons:
env: env:
global: global:
- VERSION="10.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" UNIT_TEST="0"
- VERSION="11.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" UNIT_TEST="0"
- EXCLUDE="hw_printer_network" - EXCLUDE="hw_printer_network"
- PYLINT_ODOO_JSLINTRC="/home/travis/maintainer-quality-tools/travis/cfg/.jslintrc" - PYLINT_ODOO_JSLINTRC="/home/travis/maintainer-quality-tools/travis/cfg/.jslintrc"
- TRANSIFEX_USER='i18n-bot@it-projects.info' - TRANSIFEX_USER='i18n-bot@it-projects.info'
@ -27,9 +27,6 @@ env:
- TESTS="1" ODOO_REPO="OCA/OCB" - TESTS="1" ODOO_REPO="OCA/OCB"
- TRANSIFEX="1" - TRANSIFEX="1"
virtualenv:
system_site_packages: true
install: install:
- pip install anybox.testing.openerp - pip install anybox.testing.openerp
- git clone https://github.com/it-projects-llc/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - git clone https://github.com/it-projects-llc/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools

3
README.md

@ -1,5 +1,5 @@
# mail-addons # mail-addons
Odoo (OpenERP) mail addons
Odoo mail addons
List of repositories: List of repositories:
--------------------- ---------------------
@ -7,7 +7,6 @@ List of repositories:
* https://github.com/it-projects-llc/misc-addons * https://github.com/it-projects-llc/misc-addons
* https://github.com/it-projects-llc/pos-addons * https://github.com/it-projects-llc/pos-addons
* https://github.com/it-projects-llc/mail-addons * https://github.com/it-projects-llc/mail-addons
* https://github.com/it-projects-llc/rental-addons
* https://github.com/it-projects-llc/access-addons * https://github.com/it-projects-llc/access-addons
* https://github.com/it-projects-llc/website-addons * https://github.com/it-projects-llc/website-addons
* https://github.com/it-projects-llc/l10n-addons * https://github.com/it-projects-llc/l10n-addons

35
mail_all/README.rst

@ -2,15 +2,38 @@
Show all messages Show all messages
=================== ===================
Adds ``Discuss / All`` menu, that shows all messages accesable by current user
Adds ``Discuss / All`` menu, that shows all messages accesable by current user.
Credits
=======
Contributors
------------
* Pavel Romanchenko <apps@it-projects.info>
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_all/>`__.
Thank you for understanding!
`IT-Projects Team <https://www.it-projects.info/team>`__
Further information Further information
-------------------
===================
Demo: http://runbot.it-projects.info/demo/mail_addons/11.0
HTML Description: https://apps.odoo.com/apps/modules/9.0/mail_all/
HTML Description: https://apps.odoo.com/apps/modules/11.0/mail_all/
Usage instructions: `<doc/index.rst>`__
Usage instructions: `<doc/index.rst>`_
Changelog: `<doc/changelog.rst>`__
Changelog: `<doc/changelog.rst>`_
Tested on Odoo 9.0 d3dd4161ad0598ebaa659fbd083457c77aa9448d
Tested on Odoo 11.0 ecbf7aa4714479229658d14cce28fa00376ed390

3
mail_all/__init__.py

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

11
mail_all/__manifest__.py

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
{ {
"name": "Show all messages", "name": "Show all messages",
"summary": """Checkout all messages where you have access""", "summary": """Checkout all messages where you have access""",
"category": "Discuss", "category": "Discuss",
# "live_test_url": "",
"images": ['images/1.jpg'], "images": ['images/1.jpg'],
"version": "1.0.0",
"version": "11.0.1.0.0",
"application": False,
"author": "IT-Projects LLC, Pavel Romanchenko", "author": "IT-Projects LLC, Pavel Romanchenko",
"support": "apps@it-projects.info", "support": "apps@it-projects.info",
@ -24,6 +25,12 @@
"static/src/xml/menu.xml", "static/src/xml/menu.xml",
], ],
"demo": [], "demo": [],
"post_load": None,
"pre_init_hook": None,
"post_init_hook": None,
"uninstall_hook": None,
'installable': True, 'installable': True,
"auto_install": False, "auto_install": False,
} }

3
mail_all/doc/changelog.rst

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

10
mail_all/doc/index.rst

@ -2,6 +2,16 @@
Show all messages Show all messages
=================== ===================
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Configuration
=============
This module does not require special configuration.
Usage Usage
===== =====

8
mail_all/i18n/es.po

@ -6,10 +6,10 @@
# Randall Castro <rcastro@treintaycinco.com>, 2018 # Randall Castro <rcastro@treintaycinco.com>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-19 16:49+0000\n"
"PO-Revision-Date: 2017-12-19 16:49+0000\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" "Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n" "Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -20,7 +20,7 @@ msgstr ""
#. module: mail_all #. module: mail_all
#. openerp-web #. openerp-web
#: code:addons/mail_all/static/src/js/mail_all.js:56
#: code:addons/mail_all/static/src/js/mail_all.js:49
#: code:addons/mail_all/static/src/xml/menu.xml:7 #: code:addons/mail_all/static/src/xml/menu.xml:7
#, python-format #, python-format
msgid "All messages" msgid "All messages"

1
mail_all/models/__init__.py

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

2
mail_all/static/description/index.html

@ -38,6 +38,8 @@ This menu shows all messages.
<ul> <ul>
<li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li> <li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> <li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
<li><a href="https://m.me/itprojectsllc" target="_blank">https://m.me/itprojectsllc <i class="fa fa-facebook-square"></i></a></li>
<li>skype@it-projects.info <i class="fa fa-skype"></i></li>
</ul> </ul>
</div> </div>
<div class="oe_span4"> <div class="oe_span4">

3
mail_all/static/src/css/mail_all.css

@ -0,0 +1,3 @@
.o_channel_name.mail_all i {
margin-right: 4px;
}

62
mail_all/static/src/js/mail_all.js

@ -1,20 +1,10 @@
odoo.define('mail_all.all', function (require) { odoo.define('mail_all.all', function (require) {
"use strict"; "use strict";
var base_obj = require('mail_base.base');
//-------------------------------------------------------------------------------
var bus = require('bus.bus').bus;
var config = require('web.config');
var chat_manager = require('mail_base.base').chat_manager;
var core = require('web.core'); var core = require('web.core');
var data = require('web.data');
var Model = require('web.Model');
var session = require('web.session');
var time = require('web.time');
var web_client = require('web.web_client');
var _lt = core._lt; var _lt = core._lt;
//-------------------------------------------------------------------------------
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
ChatAction.include({ ChatAction.include({
@ -25,41 +15,43 @@ ChatAction.include({
} }
}); });
// Inherit class and override methods
base_obj.MailTools.include({
get_properties: function(msg){
var properties = this._super.apply(this, arguments);
properties.is_all = this.property_descr("channel_all", msg, this);
return properties;
},
// override methods
var chat_manager_super = _.clone(chat_manager);
set_channel_flags: function(data, msg){
this._super.apply(this, arguments);
msg.is_all = data.author_id != 'ODOOBOT';
return msg;
},
chat_manager.get_properties = function (msg) {
var properties = chat_manager_super.get_properties.apply(this, arguments);
properties.is_all = this.property_descr("channel_all", msg, this);
return properties;
};
get_channel_array: function(msg){
var arr = this._super.apply(this, arguments);
return arr.concat('channel_all');
},
chat_manager.set_channel_flags = function (data, msg) {
chat_manager_super.set_channel_flags.apply(this, arguments);
msg.is_all = data.author_id !== 'ODOOBOT';
return msg;
};
get_domain: function(channel){
return (channel.id === "channel_all") ? [] : this._super.apply(this, arguments);
}
});
chat_manager.get_channel_array = function (msg) {
var arr = chat_manager_super.get_channel_array.apply(this, arguments);
return arr.concat('channel_all');
};
base_obj.chat_manager.is_ready.then(function(){
chat_manager.get_domain = function (channel) {
return (channel.id === "channel_all")
? []
: chat_manager_super.get_domain.apply(this, arguments);
};
chat_manager.is_ready.then(function () {
// Add all channel // Add all channel
base_obj.chat_manager.mail_tools.add_channel({
chat_manager.add_channel({
id: "channel_all", id: "channel_all",
name: _lt("All messages"), name: _lt("All messages"),
type: "static" type: "static"
}); });
return $.when(); return $.when();
}); });
return base_obj.chat_manager;
return chat_manager;
}); });

2
mail_all/static/src/xml/menu.xml

@ -3,7 +3,7 @@
<!--Inherit Sidebar and add All messages menu item after Starred --> <!--Inherit Sidebar and add All messages menu item after Starred -->
<t t-extend="mail.chat.Sidebar"> <t t-extend="mail.chat.Sidebar">
<t t-jquery="div[data-channel-id=channel_starred]" t-operation="after"> <t t-jquery="div[data-channel-id=channel_starred]" t-operation="after">
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_all') ? 'o_active': ''}" data-channel-id="channel_all">
<div t-attf-class="o_mail_chat_title_main o_mail_chat_channel_item #{(active_channel_id == 'channel_all') ? 'o_active': ''}" data-channel-id="channel_all">
<span class="o_channel_name mail_all"> <i class="fa fa-database"/> All messages </span> <span class="o_channel_name mail_all"> <i class="fa fa-database"/> All messages </span>
</div> </div>
</t> </t>

1
mail_all/tests/__init__.py

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

1
mail_all/tests/test_js.py

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

1
mail_all/views/templates.xml

@ -5,6 +5,7 @@
name="mail_all_assets_backend" name="mail_all_assets_backend"
inherit_id="web.assets_backend"> inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<link rel="stylesheet" href="/mail_all/static/src/css/mail_all.css"/>
<script src="/mail_all/static/src/js/mail_all.js" type="text/javascript"></script> <script src="/mail_all/static/src/js/mail_all.js" type="text/javascript"></script>
</xpath> </xpath>
</template> </template>

40
mail_archives/README.rst

@ -1,15 +1,39 @@
Mail Archives
=============
===============
Mail Archives
===============
Adds Archive menu, which shows sent/received messages Adds Archive menu, which shows sent/received messages
Usage
-----
Click Discuss/Archive menu -- sent/received messages are displayed
Credits
=======
Contributors
------------
* Pavel Romanchenko <apps@it-projects.info>
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_archives/>`__.
Thank you for understanding!
`IT-Projects Team <https://www.it-projects.info/team>`__
Further information Further information
-------------------
===================
Demo: http://runbot.it-projects.info/demo/mail_addons/11.0
HTML Description: https://apps.odoo.com/apps/modules/11.0/mail_archives/
Usage instructions: `<doc/index.rst>`_
HTML Description: https://apps.odoo.com/apps/modules/9.0/mail_archives/
Changelog: `<doc/changelog.rst>`_
Tested on Odoo 9.0 b9f206953e3f877adf18643f154d1262842564ee
Tested on Odoo 11.0 ecbf7aa4714479229658d14cce28fa00376ed390

1
mail_archives/__init__.py

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

3
mail_archives/__manifest__.py

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

0
mail_attachment_popup/doc/changelog.rst → mail_archives/doc/changelog.rst

20
mail_archives/doc/index.rst

@ -0,0 +1,20 @@
==============
Mail Archives
==============
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Configuration
=============
This module does not require special configuration.
Usage
=====
* Open ``Discuss``.
* Click ``Archive``.
* Sent/received messages are displayed.

10
mail_archives/i18n/es.po

@ -6,10 +6,10 @@
# Randall Castro <rcastro@treintaycinco.com>, 2018 # Randall Castro <rcastro@treintaycinco.com>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-19 16:49+0000\n"
"PO-Revision-Date: 2017-12-19 16:49+0000\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" "Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n" "Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -20,7 +20,7 @@ msgstr ""
#. module: mail_archives #. module: mail_archives
#. openerp-web #. openerp-web
#: code:addons/mail_archives/static/src/js/archives.js:76
#: code:addons/mail_archives/static/src/js/archives.js:68
#: code:addons/mail_archives/static/src/xml/menu.xml:7 #: code:addons/mail_archives/static/src/xml/menu.xml:7
#, python-format #, python-format
msgid "Archive" msgid "Archive"
@ -31,4 +31,4 @@ msgstr "Archivo"
#: code:addons/mail_archives/static/src/xml/menu.xml:15 #: code:addons/mail_archives/static/src/xml/menu.xml:15
#, python-format #, python-format
msgid "Archive is empty" msgid "Archive is empty"
msgstr "El archivo está vacío"
msgstr "Archivo está vacío"

6
mail_archives/static/description/index.html

@ -38,6 +38,8 @@ This menu shows archive messages, i.e. ones you sent or received.
<ul> <ul>
<li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li> <li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> <li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
<li><a href="https://m.me/itprojectsllc" target="_blank">https://m.me/itprojectsllc <i class="fa fa-facebook-square"></i></a></li>
<li>skype@it-projects.info <i class="fa fa-skype"></i></li>
</ul> </ul>
</div> </div>
<div class="oe_span4"> <div class="oe_span4">
@ -57,7 +59,7 @@ This menu shows archive messages, i.e. ones you sent or received.
-o-transform: rotate(6deg); -o-transform: rotate(6deg);
-moz-transform: rotate(6deg); -moz-transform: rotate(6deg);
-ms-transform: rotate(6deg);"> -ms-transform: rotate(6deg);">
Tested on Odoo<br/>10.0 community
Tested on Odoo<br/>11.0 community
</div> </div>
<div style="margin-top: 15px; <div style="margin-top: 15px;
position: relative; position: relative;
@ -74,7 +76,7 @@ This menu shows archive messages, i.e. ones you sent or received.
-o-transform: rotate(-7deg); -o-transform: rotate(-7deg);
-moz-transform: rotate(-7deg); -moz-transform: rotate(-7deg);
-ms-transform: rotate(-7deg);"> -ms-transform: rotate(-7deg);">
Tested on Odoo<br/>10.0 enterprise
Tested on Odoo<br/>11.0 enterprise
</div> </div>
</div> </div>
</div> </div>

3
mail_archives/static/src/css/archives.css

@ -0,0 +1,3 @@
.o_channel_name.mail_archives i {
margin-right: 4px;
}

97
mail_archives/static/src/js/archives.js

@ -1,31 +1,23 @@
odoo.define('mail_archives.archives', function (require) { odoo.define('mail_archives.archives', function (require) {
"use strict"; "use strict";
var base_obj = require('mail_base.base');
//-------------------------------------------------------------------------------
var bus = require('bus.bus').bus;
var config = require('web.config');
var core = require('web.core'); var core = require('web.core');
var data = require('web.data');
var Model = require('web.Model');
var session = require('web.session'); var session = require('web.session');
var time = require('web.time');
var web_client = require('web.web_client');
var chat_manager = require('mail_base.base').chat_manager;
var _lt = core._lt; var _lt = core._lt;
//-------------------------------------------------------------------------------
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
ChatAction.include({ ChatAction.include({
init: function(parent, action, options) {
init: function (parent, action, options) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
var channel_name = 'channel_archive'; var channel_name = 'channel_archive';
// Add channel Archive for enable "display_subject" option // Add channel Archive for enable "display_subject" option
this.channels_display_subject.push(channel_name); this.channels_display_subject.push(channel_name);
}, },
update_message_on_current_channel: function(current_channel_id, message){
update_message_on_current_channel: function (current_channel_id, message){
var result = this._super.apply(this, arguments); var result = this._super.apply(this, arguments);
var archive = current_channel_id === "channel_archive" && !message.is_archive; var archive = current_channel_id === "channel_archive" && !message.is_archive;
return archive || result; return archive || result;
@ -33,53 +25,52 @@ ChatAction.include({
}); });
// Inherit class and override methods // Inherit class and override methods
base_obj.MailTools.include({
get_properties: function(msg){
var properties = this._super.apply(this, arguments);
properties.is_archive = this.property_descr("channel_archive", msg, this);
return properties;
},
set_channel_flags: function(data, msg){
this._super.apply(this, arguments);
// Get recipients ids
var recipients_ids = [];
for (var i = 0; i < (data.partner_ids || []).length; i++){
recipients_ids.push(data.partner_ids[i][0]);
}
// If author or recipient
if (data.author_id[0] === session.partner_id || recipients_ids.indexOf(session.partner_id) != -1) {
msg.is_archive = true;
}
return msg;
},
var chat_manager_super = _.clone(chat_manager);
get_channel_array: function(msg){
var arr = this._super.apply(this, arguments);
return arr.concat('channel_archive');
},
chat_manager.get_properties = function (msg) {
var properties = chat_manager_super.get_properties.apply(this, arguments);
properties.is_archive = this.property_descr("channel_archive", msg, this);
return properties;
};
get_domain: function(channel){
return (channel.id === "channel_archive") ? [
'|', ['partner_ids', 'in', [openerp.session.partner_id]],
['author_id.user_ids', 'in', [openerp.session.uid]]
] : this._super.apply(this, arguments);
chat_manager.set_channel_flags = function (data, msg) {
chat_manager_super.set_channel_flags.apply(this, arguments);
// Get recipients ids
var recipients_ids = [];
for (var i = 0; i < (data.partner_ids || []).length; i++){
recipients_ids.push(data.partner_ids[i][0]);
} }
});
base_obj.chat_manager.is_ready.then(function(){
// Add archive channel
base_obj.chat_manager.mail_tools.add_channel({
id: "channel_archive",
name: _lt("Archive"),
type: "static"
});
// If author or recipient
if (data.author_id[0] === session.partner_id || recipients_ids.indexOf(session.partner_id) !== -1) {
msg.is_archive = true;
}
return msg;
};
chat_manager.get_channel_array = function (msg) {
var arr = chat_manager_super.get_channel_array.apply(this, arguments);
return arr.concat('channel_archive');
};
return $.when();
chat_manager.get_domain = function (channel) {
return (channel.id === "channel_archive")
? ['|', ['partner_ids', 'in', [session.partner_id]],
['author_id', 'in', [session.partner_id]]]
: chat_manager_super.get_domain.apply(this, arguments);
};
chat_manager.is_ready.then(function () {
chat_manager.add_channel({
id: "channel_archive",
name: _lt("Archive"),
type: "static"
}); });
});
return base_obj.chat_manager;
return chat_manager;
}); });

2
mail_archives/static/src/xml/menu.xml

@ -3,7 +3,7 @@
<!--Inherit Sidebar and add Archive menu item after Starred --> <!--Inherit Sidebar and add Archive menu item after Starred -->
<t t-extend="mail.chat.Sidebar"> <t t-extend="mail.chat.Sidebar">
<t t-jquery="div[data-channel-id=channel_starred]" t-operation="after"> <t t-jquery="div[data-channel-id=channel_starred]" t-operation="after">
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_archive') ? 'o_active': ''}" data-channel-id="channel_archive">
<div t-attf-class="o_mail_chat_channel_item o_mail_chat_title_main #{(active_channel_id == 'channel_archive') ? 'o_active': ''}" data-channel-id="channel_archive">
<span class="o_channel_name mail_archives"> <i class="fa fa-archive"/> Archive </span> <span class="o_channel_name mail_archives"> <i class="fa fa-archive"/> Archive </span>
</div> </div>
</t> </t>

1
mail_archives/tests/__init__.py

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

1
mail_archives/tests/test_js.py

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

1
mail_archives/views/templates.xml

@ -5,6 +5,7 @@
name="res_partner_mails_count_assets_backend" name="res_partner_mails_count_assets_backend"
inherit_id="web.assets_backend"> inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<link rel="stylesheet" href="/mail_archives/static/src/css/archives.css"/>
<script src="/mail_archives/static/src/js/archives.js" type="text/javascript"></script> <script src="/mail_archives/static/src/js/archives.js" type="text/javascript"></script>
</xpath> </xpath>
</template> </template>

35
mail_attachment_popup/README.rst

@ -1,35 +0,0 @@
===================
Popup Attachments
===================
The module opens attached mail images in popup.
Credits
=======
Contributors
------------
* Dinar Gabbasov <gabbasov@it-projects.info>
Sponsors
--------
* `IT-Projects LLC <https://it-projects.info>`_
Maintainers
-----------
* `IT-Projects LLC <https://it-projects.info>`_
The module is not maintained in future versions because it's functionality built-in since Odoo 11.0.
Further information
===================
Demo: http://runbot.it-projects.info/demo/mail-addons/9.0
HTML Description: https://apps.odoo.com/apps/modules/9.0/mail_attachment_popup/
Usage instructions: `<doc/index.rst>`_
Changelog: `<doc/changelog.rst>`_
Tested on Odoo 9.0 021878f9c41c6d652abf345c3c5537fe92f8bc5b

0
mail_attachment_popup/__init__.py

16
mail_attachment_popup/doc/index.rst

@ -1,16 +0,0 @@
===================
Popup Attachments
===================
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Usage
=====
* Open 'Messaging' menu
* Find any message with image in attachments
* Click on the image
* Browser opens image in popup instead of downloading it

26
mail_attachment_popup/i18n/ru.po

@ -1,26 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_attachment_popup
#
# Translators:
# Dinar <gabbasov@it-projects.info>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-16 08:35+0000\n"
"PO-Revision-Date: 2017-11-16 08:35+0000\n"
"Last-Translator: Dinar <gabbasov@it-projects.info>, 2017\n"
"Language-Team: Russian (https://www.transifex.com/it-projects-llc/teams/76080/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ru\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
#. module: mail_attachment_popup
#. openerp-web
#: code:addons/mail_attachment_popup/static/src/xml/mail_attachment_popup.xml:17
#, python-format
msgid "Download"
msgstr "Скачать"

BIN
mail_attachment_popup/images/popup_image.png

Before

Width: 749  |  Height: 371  |  Size: 128 KiB

BIN
mail_attachment_popup/static/description/attach_image.png

Before

Width: 361  |  Height: 382  |  Size: 63 KiB

BIN
mail_attachment_popup/static/description/download.png

Before

Width: 361  |  Height: 381  |  Size: 131 KiB

84
mail_attachment_popup/static/description/index.html

@ -1,84 +0,0 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Popup Attachments</h2>
<h3 class="oe_slogan">Open attachments in popup</h3>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
The module allows to open attachments (images) in popup. It is convenient if you want to display them only without downloading.
</p>
</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>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span6">
<p class="oe_mt32">
Go to "Messaging" menu and open email that contains image(s) in attachment.
</p>
</div>
<div class="oe_span6">
<div class="oe_row_img oe_centered">
<img class="oe_demo oe_picture oe_screenshot" src="attach_image.png"/>
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span6">
<div class="oe_row_img oe_centered">
<img class="oe_demo oe_picture oe_screenshot" src="popup.png"/>
</div>
</div>
<div class="oe_span6">
<p class="oe_mt32">
Click on the image and see how popup is appear.
</p>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span6">
<p class="oe_mt32">
Moreover, you can download it to your device by clicking on the "Download" button if needed.
</p>
</div>
<div class="oe_span6">
<div class="oe_row_img oe_centered">
<img class="oe_demo oe_picture oe_screenshot" src="download.png"/>
</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>
</div>
</section>

BIN
mail_attachment_popup/static/description/popup.png

Before

Width: 365  |  Height: 382  |  Size: 150 KiB

429
mail_attachment_popup/static/lib/js/jquery.arcticmodal.js

@ -1,429 +0,0 @@
/*
arcticModal jQuery plugin
Version: 0.3
Author: Sergey Predvoditelev (sergey.predvoditelev@gmail.com)
Company: Arctic Laboratory (http://arcticlab.ru/)
Docs & Examples: http://arcticlab.ru/arcticmodal/
*/
(function($) {
var default_options = {
type: 'html', // ajax или html
content: '',
url: '',
ajax: {},
ajax_request: null,
closeOnEsc: true,
closeOnOverlayClick: true,
clone: false,
overlay: {
block: undefined,
tpl: '<div class="arcticmodal-overlay"></div>',
css: {
backgroundColor: '#000',
opacity: .6
}
},
container: {
block: undefined,
tpl: '<div class="arcticmodal-container"><table class="arcticmodal-container_i"><tr><td class="arcticmodal-container_i2"></td></tr></table></div>'
},
wrap: undefined,
body: undefined,
errors: {
tpl: '<div class="arcticmodal-error arcticmodal-close"></div>',
autoclose_delay: 2000,
ajax_unsuccessful_load: 'Error'
},
openEffect: {
type: 'fade',
speed: 400
},
closeEffect: {
type: 'fade',
speed: 400
},
beforeOpen: $.noop,
afterOpen: $.noop,
beforeClose: $.noop,
afterClose: $.noop,
afterLoading: $.noop,
afterLoadingOnShow: $.noop,
errorLoading: $.noop
};
var modalID = 0;
var modals = $([]);
var utils = {
// Определяет произошло ли событие e вне блока block
isEventOut: function(blocks, e) {
var r = true;
$(blocks).each(function() {
if ($(e.target).get(0)==$(this).get(0)) r = false;
if ($(e.target).closest('HTML', $(this).get(0)).length==0) r = false;
});
return r;
}
};
var modal = {
// Возвращает элемент, которым был вызван плагин
getParentEl: function(el) {
var r = $(el);
if (r.data('arcticmodal')) return r;
r = $(el).closest('.arcticmodal-container').data('arcticmodalParentEl');
if (r) return r;
return false;
},
// Переход
transition: function(el, action, options, callback) {
callback = callback==undefined ? $.noop : callback;
switch (options.type) {
case 'fade':
action=='show' ? el.fadeIn(options.speed, callback) : el.fadeOut(options.speed, callback);
break;
case 'none':
action=='show' ? el.show() : el.hide();
callback();
break;
}
},
// Подготвка содержимого окна
prepare_body: function(D, $this) {
// Обработчик закрытия
$('.arcticmodal-close', D.body).unbind('click.arcticmodal').bind('click.arcticmodal', function() {
$this.arcticmodal('close');
return false;
});
},
// Инициализация элемента
init_el: function($this, options) {
var D = $this.data('arcticmodal');
if (D) return;
D = options;
modalID++;
D.modalID = modalID;
// Overlay
D.overlay.block = $(D.overlay.tpl);
D.overlay.block.css(D.overlay.css);
// Container
D.container.block = $(D.container.tpl);
// BODY
D.body = $('.arcticmodal-container_i2', D.container.block);
if (options.clone) {
D.body.html($this.clone(true));
} else {
$this.before('<div id="arcticmodalReserve' + D.modalID + '" style="display: none" />');
D.body.html($this);
}
// Подготовка содержимого
modal.prepare_body(D, $this);
// Закрытие при клике на overlay
if (D.closeOnOverlayClick)
D.overlay.block.add(D.container.block).click(function(e) {
if (utils.isEventOut($('>*', D.body), e))
$this.arcticmodal('close');
});
// Запомним настройки
D.container.block.data('arcticmodalParentEl', $this);
$this.data('arcticmodal', D);
modals = $.merge(modals, $this);
// Показать
$.proxy(actions.show, $this)();
if (D.type=='html') return $this;
// Ajax-загрузка
if (D.ajax.beforeSend!=undefined) {
var fn_beforeSend = D.ajax.beforeSend;
delete D.ajax.beforeSend;
}
if (D.ajax.success!=undefined) {
var fn_success = D.ajax.success;
delete D.ajax.success;
}
if (D.ajax.error!=undefined) {
var fn_error = D.ajax.error;
delete D.ajax.error;
}
var o = $.extend(true, {
url: D.url,
beforeSend: function() {
if (fn_beforeSend==undefined) {
D.body.html('<div class="arcticmodal-loading" />');
} else {
fn_beforeSend(D, $this);
}
},
success: function(responce) {
// Событие после загрузки до показа содержимого
$this.trigger('afterLoading');
D.afterLoading(D, $this, responce);
if (fn_success==undefined) {
D.body.html(responce);
} else {
fn_success(D, $this, responce);
}
modal.prepare_body(D, $this);
// Событие после загрузки после отображения содержимого
$this.trigger('afterLoadingOnShow');
D.afterLoadingOnShow(D, $this, responce);
},
error: function() {
// Событие при ошибке загрузки
$this.trigger('errorLoading');
D.errorLoading(D, $this);
if (fn_error==undefined) {
D.body.html(D.errors.tpl);
$('.arcticmodal-error', D.body).html(D.errors.ajax_unsuccessful_load);
$('.arcticmodal-close', D.body).click(function() {
$this.arcticmodal('close');
return false;
});
if (D.errors.autoclose_delay)
setTimeout(function() {
$this.arcticmodal('close');
}, D.errors.autoclose_delay);
} else {
fn_error(D, $this);
}
}
}, D.ajax);
D.ajax_request = $.ajax(o);
// Запомнить настройки
$this.data('arcticmodal', D);
},
// Инициализация
init: function(options) {
options = $.extend(true, {}, default_options, options);
if ($.isFunction(this)) {
if (options==undefined) {
$.error('jquery.arcticmodal: Uncorrect parameters');
return;
}
if (options.type=='') {
$.error('jquery.arcticmodal: Don\'t set parameter "type"');
return;
}
switch (options.type) {
case 'html':
if (options.content=='') {
$.error('jquery.arcticmodal: Don\'t set parameter "content"');
return
}
var c = options.content;
options.content = '';
return modal.init_el($(c), options);
break;
case 'ajax':
if (options.url=='') {
$.error('jquery.arcticmodal: Don\'t set parameter "url"');
return;
}
return modal.init_el($('<div />'), options);
break;
}
} else {
return this.each(function() {
modal.init_el($(this), $.extend(true, {}, options));
});
}
}
};
var actions = {
// Показать
show: function() {
var $this = modal.getParentEl(this);
if ($this===false) {
$.error('jquery.arcticmodal: Uncorrect call');
return;
}
var D = $this.data('arcticmodal');
// Добавить overlay и container
D.overlay.block.hide();
D.container.block.hide();
$('BODY').append(D.overlay.block);
$('BODY').append(D.container.block);
// Событие
D.beforeOpen(D, $this);
$this.trigger('beforeOpen');
// Wrap
if (D.wrap.css('overflow')!='hidden') {
D.wrap.data('arcticmodalOverflow', D.wrap.css('overflow'));
var w1 = D.wrap.outerWidth(true);
D.wrap.css('overflow', 'hidden');
var w2 = D.wrap.outerWidth(true);
if (w2!=w1)
D.wrap.css('marginRight', (w2 - w1) + 'px');
}
// Скрыть предыдущие оверлеи
modals.not($this).each(function() {
var d = $(this).data('arcticmodal');
d.overlay.block.hide();
});
// Показать
modal.transition(D.overlay.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect);
modal.transition(D.container.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect, function() {
D.afterOpen(D, $this);
$this.trigger('afterOpen');
});
return $this;
},
// Закрыть
close: function() {
if ($.isFunction(this)) {
modals.each(function() {
$(this).arcticmodal('close');
});
} else {
return this.each(function() {
var $this = modal.getParentEl(this);
if ($this===false) {
$.error('jquery.arcticmodal: Uncorrect call');
return;
}
var D = $this.data('arcticmodal');
// Событие перед закрытием
if (D.beforeClose(D, $this)===false) return;
$this.trigger('beforeClose');
// Показать предыдущие оверлеи
modals.not($this).last().each(function() {
var d = $(this).data('arcticmodal');
d.overlay.block.show();
});
modal.transition(D.overlay.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect);
modal.transition(D.container.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect, function() {
// Событие после закрытия
D.afterClose(D, $this);
$this.trigger('afterClose');
// Если не клонировали - вернём на место
if (!D.clone)
$('#arcticmodalReserve' + D.modalID).replaceWith(D.body.find('>*'));
D.overlay.block.remove();
D.container.block.remove();
$this.data('arcticmodal', null);
if (!$('.arcticmodal-container').length) {
if (D.wrap.data('arcticmodalOverflow'))
D.wrap.css('overflow', D.wrap.data('arcticmodalOverflow'));
D.wrap.css('marginRight', 0);
}
});
if (D.type=='ajax')
D.ajax_request.abort();
modals = modals.not($this);
});
}
},
// Установить опции по-умолчанию
setDefault: function(options) {
$.extend(true, default_options, options);
}
};
$(function() {
default_options.wrap = $((document.all && !document.querySelector) ? 'html' : 'body');
});
// Закрытие при нажатии Escape
$(document).bind('keyup.arcticmodal', function(e) {
var m = modals.last();
if (!m.length) return;
var D = m.data('arcticmodal');
if (D.closeOnEsc && (e.keyCode===27))
m.arcticmodal('close');
});
$.arcticmodal = $.fn.arcticmodal = function(method) {
if (actions[method]) {
return actions[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method==='object' || !method) {
return modal.init.apply(this, arguments);
} else {
$.error('jquery.arcticmodal: Method ' + method + ' does not exist');
}
};
})(jQuery);

8
mail_attachment_popup/static/src/css/jquery.arcticmodal.css

@ -1,8 +0,0 @@
.arcticmodal-overlay,
.arcticmodal-container { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 1010; }
.arcticmodal-container { overflow: auto; margin: 0; padding: 0; border: 0; border-collapse: collapse; }
*:first-child+html .arcticmodal-container { height: 100% }
.arcticmodal-container_i { height: 100%; margin: 0 auto; }
.arcticmodal-container_i2 { padding: 24px; margin: 0; border: 0; vertical-align: middle; padding-top: 50px;}
.arcticmodal-error { padding: 20px; border-radius: 10px; background: #000; color: #fff; }
.arcticmodal-loading { width: 80px; height: 80px; border-radius: 10px; background: #000 url(/mail_attachment_popup/static/src/img/loading.gif) no-repeat 50% 50%; }

11
mail_attachment_popup/static/src/css/simple.css

@ -1,11 +0,0 @@
.box-modal {
position: relative;
padding: 16px;
background: #fff;
color: #3c3c3c;
font: 14px/18px Arial, "Helvetica CY", "Nimbus Sans L", sans-serif;
box-shadow: 0 0 0 6px rgba(153, 153, 153, .3);
border-radius: 6px;
}
.box-modal_close { position: absolute; right: -25px; top: -25px; font-size: 30px; line-height: 15px; color: #ffffff; cursor: pointer; }
.box-modal_close:hover { color: #B1B1B1; }

16
mail_attachment_popup/static/src/css/styles.css

@ -1,16 +0,0 @@
.g-hidden {
display: none;
}
.box-modal img {
max-width: 900px;
width: 100%;
}
.box-modal-li li {
list-style-type: none;
}
.box-modal-li {
padding-left: 0;
}
.o_attachment .o_image {
cursor: pointer;
}

BIN
mail_attachment_popup/static/src/img/loading.gif

Before

Width: 32  |  Height: 32  |  Size: 3.1 KiB

29
mail_attachment_popup/static/src/xml/mail_attachment_popup.xml

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="mail.Attachment">
<t t-jquery="div[t-att-title='attachment.name'] .o_image" t-operation="replace">
<t t-if="attachment.mimetype and attachment.mimetype.search('image/') !== -1">
<span class="m-dotted" t-attf-onclick="$('#ImageModal{{ attachment.id }}').arcticmodal()">
<div class="o_image" target="_blank" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80">
<span class='o_attachment_name'><t t-esc='attachment.name'/></span>
</div>
</span>
<div class="g-hidden">
<div class="box-modal" t-attf-id="ImageModal{{ attachment.id }}">
<div class="box-modal_close arcticmodal-close">X</div>
<img t-att-data-mimetype="attachment.mimetype" t-attf-src="/web/image/#{attachment.id}"></img>
<ul class="box-modal-li">
<li><span class='o_attachment_name'><t t-esc='attachment.name'/></span></li>
<li><span class='oe_download_original_img'><a t-att-href='attachment.url' target="_blank">Download</a></span></li>
</ul>
</div>
</div>
</t>
<t t-if="! (attachment.mimetype and attachment.mimetype.search('image/') !== -1)">
<a class="o_image" t-att-href='attachment.url' target="_blank" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80">
<span class='o_attachment_name'><t t-esc='attachment.name'/></span>
</a>
</t>
</t>
</t>
</template>

13
mail_attachment_popup/views/mail_attachment_popup_template.xml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="assets_backend" name="mail attachment popup assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/jquery.arcticmodal.css"/>
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/simple.css"/>
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/styles.css"/>
<script type="text/javascript" src="/mail_attachment_popup/static/lib/js/jquery.arcticmodal.js"></script>
</xpath>
</template>
</data>
</openerp>

32
mail_base/README.rst

@ -1,5 +1,6 @@
Mail Base
=========
===========
Mail Base
===========
* makes built-in mail js features extendable. * makes built-in mail js features extendable.
* handles ``search_default_*`` parameters in context. * handles ``search_default_*`` parameters in context.
@ -10,19 +11,38 @@ One can say, that the module do this todo from `addons/mail/static/src/js/chat_m
// to do: move this to mail.utils // to do: move this to mail.utils
Note. Due to odoo restrictions, module makes mail initialization again. That is browser loads emoji and other chat data twice. This is the only way to make Mail feature extendable.
Credits
=======
Note. Due to odoo restrictions, module makes mail initialization again. That is browser loads emoji and other chat data twice. This is the only way to make Mail feature extendable.
Contributors
------------
* Pavel Romanchenko <apps@it-projects.info>
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_base/>`__.
Thank you for understanding!
`IT-Projects Team <https://www.it-projects.info/team>`__
Further information Further information
=================== ===================
Demo: http://runbot.it-projects.info/demo/mail-addons/10.0
Demo: http://runbot.it-projects.info/demo/mail-addons/11.0
.. HTML Description: https://apps.odoo.com/apps/modules/10.0/mail_base/
HTML Description: https://apps.odoo.com/apps/modules/11.0/mail_base/
Usage instructions: `<doc/index.rst>`_ Usage instructions: `<doc/index.rst>`_
Changelog: `<doc/changelog.rst>`_ Changelog: `<doc/changelog.rst>`_
Tested on Odoo 10.0 1be57f2825af4f3ade20a658c6f97f6cf93cc866
Tested on Odoo 11.0 ecbf7aa4714479229658d14cce28fa00376ed390

1
mail_base/__init__.py

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

3
mail_base/__manifest__.py

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
{ {
"name": "Mail Base", "name": "Mail Base",
"summary": """Makes Mail extendable""", "summary": """Makes Mail extendable""",
"category": "Discuss", "category": "Discuss",
"images": [], "images": [],
"version": "1.0.4",
"version": "11.0.1.0.2",
"author": "IT-Projects LLC, Pavel Romanchenko", "author": "IT-Projects LLC, Pavel Romanchenko",
"support": "apps@it-projects.info", "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.http import request
from openerp.addons.bus.controllers.main import BusController from openerp.addons.bus.controllers.main import BusController

20
mail_base/doc/index.rst

@ -1,4 +1,18 @@
Mail Base
=========
=========================
Mail Base
=========================
To use this module you need either install module that depends on it or create new module.
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Configuration
=============
This module does not require special configuration.
Usage
=====
* To use this module you need either install module that depends on it or create new module.

8
mail_base/i18n/es.po

@ -6,10 +6,10 @@
# Randall Castro <rcastro@treintaycinco.com>, 2018 # Randall Castro <rcastro@treintaycinco.com>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-19 16:49+0000\n"
"PO-Revision-Date: 2017-12-19 16:49+0000\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" "Last-Translator: Randall Castro <rcastro@treintaycinco.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n" "Language-Team: Spanish (https://www.transifex.com/it-projects-llc/teams/76080/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -21,7 +21,7 @@ msgstr ""
#. module: mail_base #. module: mail_base
#: model:ir.model,name:mail_base.model_mail_compose_message #: model:ir.model,name:mail_base.model_mail_compose_message
msgid "Email composition wizard" msgid "Email composition wizard"
msgstr "Asistente para crear email"
msgstr "Asistente para composición de correo"
#. module: mail_base #. module: mail_base
#: model:ir.model,name:mail_base.model_mail_message #: model:ir.model,name:mail_base.model_mail_message

10
mail_base/i18n/pt.po

@ -3,14 +3,14 @@
# * mail_base # * mail_base
# #
# Translators: # Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2017
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-15 13:31+0000\n"
"PO-Revision-Date: 2017-12-15 13:31+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2017\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" "Language-Team: Portuguese (https://www.transifex.com/it-projects-llc/teams/76080/pt/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"

10
mail_base/i18n/pt_BR.po

@ -3,14 +3,14 @@
# * mail_base # * mail_base
# #
# Translators: # Translators:
# Translation Bot <i18n-bot@it-projects.info>, 2017
# Translation Bot <i18n-bot@it-projects.info>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-15 13:31+0000\n"
"PO-Revision-Date: 2017-12-15 13:31+0000\n"
"Last-Translator: Translation Bot <i18n-bot@it-projects.info>, 2017\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" "Language-Team: Portuguese (Brazil) (https://www.transifex.com/it-projects-llc/teams/76080/pt_BR/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"

1
mail_base/models.py

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

2264
mail_base/static/lib/base.js
File diff suppressed because it is too large
View File

2
mail_base/tests/__init__.py

@ -0,0 +1,2 @@
from . import test_default

16
mail_base/tests/test_default.py

@ -0,0 +1,16 @@
import odoo.tests
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_01_mail_base(self):
# wait till page loaded
code = """
setTimeout(function () {
console.log('ok');
}, 1000);
"""
link = '/web#action=%s' % self.ref('mail.mail_channel_action_client_chat')
self.phantom_js(link, code, "odoo.__DEBUG__.services['mail_base.base']", login="admin")

1
mail_check_immediately/__init__.py

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

1
mail_check_immediately/__manifest__.py

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

1
mail_check_immediately/models.py

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

2
mail_check_immediately/static/description/index.html

@ -41,6 +41,8 @@
<li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li> <li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> <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> https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
<li><a href="https://m.me/itprojectsllc" target="_blank">https://m.me/itprojectsllc <i class="fa fa-facebook-square"></i></a></li>
<li>skype@it-projects.info <i class="fa fa-skype"></i></li>
</ul> </ul>
</div> </div>
</div> </div>

1
mail_fix_553/__init__.py

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

1
mail_fix_553/__manifest__.py

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

1
mail_fix_553/mail_fix_553.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import base64 import base64
import logging 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 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 controllers
from . import mail_move_message_models from . import mail_move_message_models

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 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.http import request
from odoo.addons.bus.controllers.main import BusController 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'))
channels.append((request.db, 'mail_move_message.delete_message')) channels.append((request.db, 'mail_move_message.delete_message'))
return super(MailChatController, self)._poll(dbname, channels, last, options) return super(MailChatController, self)._poll(dbname, channels, last, options)
class DataSetCustom(DataSet):
def _extend_name(self, model, records):
Model = request.env[model]
fields = Model.fields_get()
contact_field = False
for n, f in fields.iteritems():
if f['type'] == 'many2one' and f['relation'] == 'res.partner':
contact_field = n
break
partner_info = {}
if contact_field:
partner_info = Model.browse([r[0] for r in records]).read([contact_field])
partner_info = dict([(p['id'], p[contact_field]) for p in partner_info])
res = []
for r in records:
if partner_info.get(r[0]):
res.append((r[0], _('%s [%s] ID %s') % (r[1], partner_info.get(r[0])[1], r[0])))
else:
res.append((r[0], _('%s ID %s') % (r[1], r[0])))
return res
@http.route('/web/dataset/call_kw/<model>/name_search', type='json', auth="user")
def name_search(self, model, method, args, kwargs):
context = kwargs.get('context')
if context and context.get('extended_name_with_contact'):
# add order by ID desc
Model = request.env[model]
search_args = list(kwargs.get('args') or [])
limit = int(kwargs.get('limit') or 100)
operator = kwargs.get('operator')
name = kwargs.get('name')
if Model._rec_name and (not name == '' and operator == 'ilike'):
search_args += [(Model._rec_name, operator, name)]
records = Model.search(search_args, limit=limit, order='id desc')
res = records.name_get()
return self._extend_name(model, res)
return self._call_kw(model, method, args, kwargs)
@http.route('/web/dataset/call_kw/<model>/name_get', type='json', auth="user")
def name_get(self, model, method, args, kwargs):
res = self._call_kw(model, method, args, kwargs)
context = kwargs.get('context')
if context and context.get('extended_name_with_contact'):
res = self._extend_name(model, res)
return res

10
mail_move_message/data/mail_move_message_data.xml

@ -1,9 +1,11 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp>
<data noupdate="1">
<!--# 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).-->
<odoo>
<record id="mail_relocation_models" model="ir.config_parameter"> <record id="mail_relocation_models" model="ir.config_parameter">
<field name="key">mail_relocation_models</field> <field name="key">mail_relocation_models</field>
<field name="value">crm.lead,project.task</field> <field name="value">crm.lead,project.task</field>
</record> </record>
</data>
</openerp>
</odoo>

5
mail_move_message/doc/changelog.rst

@ -1,3 +1,8 @@
`1.0.6`
-------
- FIX: Error related to *Create Partner* button on the mail move wizard form.
`1.0.5` `1.0.5`
------- -------

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.

199
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): class Wizard(models.TransientModel):
_name = 'mail_move_message.wizard' _name = 'mail_move_message.wizard'
@api.model
def _model_selection(self): def _model_selection(self):
selection = [] selection = []
config_parameters = self.env['ir.config_parameter'] config_parameters = self.env['ir.config_parameter']
model_names = config_parameters.get_param('mail_relocation_models')
model_names = config_parameters.sudo().get_param('mail_relocation_models')
model_names = model_names.split(',') if model_names else [] model_names = model_names.split(',') if model_names else []
if 'default_message_id' in self.env.context: if 'default_message_id' in self.env.context:
message = self.env['mail.message'].browse(self.env.context['default_message_id']) message = self.env['mail.message'].browse(self.env.context['default_message_id'])
if message.model and message.model not in model_names: if message.model and message.model not in model_names:
@ -23,16 +30,16 @@ class Wizard(models.TransientModel):
model_names.append(message.moved_from_model) model_names.append(message.moved_from_model)
if model_names: if model_names:
selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])] selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])]
return selection return selection
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
res = super(Wizard, self).default_get(fields_list) res = super(Wizard, self).default_get(fields_list)
model_fields = self.fields_get()
if model_fields['model']['selection']:
res['model'] = model_fields['model']['selection'] and model_fields['model']['selection'][0][0]
available_models = self._model_selection()
if len(available_models):
record = self.env[available_models[0][0]].search([], limit=1)
res['model_record'] = len(record) and (available_models[0][0] + ',' + str(record.id)) or False
if 'message_id' in res: if 'message_id' in res:
message = self.env['mail.message'].browse(res['message_id']) message = self.env['mail.message'].browse(res['message_id'])
@ -55,10 +62,9 @@ class Wizard(models.TransientModel):
res['res_id'] = res_id[0].id res['res_id'] = res_id[0].id
config_parameters = self.env['ir.config_parameter'] config_parameters = self.env['ir.config_parameter']
res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers')
res['move_followers'] = config_parameters.sudo().get_param('mail_relocation_move_followers')
res['uid'] = self.env.uid res['uid'] = self.env.uid
return res return res
message_id = fields.Many2one('mail.message', string='Message') message_id = fields.Many2one('mail.message', string='Message')
@ -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_moved_by_user_id = fields.Many2one('res.users', related='message_id.moved_by_user_id', string='Moved by', readonly=True)
message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True) message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True)
parent_id = fields.Many2one('mail.message', string='Search by name', ) parent_id = fields.Many2one('mail.message', string='Search by name', )
model = fields.Selection(_model_selection, string='Model')
res_id = fields.Integer(string='Record')
can_move = fields.Boolean('Can move', compute='get_can_move')
model_record = fields.Reference(selection="_model_selection", string='Record')
model = fields.Char(compute="_compute_model_res_id", string='Model')
res_id = fields.Integer(compute="_compute_model_res_id", string='Record')
can_move = fields.Boolean('Can move', compute='_compute_get_can_move')
move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place') move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place')
partner_id = fields.Many2one('res.partner', string='Author') partner_id = fields.Many2one('res.partner', string='Author')
filter_by_partner = fields.Boolean('Filter Records by partner') filter_by_partner = fields.Boolean('Filter Records by partner')
message_email_from = fields.Char() message_email_from = fields.Char()
message_name_from = fields.Char() message_name_from = fields.Char()
# FIXME message_to_read should be True even if current message or any his childs are unread # FIXME message_to_read should be True even if current message or any his childs are unread
message_to_read = fields.Boolean(related='message_id.needaction')
message_to_read = fields.Boolean(compute='_compute_is_read', string="Unread message",
help="Service field shows that this message were unread when moved")
uid = fields.Integer() uid = fields.Integer()
move_followers = fields.Boolean( move_followers = fields.Boolean(
'Move Followers', 'Move Followers',
@ -86,12 +95,24 @@ class Wizard(models.TransientModel):
"You must use this option, if new record has restricted access.\n" "You must use this option, if new record has restricted access.\n"
"You can change default value for this option at Settings/System Parameters") "You can change default value for this option at Settings/System Parameters")
@api.multi
@api.depends('model_record')
def _compute_model_res_id(self):
for rec in self:
rec.model = rec.model_record and rec.model_record._name or False
rec.res_id = rec.model_record and rec.model_record.id or False
@api.depends('message_id') @api.depends('message_id')
@api.multi @api.multi
def get_can_move(self):
def _compute_get_can_move(self):
for r in self: for r in self:
r.get_can_move_one() r.get_can_move_one()
@api.multi
def _compute_is_read(self):
messages = self.env['mail.message'].sudo().browse(self.message_id.all_child_ids.ids + [self.message_id.id])
self.message_to_read = True in [m.needaction for m in messages]
@api.multi @api.multi
def get_can_move_one(self): def get_can_move_one(self):
self.ensure_one() self.ensure_one()
@ -103,12 +124,11 @@ class Wizard(models.TransientModel):
if not self.move_back: if not self.move_back:
return return
self.parent_id = self.message_id.moved_from_parent_id self.parent_id = self.message_id.moved_from_parent_id
model = self.message_id.moved_from_model
if self.message_id.is_moved:
self.model = model
self.res_id = self.message_id.moved_from_res_id
message = self.message_id
if message.is_moved:
self.model_record = self.env[message.moved_from_model].browse(message.moved_from_res_id)
@api.onchange('parent_id', 'res_id', 'model')
@api.onchange('parent_id', 'model_record')
def update_move_back(self): def update_move_back(self):
model = self.message_id.moved_from_model model = self.message_id.moved_from_model
self.move_back = self.parent_id == self.message_id.moved_from_parent_id \ self.move_back = self.parent_id == self.message_id.moved_from_parent_id \
@ -130,7 +150,7 @@ class Wizard(models.TransientModel):
if self.model and self.filter_by_partner and self.partner_id: if self.model and self.filter_by_partner and self.partner_id:
fields = self.env[self.model].fields_get(False) fields = self.env[self.model].fields_get(False)
contact_field = False contact_field = False
for n, f in fields.iteritems():
for n, f in fields.items():
if f['type'] == 'many2one' and f['relation'] == 'res.partner': if f['type'] == 'many2one' and f['relation'] == 'res.partner':
contact_field = n contact_field = n
break break
@ -179,6 +199,9 @@ class Wizard(models.TransientModel):
@api.multi @api.multi
def move(self): def move(self):
for r in self:
if not r.model:
raise exceptions.except_orm(_('Record field is empty!'), _('Select a record for relocation first'))
for r in self: for r in self:
r.check_access() r.check_access()
if not r.parent_id or not (r.parent_id.model == r.model and if not r.parent_id or not (r.parent_id.model == r.model and
@ -186,15 +209,14 @@ class Wizard(models.TransientModel):
# link with the first message of record # link with the first message of record
parent = self.env['mail.message'].search([('model', '=', r.model), ('res_id', '=', r.res_id)], order='id', limit=1) parent = self.env['mail.message'].search([('model', '=', r.model), ('res_id', '=', r.res_id)], order='id', limit=1)
r.parent_id = parent.id or None r.parent_id = parent.id or None
r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers, r.message_to_read, r.partner_id)
r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers)
if not (r.model and r.res_id):
r.message_id.needaction = False
if r.model in ['mail.message', 'mail.channel', False]:
return { return {
'type': 'ir.actions.client',
'name': 'All messages',
'tag': 'reload',
'name': 'Chess game page',
'type': 'ir.actions.act_url',
'url': '/web',
'target': 'self',
} }
return { return {
'name': _('Record'), 'name': _('Record'),
@ -223,30 +245,6 @@ class Wizard(models.TransientModel):
self.message_id.unlink() self.message_id.unlink()
return {} return {}
@api.model
def create_partner(self, message_id, relation, partner_id, message_name_from, message_email_from):
model = self.env[relation]
message = self.env['mail.message'].browse(message_id)
if not partner_id and message_name_from:
partner_id = self.env['res.partner'].with_context({'update_message_author': True}).create({
'name': message_name_from,
'email': message_email_from
}).id
context = {'partner_id': partner_id}
if model._rec_name:
context.update({'default_%s' % model._rec_name: message.subject})
fields = model.fields_get()
contact_field = False
for n, f in fields.iteritems():
if f['type'] == 'many2one' and f['relation'] == 'res.partner':
contact_field = n
break
if contact_field:
context.update({'default_%s' % contact_field: partner_id})
return context
@api.multi @api.multi
def read_close(self): def read_close(self):
for r in self: for r in self:
@ -269,10 +267,11 @@ class MailMessage(models.Model):
moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null') moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null')
moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message') moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message')
moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null') moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null')
all_child_ids = fields.One2many('mail.message', string='All childs', compute='_get_all_childs', help='all childs, including subchilds')
all_child_ids = fields.One2many('mail.message', string='All childs', compute='_compute_get_all_childs', help='all childs, including subchilds')
moved_as_unread = fields.Boolean('Was Unread', default=False)
@api.multi @api.multi
def _get_all_childs(self, include_myself=True):
def _compute_get_all_childs(self, include_myself=True):
for r in self: for r in self:
r._get_all_childs_one(include_myself=include_myself) r._get_all_childs_one(include_myself=include_myself)
@ -301,18 +300,23 @@ class MailMessage(models.Model):
self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids]) self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids])
@api.multi @api.multi
def move(self, parent_id, res_id, model, move_back, move_followers=False):
def move(self, parent_id, res_id, model, move_back, move_followers=False, message_to_read=False, author=False):
for r in self: for r in self:
r.move_one(parent_id, res_id, model, move_back, move_followers=move_followers)
r.move_one(parent_id, res_id, model, move_back, move_followers=move_followers, message_to_read=message_to_read, author=author)
@api.multi @api.multi
def move_one(self, parent_id, res_id, model, move_back, move_followers=False):
def move_one(self, parent_id, res_id, model, move_back, move_followers=False, message_to_read=False, author=False):
self.ensure_one() self.ensure_one()
if parent_id == self.id: if parent_id == self.id:
# if for any reason method is called to move message with parent # if for any reason method is called to move message with parent
# equal to oneself, we need stop to prevent infinitive loop in # equal to oneself, we need stop to prevent infinitive loop in
# building message tree # building message tree
return return
if not self.author_id:
self.write({
'author_id': author.id,
})
vals = {} vals = {}
if move_back: if move_back:
# clear variables if we move everything back # clear variables if we move everything back
@ -323,6 +327,7 @@ class MailMessage(models.Model):
vals['moved_from_res_id'] = None vals['moved_from_res_id'] = None
vals['moved_from_model'] = None vals['moved_from_model'] = None
vals['moved_from_parent_id'] = None vals['moved_from_parent_id'] = None
vals['moved_as_unread'] = None
else: else:
vals['parent_id'] = parent_id vals['parent_id'] = parent_id
vals['res_id'] = res_id vals['res_id'] = res_id
@ -331,22 +336,34 @@ class MailMessage(models.Model):
vals['is_moved'] = True vals['is_moved'] = True
vals['moved_by_user_id'] = self.env.user.id vals['moved_by_user_id'] = self.env.user.id
vals['moved_by_message_id'] = self.id vals['moved_by_message_id'] = self.id
# Update record_name in message
vals['record_name'] = self._get_record_name(vals)
vals['moved_as_unread'] = message_to_read
# Update record_name in message
vals['record_name'] = self._get_record_name(vals)
# unread message remains unread after moving back to origin
if self.moved_as_unread and move_back:
notification = {
'mail_message_id': self.id,
'res_partner_id': self.env.user.partner_id.id,
'is_read': False,
}
self.write({
'notification_ids': [(0, 0, notification)],
})
for r in self.all_child_ids: for r in self.all_child_ids:
r_vals = vals.copy() r_vals = vals.copy()
if not r.is_moved: if not r.is_moved:
# moved_from_* variables contain not last, but original # moved_from_* variables contain not last, but original
# reference # reference
r_vals['moved_from_parent_id'] = r.parent_id.id
r_vals['moved_from_res_id'] = r.res_id
r_vals['moved_from_model'] = r.model
r_vals['moved_from_parent_id'] = r.parent_id.id or r.env.context.get('uid')
r_vals['moved_from_res_id'] = r.res_id or r.id
r_vals['moved_from_model'] = r.model or r._name
elif move_back: elif move_back:
r_vals['parent_id'] = r.moved_from_parent_id.id r_vals['parent_id'] = r.moved_from_parent_id.id
r_vals['res_id'] = r.moved_from_res_id r_vals['res_id'] = r.moved_from_res_id
r_vals['model'] = r.moved_from_model
r_vals['model'] = (r.moved_from_model and r.moved_from_model not in ['mail.message', 'mail.channel', False]) and r.moved_from_model
r_vals['record_name'] = r_vals['model'] and self.env[r.moved_from_model].browse(r.moved_from_res_id).name
if move_followers: if move_followers:
r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id'))
@ -362,7 +379,7 @@ class MailMessage(models.Model):
'res_id': vals.get('res_id'), 'res_id': vals.get('res_id'),
'model': vals.get('model'), 'model': vals.get('model'),
'is_moved': vals['is_moved'], 'is_moved': vals['is_moved'],
'record_name': vals['record_name']
'record_name': 'record_name' in vals and vals['record_name'],
} }
self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message'), notification) self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message'), notification)
@ -391,34 +408,32 @@ class MailMessage(models.Model):
class MailMoveMessageConfiguration(models.TransientModel): class MailMoveMessageConfiguration(models.TransientModel):
_name = 'mail_move_message.config.settings'
_inherit = 'res.config.settings' _inherit = 'res.config.settings'
model_ids = fields.Many2many(comodel_name='ir.model', string='Models') model_ids = fields.Many2many(comodel_name='ir.model', string='Models')
move_followers = fields.Boolean('Move Followers') move_followers = fields.Boolean('Move Followers')
@api.model @api.model
def get_default_move_message_configs(self, fields):
config_parameters = self.env['ir.config_parameter']
model_obj = self.env['ir.model']
model_names = config_parameters.get_param('mail_relocation_models')
if not model_names:
return {}
def get_values(self):
res = super(MailMoveMessageConfiguration, self).get_values()
config_parameters = self.env["ir.config_parameter"].sudo()
model_names = config_parameters.sudo().get_param('mail_relocation_models')
model_names = model_names.split(',') model_names = model_names.split(',')
model_ids = model_obj.search([('model', 'in', model_names)])
return {
'model_ids': [m.id for m in model_ids],
'move_followers': config_parameters.get_param('mail_relocation_move_followers')
}
model_ids = self.env['ir.model'].sudo().search([('model', 'in', model_names)])
res.update(
model_ids=[m.id for m in model_ids],
move_followers=config_parameters.sudo().get_param('mail_relocation_move_followers'),
)
return res
@api.multi @api.multi
def set_move_message_configs(self):
config_parameters = self.env['ir.config_parameter']
model_names = ''
def set_values(self):
super(MailMoveMessageConfiguration, self).set_values()
config_parameters = self.env["ir.config_parameter"].sudo()
for record in self: for record in self:
model_names = ','.join([m.model for m in record.model_ids])
config_parameters.set_param('mail_relocation_models', model_names)
config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '')
model_names = ','.join([x.model for x in record.model_ids])
config_parameters.set_param("mail_relocation_models", model_names or '')
config_parameters.set_param("mail_relocation_move_followers", record.move_followers or '')
class ResPartner(models.Model): class ResPartner(models.Model):
@ -441,3 +456,13 @@ class ResPartner(models.Model):
if messages: if messages:
messages.sudo().write({'author_id': res.id}) messages.sudo().write({'author_id': res.id})
return res return res
@api.model
def default_get(self, default_fields):
contextual_self = self
if 'mail_move_message' in self.env.context and self.env.context['mail_move_message']:
contextual_self = self.with_context(
default_name=self.env.context['message_name_from'] or '',
default_email=self.env.context['message_email_from'] or '',
)
return super(ResPartner, contextual_self).default_get(default_fields)

97
mail_move_message/mail_move_message_views.xml

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

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

Before

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

After

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

BIN
mail_move_message/static/description/icon.png

Before

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

After

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

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

Before

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

After

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

74
mail_move_message/static/description/index.html

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

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

Before

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

After

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

174
mail_move_message/static/src/js/mail_move_message.js

@ -1,15 +1,31 @@
/*Copyright 2016 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2016 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2016 Pavel Romanchenko
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
odoo.define('mail_move_message.relocate', function (require) { odoo.define('mail_move_message.relocate', function (require) {
"use strict"; "use strict";
var bus = require('bus.bus').bus; var bus = require('bus.bus').bus;
var chat_manager = require('mail.chat_manager');
var base_obj = require('mail_base.base');
var chat_manager = require('mail_base.base').chat_manager;
var thread = require('mail.ChatThread'); var thread = require('mail.ChatThread');
var chatter = require('mail.Chatter'); var chatter = require('mail.Chatter');
var Model = require('web.Model');
var form_common = require('web.form_common');
var widgets = require('web.form_widgets');
var rpc = require('web.rpc');
var Basicmodel = require('web.BasicModel');
var view_dialogs = require('web.view_dialogs');
var field_utils_format = require('web.field_utils').format;
var BasicRenderer = require('web.BasicRenderer');
var core = require('web.core'); var core = require('web.core');
var form_widget = require('web.FormRenderer');
var session = require('web.Session');
var FormController = require('web.FormController');
var FormView = require('web.FormView');
var FormRenderer = require('web.FormRenderer');
var dialogs = require('web.view_dialogs');
var Dialog = require('web.Dialog');
var relational_fields = require('web.relational_fields');
var Widget = require('web.Widget');
var _t = core._t; var _t = core._t;
@ -31,9 +47,8 @@ odoo.define('mail_move_message.relocate', function (require) {
view_type: 'form', view_type: 'form',
views: [[false, 'form']], views: [[false, 'form']],
target: 'new', target: 'new',
context: {'default_message_id': message_id}
context: {'default_message_id': message_id},
}; };
this.do_action(action, { this.do_action(action, {
'on_close': function(){} 'on_close': function(){}
}); });
@ -44,12 +59,14 @@ odoo.define('mail_move_message.relocate', function (require) {
start: function() { start: function() {
var result = this._super.apply(this, arguments); var result = this._super.apply(this, arguments);
// For show wizard in the form // For show wizard in the form
this.thread.on('move_message', this, this.thread.on_move_message);
if (this.fields.thread && this.fields.thread.thread) {
var thread = this.fields.thread.thread;
thread.on('move_message', this, thread.on_move_message);
}
return $.when(result).done(function() {}); return $.when(result).done(function() {});
} }
}); });
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); var ChatAction = core.action_registry.get('mail.chat.instant_messaging');
ChatAction.include({ ChatAction.include({
start: function() { start: function() {
@ -60,85 +77,80 @@ 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);
}
});
// 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)
} }
}); });
widgets.WidgetButton.include({
on_click: function(){
if(this.node.attrs.special == 'quick_create'){
FormController.include({
_onButtonClicked: function(event){
if(event.data.attrs.special === 'quick_create' && event.data.attrs.field === 'partner_id'){
var self = this; 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);
}
var field_data = event.data.record.data;
this.on_saved = function(record, bool) {
var values = [{
id: record.res_id,
display_name: record.data.display_name,
}];
};
var wid = self.initialState.fieldsInfo.form.partner_id.Widget;
var relField = new relational_fields.FieldMany2One(wid,
'partner_id',
self.initialState,
{
mode: 'edit',
viewType: 'form',
}); });
relField.getParent = function() {
// necessary for correct _trigger_up implementation in mixins.js
return self;
};
var wizard_popup = relField._searchCreatePopup("form", false, {
'message_name_from': field_data.message_name_from && field_data.message_name_from.split('@')[0],
'message_email_from': field_data.message_email_from,
'message_id': field_data.res_id,
'mail_move_message': 1,
}); });
}
else {
} else {
this._super.apply(this, arguments); this._super.apply(this, arguments);
} }
}
},
}); });
}); });

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

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

3
mail_move_message/tests/__init__.py

@ -0,0 +1,3 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from . import test_mail_move

52
mail_move_message/tests/test_mail_move.py

@ -0,0 +1,52 @@
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import odoo.tests
from odoo.api import Environment
@odoo.tests.common.at_install(True)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_create_new_partner_and_move_message(self):
env = Environment(self.registry.test_cr, self.uid, {})
# needed because tests are run before the module is marked as
# installed. In js web will only load qweb coming from modules
# that are returned by the backend in module_boot. Without
# this you end up with js, css but no qweb.
env['ir.module.module'].search([('name', '=', 'mail_move_message')], limit=1).state = 'installed'
self.registry.cursor().release()
# updating models, to be able relocate messages to a partner at_install
config_parameters = self.env["ir.config_parameter"].sudo()
config_parameters.set_param("mail_relocation_models", "crm.lead,project.task,res.partner")
code = """
var delayed_button_click = function(delay, button){
setTimeout(function(){
if (button.length) {
return button.click();
}
return console.log('error', 'There is no element with the next selector: ' + button.selector);
}, delay);
};
var delay = 1000;
var message = $('.o_thread_message_core:contains("virginie")');
var relocate = message.find('.o_thread_icons .fa-exchange');
delayed_button_click(delay, relocate);
// form is opened
var create_partner_button = $('button[special="quick_create"]');
delayed_button_click(delay, create_partner_button);
// partner creation wizard is opened
var save_button = $('.modal-content .btn-primary:contains("Save")');
delayed_button_click(delay, save_button);
var move_button = $('.btn-sm.oe_highlight:contains("Move")');
delayed_button_click(delay, move_button);
console.log('ok')
"""
self.phantom_js('/web', code, login="admin", ready="$('.o_thread_icons').length")

62
mail_multi_website/README.rst

@ -0,0 +1,62 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: https://www.gnu.org/licenses/lgpl
:alt: License: LGPL-3
===========================================
Email Addresses and Templates per Website
===========================================
Mail-related stuff for multi-website support
* Makes following field in ``res.users`` website-dependent:
* ``email``
* ``signature``
* Makes following fields in ``mail.template`` website-dependent:
* ``body_html``
* ``mail_server_id``
* ``report_template``
* Overrides ``mail.template``'s ``render_template`` method to add ``website``
variable. It may cause incompatibility with other modules that redefine that
method too.
Credits
=======
Contributors
------------
* `Ivan Yelizariev <https://it-projects.info/team/yelizariev>`__
Sponsors
--------
* `e-thos SSII <http://www.e-thos.fr/>`__
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_multi_website/>`__.
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_multi_website/
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_multi_website.atom>`_, `by Email <https://blogtrottr.com/?subscribe=https://github.com/it-projects-llc/mail-addons/commits/11.0/mail_multi_website.atom>`_
Tested on Odoo 11.0 4d0a1330e05bd688265bea14df4ad12838f9f2d7

40
mail_multi_website/__init__.py

@ -0,0 +1,40 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from . import models
from . import wizard
from .tests import test_mail_model
def post_init_hook(cr, registry):
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
env.cr.execute("ALTER TABLE res_users ADD COLUMN email_multi_website VARCHAR")
# fill new email column with values from partner
for user in env['res.users'].with_context(active_test=False).search([]):
email = user.partner_id.email
if email:
user._force_default('email_multi_website', email)
def uninstall_hook(cr, registry):
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
# remove properties
field_ids = [
env.ref('base.field_res_users_email').id,
env.ref('base.field_res_users_signature').id,
env.ref('mail.field_mail_template_body_html').id,
env.ref('mail.field_mail_template_mail_server_id').id,
env.ref('mail.field_mail_template_report_template').id,
]
env['ir.property'].search([('fields_id', 'in', field_ids)]).unlink()
# copy emails from partner to user
cr.execute("SELECT partner_id,email_multi_website FROM res_users")
for partner_id, default_email in cr.fetchall():
env['res.partner'].browse(partner_id).email = default_email

51
mail_multi_website/__manifest__.py

@ -0,0 +1,51 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{
"name": """Email Addresses and Templates per Website""",
"summary": """Use single Backend to manage several Websites""",
"category": "Discuss",
"live_test_url": "http://apps.it-projects.info/shop/product/website-multi-company?version=11.0",
"images": ["images/main.jpg"],
"version": "11.0.1.0.0",
"application": False,
"author": "IT-Projects LLC, Ivan Yelizariev",
"support": "apps@it-projects.info",
"website": "https://it-projects.info/team/yelizariev",
"license": "LGPL-3",
"price": 230.00,
"currency": "EUR",
"depends": [
"ir_config_parameter_multi_company",
"web_website",
"mail",
],
"external_dependencies": {"python": [], "bin": []},
"data": [
"views/website_views.xml",
],
"demo": [
],
"qweb": [
],
"post_load": None,
"pre_init_hook": None,
"post_init_hook": "post_init_hook",
"uninstall_hook": "uninstall_hook",
"auto_install": False,
"installable": True,
# "demo_title": "Email Addresses per Website",
# "demo_addons": [
# ],
# "demo_addons_hidden": [
# ],
# "demo_url": "DEMO-URL",
# "demo_summary": "Use single Backend to manage several Websites",
# "demo_images": [
# "images/MAIN_IMAGE",
# ]
}

4
mail_multi_website/doc/changelog.rst

@ -0,0 +1,4 @@
`1.0.0`
-------
- **Init version**

73
mail_multi_website/doc/index.rst

@ -0,0 +1,73 @@
===========================================
Email Addresses and Templates per Website
===========================================
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Configuration
=============
Access to websites
------------------
* Go to menu ``[[ Settings ]] >> Users & Companies >> Users``
* Select a user
* Grant access ``[x] Multi Websites for Backend``
* Configure **Allowed Websites**
User's email per website
------------------------
* Refresh page if you just granted your user access to websites
* Use top right-hand corner button with current website name to switch between websites
* Use top right-hand corner button with user name and avatar to open
Preference popup. When you edit **Email** field, it will be saved as a value
for current website.
Email template per website
--------------------------
* Refresh page if you just granted your user access to websites
* `Activate Developer Mode <https://odoo-development.readthedocs.io/en/latest/odoo/usage/debug-mode.html>`__
* Use top right-hand corner button with current website name to switch between websites
* Go to menu ``[[ Settings ]] >> Technical >> Email >> Templates``
* When you edit template, following fields will be saved as a value for current website:
* **Body**
* **Outgoing Mail Server**
* **Optional report to print and attach**
* Additional variable ``website`` is available to configure rest fields (**Subject**, **From**, etc.)
Note. If related record (e.g. ``sale.order``) has field ``company_id`` or ``website_id`` those values will be used instead of currently selected in Website / Company Switchers
Alias domain per website
------------------------
Configure ``mail.catchall.domain`` per website. See Documentation of the module `Context-dependent values in System Parameters<https://apps.odoo.com/apps/modules/10.0/ir_config_parameter_multi_company/>`__.
Outgoing mails servers per
--------------------------
If each domain has different Outgoing Mail Server you need following adjustments
* Got to menu ``[[ Website ]] >> Configuration >> Websites``
* In each Website specify field **Outgoing Mails**
Properties
----------
To review properties by website use menu ``[[ Settings ]] >> Technical >> Parameters >> Company Properties``. See **How it works** in Documentation of module `Website Switcher in Backend<https://apps.odoo.com/apps/modules/10.0/web_website/>`__.
Usage
=====
When you work from backend, Email for current website is used.
When a user do something on website (e.g. purchase products) and some mail is sent, then email address for that website will be used (mostly Administrator's email address).
When email is sent, template's value like body, subject, etc. for current values are used.

BIN
mail_multi_website/images/main.jpg

After

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

7
mail_multi_website/models/__init__.py

@ -0,0 +1,7 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from . import res_users
from . import ir_property
from . import mail_template
from . import mail_thread
from . import mail_message
from . import website

20
mail_multi_website/models/ir_property.py

@ -0,0 +1,20 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import models, api
class IrProperty(models.Model):
_inherit = 'ir.property'
@api.multi
def write(self, vals):
res = super(IrProperty, self).write(vals)
field_object_list = [
self.env.ref('base.field_res_users_email'),
self.env.ref('mail.field_mail_template_body_html'),
self.env.ref('mail.field_mail_template_mail_server_id'),
self.env.ref('mail.field_mail_template_report_template'),
]
for fobj in field_object_list:
self._update_db_value_website_dependent(fobj)
return res

16
mail_multi_website/models/mail_message.py

@ -0,0 +1,16 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields
class Message(models.Model):
_inherit = 'mail.message'
def _default_mail_server_id(self):
website = self.env.context.get('website_id')
if not website:
return
website = self.env['website'].sudo().browse(website)
return website.mail_server_id.id
mail_server_id = fields.Many2one(default=_default_mail_server_id)

151
mail_multi_website/models/mail_template.py

@ -0,0 +1,151 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import logging
from odoo import models, fields, api, tools, _
from odoo.exceptions import UserError
from odoo.tools import pycompat
from odoo.addons.mail.models.mail_template import format_date, format_tz, format_amount
_logger = logging.getLogger(__name__)
FIELDS = ['body_html', 'mail_server_id', 'report_template']
try:
from odoo.addons.mail.models.mail_template import mako_safe_template_env, mako_template_env
except ImportError:
_logger.warning("jinja2 not available, templating features will not work!")
class MailTemplate(models.Model):
_inherit = ['mail.template', 'website_dependent.mixin']
_name = 'mail.template'
body_html = fields.Html(company_dependent=True, website_dependent=True)
mail_server_id = fields.Many2one(string='Outgoing Mail Server (Multi-Website)', company_dependent=True, website_dependent=True)
report_template = fields.Many2one(string='Optional report to print and attach (Multi-Website)', company_dependent=True, website_dependent=True)
@api.multi
def generate_email(self, res_ids, fields=None):
"""Remove mail_server_id when not set to recompute in _default_mail_server_id in mail.message"""
multi_mode = True
if isinstance(res_ids, pycompat.integer_types):
multi_mode = False
res = super(MailTemplate, self).generate_email(res_ids, fields=fields)
if not multi_mode:
list_of_dict = {0: res}
else:
list_of_dict = res
for _unused, data in list_of_dict.items():
if 'mail_server_id' in data and not data.get('mail_server_id'):
del data['mail_server_id']
return res
@api.model
def render_template(self, template_txt, model, res_ids, post_process=False):
"""Override to add website to context"""
multi_mode = True
if isinstance(res_ids, pycompat.integer_types):
multi_mode = False
res_ids = [res_ids]
results = dict.fromkeys(res_ids, u"")
# try to load the template
try:
mako_env = mako_safe_template_env if self.env.context.get('safe') else mako_template_env
template = mako_env.from_string(tools.ustr(template_txt))
except Exception:
_logger.info("Failed to load template %r", template_txt, exc_info=True)
return multi_mode and results or results[res_ids[0]]
# prepare template variables
records = self.env[model].browse(it for it in res_ids if it) # filter to avoid browsing [None]
res_to_rec = dict.fromkeys(res_ids, None)
for record in records:
res_to_rec[record.id] = record
variables = {
'format_date': lambda date, format=False, context=self._context: format_date(self.env, date, format),
'format_tz': lambda dt, tz=False, format=False, context=self._context: format_tz(self.env, dt, tz, format),
'format_amount': lambda amount, currency, context=self._context: format_amount(self.env, amount, currency),
'user': self.env.user,
'ctx': self._context, # context kw would clash with mako internals
}
# [NEW] Check website and company context
company = self.env['res.company'] # empty value
company_id = self.env.context.get('force_company')
if company_id:
company = self.env['res.company'].sudo().browse(company_id)
if self.env.context.get('website_id'):
website = self.env['website'].browse(self.env.context.get('website_id'))
else:
website = self.env.user.backend_website_id
for res_id, record in res_to_rec.items():
record_company = company
if not record_company:
if hasattr(record, 'company_id') and record.company_id:
record_company = record.company_id
record_website = website
if hasattr(record, 'website_id') and record.website_id:
record_website = record.website_id
if record_company and record_website \
and record_website.company_id != company:
# company and website are incompatible, so keep only website
record_website = self.env['website'] # empty value
record_context = dict(force_company=record_company.id, website_id=record_website.id)
variables['object'] = record.with_context(**record_context)
variables['website'] = record_website
try:
render_result = template.render(variables)
except Exception:
_logger.info("Failed to render template %r using values %r" % (template, variables), exc_info=True)
raise UserError(_("Failed to render template %r using values %r") % (template, variables))
if render_result == u"False":
render_result = u""
if post_process:
render_result = self.with_context(**record_context).render_post_process(render_result)
results[res_id] = render_result
return multi_mode and results or results[res_ids[0]]
@api.model
def create(self, vals):
res = super(MailTemplate, self).create(vals)
# make value company independent
for f in FIELDS:
res._force_default(f, vals.get(f))
return res
@api.multi
def write(self, vals):
res = super(MailTemplate, self).write(vals)
# TODO: will it work with OCA's partner_firstname module?
if 'name' in vals:
fields_to_update = FIELDS
else:
fields_to_update = [
f for f in FIELDS
if f in vals
]
for f in fields_to_update:
self._update_properties_label(f)
return res
def _auto_init(self):
for f in FIELDS:
self._auto_init_website_dependent(f)
return super(MailTemplate, self)._auto_init()

26
mail_multi_website/models/mail_thread.py

@ -0,0 +1,26 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import models, api, tools
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
@api.model
def message_route_process(self, message, message_dict, routes):
rcpt_tos = ','.join([
tools.decode_message_header(message, 'Delivered-To'),
tools.decode_message_header(message, 'To'),
tools.decode_message_header(message, 'Cc'),
tools.decode_message_header(message, 'Resent-To'),
tools.decode_message_header(message, 'Resent-Cc')])
rcpt_tos_websiteparts = [e.split('@')[1].lower() for e in tools.email_split(rcpt_tos)]
website = self.env['website'].sudo().search([
('domain', 'in', rcpt_tos_websiteparts)
])
if website:
self = self.with_context(website_id=website[0].id)
return super(MailThread, self).message_route_process(
message, message_dict, routes
)

45
mail_multi_website/models/res_users.py

@ -0,0 +1,45 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import logging
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
FIELD_NAME = 'email_multi_website'
FIELDS = ['signature']
ALL_FIELDS = [FIELD_NAME] + FIELDS
class User(models.Model):
_inherit = ['res.users', 'website_dependent.mixin']
_name = 'res.users'
signature = fields.Html(company_dependent=True, website_dependent=True)
# extra field to detach email field from res.partner
email = fields.Char(related='email_multi_website', inherited=False)
email_multi_website = fields.Char(company_dependent=True, website_dependent=True)
@api.model
def create(self, vals):
res = super(User, self).create(vals)
# make value company independent
res._force_default(FIELD_NAME, vals.get('email'))
for f in FIELDS:
res._force_default(f, vals.get(f))
return res
@api.multi
def write(self, vals):
res = super(User, self).write(vals)
# TODO: will it work with OCA's partner_firstname module?
if any(k in vals for k in ['name', 'email'] + FIELDS):
for f in ALL_FIELDS:
self._update_properties_label(f)
return res
def _auto_init(self):
for f in FIELDS:
self._auto_init_website_dependent(f)
return super(User, self)._auto_init()

10
mail_multi_website/models/website.py

@ -0,0 +1,10 @@
# Copyright 2017 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields
class Website(models.Model):
_inherit = "website"
mail_server_id = fields.Many2one('ir.mail_server', 'Outgoing Mails', help='Default outgoing mail server')

0
mail_attachment_popup/static/description/icon.png → mail_multi_website/static/description/icon.png

Before

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

After

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

97
mail_multi_website/static/description/index.html

@ -0,0 +1,97 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan" style="color:#875A7B;">Email Addresses and Templates per Website
</h2>
<h3 class="oe_slogan">Use single Backend to manage several Websites</h3>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<div class="alert alert-info oe_mt32" style="padding:0.3em 0.6em; font-size: 150%;">
<i class="fa fa-hand-o-right"></i><b> Key features: </b>
<ul class="list-unstyled">
<li>
<i class="fa fa-check-square-o text-primary"></i>
Separate Mail Templates per website
</li>
<li>
<i class="fa fa-check-square-o text-primary"></i>
<em>From</em> address in email has address for current Website
</li>
<li>
<i class="fa fa-check-square-o text-primary"></i>
<em>Reply-To</em> address in email has domain of current Website
</li>
<li>
<i class="fa fa-check-square-o text-primary"></i>
User's signature per Website
</li>
</ul>
</div>
</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>
<li><a href="https://m.me/itprojectsllc" target="_blank">https://m.me/itprojectsllc <i class="fa fa-facebook-square"></i></a></li>
<li>skype@it-projects.info <i class="fa fa-skype"></i></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(1deg);
-o-transform: rotate(1deg);
-moz-transform: rotate(1deg);
-ms-transform: rotate(1deg);">
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(1deg);
-o-transform: rotate(1deg);
-moz-transform: rotate(1deg);
-ms-transform: rotate(1deg);">
Tested on Odoo<br/>11.0 enterprise
</div>-->
</div>
</div>
</div>
</section>

4
mail_multi_website/tests/__init__.py

@ -0,0 +1,4 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from . import test_send
from . import test_render
from . import test_fetch

41
mail_multi_website/tests/test_fetch.py

@ -0,0 +1,41 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo.addons.mail.tests.common import TestMail
from odoo.tools import mute_logger
from odoo.addons.mail.tests.test_mail_gateway import MAIL_TEMPLATE
class TestFetch(TestMail):
at_install = True
post_install = True
def setUp(self):
super(TestFetch, self).setUp()
self.website = self.env['website'].create({
'name': 'Test Website',
'domain': 'example.com',
})
self.company = self.env['res.company'].create({
'name': 'New Test Website'
})
self.website.company_id = self.company
# copy-paste from mail.tests.test_mail_gateway
mail_test_model = self.env['ir.model']._get('mail.test')
# groups@.. will cause the creation of new mail.test
self.alias = self.env['mail.alias'].create({
'alias_name': 'groups',
'alias_user_id': False,
'alias_model_id': mail_test_model.id,
'alias_contact': 'everyone'})
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models')
def test_fetch_multi_website(self):
""" Incoming email on an alias creating a new record + message_new + message details """
new_groups = self.format_and_process(MAIL_TEMPLATE, subject='My Frogs', to='groups@example.com, other@gmail.com')
# Test: one group created by mailgateway administrator
self.assertEqual(len(new_groups), 1, 'message_process: a new mail.test should have been created')
self.assertEqual(new_groups.website_id, self.website, 'New record is created with wrong website')
self.assertEqual(new_groups.company_id, self.company, 'New record is created with wrong company')

10
mail_multi_website/tests/test_mail_model.py

@ -0,0 +1,10 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields
class MailTest(models.Model):
_inherit = 'mail.test'
company_id = fields.Many2one('res.company', default=lambda self: self.env['res.company']._company_default_get())
website_id = fields.Many2one('website', default=lambda self: self.env['website'].browse(self.env.context.get('website_id')))

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

Loading…
Cancel
Save