Merge pull request #70 from yelizariev/10.0-merge-839a81d
10.0 merge 839a81d
pull/72/head
-
4.travis.yml
-
4mail_all/doc/changelog.rst
-
2mail_all/tests/__init__.py
-
9mail_all/tests/test_js.py
-
2mail_archives/tests/__init__.py
-
9mail_archives/tests/test_js.py
-
29mail_attachment_popup/README.rst
-
0mail_attachment_popup/__init__.py
-
28mail_attachment_popup/__openerp__.py
-
9mail_attachment_popup/doc/changelog.rst
-
16mail_attachment_popup/doc/index.rst
-
BINmail_attachment_popup/images/popup_image.png
-
BINmail_attachment_popup/static/description/attach_image.png
-
BINmail_attachment_popup/static/description/download.png
-
BINmail_attachment_popup/static/description/icon.png
-
84mail_attachment_popup/static/description/index.html
-
BINmail_attachment_popup/static/description/popup.png
-
429mail_attachment_popup/static/lib/js/jquery.arcticmodal.js
-
8mail_attachment_popup/static/src/css/jquery.arcticmodal.css
-
11mail_attachment_popup/static/src/css/simple.css
-
16mail_attachment_popup/static/src/css/styles.css
-
BINmail_attachment_popup/static/src/img/loading.gif
-
29mail_attachment_popup/static/src/xml/mail_attachment_popup.xml
-
13mail_attachment_popup/views/mail_attachment_popup_template.xml
-
2mail_base/README.rst
-
2mail_base/__init__.py
-
17mail_base/static/src/js/base.js
-
4mail_check_immediately/doc/changelog.rst
-
5mail_fix_553/mail_fix_553.py
-
5mail_fix_empty_body/models.py
-
6mail_fix_header_from/__openerp__.py
-
10mail_move_message/controllers/main.py
-
4mail_move_message/doc/changelog.rst
-
68mail_move_message/mail_move_message_models.py
-
8mail_move_message/static/src/js/mail_move_message.js
-
6mail_outgoing/mail_outgoing_models.py
-
3mail_partner_lang/models.py
-
29mail_private/README.rst
-
3mail_private/__init__.py
-
35mail_private/__openerp__.py
-
7mail_private/doc/changelog.rst
-
26mail_private/doc/index.rst
-
BINmail_private/images/mail_private_image.png
-
11mail_private/models.py
-
BINmail_private/static/description/check_recipients.png
-
BINmail_private/static/description/icon.png
-
67mail_private/static/description/index.html
-
BINmail_private/static/description/result_message.png
-
201mail_private/static/src/js/mail_private.js
-
21mail_private/static/src/xml/mail_private.xml
-
15mail_private/template.xml
-
27mail_private/view.xml
-
4mail_recovery/doc/changelog.rst
-
2mail_recovery/static/src/js/mail_recovery.js
-
4mail_reply/README.rst
-
4mail_reply/doc/changelog.rst
-
4mail_sent/doc/changelog.rst
-
5mail_sent/models.py
-
2mail_sent/tests/__init__.py
-
11mail_sent/tests/test_js.py
-
4mail_to/doc/changelog.rst
-
2mail_to/static/src/js/mail_to.js
-
6mailgun/README.rst
-
4mailgun/__init__.py
-
2mailgun/controllers/__init__.py
-
4mailgun/controllers/main.py
-
4mailgun/doc/changelog.rst
-
2mailgun/models.py
-
16mass_mailing_extra/models.py
-
6res_partner_company_messages/README.rst
-
2res_partner_company_messages/__init__.py
-
17res_partner_company_messages/__openerp__.py
-
BINres_partner_company_messages/images/child.png
-
BINres_partner_company_messages/images/parent.png
-
19res_partner_company_messages/models.py
-
BINres_partner_company_messages/static/description/icon.png
-
3res_partner_company_messages/views.xml
-
14res_partner_mails_count/models.py
-
8res_partner_mails_count/tests/test_phantom.py
-
14res_partner_strip_email/models.py
@ -1,3 +1,3 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
import test_js |
|
||||
|
from . import test_js |
@ -1,3 +1,3 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
import test_js |
|
||||
|
from . import test_js |
@ -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,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, |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
.. _changelog: |
||||
|
|
||||
|
Updates |
||||
|
======= |
||||
|
|
||||
|
`1.0.0` |
||||
|
------- |
||||
|
|
||||
|
- Init version |
@ -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 |
After Width: 749 | Height: 371 | Size: 128 KiB |
After Width: 361 | Height: 382 | Size: 63 KiB |
After Width: 361 | Height: 381 | Size: 131 KiB |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -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> |
After Width: 365 | Height: 382 | Size: 150 KiB |
@ -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); |
@ -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%; } |
@ -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; } |
@ -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; |
||||
|
} |
After Width: 32 | Height: 32 | Size: 3.1 KiB |
@ -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> |
@ -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> |
@ -1,3 +1,3 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
import models |
|
||||
|
from . import models |
@ -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 |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import models |
@ -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, |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
Updates |
||||
|
======= |
||||
|
|
||||
|
`1.0.0` |
||||
|
------- |
||||
|
|
||||
|
- Init version |
@ -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. |
After Width: 766 | Height: 387 | Size: 33 KiB |
@ -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'), |
||||
|
} |
After Width: 751 | Height: 352 | Size: 73 KiB |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -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> |
After Width: 751 | Height: 352 | Size: 92 KiB |
@ -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; |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}; |
@ -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="'"' + widget.parent_thread.parent_message.record_name + '"'"/> |
||||
|
</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> |
@ -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> |
@ -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> |
@ -1,3 +1,3 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
import test_js |
|
||||
|
from . import test_js |
@ -1,3 +1,3 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
import controllers |
|
||||
import models |
|
||||
|
from . import controllers |
||||
|
from . import models |
@ -1,2 +1,2 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
import main |
|
||||
|
from . import main |
@ -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 |
@ -0,0 +1,2 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import models |
@ -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 |
||||
|
} |
After Width: 812 | Height: 588 | Size: 50 KiB |
After Width: 811 | Height: 583 | Size: 67 KiB |
@ -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 |
After Width: 100 | Height: 100 | Size: 3.0 KiB |
@ -0,0 +1,3 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp><data> |
||||
|
</data></openerp> |