Browse Source

Merge pull request #70 from yelizariev/10.0-merge-839a81d

10.0 merge 839a81d
pull/72/head
Ivan Yelizariev 8 years ago
committed by GitHub
parent
commit
d033e15a13
  1. 4
      .travis.yml
  2. 4
      mail_all/doc/changelog.rst
  3. 2
      mail_all/tests/__init__.py
  4. 9
      mail_all/tests/test_js.py
  5. 2
      mail_archives/tests/__init__.py
  6. 9
      mail_archives/tests/test_js.py
  7. 29
      mail_attachment_popup/README.rst
  8. 0
      mail_attachment_popup/__init__.py
  9. 28
      mail_attachment_popup/__openerp__.py
  10. 9
      mail_attachment_popup/doc/changelog.rst
  11. 16
      mail_attachment_popup/doc/index.rst
  12. BIN
      mail_attachment_popup/images/popup_image.png
  13. BIN
      mail_attachment_popup/static/description/attach_image.png
  14. BIN
      mail_attachment_popup/static/description/download.png
  15. BIN
      mail_attachment_popup/static/description/icon.png
  16. 84
      mail_attachment_popup/static/description/index.html
  17. BIN
      mail_attachment_popup/static/description/popup.png
  18. 429
      mail_attachment_popup/static/lib/js/jquery.arcticmodal.js
  19. 8
      mail_attachment_popup/static/src/css/jquery.arcticmodal.css
  20. 11
      mail_attachment_popup/static/src/css/simple.css
  21. 16
      mail_attachment_popup/static/src/css/styles.css
  22. BIN
      mail_attachment_popup/static/src/img/loading.gif
  23. 29
      mail_attachment_popup/static/src/xml/mail_attachment_popup.xml
  24. 13
      mail_attachment_popup/views/mail_attachment_popup_template.xml
  25. 2
      mail_base/README.rst
  26. 2
      mail_base/__init__.py
  27. 17
      mail_base/static/src/js/base.js
  28. 4
      mail_check_immediately/doc/changelog.rst
  29. 5
      mail_fix_553/mail_fix_553.py
  30. 5
      mail_fix_empty_body/models.py
  31. 10
      mail_move_message/controllers/main.py
  32. 4
      mail_move_message/doc/changelog.rst
  33. 68
      mail_move_message/mail_move_message_models.py
  34. 8
      mail_move_message/static/src/js/mail_move_message.js
  35. 6
      mail_outgoing/mail_outgoing_models.py
  36. 3
      mail_partner_lang/models.py
  37. 29
      mail_private/README.rst
  38. 3
      mail_private/__init__.py
  39. 35
      mail_private/__openerp__.py
  40. 7
      mail_private/doc/changelog.rst
  41. 26
      mail_private/doc/index.rst
  42. BIN
      mail_private/images/mail_private_image.png
  43. 11
      mail_private/models.py
  44. BIN
      mail_private/static/description/check_recipients.png
  45. BIN
      mail_private/static/description/icon.png
  46. 67
      mail_private/static/description/index.html
  47. BIN
      mail_private/static/description/result_message.png
  48. 201
      mail_private/static/src/js/mail_private.js
  49. 21
      mail_private/static/src/xml/mail_private.xml
  50. 15
      mail_private/template.xml
  51. 27
      mail_private/view.xml
  52. 4
      mail_recovery/doc/changelog.rst
  53. 2
      mail_recovery/static/src/js/mail_recovery.js
  54. 4
      mail_reply/README.rst
  55. 4
      mail_reply/doc/changelog.rst
  56. 4
      mail_sent/doc/changelog.rst
  57. 5
      mail_sent/models.py
  58. 2
      mail_sent/tests/__init__.py
  59. 11
      mail_sent/tests/test_js.py
  60. 4
      mail_to/doc/changelog.rst
  61. 2
      mail_to/static/src/js/mail_to.js
  62. 6
      mailgun/README.rst
  63. 4
      mailgun/__init__.py
  64. 2
      mailgun/controllers/__init__.py
  65. 4
      mailgun/controllers/main.py
  66. 4
      mailgun/doc/changelog.rst
  67. 2
      mailgun/models.py
  68. 16
      mass_mailing_extra/models.py
  69. 6
      res_partner_company_messages/README.rst
  70. 2
      res_partner_company_messages/__init__.py
  71. 17
      res_partner_company_messages/__openerp__.py
  72. BIN
      res_partner_company_messages/images/child.png
  73. BIN
      res_partner_company_messages/images/parent.png
  74. 19
      res_partner_company_messages/models.py
  75. BIN
      res_partner_company_messages/static/description/icon.png
  76. 3
      res_partner_company_messages/views.xml
  77. 14
      res_partner_mails_count/models.py
  78. 8
      res_partner_mails_count/tests/test_phantom.py
  79. 14
      res_partner_strip_email/models.py

4
.travis.yml

@ -3,6 +3,7 @@ language: python
python: python:
- "2.7" - "2.7"
dist: trusty
sudo: false sudo: false
cache: pip cache: pip
@ -32,3 +33,6 @@ script:
after_success: after_success:
coveralls coveralls
notifications:
email: false

4
mail_all/doc/changelog.rst

@ -1,5 +1,5 @@
Changelog
=========
Updates
=======
`1.0.0` `1.0.0`
------- -------

2
mail_all/tests/__init__.py

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

9
mail_all/tests/test_js.py

@ -1,9 +1,10 @@
import openerp.tests
# -*- coding: utf-8 -*-
import odoo.tests
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_01_mail_all(self): def test_01_mail_all(self):
# wait till page loaded and then click and wait again # wait till page loaded and then click and wait again

2
mail_archives/tests/__init__.py

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

9
mail_archives/tests/test_js.py

@ -1,9 +1,10 @@
import openerp.tests
# -*- coding: utf-8 -*-
import odoo.tests
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_01_mail_archives(self): def test_01_mail_archives(self):
# wait till page loaded and then click and wait again # wait till page loaded and then click and wait again

29
mail_attachment_popup/README.rst

@ -0,0 +1,29 @@
===================
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>`_
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

28
mail_attachment_popup/__openerp__.py

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
"name": """Popup Attachments""",
"summary": """Open attached mail images in popup""",
"category": "Extra Tools",
"version": "1.0.0",
"images": ['images/popup_image.png'],
"author": "IT-Projects LLC, Dinar Gabbasov",
'website': "https://twitter.com/gabbasov_dinar",
"license": "GPL-3",
"price": "50.0",
"currency": "EUR",
"depends": [
"mail",
],
"external_dependencies": {"python": [], "bin": []},
"data": [
"views/mail_attachment_popup_template.xml",
],
"qweb": [
"static/src/xml/mail_attachment_popup.xml",
],
"installable": False,
'auto_install': False,
}

9
mail_attachment_popup/doc/changelog.rst

@ -0,0 +1,9 @@
.. _changelog:
Updates
=======
`1.0.0`
-------
- Init version

16
mail_attachment_popup/doc/index.rst

@ -0,0 +1,16 @@
===================
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

BIN
mail_attachment_popup/images/popup_image.png

After

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

BIN
mail_attachment_popup/static/description/attach_image.png

After

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

BIN
mail_attachment_popup/static/description/download.png

After

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

BIN
mail_attachment_popup/static/description/icon.png

After

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

84
mail_attachment_popup/static/description/index.html

@ -0,0 +1,84 @@
<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:it@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:it@it-projects.info">it@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

After

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

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

@ -0,0 +1,429 @@
/*
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

@ -0,0 +1,8 @@
.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

@ -0,0 +1,11 @@
.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

@ -0,0 +1,16 @@
.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

After

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

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

@ -0,0 +1,29 @@
<?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 == 'image/png'">
<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 != 'image/png'">
<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

@ -0,0 +1,13 @@
<?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>

2
mail_base/README.rst

@ -14,6 +14,6 @@ Further information
------------------- -------------------
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. 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.
One can say, that the module do this todo from `addons/mail/static/src/js/chat_manager.js <https://github.com/odoo/odoo/blob/9.0/addons/mail/static/src/js/chat_manager.js#L57>`_
One can say, that the module do this todo from `addons/mail/static/src/js/chat_manager.js <https://github.com/odoo/odoo/blob/9.0/addons/mail/static/src/js/chat_manager.js#L57>`__
// to do: move this to mail.utils // to do: move this to mail.utils

2
mail_base/__init__.py

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

17
mail_base/static/src/js/base.js

@ -214,7 +214,7 @@ var MailTools = core.Class.extend({
return { return {
is_starred: chat_manager.mail_tools.property_descr("channel_starred", msg, chat_manager.mail_tools), is_starred: chat_manager.mail_tools.property_descr("channel_starred", msg, chat_manager.mail_tools),
is_needaction: chat_manager.mail_tools.property_descr("channel_inbox", msg, chat_manager.mail_tools) is_needaction: chat_manager.mail_tools.property_descr("channel_inbox", msg, chat_manager.mail_tools)
}
};
}, },
set_channel_flags: function(data, msg){ set_channel_flags: function(data, msg){
@ -224,7 +224,7 @@ var MailTools = core.Class.extend({
if (_.contains(data.starred_partner_ids, session.partner_id)) { if (_.contains(data.starred_partner_ids, session.partner_id)) {
msg.is_starred = true; msg.is_starred = true;
} }
return msg
return msg;
}, },
get_channel_array: function(msg){ get_channel_array: function(msg){
@ -241,8 +241,7 @@ var MailTools = core.Class.extend({
subtype_description: data.subtype_description, subtype_description: data.subtype_description,
is_author: data.author_id && data.author_id[0] === session.partner_id, is_author: data.author_id && data.author_id[0] === session.partner_id,
is_note: data.is_note, is_note: data.is_note,
is_system_notification: data.message_type === 'notification' && data.model === 'mail.channel'
|| data.info === 'transient_message',
is_system_notification: data.message_type === 'notification' && data.model === 'mail.channel' || data.info === 'transient_message',
attachment_ids: data.attachment_ids || [], attachment_ids: data.attachment_ids || [],
subject: data.subject, subject: data.subject,
email_from: data.email_from, email_from: data.email_from,
@ -325,7 +324,7 @@ var MailTools = core.Class.extend({
} else { } else {
channel = chat_manager.mail_tools.make_channel(data, options); channel = chat_manager.mail_tools.make_channel(data, options);
channels.push(channel); channels.push(channel);
channels = _.sortBy(channels, function (channel) { return _.isString(channel.name) ? channel.name.toLowerCase() : '' });
channels = _.sortBy(channels, function (channel) { return _.isString(channel.name) ? channel.name.toLowerCase() : ''; });
if (!options.silent) { if (!options.silent) {
chat_manager.bus.trigger("new_channel", channel); chat_manager.bus.trigger("new_channel", channel);
} }
@ -744,10 +743,10 @@ var cls = new MailTools();
chat_manager.mail_tools = cls; chat_manager.mail_tools = cls;
// we add this function this way in order to make them extendable via MailTools.include({...}) // we add this function this way in order to make them extendable via MailTools.include({...})
chat_manager.make_message = function(){ chat_manager.make_message = function(){
return chat_manager.mail_tools.make_message.apply(chat_manager.mail_tools, arguments)
return chat_manager.mail_tools.make_message.apply(chat_manager.mail_tools, arguments);
}; };
chat_manager.make_channel = function(){ chat_manager.make_channel = function(){
return chat_manager.mail_tools.make_channel.apply(chat_manager.mail_tools, arguments)
return chat_manager.mail_tools.make_channel.apply(chat_manager.mail_tools, arguments);
}; };
chat_manager.post_message = function (data, options) { chat_manager.post_message = function (data, options) {
options = options || {}; options = options || {};
@ -1116,9 +1115,9 @@ function init () {
}); });
// unsubscribe and then subscribe to the event, to avoid duplication of new messages // unsubscribe and then subscribe to the event, to avoid duplication of new messages
bus.off('notification')
bus.off('notification');
bus.on('notification', null, function(){ bus.on('notification', null, function(){
chat_manager.mail_tools.on_notification.apply(chat_manager.mail_tools, arguments)
chat_manager.mail_tools.on_notification.apply(chat_manager.mail_tools, arguments);
}); });
return session.rpc('/mail/client_action').then(function (result) { return session.rpc('/mail/client_action').then(function (result) {

4
mail_check_immediately/doc/changelog.rst

@ -1,7 +1,7 @@
.. _changelog: .. _changelog:
Changelog
=========
Updates
=======
`1.0.1` `1.0.1`
------- -------

5
mail_fix_553/mail_fix_553.py

@ -15,8 +15,7 @@ from openerp.tools.translate import _
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class mail_mail(osv.Model):
class MailMail(osv.Model):
_inherit = "mail.mail" _inherit = "mail.mail"
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None):
@ -41,7 +40,7 @@ class mail_mail(osv.Model):
catchall_alias_name = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.name_alias_from", context=context) catchall_alias_name = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.name_alias_from", context=context)
catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context)
correct_email_from = '@%s>?\s*$' % catchall_domain
correct_email_from = r'@%s>?\s*$' % catchall_domain
default_email_from = '%s@%s' % (catchall_alias, catchall_domain) default_email_from = '%s@%s' % (catchall_alias, catchall_domain)
context = dict(context or {}) context = dict(context or {})

5
mail_fix_empty_body/models.py

@ -2,12 +2,11 @@
from openerp import models from openerp import models
class mail_compose_message(models.TransientModel):
class MailComposeMessage(models.TransientModel):
_inherit = 'mail.compose.message' _inherit = 'mail.compose.message'
def get_mail_values(self, cr, uid, wizard, res_ids, context=None): def get_mail_values(self, cr, uid, wizard, res_ids, context=None):
res = super(mail_compose_message, self).get_mail_values(cr, uid, wizard, res_ids, context)
res = super(MailComposeMessage, self).get_mail_values(cr, uid, wizard, res_ids, context)
for id, d in res.iteritems(): for id, d in res.iteritems():
d['body'] = d.get('body') or '' d['body'] = d.get('body') or ''
return res return res

10
mail_move_message/controllers/main.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp.addons.web.controllers.main import DataSet
from openerp.tools.translate import _
from openerp import http
from openerp.http import request
from openerp.addons.bus.controllers.main import BusController
from odoo.addons.web.controllers.main import DataSet
from odoo.tools.translate import _
from odoo import http
from odoo.http import request
from odoo.addons.bus.controllers.main import BusController
class MailChatController(BusController): class MailChatController(BusController):

4
mail_move_message/doc/changelog.rst

@ -1,7 +1,7 @@
.. _changelog: .. _changelog:
Changelog
=========
Updates
=======
`1.0.4` `1.0.4`
------- -------

68
mail_move_message/mail_move_message_models.py

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp import api, models, fields
from openerp import api
from openerp import fields
from openerp import models
from openerp.tools import email_split from openerp.tools import email_split
from openerp.tools.translate import _ from openerp.tools.translate import _
class wizard(models.TransientModel):
class Wizard(models.TransientModel):
_name = 'mail_move_message.wizard' _name = 'mail_move_message.wizard'
def _model_selection(self): def _model_selection(self):
@ -26,7 +28,7 @@ class wizard(models.TransientModel):
@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() model_fields = self.fields_get()
if model_fields['model']['selection']: if model_fields['model']['selection']:
@ -85,8 +87,14 @@ class wizard(models.TransientModel):
"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.depends('message_id') @api.depends('message_id')
@api.one
@api.multi
def get_can_move(self): def get_can_move(self):
for r in self:
r.get_can_move_one(self)
@api.multi
def get_can_move_one(self):
self.ensure_one()
# message was not moved before OR message is a top message of previous move # message was not moved before OR message is a top message of previous move
self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id
@ -135,8 +143,14 @@ class wizard(models.TransientModel):
self.res_id = None self.res_id = None
return {'domain': domain} return {'domain': domain}
@api.one
@api.multi
def check_access(self): def check_access(self):
for r in self:
r.check_access_one(self)
@api.multi
def check_access_one(self):
self.ensure_one()
operation = 'write' operation = 'write'
if not (self.model and self.res_id): if not (self.model and self.res_id):
@ -192,8 +206,14 @@ class wizard(models.TransientModel):
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
} }
@api.one
@api.multi
def delete(self): def delete(self):
for r in self:
r.delete_one(self)
@api.multi
def delete_one(self):
self.ensure_one()
msg_id = self.message_id.id msg_id = self.message_id.id
# Send notification # Send notification
@ -227,14 +247,20 @@ class wizard(models.TransientModel):
context.update({'default_%s' % contact_field: partner_id}) context.update({'default_%s' % contact_field: partner_id})
return context return context
@api.one
@api.multi
def read_close(self): def read_close(self):
for r in self:
r.read_close_one(self)
@api.multi
def read_close_one(self):
self.ensure_one()
self.message_id.set_message_done() self.message_id.set_message_done()
self.message_id.child_ids.set_message_done() self.message_id.child_ids.set_message_done()
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}
class mail_message(models.Model):
class MailMessage(models.Model):
_inherit = 'mail.message' _inherit = 'mail.message'
is_moved = fields.Boolean('Is moved') is_moved = fields.Boolean('Is moved')
@ -245,8 +271,14 @@ class mail_message(models.Model):
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='_get_all_childs', help='all childs, including subchilds')
@api.one
@api.multi
def _get_all_childs(self, include_myself=True): def _get_all_childs(self, include_myself=True):
for r in self:
r._get_all_childs_one(self, include_myself=True)
@api.multi
def _get_all_childs_one(self, include_myself=True):
self.ensure_one()
ids = [] ids = []
if include_myself: if include_myself:
ids.append(self.id) ids.append(self.id)
@ -268,8 +300,14 @@ class mail_message(models.Model):
for f in followers: for f in followers:
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.one
@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):
for r in self:
r.move_one(self, parent_id, res_id, model, move_back, move_followers=False)
@api.multi
def move_one(self, parent_id, res_id, model, move_back, move_followers=False):
self.ensure_one()
if parent_id == res_id: if parent_id == res_id:
return return
vals = {} vals = {}
@ -329,7 +367,7 @@ class mail_message(models.Model):
def name_get(self): def name_get(self):
context = self.env.context context = self.env.context
if not (context or {}).get('extended_name'): if not (context or {}).get('extended_name'):
return super(mail_message, self).name_get()
return super(MailMessage, self).name_get()
reads = self.read(['record_name', 'model', 'res_id']) reads = self.read(['record_name', 'model', 'res_id'])
res = [] res = []
for record in reads: for record in reads:
@ -340,7 +378,7 @@ class mail_message(models.Model):
@api.multi @api.multi
def message_format(self): def message_format(self):
message_values = super(mail_message, self).message_format()
message_values = super(MailMessage, self).message_format()
message_index = {message['id']: message for message in message_values} message_index = {message['id']: message for message in message_values}
for item in self: for item in self:
msg = message_index.get(item.id) msg = message_index.get(item.id)
@ -349,7 +387,7 @@ class mail_message(models.Model):
return message_values return message_values
class mail_move_message_configuration(models.TransientModel):
class MailMoveMessageConfiguration(models.TransientModel):
_name = 'mail_move_message.config.settings' _name = 'mail_move_message.config.settings'
_inherit = 'res.config.settings' _inherit = 'res.config.settings'
@ -380,12 +418,12 @@ class mail_move_message_configuration(models.TransientModel):
config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '') config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '')
class res_partner(models.Model):
class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
@api.model @api.model
def create(self, vals): def create(self, vals):
res = super(res_partner, self).create(vals)
res = super(ResPartner, self).create(vals)
if 'update_message_author' in self.env.context and 'email' in vals: if 'update_message_author' in self.env.context and 'email' in vals:
mail_message_obj = self.env['mail.message'] mail_message_obj = self.env['mail.message']
# Escape special SQL characters in email_address to avoid invalid matches # Escape special SQL characters in email_address to avoid invalid matches

8
mail_move_message/static/src/js/mail_move_message.js

@ -20,7 +20,7 @@ odoo.define('mail_move_message.relocate', function (require) {
this.events['click .oe_move'] = function(event) { this.events['click .oe_move'] = function(event) {
var message_id = $(event.currentTarget).data('message-id'); var message_id = $(event.currentTarget).data('message-id');
this.trigger("move_message", message_id); this.trigger("move_message", message_id);
}
};
}, },
on_move_message: function(message_id){ on_move_message: function(message_id){
var action = { var action = {
@ -100,9 +100,9 @@ odoo.define('mail_move_message.relocate', function (require) {
var context_built = $.Deferred(); var context_built = $.Deferred();
if(this.node.attrs.use_for_mail_move_message) { if(this.node.attrs.use_for_mail_move_message) {
var model = new Model(this.view.dataset.model); 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();
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', [ context_built = model.call('create_partner', [
self.view.dataset.context.default_message_id, self.view.dataset.context.default_message_id,
related_field.field.relation, related_field.field.relation,

6
mail_outgoing/mail_outgoing_models.py

@ -2,7 +2,7 @@
from openerp.osv import osv from openerp.osv import osv
class mail_message(osv.Model):
class MailMessage(osv.Model):
_inherit = 'mail.message' _inherit = 'mail.message'
def check_access_rule(self, cr, uid, ids, operation, context=None): def check_access_rule(self, cr, uid, ids, operation, context=None):
@ -13,10 +13,10 @@ class mail_message(osv.Model):
if user_groups.issuperset(group_all_emails): if user_groups.issuperset(group_all_emails):
return return
return super(mail_message, self).check_access_rule(cr, uid, ids, operation, context)
return super(MailMessage, self).check_access_rule(cr, uid, ids, operation, context)
class mail_mail(osv.Model):
class MailMail(osv.Model):
_name = 'mail.mail' _name = 'mail.mail'
_inherit = ['mail.mail', 'ir.needaction_mixin'] _inherit = ['mail.mail', 'ir.needaction_mixin']
_needaction = True _needaction = True

3
mail_partner_lang/models.py

@ -2,8 +2,7 @@
from openerp.osv import osv from openerp.osv import osv
class mail_thread(osv.Model):
class MailThread(osv.Model):
_inherit = "mail.thread" _inherit = "mail.thread"
def message_track(self, cr, uid, ids, tracked_fields, initial_values, context={}): def message_track(self, cr, uid, ids, tracked_fields, initial_values, context={}):

29
mail_private/README.rst

@ -0,0 +1,29 @@
====================
Internal Messaging
====================
Send private messages to specified recipients, regardless of who are in followers list.
Credits
=======
Contributors
------------
* Pavel Romanchenko <romanchenko@it-projects.info>
Sponsors
--------
* `IT-Projects LLC <https://it-projects.info>`__
Further information
===================
Demo: http://runbot.it-projects.info/demo/mail-addons/8.0
HTML Description: https://apps.odoo.com/apps/modules/8.0/mail_private/
Usage instructions: `<doc/index.rst>`__
Changelog: `<doc/changelog.rst>`__
Tested on Odoo 8.0 0af32f3f84bae07b11abb8538d02e35c7369a348

3
mail_private/__init__.py

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

35
mail_private/__openerp__.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
{
"name": """Internal Messaging""",
"summary": """Send private messages to specified recipients, regardless of who are in followers list.""",
"category": "Discuss",
"images": ['images/mail_private_image.png'],
"version": "1.0.0",
"application": False,
"author": "IT-Projects LLC, Pavel Romanchenko",
"website": "https://it-projects.info",
"license": "GPL-3",
"price": 50.00,
"currency": "EUR",
"depends": [
"mail",
],
"external_dependencies": {"python": [], "bin": []},
"data": [
'template.xml',
'view.xml',
],
"qweb": [
'static/src/xml/mail_private.xml',
],
"demo": [],
"post_load": None,
"pre_init_hook": None,
"post_init_hook": None,
"auto_install": False,
"installable": False,
}

7
mail_private/doc/changelog.rst

@ -0,0 +1,7 @@
Updates
=======
`1.0.0`
-------
- Init version

26
mail_private/doc/index.rst

@ -0,0 +1,26 @@
====================
Internal Messaging
====================
Installation
============
Nothing special is needed to install this module.
Usage
=====
To send a message to specified recipients:
* Click on the ``[Send internal message]``.
* Choose the recipients that should receive a message by ticking the checkboxes.
* To add more recipients click on the ``[Open the full mail composer]`` on the right upper corner of the message block and choose recipients in the **Recipient** field.
* Click on the ``[Send]`` button.
Uninstallation
==============
Nothing special is needed to uninstall this module.

BIN
mail_private/images/mail_private_image.png

After

Width: 766  |  Height: 387  |  Size: 33 KiB

11
mail_private/models.py

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv, fields
class MailComposeMessage(osv.TransientModel):
_inherit = 'mail.compose.message'
_columns = {
'private': fields.boolean('Send Internal Message'),
}

BIN
mail_private/static/description/check_recipients.png

After

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

BIN
mail_private/static/description/icon.png

After

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

67
mail_private/static/description/index.html

@ -0,0 +1,67 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Internal Messaging</h2>
<h3 class="oe_slogan">Send private messages to specified recipients, regardless of who are in followers list.</h3>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<blockquote>
The module will be available for Odoo 9.0 soon. You have time to buy Internal Messaging before price increase! (When you purchase 8.0 version, you get a right to download 9.0 and any further module versions).
</blockquote>
<p class="oe_mt32">
By default, to send a private message to specific recipient(s) you need to delete other followers included to the same document. The module allows to send private messages to recipients you specified, regardless of who are in the followers list.
</p>
<p>
It simplify internal communication in leads, when you need to send some private messages to your colleagues before reply to customer.
It helps in records like project tasks too. In a task with many participants, you can easily send message and only specified colleagues will be notified. It allows to have clean inbox for everybody. It's very essential, because people often neglect important message in Inbox full of useless messages.
</p>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h3 class="oe_slogan">How it works</h3>
<p class="oe_mt32">
Click on the "Send internal message" and choose the recipient(s). Then type any message and click on the "Send" button.
</p>
<div class="oe_demo oe_picture oe_screenshot">
<img src="check_recipients.png"/>
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<p class="oe_mt32">
As a result you can see the message with a dark background, which is sent to the corresponding recipient(s). Other followers of the document will not receive your message.
</p>
<div class="oe_demo oe_picture oe_screenshot">
<img src="result_message.png"/>
</div>
</div>
</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:it@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:it@it-projects.info">it@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_private/static/description/result_message.png

After

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

201
mail_private/static/src/js/mail_private.js

@ -0,0 +1,201 @@
openerp.mail_private = function(instance){
var mail = instance.mail;
instance.mail.ThreadComposeMessage.include({
init: function (parent, datasets, options) {
this._super.apply(this, arguments);
this.private = false;
},
bind_events: function(){
var self = this;
this.$('.oe_compose_post_private').on('click', self.on_toggle_quick_composer_private);
this._super.apply(this, arguments);
},
on_compose_fullmail: function (default_composition_mode) {
var self = this;
if(!this.do_check_attachment_upload()) {
return false;
}
var recipient_done = $.Deferred();
if (this.is_log) {
recipient_done.resolve([]);
}
else {
recipient_done = this.check_recipient_partners();
}
$.when(recipient_done).done(function (partner_ids) {
var context = {
'default_parent_id': self.id,
'default_body': mail.ChatterUtils.get_text2html(self.$el ? (self.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
'default_attachment_ids': _.map(self.attachment_ids, function (file) {return file.id;}),
'default_partner_ids': partner_ids,
'default_is_log': self.is_log,
'default_private': self.private,
'mail_post_autofollow': true,
'mail_post_autofollow_partner_ids': partner_ids,
'is_private': self.is_private
};
if (default_composition_mode != 'reply' && self.context.default_model && self.context.default_res_id) {
context.default_model = self.context.default_model;
context.default_res_id = self.context.default_res_id;
}
var action = {
type: 'ir.actions.act_window',
res_model: 'mail.compose.message',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'new',
context: context,
};
self.do_action(action, {
'on_close': function(){ !self.parent_thread.options.view_inbox && self.parent_thread.message_fetch(); }
});
self.on_cancel();
});
},
do_send_message_post: function (partner_ids, log) {
var self = this;
var values = {
'body': this.$('textarea').val(),
'subject': false,
'parent_id': this.context.default_parent_id,
'attachment_ids': _.map(this.attachment_ids, function (file) {return file.id;}),
'partner_ids': partner_ids,
'context': _.extend(this.parent_thread.context, {
'mail_post_autofollow': true,
'mail_post_autofollow_partner_ids': partner_ids,
'default_private': self.private
}),
'type': 'comment',
'content_subtype': 'plaintext',
};
if (log || self.private) {
values.subtype = false;
}
else {
values.subtype = 'mail.mt_comment';
}
this.parent_thread.ds_thread._model.call('message_post', [this.context.default_res_id], values).done(function (message_id) {
var thread = self.parent_thread;
var root = thread == self.options.root_thread;
if (self.options.display_indented_thread < self.thread_level && thread.parent_message) {
var thread = thread.parent_message.parent_thread;
}
// create object and attach to the thread object
thread.message_fetch([["id", "=", message_id]], false, [message_id], function (arg, data) {
var message = thread.create_message_object( data.slice(-1)[0] );
// insert the message on dom
thread.insert_message( message, root ? undefined : self.$el, root );
});
self.on_cancel();
self.flag_post = false;
});
},
on_toggle_quick_composer: function (event) {
if (event.target.className == 'oe_compose_post') {
this.recipients = [];
this.private = false;
}
this._super.apply(this, arguments);
},
on_toggle_quick_composer_private: function (event) {
this.recipients = [];
var self = this;
var $input = $(event.target);
this.compute_emails_from();
var email_addresses = _.pluck(this.recipients, 'email_address');
var suggested_partners = $.Deferred();
// if clicked: call for suggested recipients
if (event.type == 'click') {
this.private = $input.hasClass('oe_compose_post_private');
this.is_log = false;
suggested_partners = this.parent_thread.get_recipients_for_internal_message([this.context.default_res_id], this.context)
.done(function (additional_recipients) {
var thread_recipients = additional_recipients[self.context.default_res_id];
_.each(thread_recipients, function (recipient) {
var parsed_email = mail.ChatterUtils.parse_email(recipient[1]);
if (_.indexOf(email_addresses, parsed_email[1]) == -1) {
self.recipients.push({
'checked': false,
'partner_id': recipient[0],
'full_name': recipient[1],
'name': parsed_email[0],
'email_address': parsed_email[1],
'reason': recipient[2],
});
}
});
});
}
else {
suggested_partners.resolve({});
}
// uncheck partners from compute_emails_from
_.each(this.recipients, function(r){
if (!r.partner_id){
r.checked = false;
}
});
// when call for suggested partners finished: re-render the widget
$.when(suggested_partners).pipe(function (additional_recipients) {
if ((!self.stay_open || (event && event.type == 'click')) && (!self.show_composer || !self.$('textarea:not(.oe_compact)').val().match(/\S+/) && !self.attachment_ids.length)) {
self.show_composer = !self.show_composer || self.stay_open;
self.reinit();
}
if (!self.stay_open && self.show_composer && (!event || event.type != 'blur')) {
self.$('textarea:not(.oe_compact):first').focus();
}
});
return suggested_partners;
}
});
instance.mail.Thread.include({
get_recipients_for_internal_message: function(ids, context){
var self = this;
self.result = {};
return new instance.web.Model(context.default_model).call(
'read', [ids, ['message_follower_ids', 'partner_id'], context]
).then(function (thread) {
for (var i = 0; i < thread.length; i++) {
var res_id = thread[i].id;
var recipient_ids = thread[i].message_follower_ids;
self.result[res_id] = [];
// Add customer
self.customer = thread[i].partner_id;
if (self.customer && !recipient_ids.includes(self.customer[0])) {
recipient_ids.splice(0, 0, self.customer[0]);
}
return new instance.web.Model('res.partner').call(
'read', [recipient_ids, ['name', 'email', 'user_ids']]
).then(function (res_partners){
for (var j = 0; j < res_partners.length; j++) {
var partner = res_partners[j];
if (!_.include(partner.user_ids, self.session.uid)){
var reason = 'Follower';
if (self.customer && partner.id == self.customer[0]){
reason = 'Partner';
}
self.result[res_id].push(
[partner.id, partner.name + '<' + partner.email + '>', reason]
);
}
}
return self.result;
});
}
return self.result;
});
}
});
};

21
mail_private/static/src/xml/mail_private.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="mail.compose_message">
<t t-jquery="a[title='Send a message to all followers of the document']" t-operation="after">
<span class="oe_grey oe_sep_word">or</span><a class="oe_compose_post_private" t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" title="Send a message to specified recipients only">Send internal message</a>
</t>
</t>
<t t-extend="mail.thread.list_recipients">
<t t-jquery="[t-if='!widget.is_private']" t-operation="replace">
<t t-if="!widget.is_private and !widget.private">
<span class="oe_all_follower">
<t t-if="widget.parent_thread.parent_message.record_name">
Followers of <t t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'"/>
</t>
<t t-if="!widget.parent_thread.parent_message.record_name and widget.options.view_inbox">My Followers</t>
<t t-if="!widget.parent_thread.parent_message.record_name and !widget.options.view_inbox">Followers of this document</t>
</span>
</t>
</t>
</t>
</template>

15
mail_private/template.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template
id="assets_backend"
name="mail_private_assets_backend"
inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/mail_private/static/src/js/mail_private.js"></script>
</xpath>
</template>
</data>
</openerp>

27
mail_private/view.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="mail_private_email_compose_message_wizard_form" model="ir.ui.view">
<field name="name">mail.private.mail.compose.message.form</field>
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@groups='base.group_user']" position="replace">
<div groups="base.group_user" attrs="{'invisible': [('is_log', '=', True)]}">
<field name="private" invisible="1"/>
<!--<field name="private"/>-->
<span attrs="{'invisible': [('composition_mode', '!=', 'mass_mail')]}">
<strong>Email mass mailing</strong> on
<span attrs="{'invisible': [('use_active_domain', '=', True)]}">the selected records</span>
<span attrs="{'invisible': [('use_active_domain', '=', False)]}">the current search filter</span>.
</span>
<span attrs="{'invisible':['|', ('composition_mode', '!=', 'comment'), ('private', '=', True)]}">Followers of the document and</span>
<field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
context="{'force_email':True, 'show_email':True}"
attrs="{'invisible': [('composition_mode', '!=', 'comment')]}"/>
</div>
</xpath>
</field>
</record>
</data>
</openerp>

4
mail_recovery/doc/changelog.rst

@ -1,5 +1,5 @@
Changelog
=========
Updates
=======
`1.0.0` `1.0.0`
------- -------

2
mail_recovery/static/src/js/mail_recovery.js

@ -21,4 +21,4 @@ odoo.define('mail_recovery', function (require) {
return this._super(event); return this._super(event);
}, },
}); });
})
});

4
mail_reply/README.rst

@ -9,8 +9,8 @@ Further information
HTML Description: https://apps.odoo.com/apps/modules/9.0/mail_reply/ HTML Description: https://apps.odoo.com/apps/modules/9.0/mail_reply/
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 9.0 d3dd4161ad0598ebaa659fbd083457c77aa9448d

4
mail_reply/doc/changelog.rst

@ -1,5 +1,5 @@
Changelog
=========
Updates
=======
`1.0.0` `1.0.0`
------- -------

4
mail_sent/doc/changelog.rst

@ -1,7 +1,7 @@
.. _changelog: .. _changelog:
Changelog
=========
Updates
=======
`1.0.2` `1.0.2`
------- -------

5
mail_sent/models.py

@ -5,11 +5,11 @@ from openerp import api, models, fields
class MailMessage(models.Model): class MailMessage(models.Model):
_inherit = 'mail.message' _inherit = 'mail.message'
sent = fields.Boolean('Sent', compute="_get_sent", help='Was message sent to someone', store=True)
sent = fields.Boolean('Sent', compute="_compute_sent", help='Was message sent to someone', store=True)
@api.one @api.one
@api.depends('author_id', 'partner_ids') @api.depends('author_id', 'partner_ids')
def _get_sent(self):
def _compute_sent(self):
self_sudo = self.sudo() self_sudo = self.sudo()
self_sudo.sent = len(self_sudo.partner_ids) > 1 \ self_sudo.sent = len(self_sudo.partner_ids) > 1 \
or len(self_sudo.partner_ids) == 1 \ or len(self_sudo.partner_ids) == 1 \
@ -28,5 +28,6 @@ class MailMessage(models.Model):
class MailComposeMessage(models.TransientModel): class MailComposeMessage(models.TransientModel):
_inherit = 'mail.compose.message' _inherit = 'mail.compose.message'
sent = fields.Boolean('Sent', help='dummy field to fix inherit error') sent = fields.Boolean('Sent', help='dummy field to fix inherit error')

2
mail_sent/tests/__init__.py

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

11
mail_sent/tests/test_js.py

@ -1,9 +1,10 @@
import openerp.tests
# -*- coding: utf-8 -*-
import odoo.tests
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_01_mail_sent(self): def test_01_mail_sent(self):
# wait till page loaded and then click and wait again # wait till page loaded and then click and wait again
@ -14,4 +15,4 @@ class TestUi(openerp.tests.HttpCase):
}, 1000); }, 1000);
""" """
link = '/web#action=%s' % self.ref('mail.mail_channel_action_client_chat') link = '/web#action=%s' % self.ref('mail.mail_channel_action_client_chat')
self.phantom_js(link, code, "odoo.__DEBUG__.services['mail_sent.sent']", login="demo")
self.phantom_js(link, code, "odoo.__DEBUG__.services['mail_sent.sent'].is_ready", login="demo")

4
mail_to/doc/changelog.rst

@ -1,5 +1,5 @@
Changelog
=========
Updates
=======
`1.0.0` `1.0.0`
------- -------

2
mail_to/static/src/js/mail_to.js

@ -8,7 +8,7 @@ odoo.define('mail_to.MailTo', function (require) {
var msg = this._super(data); var msg = this._super(data);
msg.partner_ids = data.partner_ids; msg.partner_ids = data.partner_ids;
if (!msg.partner_ids) if (!msg.partner_ids)
return msg
return msg;
var more_recipients = ''; var more_recipients = '';
// value which define more recipients // value which define more recipients

6
mailgun/README.rst

@ -22,15 +22,15 @@ Contributors
Sponsors Sponsors
-------- --------
* `IT-Projects LLC <https://it-projects.info>`_
* `IT-Projects LLC <https://it-projects.info>`__
Further information Further information
=================== ===================
HTML Description: https://apps.odoo.com/apps/modules/9.0/mailgun/ HTML Description: https://apps.odoo.com/apps/modules/9.0/mailgun/
Usage instructions: `<doc/index.rst>`_
Usage instructions: `<doc/index.rst>`__
Changelog: `<doc/changelog.rst>`_
Changelog: `<doc/changelog.rst>`__
Tested on Odoo 9.0 c8cd67c5d98b410cabe0a6efb3347a8a4de731d8 Tested on Odoo 9.0 c8cd67c5d98b410cabe0a6efb3347a8a4de731d8

4
mailgun/__init__.py

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

2
mailgun/controllers/__init__.py

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

4
mailgun/controllers/main.py

@ -1,10 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
import werkzeug
import email
import requests
import simplejson
import re import re

4
mailgun/doc/changelog.rst

@ -1,5 +1,5 @@
Changelog
=========
Updates
=======
`1.1.0` `1.1.0`
------- -------

2
mailgun/models.py

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
import requests import requests
import simplejson import simplejson
from openerp import models, api from openerp import models, api
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)

16
mass_mailing_extra/models.py

@ -8,8 +8,14 @@ class MailMailStats(models.Model):
partner_ids = fields.Many2many('res.partner', related='mail_mail_id.recipient_ids', string='Partners (Mail)') partner_ids = fields.Many2many('res.partner', related='mail_mail_id.recipient_ids', string='Partners (Mail)')
@api.one
@api.multi
def _get_partner_id(self): def _get_partner_id(self):
for r in self:
r._get_partner_id_one(self)
@api.multi
def _get_partner_id_one(self):
self.ensure_one()
if self.model == 'res.partner': if self.model == 'res.partner':
self.partner_id = self.res_id self.partner_id = self.res_id
else: else:
@ -17,8 +23,14 @@ class MailMailStats(models.Model):
partner_id = fields.Many2one('res.partner', compute=_get_partner_id, string='Partner (Document ID)') partner_id = fields.Many2one('res.partner', compute=_get_partner_id, string='Partner (Document ID)')
@api.one
@api.multi
def _get_partners(self): def _get_partners(self):
for r in self:
r._get_partners_one(self)
@api.multi
def _get_partners_one(self):
self.ensure_one()
res = {} res = {}
for p in self.partner_ids: for p in self.partner_ids:
res[p.id] = p res[p.id] = p

6
res_partner_company_messages/README.rst

@ -0,0 +1,6 @@
Aggregate messages from company's contacts
==========================================
By default, odoo displays under partner form only its own messages. The module shows under company form both their own messages and the messages of any contacts attached to that company.
Tested on Odoo 10.0 e8b0c1db69f04b0f8391d7ee4848ccf576018bf3

2
res_partner_company_messages/__init__.py

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

17
res_partner_company_messages/__openerp__.py

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
{
'name': "Aggregate messages from company's contacts",
'version': '1.0.0',
'author': 'IT-Projects LLC, Ivan Yelizariev',
'license': 'LGPL-3',
"price": 70.00,
"currency": "EUR",
'category': 'Discuss',
'website': 'https://twitter.com/yelizariev',
'images': ['images/child.png', 'images/parent.png'],
'depends': ['mail'],
'data': [
'views.xml',
],
'installable': True
}

BIN
res_partner_company_messages/images/child.png

After

Width: 812  |  Height: 588  |  Size: 50 KiB

BIN
res_partner_company_messages/images/parent.png

After

Width: 811  |  Height: 583  |  Size: 67 KiB

19
res_partner_company_messages/models.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from openerp import api
from openerp import models
class Partner(models.Model):
_inherit = 'res.partner'
@api.multi
def read(self, fields=None, load='_classic_read'):
res = super(Partner, self).read(fields=fields, load=load)
if fields and 'message_ids' in fields:
for vals in res:
partner = self.browse(vals['id'])
if not partner.is_company:
continue
domain = [('model', '=', 'res.partner'), ('res_id', 'in', [partner.id] + partner.child_ids.ids)]
vals['message_ids'] = self.env['mail.message'].search(domain).ids
return res

BIN
res_partner_company_messages/static/description/icon.png

After

Width: 100  |  Height: 100  |  Size: 3.0 KiB

3
res_partner_company_messages/views.xml

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp><data>
</data></openerp>

14
res_partner_mails_count/models.py

@ -3,17 +3,17 @@
from openerp import models, fields, api from openerp import models, fields, api
class res_partner(models.Model):
class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
mails_to = fields.Integer(compute="_mails_to")
mails_from = fields.Integer(compute="_mails_from")
mails_to = fields.Integer(compute="_compute_mails_to")
mails_from = fields.Integer(compute="_compute_mails_from")
@api.one
def _mails_to(self):
@api.multi
def _compute_mails_to(self):
for r in self: for r in self:
r.mails_to = self.env['mail.message'].sudo().search_count([('partner_ids', 'in', r.id)]) r.mails_to = self.env['mail.message'].sudo().search_count([('partner_ids', 'in', r.id)])
@api.one
def _mails_from(self):
@api.multi
def _compute_mails_from(self):
for r in self: for r in self:
r.mails_from = self.env['mail.message'].sudo().search_count([('author_id', '=', r.id)]) r.mails_from = self.env['mail.message'].sudo().search_count([('author_id', '=', r.id)])

8
res_partner_mails_count/tests/test_phantom.py

@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import openerp.tests
import odoo.tests
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestUi(odoo.tests.HttpCase):
def test_01_res_partner_mails_to_count(self): def test_01_res_partner_mails_to_count(self):
# self.phantom_js('/', "openerp.Tour.run('mails_count_tour', 'test')", "openerp.Tour.tours.mails_count_tour", login="admin") # self.phantom_js('/', "openerp.Tour.run('mails_count_tour', 'test')", "openerp.Tour.tours.mails_count_tour", login="admin")

14
res_partner_strip_email/models.py

@ -4,18 +4,24 @@ from openerp import api
from openerp import models from openerp import models
class res_partner_strip_email(models.Model):
class ResPartnerStripEmail(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
@api.one
@api.multi
def write(self, vals): def write(self, vals):
for r in self:
r.write_one(self, vals)
@api.multi
def write_one(self, vals):
self.ensure_one()
vals = self._check_email_field(vals) vals = self._check_email_field(vals)
return super(res_partner_strip_email, self).write(vals)
return super(ResPartnerStripEmail, self).write(vals)
@api.model @api.model
def create(self, vals): def create(self, vals):
vals = self._check_email_field(vals) vals = self._check_email_field(vals)
return super(res_partner_strip_email, self).create(vals)
return super(ResPartnerStripEmail, self).create(vals)
def _check_email_field(self, vals): def _check_email_field(self, vals):
if vals.get('email'): if vals.get('email'):

Loading…
Cancel
Save