[IMP] mail_tracking: Failed Messages to 12.0
This commit is contained in:
parent
5ec699ab9a
commit
eba1e0de8a
@ -52,44 +52,28 @@ status icon will appear just right to name of notified partner.
|
|||||||
|
|
||||||
These are all available status icons:
|
These are all available status icons:
|
||||||
|
|
||||||
<<<<<<< HEAD
|
.. |sent| image:: ../static/src/img/sent.png
|
||||||
.. |sent| image:: mail_tracking/static/src/img/sent.png
|
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
.. |delivered| image:: mail_tracking/static/src/img/delivered.png
|
.. |delivered| image:: ../static/src/img/delivered.png
|
||||||
:width: 15px
|
:width: 15px
|
||||||
|
|
||||||
.. |opened| image:: mail_tracking/static/src/img/opened.png
|
.. |opened| image:: ../static/src/img/opened.png
|
||||||
:width: 15px
|
:width: 15px
|
||||||
|
|
||||||
.. |error| image:: mail_tracking/static/src/img/error.png
|
.. |error| image:: ../static/src/img/error.png
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
.. |waiting| image:: mail_tracking/static/src/img/waiting.png
|
.. |waiting| image:: ../static/src/img/waiting.png
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
.. |unknown| image:: mail_tracking/static/src/img/unknown.png
|
.. |unknown| image:: ../static/src/img/unknown.png
|
||||||
=======
|
|
||||||
.. |sent| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/sent.png
|
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
.. |delivered| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/delivered.png
|
.. |cc| image:: ../static/src/img/cc.png
|
||||||
:width: 15px
|
|
||||||
|
|
||||||
.. |opened| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/opened.png
|
|
||||||
:width: 15px
|
|
||||||
|
|
||||||
.. |error| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/error.png
|
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
.. |waiting| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/waiting.png
|
.. |noemail| image:: ../static/src/img/no_email.png
|
||||||
:width: 10px
|
|
||||||
|
|
||||||
.. |unknown| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/unknown.png
|
|
||||||
>>>>>>> 75b9662... [IMP] mail_tracking: Failed Messages (Discuss & View)
|
|
||||||
:width: 10px
|
|
||||||
|
|
||||||
.. |cc| image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/cc.png
|
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|
||||||
@ -106,34 +90,37 @@ These are all available status icons:
|
|||||||
|
|
||||||
|cc| **Cc**: It's a Carbon-Copy recipient. Can't know the status so is 'Unknown'
|
|cc| **Cc**: It's a Carbon-Copy recipient. Can't know the status so is 'Unknown'
|
||||||
|
|
||||||
|
|noemail| **No Email**: The partner doesn't have a defined email
|
||||||
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
If you want to see all tracking emails and events you can go to
|
If you want to see all tracking emails and events you can go to
|
||||||
|
|
||||||
* Settings > Technical > Email > Tracking emails
|
* Settings > Technical > Email > Tracking emails
|
||||||
* Settings > Technical > Email > Tracking events
|
* Settings > Technical > Email > Tracking events
|
||||||
=======
|
|
||||||
When the message generates and 'error' status, it will apear on discuss 'Failed'
|
When the message generates an 'error' status, it will apear on discuss 'Failed'
|
||||||
channel. Any view that uses 'mail_thread' widget can show the failed messages
|
channel. Any view with chatter can show the failed messages
|
||||||
too.
|
too.
|
||||||
|
|
||||||
* Discuss
|
* Discuss
|
||||||
|
|
||||||
.. image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_discuss.png
|
.. image:: https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_discuss.png
|
||||||
|
|
||||||
* Chatter
|
* Chatter
|
||||||
|
|
||||||
.. image:: https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_widget.png
|
.. image:: https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_widget.png
|
||||||
|
|
||||||
|
You can use "Failed sent messages" filter present in all views to show records
|
||||||
|
with messages in failed status and that needs an user action.
|
||||||
|
|
||||||
|
* Filter
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_filter.png
|
||||||
|
|
||||||
Known issues / Roadmap
|
Known issues / Roadmap
|
||||||
======================
|
======================
|
||||||
|
|
||||||
* Handle message updates on discuss 'channel_failed' instead of showing the
|
|
||||||
'outdated' message.
|
|
||||||
* Adapt chat_manager changes in v12
|
|
||||||
* Adapt discuss changes in v12
|
|
||||||
* Add pivot for tracking events and mail trackings
|
* Add pivot for tracking events and mail trackings
|
||||||
>>>>>>> 75b9662... [IMP] mail_tracking: Failed Messages (Discuss & View)
|
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
|
@ -28,12 +28,12 @@
|
|||||||
"views/mail_tracking_event_view.xml",
|
"views/mail_tracking_event_view.xml",
|
||||||
"views/mail_message_view.xml",
|
"views/mail_message_view.xml",
|
||||||
"views/res_partner_view.xml",
|
"views/res_partner_view.xml",
|
||||||
"wizard/mail_compose_message_view.xml",
|
|
||||||
],
|
],
|
||||||
"qweb": [
|
"qweb": [
|
||||||
"static/src/xml/mail_tracking.xml",
|
"static/src/xml/mail_tracking.xml",
|
||||||
"static/src/xml/failed_message.xml",
|
"static/src/xml/failed_message/common.xml",
|
||||||
"static/src/xml/client_action.xml",
|
"static/src/xml/failed_message/thread.xml",
|
||||||
|
"static/src/xml/failed_message/discuss.xml",
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
'demo/demo.xml',
|
'demo/demo.xml',
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
import werkzeug
|
import werkzeug
|
||||||
from psycopg2 import OperationalError
|
import odoo
|
||||||
from odoo import api, http, registry, SUPERUSER_ID, _
|
from contextlib import contextmanager
|
||||||
|
from odoo import api, http, SUPERUSER_ID
|
||||||
|
|
||||||
from odoo.addons.mail.controllers.main import MailController
|
from odoo.addons.mail.controllers.main import MailController
|
||||||
from odoo.http import request
|
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@ -13,35 +14,23 @@ _logger = logging.getLogger(__name__)
|
|||||||
BLANK = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
|
BLANK = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
|
||||||
|
|
||||||
|
|
||||||
def _env_get(db, callback, tracking_id, event_type, **kw):
|
@contextmanager
|
||||||
res = 'NOT FOUND'
|
def db_env(dbname):
|
||||||
reg = False
|
if not http.db_filter([dbname]):
|
||||||
current = http.request.db and db == http.request.db
|
raise werkzeug.exceptions.BadRequest()
|
||||||
env = current and http.request.env
|
cr = None
|
||||||
if not env:
|
if dbname == http.request.db:
|
||||||
|
cr = http.request.cr
|
||||||
|
if not cr:
|
||||||
|
cr = odoo.sql_db.db_connect(dbname).cursor()
|
||||||
with api.Environment.manage():
|
with api.Environment.manage():
|
||||||
try:
|
yield api.Environment(cr, SUPERUSER_ID, {})
|
||||||
reg = registry(db)
|
|
||||||
except OperationalError:
|
|
||||||
_logger.warning("Selected BD '%s' not found", db)
|
|
||||||
except Exception: # pragma: no cover
|
|
||||||
_logger.warning("Selected BD '%s' connection error", db)
|
|
||||||
if reg:
|
|
||||||
_logger.info("New environment for database '%s'", db)
|
|
||||||
with reg.cursor() as new_cr:
|
|
||||||
new_env = api.Environment(new_cr, SUPERUSER_ID, {})
|
|
||||||
res = callback(new_env, tracking_id, event_type, **kw)
|
|
||||||
new_env.cr.commit()
|
|
||||||
else:
|
|
||||||
# make sudo when reusing environment
|
|
||||||
env = env(user=SUPERUSER_ID)
|
|
||||||
res = callback(env, tracking_id, event_type, **kw)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
class MailTrackingController(MailController):
|
class MailTrackingController(MailController):
|
||||||
|
|
||||||
def _request_metadata(self):
|
def _request_metadata(self):
|
||||||
|
"""Prepare remote info metadata"""
|
||||||
request = http.request.httprequest
|
request = http.request.httprequest
|
||||||
return {
|
return {
|
||||||
'ip': request.remote_addr or False,
|
'ip': request.remote_addr or False,
|
||||||
@ -50,37 +39,45 @@ class MailTrackingController(MailController):
|
|||||||
'ua_family': request.user_agent.browser or False,
|
'ua_family': request.user_agent.browser or False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _tracking_open(self, env, tracking_id, event_type, **kw):
|
@http.route(['/mail/tracking/all/<string:db>',
|
||||||
|
'/mail/tracking/event/<string:db>/<string:event_type>'],
|
||||||
|
type='http', auth='none', csrf=False)
|
||||||
|
def mail_tracking_event(self, db, event_type=None, **kw):
|
||||||
|
"""Route used by external mail service"""
|
||||||
|
metadata = self._request_metadata()
|
||||||
|
res = None
|
||||||
|
with db_env(db) as env:
|
||||||
|
try:
|
||||||
|
res = env['mail.tracking.email'].event_process(
|
||||||
|
http.request, kw, metadata, event_type=event_type)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not res or res == 'NOT FOUND':
|
||||||
|
return werkzeug.exceptions.NotAcceptable()
|
||||||
|
return res
|
||||||
|
|
||||||
|
@http.route(['/mail/tracking/open/<string:db>'
|
||||||
|
'/<int:tracking_email_id>/blank.gif',
|
||||||
|
'/mail/tracking/open/<string:db>'
|
||||||
|
'/<int:tracking_email_id>/<string:token>/blank.gif'],
|
||||||
|
type='http', auth='none', methods=['GET'])
|
||||||
|
def mail_tracking_open(self, db, tracking_email_id, token=False, **kw):
|
||||||
|
"""Route used to track mail openned (With & Without Token)"""
|
||||||
|
metadata = self._request_metadata()
|
||||||
|
with db_env(db) as env:
|
||||||
|
try:
|
||||||
tracking_email = env['mail.tracking.email'].search([
|
tracking_email = env['mail.tracking.email'].search([
|
||||||
('id', '=', tracking_id),
|
('id', '=', tracking_email_id),
|
||||||
|
('state', 'in', ['sent', 'delivered']),
|
||||||
|
('token', '=', token),
|
||||||
])
|
])
|
||||||
if tracking_email:
|
if tracking_email:
|
||||||
metadata = self._request_metadata()
|
|
||||||
tracking_email.event_create('open', metadata)
|
tracking_email.event_create('open', metadata)
|
||||||
else:
|
else:
|
||||||
_logger.warning(
|
_logger.warning(
|
||||||
"MailTracking email '%s' not found", tracking_id)
|
"MailTracking email '%s' not found", tracking_email_id)
|
||||||
|
except Exception:
|
||||||
def _tracking_event(self, env, tracking_id, event_type, **kw):
|
pass
|
||||||
metadata = self._request_metadata()
|
|
||||||
return env['mail.tracking.email'].event_process(
|
|
||||||
http.request, kw, metadata, event_type=event_type)
|
|
||||||
|
|
||||||
@http.route('/mail/tracking/all/<string:db>',
|
|
||||||
type='http', auth='none', csrf=False)
|
|
||||||
def mail_tracking_all(self, db, **kw):
|
|
||||||
return _env_get(db, self._tracking_event, None, None, **kw)
|
|
||||||
|
|
||||||
@http.route('/mail/tracking/event/<string:db>/<string:event_type>',
|
|
||||||
type='http', auth='none', csrf=False)
|
|
||||||
def mail_tracking_event(self, db, event_type, **kw):
|
|
||||||
return _env_get(db, self._tracking_event, None, event_type, **kw)
|
|
||||||
|
|
||||||
@http.route('/mail/tracking/open/<string:db>'
|
|
||||||
'/<int:tracking_email_id>/blank.gif',
|
|
||||||
type='http', auth='none')
|
|
||||||
def mail_tracking_open(self, db, tracking_email_id, **kw):
|
|
||||||
_env_get(db, self._tracking_open, tracking_email_id, None, **kw)
|
|
||||||
|
|
||||||
# Always return GIF blank image
|
# Always return GIF blank image
|
||||||
response = werkzeug.wrappers.Response()
|
response = werkzeug.wrappers.Response()
|
||||||
@ -89,20 +86,11 @@ class MailTrackingController(MailController):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@http.route()
|
@http.route()
|
||||||
def mail_client_action(self):
|
def mail_init_messaging(self):
|
||||||
values = super().mail_client_action()
|
"""Route used to initial values of Discuss app"""
|
||||||
values['channel_slots']['channel_channel'].append({
|
values = super().mail_init_messaging()
|
||||||
'id': 'channel_failed',
|
|
||||||
'name': _("Failed"),
|
|
||||||
'uuid': None,
|
|
||||||
'state': 'open',
|
|
||||||
'is_minimized': False,
|
|
||||||
'channel_type': 'static',
|
|
||||||
'public': False,
|
|
||||||
'mass_mailing': None,
|
|
||||||
'group_based_subscription': None,
|
|
||||||
})
|
|
||||||
values.update({
|
values.update({
|
||||||
'failed_counter': request.env['mail.message'].get_failed_count(),
|
'failed_counter':
|
||||||
|
http.request.env['mail.message'].get_failed_count(),
|
||||||
})
|
})
|
||||||
return values
|
return values
|
||||||
|
@ -2,6 +2,31 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="0">
|
<data noupdate="0">
|
||||||
|
|
||||||
|
<!-- Message with CC -->
|
||||||
|
<record id="mail_message_cc" model="mail.message">
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="res_id" ref="base.partner_demo" />
|
||||||
|
<field name="message_type">comment</field>
|
||||||
|
<field name="subtype_id" ref="mail.mt_comment" />
|
||||||
|
<field name="email_cc">acc@testmail.com,wood.corner26@example.com,toni.rhodes11@example.com</field>
|
||||||
|
<field name="mail_tracking_needs_action">1</field>
|
||||||
|
<field name="body"><![CDATA[<p>This is a message with CC</p>]]></field>
|
||||||
|
<field name="email_from">wood.corner26@example.com</field>
|
||||||
|
<field name="author_id" ref="base.res_partner_1" />
|
||||||
|
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||||
|
<field name="subject">Message with CC</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="mail_tracking_email_cc" model="mail.tracking.email">
|
||||||
|
<field name="name">Message with CC</field>
|
||||||
|
<field name="mail_message_id" ref="mail_message_cc" />
|
||||||
|
<field name="partner_id" ref="base.res_partner_1" />
|
||||||
|
<field name="recipient">demo@yourcompany.example.com</field>
|
||||||
|
<field name="sender">wood.corner26@example.com</field>
|
||||||
|
<field name="state">sent</field>
|
||||||
|
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
<!-- Failed Message A -->
|
<!-- Failed Message A -->
|
||||||
<record id="mail_message_failed" model="mail.message">
|
<record id="mail_message_failed" model="mail.message">
|
||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
@ -10,7 +35,7 @@
|
|||||||
<field name="subtype_id" ref="mail.mt_comment" />
|
<field name="subtype_id" ref="mail.mt_comment" />
|
||||||
<field name="mail_tracking_needs_action">1</field>
|
<field name="mail_tracking_needs_action">1</field>
|
||||||
<field name="body"><![CDATA[<p>This is a failed message</p>]]></field>
|
<field name="body"><![CDATA[<p>This is a failed message</p>]]></field>
|
||||||
<field name="email_from">res1@yourcompany.example.com</field>
|
<field name="email_from">wood.corner26@example.com</field>
|
||||||
<field name="author_id" ref="base.res_partner_1" />
|
<field name="author_id" ref="base.res_partner_1" />
|
||||||
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||||
<field name="subject">Failed Message</field>
|
<field name="subject">Failed Message</field>
|
||||||
@ -20,8 +45,8 @@
|
|||||||
<field name="name">Failed Message</field>
|
<field name="name">Failed Message</field>
|
||||||
<field name="mail_message_id" ref="mail_message_failed" />
|
<field name="mail_message_id" ref="mail_message_failed" />
|
||||||
<field name="partner_id" ref="base.res_partner_1" />
|
<field name="partner_id" ref="base.res_partner_1" />
|
||||||
<field name="recipient">res1@yourcompany.example.com</field>
|
<field name="recipient">demo@yourcompany.example.com</field>
|
||||||
<field name="sender">demo@yourcompany.example.com</field>
|
<field name="sender">wood.corner26@example.com</field>
|
||||||
<field name="state">error</field>
|
<field name="state">error</field>
|
||||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||||
</record>
|
</record>
|
||||||
@ -34,7 +59,7 @@
|
|||||||
<field name="subtype_id" ref="mail.mt_comment" />
|
<field name="subtype_id" ref="mail.mt_comment" />
|
||||||
<field name="mail_tracking_needs_action">1</field>
|
<field name="mail_tracking_needs_action">1</field>
|
||||||
<field name="body"><![CDATA[<p>This is another failed message</p>]]></field>
|
<field name="body"><![CDATA[<p>This is another failed message</p>]]></field>
|
||||||
<field name="email_from">res10@yourcompany.example.com</field>
|
<field name="email_from">jackson.group82@example.com</field>
|
||||||
<field name="author_id" ref="base.res_partner_10" />
|
<field name="author_id" ref="base.res_partner_10" />
|
||||||
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||||
<field name="subject">Failed Message</field>
|
<field name="subject">Failed Message</field>
|
||||||
@ -44,8 +69,32 @@
|
|||||||
<field name="name">Failed Message</field>
|
<field name="name">Failed Message</field>
|
||||||
<field name="mail_message_id" ref="mail_message_failed_b" />
|
<field name="mail_message_id" ref="mail_message_failed_b" />
|
||||||
<field name="partner_id" ref="base.res_partner_10" />
|
<field name="partner_id" ref="base.res_partner_10" />
|
||||||
<field name="recipient">res10@yourcompany.example.com</field>
|
<field name="recipient">demo@yourcompany.example.com</field>
|
||||||
<field name="sender">demo@yourcompany.example.com</field>
|
<field name="sender">jackson.group82@example.com</field>
|
||||||
|
<field name="state">error</field>
|
||||||
|
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Failed Message C -->
|
||||||
|
<record id="mail_message_failed_c" model="mail.message">
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="res_id" ref="base.partner_demo" />
|
||||||
|
<field name="message_type">comment</field>
|
||||||
|
<field name="subtype_id" ref="mail.mt_comment" />
|
||||||
|
<field name="mail_tracking_needs_action">1</field>
|
||||||
|
<field name="body"><![CDATA[<p>This is another failed message</p>]]></field>
|
||||||
|
<field name="email_from">admin@example.com</field>
|
||||||
|
<field name="author_id" ref="base.partner_admin" />
|
||||||
|
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||||
|
<field name="subject">Failed Message</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="mail_tracking_email_failed_c" model="mail.tracking.email">
|
||||||
|
<field name="name">Failed Message</field>
|
||||||
|
<field name="mail_message_id" ref="mail_message_failed_c" />
|
||||||
|
<field name="partner_id" ref="base.partner_admin" />
|
||||||
|
<field name="recipient">demo@yourcompany.example.com</field>
|
||||||
|
<field name="sender">admin@example.com</field>
|
||||||
<field name="state">error</field>
|
<field name="state">error</field>
|
||||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -2,22 +2,20 @@
|
|||||||
# This file contains the translation of the following modules:
|
# This file contains the translation of the following modules:
|
||||||
# * mail_tracking
|
# * mail_tracking
|
||||||
#
|
#
|
||||||
# Translators:
|
|
||||||
# OCA Transbot <transbot@odoo-community.org>, 2017
|
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 10.0\n"
|
"Project-Id-Version: Odoo Server 12.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-12-01 02:19+0000\n"
|
"POT-Creation-Date: 2019-11-12 17:22+0000\n"
|
||||||
"PO-Revision-Date: 2019-08-04 17:44+0000\n"
|
"PO-Revision-Date: 2019-11-12 18:26+0100\n"
|
||||||
"Last-Translator: eduardgm <eduard.garcia@qubiq.es>\n"
|
"Last-Translator: <>\n"
|
||||||
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n"
|
"Language-Team: \n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: \n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
"Plural-Forms: \n"
|
||||||
"X-Generator: Weblate 3.7.1\n"
|
"X-Generator: Poedit 2.2.4\n"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,help:mail_tracking.field_mail_tracking_email__state
|
#: model:ir.model.fields,help:mail_tracking.field_mail_tracking_email__state
|
||||||
@ -43,11 +41,18 @@ msgid ""
|
|||||||
"recipient Mail Exchange (MX) server.\n"
|
"recipient Mail Exchange (MX) server.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:189
|
||||||
|
#, python-format
|
||||||
|
msgid "-Unknown Author-"
|
||||||
|
msgstr "-Autor Desconocido-"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__email_cc
|
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__email_cc
|
||||||
#: model:ir.model.fields,help:mail_tracking.field_mail_message__email_cc
|
#: model:ir.model.fields,help:mail_tracking.field_mail_message__email_cc
|
||||||
msgid "Additional recipients that receive a \"Carbon Copy\" of the e-mail"
|
msgid "Additional recipients that receive a \"Carbon Copy\" of the e-mail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Destinatarios adicionales que reciben una \"Copia de Carbón\" del correo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
@ -70,8 +75,8 @@ msgid "Bounced"
|
|||||||
msgstr "Rebotado"
|
msgstr "Rebotado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: code:addons/mail_tracking/models/mail_thread.py:39
|
#: code:addons/mail_tracking/models/mail_thread.py:64
|
||||||
#: code:addons/mail_tracking/models/mail_thread.py:43
|
#: code:addons/mail_tracking/models/mail_thread.py:68
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__email_cc
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__email_cc
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__email_cc
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__email_cc
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -93,6 +98,20 @@ msgstr "Clicado"
|
|||||||
msgid "Clicked URL"
|
msgid "Clicked URL"
|
||||||
msgstr "URL Clicada"
|
msgstr "URL Clicada"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:22
|
||||||
|
#, python-format
|
||||||
|
msgid "Congratulations, you don't have any failed messages"
|
||||||
|
msgstr "¡Enhorabuena! No tienes mensajes fallidos"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/js/failed_message/discuss.js:231
|
||||||
|
#, python-format
|
||||||
|
msgid "Congratulations, your failed mailbox is empty"
|
||||||
|
msgstr "¡Enhorabuena! tu buzón de fallidos está vacio"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_res_partner
|
#: model:ir.model,name:mail_tracking.model_res_partner
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
@ -128,21 +147,23 @@ msgstr "Fecha"
|
|||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Deferral"
|
msgid "Deferral"
|
||||||
msgstr ""
|
msgstr "Aplazamiento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
msgid "Deferred"
|
msgid "Deferred"
|
||||||
msgstr ""
|
msgstr "Diferido"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:74
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
|
#, python-format
|
||||||
msgid "Delivered"
|
msgid "Delivered"
|
||||||
msgstr ""
|
msgstr "Entregado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__display_name
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__display_name
|
||||||
@ -165,22 +186,19 @@ msgstr "Correo electrónico"
|
|||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__email_bounced
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__email_bounced
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__email_bounced
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__email_bounced
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__email_bounced
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__email_bounced
|
||||||
#, fuzzy
|
|
||||||
msgid "Email Bounced"
|
msgid "Email Bounced"
|
||||||
msgstr "E mail rebotado"
|
msgstr "Correo rebotado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__email_score
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__email_score
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__email_score
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__email_score
|
||||||
#, fuzzy
|
|
||||||
msgid "Email Score"
|
msgid "Email Score"
|
||||||
msgstr "Reputación del email"
|
msgstr "Reputación del correo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_thread
|
#: model:ir.model,name:mail_tracking.model_mail_thread
|
||||||
#, fuzzy
|
|
||||||
msgid "Email Thread"
|
msgid "Email Thread"
|
||||||
msgstr "Reputación del email"
|
msgstr "Hilo de mensajes"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_res_partner_filter
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_res_partner_filter
|
||||||
@ -188,71 +206,131 @@ msgid "Email bounced"
|
|||||||
msgstr "Email rebotado"
|
msgstr "Email rebotado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model,name:mail_tracking.model_mail_resend_message
|
||||||
|
msgid "Email resend wizard"
|
||||||
|
msgstr "Asistente de reenvio de correo electrónico"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:73
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
|
#, python-format
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_smtp_server
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_smtp_server
|
||||||
msgid "Error SMTP server"
|
msgid "Error SMTP server"
|
||||||
msgstr ""
|
msgstr "Error servidor SMTP"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_description
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_description
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_description
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_description
|
||||||
msgid "Error description"
|
msgid "Error description"
|
||||||
msgstr ""
|
msgstr "Descripción del error"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_details
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_details
|
||||||
msgid "Error details"
|
msgid "Error details"
|
||||||
msgstr ""
|
msgstr "Detalles del error"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_type
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__error_type
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_type
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__error_type
|
||||||
msgid "Error type"
|
msgid "Error type"
|
||||||
msgstr ""
|
msgstr "Tipo de error"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__event_type
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__event_type
|
||||||
msgid "Event type"
|
msgid "Event type"
|
||||||
msgstr ""
|
msgstr "Tipo de evento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/js/failed_message/discuss.js:350
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:13
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:21
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
|
#, python-format
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr ""
|
msgstr "Fallido"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_account_analytic_account__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_calendar_event__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_hr_department__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_hr_employee__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_hr_job__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_blacklist__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_channel__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_thread__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__failed_message_ids
|
||||||
|
msgid "Failed Messages"
|
||||||
|
msgstr "Mensajes Fallidos"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:39
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed Recipients:"
|
||||||
|
msgstr "Destinatarios Fallidos:"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:7
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed messages"
|
||||||
|
msgstr "Mensajes fallidos"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:23
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed messages appear here."
|
||||||
|
msgstr "Los mensajes fallidos se muestran aquí."
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_thread.py:92
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed sent messages"
|
||||||
|
msgstr "Mensajes enviados fallidos"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Group By"
|
msgid "Group By"
|
||||||
msgstr ""
|
msgstr "Agrupar por"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Hard bounce"
|
msgid "Hard bounce"
|
||||||
msgstr ""
|
msgstr "Rebote duro"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__id
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin__id
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__id
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__id
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__id
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__id
|
||||||
msgid "ID"
|
msgid "ID"
|
||||||
msgstr "ID"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "IP"
|
msgid "IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__is_failed_message
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__is_failed_message
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__is_failed_message
|
||||||
|
msgid "Is Failed Message"
|
||||||
|
msgstr "Es un Mensajes Fallido"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__mobile
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__mobile
|
||||||
msgid "Is mobile?"
|
msgid "Is mobile?"
|
||||||
msgstr ""
|
msgstr "Es móvil?"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin____last_update
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_bounced_mixin____last_update
|
||||||
@ -275,49 +353,68 @@ msgstr "Última actualización en"
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_ir_mail_server
|
#: model:ir.model,name:mail_tracking.model_ir_mail_server
|
||||||
#, fuzzy
|
|
||||||
msgid "Mail Server"
|
msgid "Mail Server"
|
||||||
msgstr "ir.mail_server"
|
msgstr "Servidor de correo"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__mail_tracking_needs_action
|
||||||
|
msgid "Mail Tracking Needs Action"
|
||||||
|
msgstr "Mail Tracking necesita acción"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__mail_tracking_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__mail_tracking_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__mail_tracking_ids
|
||||||
|
msgid "Mail Trackings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_bounced_mixin
|
#: model:ir.model,name:mail_tracking.model_mail_bounced_mixin
|
||||||
#, fuzzy
|
|
||||||
msgid "Mail bounced mixin"
|
msgid "Mail bounced mixin"
|
||||||
msgstr "E mail rebotado"
|
msgstr "Correo rebotado mixin"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_tracking_email
|
#: model:ir.model,name:mail_tracking.model_mail_tracking_email
|
||||||
msgid "MailTracking email"
|
msgid "MailTracking email"
|
||||||
msgstr "MailTracking email"
|
msgstr "MailTracking correo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
msgid "MailTracking email search"
|
msgid "MailTracking email search"
|
||||||
msgstr ""
|
msgstr "MailTracking búsqueda de correo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.actions.act_window,name:mail_tracking.action_view_mail_tracking_email
|
#: model:ir.actions.act_window,name:mail_tracking.action_view_mail_tracking_email
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
||||||
msgid "MailTracking emails"
|
msgid "MailTracking emails"
|
||||||
msgstr ""
|
msgstr "MailTracking correos"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_tracking_event
|
#: model:ir.model,name:mail_tracking.model_mail_tracking_event
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_form
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_form
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
||||||
msgid "MailTracking event"
|
msgid "MailTracking event"
|
||||||
msgstr "MailTracking event"
|
msgstr "MailTracking evento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "MailTracking event search"
|
msgid "MailTracking event search"
|
||||||
msgstr ""
|
msgstr "MailTracking búsqueda de eventos"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.actions.act_window,name:mail_tracking.action_view_mail_tracking_event
|
#: model:ir.actions.act_window,name:mail_tracking.action_view_mail_tracking_event
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_tree
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_tree
|
||||||
msgid "MailTracking events"
|
msgid "MailTracking events"
|
||||||
msgstr ""
|
msgstr "MailTracking eventos"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:30
|
||||||
|
#, python-format
|
||||||
|
msgid "Mark all as reviewed"
|
||||||
|
msgstr "Marcar todos como revisado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_message
|
#: model:ir.model,name:mail_tracking.model_mail_message
|
||||||
@ -329,10 +426,10 @@ msgstr "Mensaje"
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:135
|
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:147
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Message tracking"
|
msgid "Message tracking"
|
||||||
msgstr ""
|
msgstr "Seguimiento del mensaje"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
@ -351,17 +448,19 @@ msgstr "SO"
|
|||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Open"
|
msgid "Open"
|
||||||
msgstr ""
|
msgstr "Abrir"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:75
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
|
#, python-format
|
||||||
msgid "Opened"
|
msgid "Opened"
|
||||||
msgstr ""
|
msgstr "Abierto"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__os_family
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__os_family
|
||||||
msgid "Operating system family"
|
msgid "Operating system family"
|
||||||
msgstr ""
|
msgstr "Familia del sistema operativo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_mail
|
#: model:ir.model,name:mail_tracking.model_mail_mail
|
||||||
@ -379,83 +478,127 @@ msgstr "Empresa"
|
|||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Recipient"
|
msgid "Recipient"
|
||||||
msgstr ""
|
msgstr "Destinatario"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__recipient
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__recipient
|
||||||
msgid "Recipient email"
|
msgid "Recipient email"
|
||||||
msgstr ""
|
msgstr "Correo del destinatario"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__recipient_address
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__recipient_address
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__recipient_address
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__recipient_address
|
||||||
msgid "Recipient email address"
|
msgid "Recipient email address"
|
||||||
msgstr ""
|
msgstr "Dirección de correo de destinatario"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Rejected"
|
msgid "Rejected"
|
||||||
msgstr ""
|
msgstr "Rechazado"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/common.xml:10
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:35
|
||||||
|
#, python-format
|
||||||
|
msgid "Retry"
|
||||||
|
msgstr "Reintentar"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__smtp_server
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__smtp_server
|
||||||
msgid "SMTP server"
|
msgid "SMTP server"
|
||||||
msgstr ""
|
msgstr "Servidor SMTP"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__token
|
||||||
|
msgid "Security Token"
|
||||||
|
msgstr "Token de seguridad"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
||||||
msgid "Sender"
|
msgid "Sender"
|
||||||
msgstr ""
|
msgstr "Remitente"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__sender
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__sender
|
||||||
msgid "Sender email"
|
msgid "Sender email"
|
||||||
msgstr ""
|
msgstr "Correo del remitente"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:74
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
|
#, python-format
|
||||||
msgid "Sent"
|
msgid "Sent"
|
||||||
msgstr ""
|
msgstr "Enviado"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:30
|
||||||
|
#, python-format
|
||||||
|
msgid "Set all as reviewed"
|
||||||
|
msgstr "Marcar todos como revisados"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/common.xml:7
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:32
|
||||||
|
#, python-format
|
||||||
|
msgid "Set as Reviewed"
|
||||||
|
msgstr "Marcar como Revisado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Soft bounce"
|
msgid "Soft bounce"
|
||||||
msgstr ""
|
msgstr "Rebote débil"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
msgid "Soft bounced"
|
msgid "Soft bounced"
|
||||||
msgstr ""
|
msgstr "Rebotado débil"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Spam"
|
msgid "Spam"
|
||||||
msgstr ""
|
msgstr "No deseado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__state
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__state
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
msgid "State"
|
msgid "State"
|
||||||
msgstr ""
|
msgstr "Estado"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#: code:addons/mail_tracking/models/mail_message.py:76
|
||||||
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:96
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Status: unknown"
|
msgid "Status: %s"
|
||||||
msgstr ""
|
msgstr "Estado: %s"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__name
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__name
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
msgid "Subject"
|
msgid "Subject"
|
||||||
|
msgstr "Asunto"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_mail__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_message__mail_tracking_needs_action
|
||||||
|
msgid "The message tracking will be considered to filter tracking issues"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"El seguimiento del correo puede ser considerado para filtrar incidencia de "
|
||||||
|
"seguimiento"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:82
|
||||||
|
#, python-format
|
||||||
|
msgid "The partner doesn't have a defined email"
|
||||||
|
msgstr "El socio no tiene un correo definido"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__time
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__time
|
||||||
@ -463,27 +606,26 @@ msgstr ""
|
|||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Time"
|
msgid "Time"
|
||||||
msgstr ""
|
msgstr "Tiempo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:53
|
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:54
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "To:"
|
msgid "To:"
|
||||||
msgstr ""
|
msgstr "Para:"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__tracking_emails_count
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__tracking_emails_count
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__tracking_emails_count
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__tracking_emails_count
|
||||||
#, fuzzy
|
|
||||||
msgid "Tracking Emails Count"
|
msgid "Tracking Emails Count"
|
||||||
msgstr "MailTracking email"
|
msgstr "Contador de correos con seguimiento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.ui.menu,name:mail_tracking.menu_mail_tracking_email
|
#: model:ir.ui.menu,name:mail_tracking.menu_mail_tracking_email
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_partner_form
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_partner_form
|
||||||
msgid "Tracking emails"
|
msgid "Tracking emails"
|
||||||
msgstr ""
|
msgstr "Correos con seguimiento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__tracking_event_ids
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__tracking_event_ids
|
||||||
@ -494,15 +636,15 @@ msgstr "Eventos de seguimiento"
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:115
|
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:127
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Tracking partner"
|
msgid "Tracking partner"
|
||||||
msgstr ""
|
msgstr "Socio del seguimiento"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr ""
|
msgstr "Tipo"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
@ -515,22 +657,28 @@ msgstr ""
|
|||||||
msgid "UTC timestamp"
|
msgid "UTC timestamp"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:75
|
||||||
|
#, python-format
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Desconocido"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Unsubscribe"
|
msgid "Unsubscribe"
|
||||||
msgstr ""
|
msgstr "Darse de baja"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Unsubscribed"
|
msgid "Unsubscribed"
|
||||||
msgstr ""
|
msgstr "Dado de baja"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__ip
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__ip
|
||||||
msgid "User IP"
|
msgid "User IP"
|
||||||
msgstr ""
|
msgstr "IP del usuario"
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__user_agent
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__user_agent
|
||||||
@ -554,4 +702,10 @@ msgstr ""
|
|||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__user_country_id
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__user_country_id
|
||||||
msgid "User country"
|
msgid "User country"
|
||||||
msgstr ""
|
msgstr "País del Usuario"
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:73
|
||||||
|
#, python-format
|
||||||
|
msgid "Waiting"
|
||||||
|
msgstr "Esperando"
|
||||||
|
@ -27,6 +27,12 @@ msgid " * The 'Error' status indicates that there was an error when trying to se
|
|||||||
""
|
""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:189
|
||||||
|
#, python-format
|
||||||
|
msgid "-Unknown Author-"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__email_cc
|
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__email_cc
|
||||||
#: model:ir.model.fields,help:mail_tracking.field_mail_message__email_cc
|
#: model:ir.model.fields,help:mail_tracking.field_mail_message__email_cc
|
||||||
@ -54,8 +60,8 @@ msgid "Bounced"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: code:addons/mail_tracking/models/mail_thread.py:39
|
#: code:addons/mail_tracking/models/mail_thread.py:64
|
||||||
#: code:addons/mail_tracking/models/mail_thread.py:43
|
#: code:addons/mail_tracking/models/mail_thread.py:68
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__email_cc
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__email_cc
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__email_cc
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__email_cc
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -77,6 +83,20 @@ msgstr ""
|
|||||||
msgid "Clicked URL"
|
msgid "Clicked URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:22
|
||||||
|
#, python-format
|
||||||
|
msgid "Congratulations, you don't have any failed messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/js/failed_message/discuss.js:231
|
||||||
|
#, python-format
|
||||||
|
msgid "Congratulations, your failed mailbox is empty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_res_partner
|
#: model:ir.model,name:mail_tracking.model_res_partner
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
@ -121,10 +141,12 @@ msgid "Deferred"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:74
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
|
#, python-format
|
||||||
msgid "Delivered"
|
msgid "Delivered"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -169,8 +191,15 @@ msgid "Email bounced"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model,name:mail_tracking.model_mail_resend_message
|
||||||
|
msgid "Email resend wizard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:73
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_form
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
|
#, python-format
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -202,11 +231,54 @@ msgid "Event type"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/js/failed_message/discuss.js:350
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:13
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:21
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
|
#, python-format
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_account_analytic_account__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_calendar_event__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_blacklist__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_channel__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_thread__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_partner__failed_message_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_res_users__failed_message_ids
|
||||||
|
msgid "Failed Messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:39
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed Recipients:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:7
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:23
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed messages appear here."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_thread.py:92
|
||||||
|
#, python-format
|
||||||
|
msgid "Failed sent messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
@ -230,6 +302,13 @@ msgstr ""
|
|||||||
msgid "IP"
|
msgid "IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__is_failed_message
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__is_failed_message
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__is_failed_message
|
||||||
|
msgid "Is Failed Message"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__mobile
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__mobile
|
||||||
msgid "Is mobile?"
|
msgid "Is mobile?"
|
||||||
@ -259,6 +338,20 @@ msgstr ""
|
|||||||
msgid "Mail Server"
|
msgid "Mail Server"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__mail_tracking_needs_action
|
||||||
|
msgid "Mail Tracking Needs Action"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_compose_message__mail_tracking_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_mail__mail_tracking_ids
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_message__mail_tracking_ids
|
||||||
|
msgid "Mail Trackings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_bounced_mixin
|
#: model:ir.model,name:mail_tracking.model_mail_bounced_mixin
|
||||||
msgid "Mail bounced mixin"
|
msgid "Mail bounced mixin"
|
||||||
@ -298,6 +391,13 @@ msgstr ""
|
|||||||
msgid "MailTracking events"
|
msgid "MailTracking events"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:30
|
||||||
|
#, python-format
|
||||||
|
msgid "Mark all as reviewed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model,name:mail_tracking.model_mail_message
|
#: model:ir.model,name:mail_tracking.model_mail_message
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__mail_message_id
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__mail_message_id
|
||||||
@ -308,7 +408,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:135
|
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:147
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Message tracking"
|
msgid "Message tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -333,7 +433,9 @@ msgid "Open"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:75
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
|
#, python-format
|
||||||
msgid "Opened"
|
msgid "Opened"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -377,11 +479,24 @@ msgstr ""
|
|||||||
msgid "Rejected"
|
msgid "Rejected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/common.xml:10
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:35
|
||||||
|
#, python-format
|
||||||
|
msgid "Retry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__smtp_server
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__smtp_server
|
||||||
msgid "SMTP server"
|
msgid "SMTP server"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__token
|
||||||
|
msgid "Security Token"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_tree
|
||||||
@ -394,13 +509,30 @@ msgid "Sender email"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:74
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_email_search
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
#: selection:mail.tracking.email,state:0
|
#: selection:mail.tracking.email,state:0
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
|
#, python-format
|
||||||
msgid "Sent"
|
msgid "Sent"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/discuss.xml:30
|
||||||
|
#, python-format
|
||||||
|
msgid "Set all as reviewed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/common.xml:7
|
||||||
|
#: code:addons/mail_tracking/static/src/xml/failed_message/thread.xml:32
|
||||||
|
#, python-format
|
||||||
|
msgid "Set as Reviewed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: selection:mail.tracking.event,event_type:0
|
#: selection:mail.tracking.event,event_type:0
|
||||||
msgid "Soft bounce"
|
msgid "Soft bounce"
|
||||||
@ -424,10 +556,9 @@ msgid "State"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#: code:addons/mail_tracking/models/mail_message.py:76
|
||||||
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:96
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Status: unknown"
|
msgid "Status: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
@ -436,6 +567,19 @@ msgstr ""
|
|||||||
msgid "Subject"
|
msgid "Subject"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_compose_message__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_mail__mail_tracking_needs_action
|
||||||
|
#: model:ir.model.fields,help:mail_tracking.field_mail_message__mail_tracking_needs_action
|
||||||
|
msgid "The message tracking will be considered to filter tracking issues"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:82
|
||||||
|
#, python-format
|
||||||
|
msgid "The partner doesn't have a defined email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__time
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__time
|
||||||
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__time
|
#: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_event__time
|
||||||
@ -446,7 +590,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:53
|
#: code:addons/mail_tracking/static/src/xml/mail_tracking.xml:54
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "To:"
|
msgid "To:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -472,7 +616,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:115
|
#: code:addons/mail_tracking/static/src/js/mail_tracking.js:127
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Tracking partner"
|
msgid "Tracking partner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -493,6 +637,12 @@ msgstr ""
|
|||||||
msgid "UTC timestamp"
|
msgid "UTC timestamp"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:75
|
||||||
|
#, python-format
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: mail_tracking
|
#. module: mail_tracking
|
||||||
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
#: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search
|
||||||
msgid "Unsubscribe"
|
msgid "Unsubscribe"
|
||||||
@ -534,3 +684,9 @@ msgstr ""
|
|||||||
msgid "User country"
|
msgid "User country"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mail_tracking
|
||||||
|
#: code:addons/mail_tracking/models/mail_message.py:73
|
||||||
|
#, python-format
|
||||||
|
msgid "Waiting"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
11
mail_tracking/migrations/12.0.2.0.0/post-migrate.py
Normal file
11
mail_tracking/migrations/12.0.2.0.0/post-migrate.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Copyright 2019 Alexandre Díaz
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
|
||||||
|
from openupgradelib.openupgrade import migrate
|
||||||
|
|
||||||
|
|
||||||
|
@migrate()
|
||||||
|
def migrate(env, version):
|
||||||
|
cr = env.cr
|
||||||
|
cr.execute("UPDATE mail_tracking_email SET token = NULL")
|
@ -6,6 +6,6 @@ from . import mail_mail
|
|||||||
from . import mail_message
|
from . import mail_message
|
||||||
from . import mail_tracking_email
|
from . import mail_tracking_email
|
||||||
from . import mail_tracking_event
|
from . import mail_tracking_event
|
||||||
from . import mail_composer
|
|
||||||
from . import res_partner
|
from . import res_partner
|
||||||
from . import mail_thread
|
from . import mail_thread
|
||||||
|
from . import mail_resend_message
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
# Copyright 2018 Tecnativa - Ernesto Tejeda
|
# Copyright 2018 Tecnativa - Ernesto Tejeda
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class MailBouncedMixin(models.AbstractModel):
|
class MailBouncedMixin(models.AbstractModel):
|
||||||
""" A mixin class to use if you want to add is_bounced flag on a model.
|
""" A mixin class to use if you want to add is_bounced flag on a model.
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
# Copyright 2019 Alexandre Díaz
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|
||||||
|
|
||||||
from odoo import models, fields, api
|
|
||||||
|
|
||||||
|
|
||||||
class MailComposer(models.TransientModel):
|
|
||||||
_inherit = 'mail.compose.message'
|
|
||||||
|
|
||||||
hide_followers = fields.Boolean(string="Hide follower message",
|
|
||||||
default=False)
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def send_mail(self, auto_commit=False):
|
|
||||||
""" This method marks as reviewed the message when using the 'Retry'
|
|
||||||
option in the mail_failed_message widget"""
|
|
||||||
message = self.env['mail.message'].browse(
|
|
||||||
self._context.get('message_id'))
|
|
||||||
if message.exists():
|
|
||||||
message.mail_tracking_needs_action = False
|
|
||||||
return super().send_mail(auto_commit=auto_commit)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def get_record_data(self, values):
|
|
||||||
values = super(MailComposer, self).get_record_data(values)
|
|
||||||
if self._context.get('default_hide_followers', False):
|
|
||||||
values['partner_ids'] = [
|
|
||||||
(6, 0, self._context.get('default_partner_ids', list()))
|
|
||||||
]
|
|
||||||
return values
|
|
@ -12,6 +12,7 @@ class MailMail(models.Model):
|
|||||||
_inherit = 'mail.mail'
|
_inherit = 'mail.mail'
|
||||||
|
|
||||||
def _tracking_email_prepare(self, partner, email):
|
def _tracking_email_prepare(self, partner, email):
|
||||||
|
"""Prepare email.tracking.email record values"""
|
||||||
ts = time.time()
|
ts = time.time()
|
||||||
dt = datetime.utcfromtimestamp(ts)
|
dt = datetime.utcfromtimestamp(ts)
|
||||||
email_to_list = email.get('email_to', [])
|
email_to_list = email.get('email_to', [])
|
||||||
@ -28,7 +29,9 @@ class MailMail(models.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _send_prepare_values(self, partner=None):
|
def _send_prepare_values(self, partner=None):
|
||||||
email = super(MailMail, self)._send_prepare_values(partner=partner)
|
"""Creates the mail.tracking.email record and adds the image tracking
|
||||||
|
to the email"""
|
||||||
|
email = super()._send_prepare_values(partner=partner)
|
||||||
vals = self._tracking_email_prepare(partner, email)
|
vals = self._tracking_email_prepare(partner, email)
|
||||||
tracking_email = self.env['mail.tracking.email'].sudo().create(vals)
|
tracking_email = self.env['mail.tracking.email'].sudo().create(vals)
|
||||||
return tracking_email.tracking_img_add(email)
|
return tracking_email.tracking_img_add(email)
|
||||||
|
@ -22,12 +22,29 @@ class MailMessage(models.Model):
|
|||||||
" to filter tracking issues",
|
" to filter tracking issues",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
is_failed_message = fields.Boolean(compute="_compute_is_failed_message")
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_failed_states(self):
|
def get_failed_states(self):
|
||||||
|
"""The 'failed' states of the message"""
|
||||||
return {'error', 'rejected', 'spam', 'bounced', 'soft-bounced'}
|
return {'error', 'rejected', 'spam', 'bounced', 'soft-bounced'}
|
||||||
|
|
||||||
|
@api.depends('mail_tracking_needs_action', 'author_id', 'partner_ids',
|
||||||
|
'mail_tracking_ids.state')
|
||||||
|
def _compute_is_failed_message(self):
|
||||||
|
"""Compute 'is_failed_message' field for the active user"""
|
||||||
|
failed_states = self.get_failed_states()
|
||||||
|
for message in self:
|
||||||
|
needs_action = message.mail_tracking_needs_action
|
||||||
|
involves_me = self.env.user.partner_id in (
|
||||||
|
message.author_id | message.partner_ids)
|
||||||
|
has_failed_trackings = failed_states.intersection(
|
||||||
|
message.mapped("mail_tracking_ids.state"))
|
||||||
|
message.is_failed_message = bool(
|
||||||
|
needs_action and involves_me and has_failed_trackings)
|
||||||
|
|
||||||
def _tracking_status_map_get(self):
|
def _tracking_status_map_get(self):
|
||||||
|
"""Map tracking states to be used in chatter"""
|
||||||
return {
|
return {
|
||||||
'False': 'waiting',
|
'False': 'waiting',
|
||||||
'error': 'error',
|
'error': 'error',
|
||||||
@ -43,6 +60,7 @@ class MailMessage(models.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _partner_tracking_status_get(self, tracking_email):
|
def _partner_tracking_status_get(self, tracking_email):
|
||||||
|
"""Determine tracking status"""
|
||||||
tracking_status_map = self._tracking_status_map_get()
|
tracking_status_map = self._tracking_status_map_get()
|
||||||
status = 'unknown'
|
status = 'unknown'
|
||||||
if tracking_email:
|
if tracking_email:
|
||||||
@ -51,12 +69,23 @@ class MailMessage(models.Model):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
def _partner_tracking_status_human_get(self, status):
|
def _partner_tracking_status_human_get(self, status):
|
||||||
|
"""Translations for tracking statuses to be used on qweb"""
|
||||||
statuses = {'waiting': _('Waiting'), 'error': _('Error'),
|
statuses = {'waiting': _('Waiting'), 'error': _('Error'),
|
||||||
'sent': _('Sent'), 'delivered': _('Delivered'),
|
'sent': _('Sent'), 'delivered': _('Delivered'),
|
||||||
'opened': _('Opened'), 'unknown': _('Unknown')}
|
'opened': _('Opened'), 'unknown': _('Unknown')}
|
||||||
return _("Status: %s") % statuses[status]
|
return _("Status: %s") % statuses[status]
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_error_description(self, tracking):
|
||||||
|
"""Translations for error descriptions to be used on qweb"""
|
||||||
|
descriptions = {
|
||||||
|
'no_recipient': _("The partner doesn't have a defined email"),
|
||||||
|
}
|
||||||
|
return descriptions.get(tracking.error_type,
|
||||||
|
tracking.error_description)
|
||||||
|
|
||||||
def tracking_status(self):
|
def tracking_status(self):
|
||||||
|
"""Generates a complete status tracking of the messages by partner"""
|
||||||
res = {}
|
res = {}
|
||||||
for message in self:
|
for message in self:
|
||||||
partner_trackings = []
|
partner_trackings = []
|
||||||
@ -79,6 +108,9 @@ class MailMessage(models.Model):
|
|||||||
'status': status,
|
'status': status,
|
||||||
'status_human':
|
'status_human':
|
||||||
self._partner_tracking_status_human_get(status),
|
self._partner_tracking_status_human_get(status),
|
||||||
|
'error_type': tracking.error_type,
|
||||||
|
'error_description':
|
||||||
|
self._get_error_description(tracking),
|
||||||
'tracking_id': tracking.id,
|
'tracking_id': tracking.id,
|
||||||
'recipient': recipient,
|
'recipient': recipient,
|
||||||
'partner_id': tracking.partner_id.id,
|
'partner_id': tracking.partner_id.id,
|
||||||
@ -94,114 +126,132 @@ class MailMessage(models.Model):
|
|||||||
partners |= message.needaction_partner_ids
|
partners |= message.needaction_partner_ids
|
||||||
# Remove recipients already included
|
# Remove recipients already included
|
||||||
partners -= partners_already
|
partners -= partners_already
|
||||||
|
tracking_unkown_values = {
|
||||||
|
'status': 'unknown',
|
||||||
|
'status_human': self._partner_tracking_status_human_get(
|
||||||
|
'unknown'),
|
||||||
|
'error_type': False,
|
||||||
|
'error_description': False,
|
||||||
|
'tracking_id': False,
|
||||||
|
}
|
||||||
for partner in partners:
|
for partner in partners:
|
||||||
# If there is partners not included, then status is 'unknown'
|
# If there is partners not included, then status is 'unknown'
|
||||||
# Because can be an Cc recipient
|
# and perhaps a Cc recipient
|
||||||
isCc = False
|
isCc = False
|
||||||
if partner.email in email_cc_list:
|
if partner.email in email_cc_list:
|
||||||
email_cc_list.discard(partner.email)
|
email_cc_list.discard(partner.email)
|
||||||
isCc = True
|
isCc = True
|
||||||
partner_trackings.append({
|
tracking_unkown_values.update({
|
||||||
'status': 'unknown',
|
|
||||||
'status_human':
|
|
||||||
self._partner_tracking_status_human_get('unknown'),
|
|
||||||
'tracking_id': False,
|
|
||||||
'recipient': partner.name,
|
'recipient': partner.name,
|
||||||
'partner_id': partner.id,
|
'partner_id': partner.id,
|
||||||
'isCc': isCc,
|
'isCc': isCc,
|
||||||
})
|
})
|
||||||
|
partner_trackings.append(tracking_unkown_values.copy())
|
||||||
for email in email_cc_list:
|
for email in email_cc_list:
|
||||||
# If there is Cc without partner
|
# If there is Cc without partner
|
||||||
partner_trackings.append({
|
tracking_unkown_values.update({
|
||||||
'status': 'unknown',
|
|
||||||
'status_human':
|
|
||||||
self._partner_tracking_status_human_get('unknown'),
|
|
||||||
'tracking_id': False,
|
|
||||||
'recipient': email,
|
'recipient': email,
|
||||||
'partner_id': False,
|
'partner_id': False,
|
||||||
'isCc': True,
|
'isCc': True,
|
||||||
})
|
})
|
||||||
res[message.id] = partner_trackings
|
partner_trackings.append(tracking_unkown_values.copy())
|
||||||
|
res[message.id] = {
|
||||||
|
'partner_trackings': partner_trackings,
|
||||||
|
'is_failed_message': message.is_failed_message,
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _message_read_dict_postprocess(self, messages, message_tree):
|
def _message_read_dict_postprocess(self, messages, message_tree):
|
||||||
res = super(MailMessage, self)._message_read_dict_postprocess(
|
"""Preare values to be used by the chatter widget"""
|
||||||
|
res = super()._message_read_dict_postprocess(
|
||||||
messages, message_tree)
|
messages, message_tree)
|
||||||
mail_message_ids = {m.get('id') for m in messages if m.get('id')}
|
mail_message_ids = {m.get('id') for m in messages if m.get('id')}
|
||||||
mail_messages = self.browse(mail_message_ids)
|
mail_messages = self.browse(mail_message_ids)
|
||||||
partner_trackings = mail_messages.tracking_status()
|
tracking_statuses = mail_messages.tracking_status()
|
||||||
failed_message = mail_messages._get_failed_message()
|
|
||||||
for message_dict in messages:
|
for message_dict in messages:
|
||||||
mail_message_id = message_dict.get('id', False)
|
mail_message_id = message_dict.get('id', False)
|
||||||
if mail_message_id:
|
if mail_message_id:
|
||||||
message_dict.update({
|
message_dict.update(tracking_statuses[mail_message_id])
|
||||||
'partner_trackings': partner_trackings[mail_message_id],
|
|
||||||
'failed_message': failed_message[mail_message_id],
|
|
||||||
})
|
|
||||||
message_dict['partner_trackings'] = \
|
|
||||||
partner_trackings[mail_message_id]
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.model
|
@api.multi
|
||||||
def _prepare_dict_failed_message(self, message):
|
def _prepare_dict_failed_message(self):
|
||||||
failed_trackings = message.mail_tracking_ids.filtered(
|
"""Preare values to be used by the chatter widget"""
|
||||||
|
self.ensure_one()
|
||||||
|
failed_trackings = self.mail_tracking_ids.filtered(
|
||||||
lambda x: x.state in self.get_failed_states())
|
lambda x: x.state in self.get_failed_states())
|
||||||
failed_partners = failed_trackings.mapped('partner_id')
|
failed_partners = failed_trackings.mapped('partner_id')
|
||||||
failed_recipients = failed_partners.name_get()
|
failed_recipients = failed_partners.name_get()
|
||||||
|
if self.author_id:
|
||||||
|
author = self.author_id.name_get()[0]
|
||||||
|
else:
|
||||||
|
author = (-1, _('-Unknown Author-'))
|
||||||
return {
|
return {
|
||||||
'id': message.id,
|
'id': self.id,
|
||||||
'date': message.date,
|
'date': self.date,
|
||||||
'author_id': message.author_id.name_get()[0],
|
'author': author,
|
||||||
'body': message.body,
|
'body': self.body,
|
||||||
'failed_recipients': failed_recipients,
|
'failed_recipients': failed_recipients,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def get_failed_messages(self):
|
def get_failed_messages(self):
|
||||||
return [self._prepare_dict_failed_message(msg) for msg in self]
|
"""Returns the list of failed messages to be used by the
|
||||||
|
failed_messages widget"""
|
||||||
|
return [msg._prepare_dict_failed_message()
|
||||||
|
for msg in self.sorted('date', reverse=True)]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def toggle_tracking_status(self):
|
def toggle_tracking_status(self):
|
||||||
"""Toggle message tracking action needed to ignore them in the tracking
|
"""Toggle message tracking action.
|
||||||
issues filter"""
|
|
||||||
|
This will mark them to be (or not) ignored in the tracking issues
|
||||||
|
filter.
|
||||||
|
"""
|
||||||
|
self.check_access_rule('read')
|
||||||
self.mail_tracking_needs_action = not self.mail_tracking_needs_action
|
self.mail_tracking_needs_action = not self.mail_tracking_needs_action
|
||||||
return self.mail_tracking_needs_action
|
notification = {
|
||||||
|
'type': 'toggle_tracking_status',
|
||||||
|
'message_ids': [self.id],
|
||||||
|
'needs_actions': self.mail_tracking_needs_action
|
||||||
|
}
|
||||||
|
self.env['bus.bus'].sendone(
|
||||||
|
(self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
|
||||||
|
notification)
|
||||||
|
|
||||||
def _get_failed_message_domain(self):
|
def _get_failed_message_domain(self):
|
||||||
return [
|
domain = self.env['mail.thread']._get_failed_message_domain()
|
||||||
('mail_tracking_ids.state', 'in', list(self.get_failed_states())),
|
domain += [
|
||||||
('mail_tracking_needs_action', '=', True)
|
'|',
|
||||||
|
('partner_ids', 'in', [self.env.user.partner_id.id]),
|
||||||
|
('author_id', '=', self.env.user.partner_id.id),
|
||||||
]
|
]
|
||||||
|
return domain
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_failed_count(self):
|
def get_failed_count(self):
|
||||||
""" Gets the number of failed messages """
|
""" Gets the number of failed messages used on discuss mailbox item"""
|
||||||
return self.search_count(self._get_failed_message_domain())
|
return self.search_count(self._get_failed_message_domain())
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def message_fetch(self, domain, limit=20):
|
def set_all_as_reviewed(self):
|
||||||
# HACK: Because can't modify the domain in discuss JS to search the
|
""" Sets all messages in the given domain as reviewed.
|
||||||
# failed messages we force the change here to clean it of
|
|
||||||
# not valid criterias
|
|
||||||
if self.env.context.get('filter_failed_message'):
|
|
||||||
domain = self._get_failed_message_domain()
|
|
||||||
return super().message_fetch(domain, limit=limit)
|
|
||||||
|
|
||||||
@api.multi
|
Used by Discuss """
|
||||||
def _notify(self, force_send=False, send_after_commit=True,
|
|
||||||
user_signature=True):
|
unreviewed_messages = self.search(self._get_failed_message_domain())
|
||||||
self_sudo = self.sudo()
|
unreviewed_messages.write({'mail_tracking_needs_action': False})
|
||||||
hide_followers = self_sudo._context.get('default_hide_followers',
|
ids = unreviewed_messages.ids
|
||||||
False)
|
|
||||||
if hide_followers:
|
self.env['bus.bus'].sendone(
|
||||||
# HACK: Because Odoo uses subtype to found message followers
|
(self._cr.dbname, 'res.partner',
|
||||||
# whe modify it to False to avoid include them.
|
self.env.user.partner_id.id),
|
||||||
orig_subtype_id = self_sudo.subtype_id
|
{
|
||||||
self_sudo.subtype_id = False
|
'type': 'toggle_tracking_status',
|
||||||
res = super()._notify(force_send=force_send,
|
'message_ids': ids,
|
||||||
send_after_commit=send_after_commit,
|
'needs_actions': False,
|
||||||
user_signature=user_signature)
|
}
|
||||||
if hide_followers:
|
)
|
||||||
self_sudo.subtype_id = orig_subtype_id
|
|
||||||
return res
|
return ids
|
||||||
|
64
mail_tracking/models/mail_resend_message.py
Normal file
64
mail_tracking/models/mail_resend_message.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Copyright 2019 Alexandre Díaz
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from odoo import models, api
|
||||||
|
|
||||||
|
|
||||||
|
class MailResendMessage(models.TransientModel):
|
||||||
|
_inherit = "mail.resend.message"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields):
|
||||||
|
rec = super().default_get(fields)
|
||||||
|
message_id = self._context.get('mail_message_to_resend')
|
||||||
|
if message_id:
|
||||||
|
MailMessageObj = self.env['mail.message']
|
||||||
|
mail_message_id = MailMessageObj.browse(message_id)
|
||||||
|
failed_states = MailMessageObj.get_failed_states()
|
||||||
|
tracking_ids = mail_message_id.mail_tracking_ids.filtered(
|
||||||
|
lambda x: x.state in failed_states)
|
||||||
|
if any(tracking_ids):
|
||||||
|
partner_ids = [(0, 0, {
|
||||||
|
"partner_id": tracking.partner_id.id,
|
||||||
|
"name": tracking.partner_id.name,
|
||||||
|
"email": tracking.partner_id.email,
|
||||||
|
"resend": True,
|
||||||
|
"message": tracking.error_description,
|
||||||
|
}) for tracking in tracking_ids]
|
||||||
|
rec['partner_ids'].extend(partner_ids)
|
||||||
|
return rec
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def resend_mail_action(self):
|
||||||
|
for wizard in self:
|
||||||
|
to_send = wizard.partner_ids.filtered("resend").mapped(
|
||||||
|
"partner_id")
|
||||||
|
if to_send:
|
||||||
|
# Set as reviewed
|
||||||
|
wizard.mail_message_id.mail_tracking_needs_action = False
|
||||||
|
# Reset mail.tracking.email state
|
||||||
|
tracking_ids = wizard.mail_message_id.mail_tracking_ids\
|
||||||
|
.filtered(lambda x: x.partner_id in to_send)
|
||||||
|
tracking_ids.write({'state': False})
|
||||||
|
# Send bus notifications to update Discuss and
|
||||||
|
# mail_failed_messages widget
|
||||||
|
notifications = [
|
||||||
|
[
|
||||||
|
(self._cr.dbname, 'res.partner',
|
||||||
|
self.env.user.partner_id.id),
|
||||||
|
{
|
||||||
|
'type': 'update_failed_messages',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
(self._cr.dbname, 'res.partner',
|
||||||
|
self.env.user.partner_id.id),
|
||||||
|
{
|
||||||
|
'type': 'toggle_tracking_status',
|
||||||
|
'message_ids': [self.mail_message_id.id],
|
||||||
|
'needs_actions': False,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
self.env['bus.bus'].sendmany(notifications)
|
||||||
|
super().resend_mail_action()
|
@ -14,12 +14,25 @@ class MailThread(models.AbstractModel):
|
|||||||
'mail.message', 'res_id', string='Failed Messages',
|
'mail.message', 'res_id', string='Failed Messages',
|
||||||
domain=lambda self:
|
domain=lambda self:
|
||||||
[('model', '=', self._name)]
|
[('model', '=', self._name)]
|
||||||
+ self.env['mail.message']._get_failed_message_domain(),
|
+ self._get_failed_message_domain())
|
||||||
auto_join=True)
|
|
||||||
|
def _get_failed_message_domain(self):
|
||||||
|
"""Domain used to display failed messages on the 'failed_messages'
|
||||||
|
widget"""
|
||||||
|
failed_states = self.env['mail.message'].get_failed_states()
|
||||||
|
return [
|
||||||
|
('mail_tracking_needs_action', '=', True),
|
||||||
|
('mail_tracking_ids.state', 'in', list(failed_states)),
|
||||||
|
]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.returns('self', lambda value: value.id)
|
@api.returns('self', lambda value: value.id)
|
||||||
def message_post(self, *args, **kwargs):
|
def message_post(self, *args, **kwargs):
|
||||||
|
"""Adds CC recipient to the message.
|
||||||
|
|
||||||
|
Because Odoo implementation avoid store cc recipients we ensure that
|
||||||
|
this information its written into the mail.message record.
|
||||||
|
"""
|
||||||
new_message = super().message_post(*args, **kwargs)
|
new_message = super().message_post(*args, **kwargs)
|
||||||
email_cc = kwargs.get('cc')
|
email_cc = kwargs.get('cc')
|
||||||
if email_cc:
|
if email_cc:
|
||||||
@ -31,7 +44,9 @@ class MailThread(models.AbstractModel):
|
|||||||
@api.multi
|
@api.multi
|
||||||
def message_get_suggested_recipients(self):
|
def message_get_suggested_recipients(self):
|
||||||
"""Adds email Cc recipients as suggested recipients.
|
"""Adds email Cc recipients as suggested recipients.
|
||||||
If the recipient have an res.partner uses it."""
|
|
||||||
|
If the recipient has a res.partner, use it.
|
||||||
|
"""
|
||||||
res = super().message_get_suggested_recipients()
|
res = super().message_get_suggested_recipients()
|
||||||
ResPartnerObj = self.env['res.partner']
|
ResPartnerObj = self.env['res.partner']
|
||||||
email_cc_formated_list = []
|
email_cc_formated_list = []
|
||||||
@ -64,7 +79,7 @@ class MailThread(models.AbstractModel):
|
|||||||
res = super().fields_view_get(
|
res = super().fields_view_get(
|
||||||
view_id=view_id, view_type=view_type, toolbar=toolbar,
|
view_id=view_id, view_type=view_type, toolbar=toolbar,
|
||||||
submenu=submenu)
|
submenu=submenu)
|
||||||
if view_type != 'search' and view_type != 'form':
|
if view_type not in {'search', 'form'}:
|
||||||
return res
|
return res
|
||||||
doc = etree.XML(res['arch'])
|
doc = etree.XML(res['arch'])
|
||||||
if view_type == 'search':
|
if view_type == 'search':
|
||||||
|
@ -5,6 +5,7 @@ import logging
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from odoo import models, api, fields, tools
|
from odoo import models, api, fields, tools
|
||||||
@ -91,14 +92,30 @@ class MailTrackingEmail(models.Model):
|
|||||||
tracking_event_ids = fields.One2many(
|
tracking_event_ids = fields.One2many(
|
||||||
string="Tracking events", comodel_name='mail.tracking.event',
|
string="Tracking events", comodel_name='mail.tracking.event',
|
||||||
inverse_name='tracking_email_id', readonly=True)
|
inverse_name='tracking_email_id', readonly=True)
|
||||||
|
# Token isn't generated here to have compatibility with older trackings.
|
||||||
|
# New trackings have token and older not
|
||||||
|
token = fields.Char(string="Security Token", readonly=True,
|
||||||
|
default=lambda s: uuid.uuid4().hex,
|
||||||
|
groups="base.group_system")
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
records = super().create(vals_list)
|
||||||
|
failed_states = self.env['mail.message'].get_failed_states()
|
||||||
|
records \
|
||||||
|
.filtered(lambda one: one.state in failed_states) \
|
||||||
|
.mapped("mail_message_id") \
|
||||||
|
.write({'mail_tracking_needs_action': True})
|
||||||
|
return records
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
if vals.get('state') in self.env['mail.message'].get_failed_states():
|
super().write(vals)
|
||||||
|
state = vals.get('state')
|
||||||
|
if state and state in self.env['mail.message'].get_failed_states():
|
||||||
self.mapped('mail_message_id').write({
|
self.mapped('mail_message_id').write({
|
||||||
'mail_tracking_needs_action': True,
|
'mail_tracking_needs_action': True,
|
||||||
})
|
})
|
||||||
super().write(vals)
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def email_is_bounced(self, email):
|
def email_is_bounced(self, email):
|
||||||
@ -162,7 +179,9 @@ class MailTrackingEmail(models.Model):
|
|||||||
@api.depends('recipient')
|
@api.depends('recipient')
|
||||||
def _compute_recipient_address(self):
|
def _compute_recipient_address(self):
|
||||||
for email in self:
|
for email in self:
|
||||||
if email.recipient:
|
is_empty_recipient = (not email.recipient
|
||||||
|
or '<False>' in email.recipient)
|
||||||
|
if not is_empty_recipient:
|
||||||
matches = re.search(r'<(.*@.*)>', email.recipient)
|
matches = re.search(r'<(.*@.*)>', email.recipient)
|
||||||
if matches:
|
if matches:
|
||||||
email.recipient_address = matches.group(1).lower()
|
email.recipient_address = matches.group(1).lower()
|
||||||
@ -187,8 +206,18 @@ class MailTrackingEmail(models.Model):
|
|||||||
|
|
||||||
def _get_mail_tracking_img(self):
|
def _get_mail_tracking_img(self):
|
||||||
m_config = self.env['ir.config_parameter']
|
m_config = self.env['ir.config_parameter']
|
||||||
base_url = (m_config.get_param('mail_tracking.base.url') or
|
base_url = (m_config.get_param('mail_tracking.base.url')
|
||||||
m_config.get_param('web.base.url'))
|
or m_config.get_param('web.base.url'))
|
||||||
|
if self.token:
|
||||||
|
path_url = (
|
||||||
|
'mail/tracking/open/%(db)s/%(tracking_email_id)s/%(token)s/'
|
||||||
|
'blank.gif' % {
|
||||||
|
'db': self.env.cr.dbname,
|
||||||
|
'tracking_email_id': self.id,
|
||||||
|
'token': self.token,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
# This is here for compatibility with older records
|
||||||
path_url = (
|
path_url = (
|
||||||
'mail/tracking/open/%(db)s/%(tracking_email_id)s/blank.gif' % {
|
'mail/tracking/open/%(db)s/%(tracking_email_id)s/blank.gif' % {
|
||||||
'db': self.env.cr.dbname,
|
'db': self.env.cr.dbname,
|
||||||
@ -216,14 +245,25 @@ class MailTrackingEmail(models.Model):
|
|||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def smtp_error(self, mail_server, smtp_server, exception):
|
def smtp_error(self, mail_server, smtp_server, exception):
|
||||||
self.sudo().write({
|
values = {
|
||||||
|
'state': 'error',
|
||||||
|
}
|
||||||
|
IrMailServer = self.env['ir.mail_server']
|
||||||
|
if str(exception) == IrMailServer.NO_VALID_RECIPIENT \
|
||||||
|
and not self.recipient_address:
|
||||||
|
values.update({
|
||||||
|
'error_type': 'no_recipient',
|
||||||
|
'error_description':
|
||||||
|
"The partner doesn't have a defined email",
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
values.update({
|
||||||
'error_smtp_server': tools.ustr(smtp_server),
|
'error_smtp_server': tools.ustr(smtp_server),
|
||||||
'error_type': exception.__class__.__name__,
|
'error_type': exception.__class__.__name__,
|
||||||
'error_description': tools.ustr(exception),
|
'error_description': tools.ustr(exception),
|
||||||
'state': 'error',
|
|
||||||
})
|
})
|
||||||
self.sudo()._partners_email_bounced_set('error')
|
self.sudo()._partners_email_bounced_set('error')
|
||||||
return True
|
self.sudo().write(values)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def tracking_img_add(self, email):
|
def tracking_img_add(self, email):
|
||||||
|
@ -1,5 +1 @@
|
|||||||
* Handle message updates on discuss 'channel_failed' instead of showing the
|
|
||||||
'outdated' message.
|
|
||||||
* Adapt chat_manager changes in v12
|
|
||||||
* Adapt discuss changes in v12
|
|
||||||
* Add pivot for tracking events and mail trackings
|
* Add pivot for tracking events and mail trackings
|
||||||
|
@ -25,6 +25,9 @@ These are all available status icons:
|
|||||||
.. |cc| image:: ../static/src/img/cc.png
|
.. |cc| image:: ../static/src/img/cc.png
|
||||||
:width: 10px
|
:width: 10px
|
||||||
|
|
||||||
|
.. |noemail| image:: ../static/src/img/no_email.png
|
||||||
|
:width: 10px
|
||||||
|
|
||||||
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|
||||||
|
|
||||||
|waiting| **Waiting**: Waiting to be sent
|
|waiting| **Waiting**: Waiting to be sent
|
||||||
@ -39,14 +42,16 @@ These are all available status icons:
|
|||||||
|
|
||||||
|cc| **Cc**: It's a Carbon-Copy recipient. Can't know the status so is 'Unknown'
|
|cc| **Cc**: It's a Carbon-Copy recipient. Can't know the status so is 'Unknown'
|
||||||
|
|
||||||
|
|noemail| **No Email**: The partner doesn't have a defined email
|
||||||
|
|
||||||
|
|
||||||
If you want to see all tracking emails and events you can go to
|
If you want to see all tracking emails and events you can go to
|
||||||
|
|
||||||
* Settings > Technical > Email > Tracking emails
|
* Settings > Technical > Email > Tracking emails
|
||||||
* Settings > Technical > Email > Tracking events
|
* Settings > Technical > Email > Tracking events
|
||||||
|
|
||||||
When the message generates and 'error' status, it will apear on discuss 'Failed'
|
When the message generates an 'error' status, it will apear on discuss 'Failed'
|
||||||
channel. Any view that uses 'mail_thread' widget can show the failed messages
|
channel. Any view with chatter can show the failed messages
|
||||||
too.
|
too.
|
||||||
|
|
||||||
* Discuss
|
* Discuss
|
||||||
@ -56,3 +61,10 @@ too.
|
|||||||
* Chatter
|
* Chatter
|
||||||
|
|
||||||
.. image:: ../static/img/failed_message_widget.png
|
.. image:: ../static/img/failed_message_widget.png
|
||||||
|
|
||||||
|
You can use "Failed sent messages" filter present in all views to show records
|
||||||
|
with messages in failed status and that needs an user action.
|
||||||
|
|
||||||
|
* Filter
|
||||||
|
|
||||||
|
.. image:: ../static/img/failed_message_filter.png
|
||||||
|
@ -376,24 +376,16 @@ right to his name.</p>
|
|||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
|
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
|
||||||
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
|
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
|
||||||
<<<<<<< HEAD
|
|
||||||
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
|
|
||||||
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
|
|
||||||
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
|
|
||||||
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
|
|
||||||
<li><a class="reference internal" href="#other-credits" id="id7">Other credits</a><ul>
|
|
||||||
<li><a class="reference internal" href="#images" id="id8">Images</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#maintainers" id="id9">Maintainers</a></li>
|
|
||||||
=======
|
|
||||||
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
|
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
|
||||||
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
|
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
|
||||||
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
|
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
|
||||||
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
|
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
|
||||||
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
|
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
|
||||||
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
|
<li><a class="reference internal" href="#other-credits" id="id8">Other credits</a><ul>
|
||||||
>>>>>>> 75b9662... [IMP] mail_tracking: Failed Messages (Discuss & View)
|
<li><a class="reference internal" href="#images" id="id9">Images</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a class="reference internal" href="#maintainers" id="id10">Maintainers</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -412,47 +404,42 @@ For example, <tt class="docutils literal"><span class="pre">--load=web,mail_trac
|
|||||||
form, then an email tracking is created for each email notification. Then a
|
form, then an email tracking is created for each email notification. Then a
|
||||||
status icon will appear just right to name of notified partner.</p>
|
status icon will appear just right to name of notified partner.</p>
|
||||||
<p>These are all available status icons:</p>
|
<p>These are all available status icons:</p>
|
||||||
<<<<<<< HEAD
|
<p><img alt="unknown" src="../static/src/img/unknown.png" style="width: 10px;" /> <strong>Unknown</strong>: No email tracking info available. Maybe this notified partner has ‘Receive Inbox Notifications by Email’ == ‘Never’</p>
|
||||||
<p><img alt="unknown" src="mail_tracking/static/src/img/unknown.png" style="width: 10px;" /> <strong>Unknown</strong>: No email tracking info available. Maybe this notified partner has ‘Receive Inbox Notifications by Email’ == ‘Never’</p>
|
<p><img alt="waiting" src="../static/src/img/waiting.png" style="width: 10px;" /> <strong>Waiting</strong>: Waiting to be sent</p>
|
||||||
<p><img alt="waiting" src="mail_tracking/static/src/img/waiting.png" style="width: 10px;" /> <strong>Waiting</strong>: Waiting to be sent</p>
|
<p><img alt="error" src="../static/src/img/error.png" style="width: 10px;" /> <strong>Error</strong>: Error while sending</p>
|
||||||
<p><img alt="error" src="mail_tracking/static/src/img/error.png" style="width: 10px;" /> <strong>Error</strong>: Error while sending</p>
|
<p><img alt="sent" src="../static/src/img/sent.png" style="width: 10px;" /> <strong>Sent</strong>: Sent to SMTP server configured</p>
|
||||||
<p><img alt="sent" src="mail_tracking/static/src/img/sent.png" style="width: 10px;" /> <strong>Sent</strong>: Sent to SMTP server configured</p>
|
<p><img alt="delivered" src="../static/src/img/delivered.png" style="width: 15px;" /> <strong>Delivered</strong>: Delivered to final MX server</p>
|
||||||
<p><img alt="delivered" src="mail_tracking/static/src/img/delivered.png" style="width: 15px;" /> <strong>Delivered</strong>: Delivered to final MX server</p>
|
<p><img alt="opened" src="../static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p>
|
||||||
<p><img alt="opened" src="mail_tracking/static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p>
|
<p><img alt="cc" src="../static/src/img/cc.png" style="width: 10px;" /> <strong>Cc</strong>: It’s a Carbon-Copy recipient. Can’t know the status so is ‘Unknown’</p>
|
||||||
<p><img alt="cc" src="static/src/img/cc.png" style="width: 10px;" /> <strong>Cc</strong>: It’s a Carbon-Copy recipient. Can’t know the status so is ‘Unknown’</p>
|
<p><img alt="noemail" src="../static/src/img/no_email.png" style="width: 10px;" /> <strong>No Email</strong>: The partner doesn’t have a defined email</p>
|
||||||
<p>If you want to see all tracking emails and events you can go to</p>
|
<p>If you want to see all tracking emails and events you can go to</p>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Settings > Technical > Email > Tracking emails</li>
|
<li>Settings > Technical > Email > Tracking emails</li>
|
||||||
<li>Settings > Technical > Email > Tracking events</li>
|
<li>Settings > Technical > Email > Tracking events</li>
|
||||||
=======
|
</ul>
|
||||||
<p><img alt="unknown" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/unknown.png" style="width: 10px;" /> <strong>Unknown</strong>: No email tracking info available. Maybe this notified partner has ‘Receive Inbox Notifications by Email’ == ‘Never’</p>
|
<p>When the message generates an ‘error’ status, it will apear on discuss ‘Failed’
|
||||||
<p><img alt="waiting" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/waiting.png" style="width: 10px;" /> <strong>Waiting</strong>: Waiting to be sent</p>
|
channel. Any view with chatter can show the failed messages
|
||||||
<p><img alt="error" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/error.png" style="width: 10px;" /> <strong>Error</strong>: Error while sending</p>
|
|
||||||
<p><img alt="sent" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/sent.png" style="width: 10px;" /> <strong>Sent</strong>: Sent to SMTP server configured</p>
|
|
||||||
<p><img alt="delivered" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/delivered.png" style="width: 15px;" /> <strong>Delivered</strong>: Delivered to final MX server</p>
|
|
||||||
<p><img alt="opened" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p>
|
|
||||||
<p><img alt="cc" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/src/img/cc.png" style="width: 10px;" /> <strong>Cc</strong>: It’s a Carbon-Copy recipient. Can’t know the status so is ‘Unknown’</p>
|
|
||||||
<p>When the message generates and ‘error’ status, it will apear on discuss ‘Failed’
|
|
||||||
channel. Any view that uses ‘mail_thread’ widget can show the failed messages
|
|
||||||
too.</p>
|
too.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><p class="first">Discuss</p>
|
<li><p class="first">Discuss</p>
|
||||||
<img alt="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_discuss.png" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_discuss.png" />
|
<img alt="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_discuss.png" src="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_discuss.png" />
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first">Chatter</p>
|
<li><p class="first">Chatter</p>
|
||||||
<img alt="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_widget.png" src="https://raw.githubusercontent.com/OCA/social/11.0/mail_tracking/static/img/failed_message_widget.png" />
|
<img alt="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_widget.png" src="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_widget.png" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>You can use “Failed sent messages” filter present in all views to show records
|
||||||
|
with messages in failed status and that needs an user action.</p>
|
||||||
|
<ul>
|
||||||
|
<li><p class="first">Filter</p>
|
||||||
|
<img alt="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_filter.png" src="https://raw.githubusercontent.com/OCA/social/12.0/mail_tracking/static/img/failed_message_filter.png" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="known-issues-roadmap">
|
<div class="section" id="known-issues-roadmap">
|
||||||
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
|
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Handle message updates on discuss ‘channel_failed’ instead of showing the
|
|
||||||
‘outdated’ message.</li>
|
|
||||||
<li>Adapt chat_manager changes in v12</li>
|
|
||||||
<li>Adapt discuss changes in v12</li>
|
|
||||||
<li>Add pivot for tracking events and mail trackings</li>
|
<li>Add pivot for tracking events and mail trackings</li>
|
||||||
>>>>>>> 75b9662... [IMP] mail_tracking: Failed Messages (Discuss & View)
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="bug-tracker">
|
<div class="section" id="bug-tracker">
|
||||||
@ -486,9 +473,9 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="other-credits">
|
<div class="section" id="other-credits">
|
||||||
<h2><a class="toc-backref" href="#id7">Other credits</a></h2>
|
<h2><a class="toc-backref" href="#id8">Other credits</a></h2>
|
||||||
<div class="section" id="images">
|
<div class="section" id="images">
|
||||||
<h3><a class="toc-backref" href="#id8">Images</a></h3>
|
<h3><a class="toc-backref" href="#id9">Images</a></h3>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Odoo Community Association: <a class="reference external" href="https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg">Icon</a>.</li>
|
<li>Odoo Community Association: <a class="reference external" href="https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg">Icon</a>.</li>
|
||||||
<li>Thanks to <a class="reference external" href="https://openclipart.org/user-detail/LlubNek">LlubNek</a> and <a class="reference external" href="https://openclipart.org">Openclipart</a> for <a class="reference external" href="https://openclipart.org/detail/19342/open-envelope">the icon</a>.</li>
|
<li>Thanks to <a class="reference external" href="https://openclipart.org/user-detail/LlubNek">LlubNek</a> and <a class="reference external" href="https://openclipart.org">Openclipart</a> for <a class="reference external" href="https://openclipart.org/detail/19342/open-envelope">the icon</a>.</li>
|
||||||
@ -496,11 +483,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<div class="section" id="maintainers">
|
||||||
<<<<<<< HEAD
|
<h2><a class="toc-backref" href="#id10">Maintainers</a></h2>
|
||||||
<h2><a class="toc-backref" href="#id9">Maintainers</a></h2>
|
|
||||||
=======
|
|
||||||
<h2><a class="toc-backref" href="#id8">Maintainers</a></h2>
|
|
||||||
>>>>>>> 75b9662... [IMP] mail_tracking: Failed Messages (Discuss & View)
|
|
||||||
<p>This module is maintained by the OCA.</p>
|
<p>This module is maintained by the OCA.</p>
|
||||||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
|
BIN
mail_tracking/static/img/failed_message_filter.png
Normal file
BIN
mail_tracking/static/img/failed_message_filter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
@ -1,108 +0,0 @@
|
|||||||
/* Copyright 2019 Alexandre Díaz
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
|
|
||||||
.o_mail_failed_message {
|
|
||||||
&.o_field_widget {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_thread_date_separator
|
|
||||||
{
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
@media (max-width: @screen-xs-max) {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
border-bottom: 1px solid @gray-lighter-darker;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&.o_border_dashed {
|
|
||||||
border-bottom-style: dashed;
|
|
||||||
|
|
||||||
&[data-toggle="collapse"] {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.o_chatter_failed_message_summary {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.collapsed {
|
|
||||||
margin-bottom: 0;
|
|
||||||
.o-transition(margin, 0.8s);
|
|
||||||
|
|
||||||
.o_chatter_failed_message_summary {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
span {
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
border-radius: 100%;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i.fa-caret-down:before {
|
|
||||||
content: '\f0da';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_thread_date {
|
|
||||||
position: relative;
|
|
||||||
top: 1rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 1rem;
|
|
||||||
font-weight: bold;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_thread_message {
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: -moz-box;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: flex;
|
|
||||||
padding: 0.4rem @odoo-horizontal-padding;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
|
|
||||||
.o_thread_message_sidebar {
|
|
||||||
.o-flex(0, 0, @mail-thread-avatar-size);
|
|
||||||
margin-right: 1rem;
|
|
||||||
margin-top: 0.2rem;
|
|
||||||
text-align: center;
|
|
||||||
font-size: smaller;
|
|
||||||
|
|
||||||
.o_avatar_stack {
|
|
||||||
position: relative;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
|
|
||||||
img {
|
|
||||||
.square(31px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_avatar_icon {
|
|
||||||
.o-position-absolute(@right: -5px, @bottom: -5px);
|
|
||||||
.square(25px);
|
|
||||||
padding: 0.6rem 0.5rem;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 1.2;
|
|
||||||
color: white;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 2px solid white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_thread_message_core .o_mail_info {
|
|
||||||
.text-muted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_mail_chat .o_mail_chat_sidebar .o_mail_failed_message_refresh {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
margin-top: 0.2em;
|
|
||||||
}
|
|
343
mail_tracking/static/src/css/failed_message.scss
Normal file
343
mail_tracking/static/src/css/failed_message.scss
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
/* Copyright 2019 Alexandre Díaz
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
|
||||||
|
// FIXME: More of these classes are cloned from other scss files.
|
||||||
|
.o_mail_failed_message {
|
||||||
|
&.o_field_widget {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_date_separator.o_border_dashed {
|
||||||
|
border-bottom-style: dashed;
|
||||||
|
|
||||||
|
&[data-toggle="collapse"] {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.o_chatter_failed_message_summary {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapsed {
|
||||||
|
margin-bottom: 0;
|
||||||
|
transition: margin 0.8s ease 0s;
|
||||||
|
|
||||||
|
.o_chatter_failed_message_summary {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 100%;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-caret-down:before {
|
||||||
|
content: '\f0da';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_show_more {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_thread_content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_bottom_free_space {
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_typing_notification_free_space {
|
||||||
|
flex-grow: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_typing_notification_bar {
|
||||||
|
flex: 0, 0, 20px;
|
||||||
|
background-color: rgba($white, 0.75);
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
color: gray('600');
|
||||||
|
|
||||||
|
&.o_thread_order_asc {
|
||||||
|
@include o-position-sticky($bottom: 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.o_thread_order_desc {
|
||||||
|
@include o-position-sticky($top: 0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_tooltip_container {
|
||||||
|
display: inline;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_date_separator {
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
border-bottom: 1px solid gray('400');
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.o_thread_date {
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_new_messages_separator {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: solid lighten($o-brand-odoo, 15%) 1px;
|
||||||
|
text-align: right;
|
||||||
|
.o_thread_separator_label {
|
||||||
|
position: relative;
|
||||||
|
top: 8px;
|
||||||
|
padding: 0 10px;
|
||||||
|
background: white;
|
||||||
|
color: lighten($o-brand-odoo, 15%);
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px $o-horizontal-padding;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
|
||||||
|
&.o_mail_not_discussion {
|
||||||
|
background-color: rgba(gray('300'), 0.5);
|
||||||
|
border-bottom: 1px solid gray('400');
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_sidebar {
|
||||||
|
flex: 0 0 $o-mail-thread-avatar-size;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 2px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: smaller;
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: x-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_avatar {
|
||||||
|
max-width: $o-mail-thread-avatar-size;
|
||||||
|
}
|
||||||
|
.o_thread_message_side_date {
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
.o_thread_message_star {
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_side_date {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.o_thread_icon {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
&.fa-star {
|
||||||
|
opacity: $o-mail-thread-icon-opacity;
|
||||||
|
color: gold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &.o_thread_selected_message {
|
||||||
|
.o_thread_message_side_date {
|
||||||
|
opacity: $o-mail-thread-side-date-opacity;
|
||||||
|
}
|
||||||
|
.o_thread_icon {
|
||||||
|
opacity: $o-mail-thread-icon-opacity;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_redirect {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_core {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
word-wrap: break-word;
|
||||||
|
> pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.o_mail_subject {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_notification {
|
||||||
|
font-style: italic;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
[summary~=o_mail_notification] { // name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 9px; // Required by the old design to override a general rule on p's
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
:not(.o_image_box) > img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_body_long {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_info {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: $headings-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_star, .o_thread_message_needaction, .o_thread_message_reply, .o_thread_message_email {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.o_thread_message_email {
|
||||||
|
&.o_thread_message_email_ready {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
&.o_thread_message_email_exception, &.o_thread_message_email_bounce {
|
||||||
|
color: red;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_attachments_list, .o_attachments_previews {
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: $grid-gutter-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_tooltip_container {
|
||||||
|
display: inline;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.o_thread_title {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_no_content {
|
||||||
|
@include o-position-absolute(30%, 0, 0, 0);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 115%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message .o_thread_message_core .o_mail_read_more {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#o_chatter_failed_message {
|
||||||
|
.o_thread_message {
|
||||||
|
.o_thread_message_sidebar {
|
||||||
|
.o_avatar_stack {
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 31px;
|
||||||
|
height: 31px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_avatar_icon {
|
||||||
|
@include o-position-absolute($right: -5px, $bottom: -5px);
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
padding: 6px 5px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: white;
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_mail_info {
|
||||||
|
.o_activity_info {
|
||||||
|
vertical-align: baseline;
|
||||||
|
padding: 4px 6px;
|
||||||
|
background: theme-color('light');
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
@include o-hover-opacity(1, 1);
|
||||||
|
|
||||||
|
&.collapsed {
|
||||||
|
@include o-hover-opacity(0.5, 1);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_collapse .dl-horizontal.card {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
dt {
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
margin-left: 95px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_note {
|
||||||
|
margin: 2px 0 5px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.o_thread_message_warning {
|
||||||
|
margin: 2px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_thread_message_tools {
|
||||||
|
.o_failed_message_link {
|
||||||
|
padding: 0 $input-btn-padding-x;
|
||||||
|
}
|
||||||
|
.o_failed_message_retry {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
.mail_tracking {
|
.mail_tracking {
|
||||||
span {
|
span {
|
||||||
color: @odoo-color-0;
|
color: #909090;
|
||||||
|
|
||||||
&.mail_tracking_opened {
|
&.mail_tracking_opened {
|
||||||
color: @odoo-color-5;
|
color: #a34a8b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,365 +0,0 @@
|
|||||||
/* Copyright 2019 Alexandre Díaz
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
|
|
||||||
odoo.define('mail_tracking.FailedMessage', function (require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var ChatAction = require('mail.chat_client_action');
|
|
||||||
var AbstractField = require('web.AbstractField');
|
|
||||||
var BasicModel = require('web.BasicModel');
|
|
||||||
var BasicView = require('web.BasicView');
|
|
||||||
var Chatter = require('mail.Chatter');
|
|
||||||
var utils = require('mail.utils');
|
|
||||||
var chat_manager = require('mail.chat_manager');
|
|
||||||
var core = require('web.core');
|
|
||||||
var field_registry = require('web.field_registry');
|
|
||||||
var time = require('web.time');
|
|
||||||
var session = require('web.session');
|
|
||||||
var config = require('web.config');
|
|
||||||
|
|
||||||
var QWeb = core.qweb;
|
|
||||||
var _t = core._t;
|
|
||||||
|
|
||||||
/* DISCUSS */
|
|
||||||
var failed_counter = 0;
|
|
||||||
var is_channel_failed_outdated = false;
|
|
||||||
ChatAction.include({
|
|
||||||
init: function () {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
// HACK: Custom event to update messsages
|
|
||||||
core.bus.on('force_update_message', this, function (data) {
|
|
||||||
is_channel_failed_outdated = true;
|
|
||||||
this._onMessageUpdated(data);
|
|
||||||
this.throttledUpdateChannels();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_renderSidebar: function (options) {
|
|
||||||
options.failed_counter = chat_manager.get_failed_counter();
|
|
||||||
return this._super.apply(this, arguments);
|
|
||||||
},
|
|
||||||
_onMessageUpdated: function (message, type) {
|
|
||||||
var self = this;
|
|
||||||
var current_channel_id = this.channel.id;
|
|
||||||
// HACK: break inheritance because can't override properly
|
|
||||||
if (current_channel_id === "channel_failed" &&
|
|
||||||
!message.is_failed) {
|
|
||||||
chat_manager.get_messages({
|
|
||||||
channel_id: this.channel.id,
|
|
||||||
domain: this.domain,
|
|
||||||
}).then(function (messages) {
|
|
||||||
var options = self._getThreadRenderingOptions(messages);
|
|
||||||
self.thread.remove_message_and_render(
|
|
||||||
message.id, messages, options).then(function () {
|
|
||||||
self._updateButtonStatus(messages.length === 0, type);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_updateChannels: function () {
|
|
||||||
var self = this;
|
|
||||||
// HACK: break inheritance because can't override properly
|
|
||||||
if (this.channel.id === "channel_failed") {
|
|
||||||
var $sidebar = this._renderSidebar({
|
|
||||||
active_channel_id:
|
|
||||||
this.channel ? this.channel.id: undefined,
|
|
||||||
channels: chat_manager.get_channels(),
|
|
||||||
needaction_counter: chat_manager.get_needaction_counter(),
|
|
||||||
starred_counter: chat_manager.get_starred_counter(),
|
|
||||||
failed_counter: chat_manager.get_failed_counter(),
|
|
||||||
});
|
|
||||||
this.$(".o_mail_chat_sidebar").html($sidebar.contents());
|
|
||||||
_.each(['dm', 'public', 'private'], function (type) {
|
|
||||||
var $input = self.$(
|
|
||||||
'.o_mail_add_channel[data-type=' + type + '] input');
|
|
||||||
self._prepareAddChannelInput($input, type);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Because can't refresh "channel_failed" we add a flag
|
|
||||||
// to indicate that the data is outdated
|
|
||||||
var refresh_elm = this.$(
|
|
||||||
".o_mail_chat_sidebar .o_mail_failed_message_refresh");
|
|
||||||
refresh_elm.click(function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
if (is_channel_failed_outdated) {
|
|
||||||
refresh_elm.removeClass('hidden');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
chat_manager.get_failed_counter = function () {
|
|
||||||
return failed_counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
chat_manager._onMailClientAction_failed_message_super =
|
|
||||||
chat_manager._onMailClientAction;
|
|
||||||
chat_manager._onMailClientAction = function (result) {
|
|
||||||
failed_counter = result.failed_counter;
|
|
||||||
return this._onMailClientAction_failed_message_super(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
function add_channel_to_message (message, channel_id) {
|
|
||||||
message.channel_ids.push(channel_id);
|
|
||||||
message.channel_ids = _.uniq(message.channel_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
chat_manager._make_message_failed_message_super = chat_manager.make_message;
|
|
||||||
chat_manager.make_message = function (data) {
|
|
||||||
var msg = this._make_message_failed_message_super(data);
|
|
||||||
function property_descr (channel) {
|
|
||||||
return {
|
|
||||||
enumerable: true,
|
|
||||||
get: function () {
|
|
||||||
return _.contains(msg.channel_ids, channel);
|
|
||||||
},
|
|
||||||
set: function (bool) {
|
|
||||||
if (bool) {
|
|
||||||
add_channel_to_message(msg, channel);
|
|
||||||
} else {
|
|
||||||
msg.channel_ids = _.without(msg.channel_ids, channel);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperties(msg, {
|
|
||||||
is_failed: property_descr("channel_failed"),
|
|
||||||
});
|
|
||||||
msg.is_failed = data.failed_message;
|
|
||||||
return msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
chat_manager._fetchFromChannel_failed_message_super =
|
|
||||||
chat_manager._fetchFromChannel;
|
|
||||||
chat_manager._fetchFromChannel = function (channel, options) {
|
|
||||||
if (channel.id !== "channel_failed") {
|
|
||||||
return this._fetchFromChannel_failed_message_super(
|
|
||||||
channel, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: Can't override '_fetchFromChannel' properly to modify the
|
|
||||||
// domain, uses context instead and does it in python.
|
|
||||||
session.user_context.filter_failed_message = true;
|
|
||||||
var res = this._fetchFromChannel_failed_message_super(
|
|
||||||
channel, options);
|
|
||||||
res.then(function () {
|
|
||||||
delete session.user_context.filter_failed_message;
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
// HACK: Get failed_counter. Because 'chat_manager' call 'start' need call
|
|
||||||
// to '/mail/client_action' again with overrided '_onMailClientAction'
|
|
||||||
session.is_bound.then(function () {
|
|
||||||
var context = _.extend({isMobile: config.device.isMobile},
|
|
||||||
session.user_context);
|
|
||||||
return session.rpc('/mail/client_action', {context: context});
|
|
||||||
}).then(chat_manager._onMailClientAction.bind(chat_manager));
|
|
||||||
|
|
||||||
|
|
||||||
/* FAILED MESSAGES CHATTER WIDGET */
|
|
||||||
// TODO: Use timeFromNow() in v12
|
|
||||||
function time_from_now (date) {
|
|
||||||
if (moment().diff(date, 'seconds') < 45) {
|
|
||||||
return _t("now");
|
|
||||||
}
|
|
||||||
return date.fromNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _readMessages (self, ids) {
|
|
||||||
if (!ids.length) {
|
|
||||||
return $.when([]);
|
|
||||||
}
|
|
||||||
var context = self.record && self.record.getContext();
|
|
||||||
return self._rpc({
|
|
||||||
model: 'mail.message',
|
|
||||||
method: 'get_failed_messages',
|
|
||||||
args: [ids],
|
|
||||||
context: context || self.getSession().user_context,
|
|
||||||
}).then(function (messages) {
|
|
||||||
// Convert date to moment
|
|
||||||
_.each(messages, function (msg) {
|
|
||||||
msg.date = moment(time.auto_str_to_date(msg.date));
|
|
||||||
msg.hour = time_from_now(msg.date);
|
|
||||||
});
|
|
||||||
return _.sortBy(messages, 'date');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicModel.include({
|
|
||||||
_fetchSpecialFailedMessages: function (record, fieldName) {
|
|
||||||
var localID = record._changes && fieldName in record._changes
|
|
||||||
? record._changes[fieldName] : record.data[fieldName];
|
|
||||||
return _readMessages(this, this.localData[localID].res_ids);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
var AbstractFailedMessagesField = AbstractField.extend({
|
|
||||||
_markFailedMessageReviewed: function (id) {
|
|
||||||
return this._rpc({
|
|
||||||
model: 'mail.message',
|
|
||||||
method: 'toggle_tracking_status',
|
|
||||||
args: [[id]],
|
|
||||||
context: this.record.getContext(),
|
|
||||||
}).then(function (status) {
|
|
||||||
var fake_message = {
|
|
||||||
'id': id,
|
|
||||||
'is_failed': status,
|
|
||||||
};
|
|
||||||
chat_manager.bus.trigger('update_message', fake_message);
|
|
||||||
core.bus.trigger('force_update_message', fake_message);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
var FailedMessage = AbstractFailedMessagesField.extend({
|
|
||||||
className: 'o_mail_failed_message',
|
|
||||||
events: {
|
|
||||||
'click .o_failed_message_retry': '_onRetryFailedMessage',
|
|
||||||
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
|
|
||||||
},
|
|
||||||
specialData: '_fetchSpecialFailedMessages',
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
this.failed_messages = this.record.specialData[this.name];
|
|
||||||
},
|
|
||||||
_render: function () {
|
|
||||||
if (this.failed_messages.length) {
|
|
||||||
this.$el.html(QWeb.render(
|
|
||||||
'mail_tracking.failed_message_items', {
|
|
||||||
failed_messages: this.failed_messages,
|
|
||||||
nbFailedMessages: this.failed_messages.length,
|
|
||||||
date_format: time.getLangDateFormat(),
|
|
||||||
datetime_format: time.getLangDatetimeFormat(),
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this.$el.empty();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_reset: function (record) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
this.failed_messages = this.record.specialData[this.name];
|
|
||||||
this.res_id = record.res_id;
|
|
||||||
},
|
|
||||||
|
|
||||||
_reload: function (fieldsToReload) {
|
|
||||||
this.trigger_up('reload_mail_fields', fieldsToReload);
|
|
||||||
},
|
|
||||||
|
|
||||||
_openComposer: function (context) {
|
|
||||||
var self = this;
|
|
||||||
this.do_action({
|
|
||||||
type: 'ir.actions.act_window',
|
|
||||||
res_model: 'mail.compose.message',
|
|
||||||
view_mode: 'form',
|
|
||||||
view_type: 'form',
|
|
||||||
views: [[false, 'form']],
|
|
||||||
target: 'new',
|
|
||||||
context: context,
|
|
||||||
}, {
|
|
||||||
on_close: function () {
|
|
||||||
self._reload({failed_message: true});
|
|
||||||
self.trigger('need_refresh');
|
|
||||||
chat_manager.get_messages({
|
|
||||||
model: self.model,
|
|
||||||
res_id: self.res_id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}).then(this.trigger.bind(this, 'close_composer'));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Handlers
|
|
||||||
_onRetryFailedMessage: function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var message_id = $(event.currentTarget).data('message-id');
|
|
||||||
var failed_msg = _.findWhere(this.failed_messages,
|
|
||||||
{'id': message_id});
|
|
||||||
var failed_partner_ids = _.map(failed_msg.failed_recipients,
|
|
||||||
function (item) {
|
|
||||||
return item[0];
|
|
||||||
});
|
|
||||||
this._openComposer({
|
|
||||||
default_body: utils.get_text2html(failed_msg.body),
|
|
||||||
default_partner_ids: failed_partner_ids,
|
|
||||||
default_is_log: false,
|
|
||||||
default_model: this.model,
|
|
||||||
default_res_id: this.res_id,
|
|
||||||
default_composition_mode: 'comment',
|
|
||||||
// Omit followers
|
|
||||||
default_hide_followers: true,
|
|
||||||
mail_post_autofollow: true,
|
|
||||||
message_id: message_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMarkFailedMessageReviewed: function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var message_id = $(event.currentTarget).data('message-id');
|
|
||||||
this._markFailedMessageReviewed(message_id).then(
|
|
||||||
this._reload.bind(this, {failed_message: true}));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
field_registry.add('mail_failed_message', FailedMessage);
|
|
||||||
|
|
||||||
var mailWidgets = ['mail_failed_message'];
|
|
||||||
BasicView.include({
|
|
||||||
init: function (viewInfo) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
// Adds mail_failed_message as valid mail widget
|
|
||||||
var fieldsInfo = viewInfo.fieldsInfo[this.viewType];
|
|
||||||
for (var fieldName in fieldsInfo) {
|
|
||||||
var fieldInfo = fieldsInfo[fieldName];
|
|
||||||
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
|
||||||
this.mailFields[fieldInfo.widget] = fieldName;
|
|
||||||
fieldInfo.__no_fetch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.assign(this.rendererParams.mailFields, this.mailFields);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Chatter.include({
|
|
||||||
init: function (parent, record, mailFields, options) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
// Initialize mail_failed_message widget
|
|
||||||
if (mailFields.mail_failed_message) {
|
|
||||||
this.fields.failed_message = new FailedMessage(
|
|
||||||
this, mailFields.mail_failed_message, record, options);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_render: function () {
|
|
||||||
var self = this;
|
|
||||||
return this._super.apply(this, arguments).then(function () {
|
|
||||||
if (self.fields.failed_message) {
|
|
||||||
self.fields.failed_message.$el.insertBefore(
|
|
||||||
self.$el.find('.o_mail_thread'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_onReloadMailFields: function (event) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
var fieldNames = [];
|
|
||||||
if (this.fields.failed_message && event.data.failed_message) {
|
|
||||||
fieldNames.push(this.fields.failed_message.name);
|
|
||||||
}
|
|
||||||
this.trigger_up('reload', {
|
|
||||||
fieldNames: fieldNames,
|
|
||||||
keepChanges: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return FailedMessage;
|
|
||||||
|
|
||||||
});
|
|
397
mail_tracking/static/src/js/failed_message/discuss.js
Normal file
397
mail_tracking/static/src/js/failed_message/discuss.js
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
/* Copyright 2019 Alexandre Díaz
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
|
||||||
|
odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// To be considered:
|
||||||
|
// - One message can be displayed in many threads
|
||||||
|
// - A thread can be a mailbox, channel, ...
|
||||||
|
// - A mailbox is a type of thread that is displayed on top of
|
||||||
|
// the discuss menu, has a counter, etc...
|
||||||
|
|
||||||
|
var MailManagerNotif = require('mail.Manager.Notification');
|
||||||
|
var AbstractMessage = require('mail.model.AbstractMessage');
|
||||||
|
var Message = require('mail.model.Message');
|
||||||
|
var Discuss = require('mail.Discuss');
|
||||||
|
var MailManager = require('mail.Manager');
|
||||||
|
var Mailbox = require('mail.model.Mailbox');
|
||||||
|
var core = require('web.core');
|
||||||
|
var session = require('web.session');
|
||||||
|
|
||||||
|
var QWeb = core.qweb;
|
||||||
|
var _t = core._t;
|
||||||
|
|
||||||
|
/* The states to consider a message as failed message */
|
||||||
|
var FAILED_STATES = [
|
||||||
|
'error', 'rejected', 'spam', 'bounced', 'soft-bounced',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
AbstractMessage.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract declaration to know if a message is included in the
|
||||||
|
* failed mailbox. By default it should be false.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isFailed: function () {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Message.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to store information from server
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function (parent, data) {
|
||||||
|
this._isFailedMessage = data.is_failed_message;
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation to know if a message is included in the
|
||||||
|
* failed mailbox.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
isFailed: function () {
|
||||||
|
return _.contains(this._threadIDs, 'mailbox_failed');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds/Removes message to/from failed mailbox
|
||||||
|
*
|
||||||
|
* @param {Boolean} failed
|
||||||
|
*/
|
||||||
|
setFailed: function (failed) {
|
||||||
|
if (failed) {
|
||||||
|
this._addThread('mailbox_failed');
|
||||||
|
} else {
|
||||||
|
this.removeThread('mailbox_failed');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include the message in the 'failed' mailbox if needed
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_processMailboxes: function () {
|
||||||
|
this.setFailed(this._isFailedMessage);
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
MailManagerNotif.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to handle changes in the 'mail_tracking_needs_action' flag
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_handlePartnerNotification: function (data) {
|
||||||
|
if (data.type === 'toggle_tracking_status') {
|
||||||
|
this._handlePartnerToggleFailedNotification(data);
|
||||||
|
} else {
|
||||||
|
// Workaround to avoid call '_handlePartnerChannelNotification'
|
||||||
|
// because this is related with the failed mailbox, not a
|
||||||
|
// channel.
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method updates messages in the failed mailbox when the flag
|
||||||
|
* 'mail_tracking_needs_action' is toggled. This can remove/add
|
||||||
|
* the message from/to failed mailbox and update mailbox counter.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
_handlePartnerToggleFailedNotification: function (data) {
|
||||||
|
var self = this;
|
||||||
|
var failed = this.getMailbox('failed');
|
||||||
|
_.each(data.message_ids, function (messageID) {
|
||||||
|
var message = _.find(self._messages, function (msg) {
|
||||||
|
return msg.getID() === messageID;
|
||||||
|
});
|
||||||
|
if (message) {
|
||||||
|
message.setFailed(data.needs_actions);
|
||||||
|
if (message.isFailed() === false) {
|
||||||
|
self._removeMessageFromThread(
|
||||||
|
'mailbox_failed', message);
|
||||||
|
} else {
|
||||||
|
self._addMessageToThreads(message, []);
|
||||||
|
var channelFailed = self.getMailbox('failed');
|
||||||
|
channelFailed.invalidateCaches();
|
||||||
|
}
|
||||||
|
self._mailBus.trigger('update_message', message, data.type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.needs_actions) {
|
||||||
|
// Increase failed counter if message is marked as failed
|
||||||
|
failed.incrementMailboxCounter(data.message_ids.length);
|
||||||
|
} else {
|
||||||
|
// Decrease failed counter if message is removed from failed
|
||||||
|
failed.decrementMailboxCounter(data.message_ids.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger event to refresh threads
|
||||||
|
this._mailBus.trigger('update_failed', failed.getMailboxCounter());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Discuss.include({
|
||||||
|
events: _.extend({}, Discuss.prototype.events, {
|
||||||
|
'click .o_failed_message_retry': '_onRetryFailedMessage',
|
||||||
|
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paramaters used to render 'failed' mailbox entry in Discuss
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
_sidebarQWebParams: function () {
|
||||||
|
var failed = this.call('mail_service', 'getMailbox', 'failed');
|
||||||
|
return {
|
||||||
|
activeThreadID: this._thread ? this._thread.getID() : undefined,
|
||||||
|
failedCounter: failed.getMailboxCounter(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render 'failed' mailbox menu entry in Discuss
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {jQueryElementt}
|
||||||
|
*/
|
||||||
|
_renderSidebar: function () {
|
||||||
|
var $sidebar = this._super.apply(this, arguments);
|
||||||
|
// Because Odoo implementation isn't designed to be inherited
|
||||||
|
// properly, we inject 'failed' button using jQuery.
|
||||||
|
var $failed_item = $(QWeb.render('mail_tracking.SidebarFailed',
|
||||||
|
this._sidebarQWebParams()));
|
||||||
|
$failed_item.insertAfter(
|
||||||
|
$sidebar.find(".o_mail_discuss_title_main").filter(":last"));
|
||||||
|
return $sidebar;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to listen click on 'Set all as reviewed' button
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_renderButtons: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.$btn_set_all_reviewed = this.$buttons.find(
|
||||||
|
'.o_mail_discuss_button_set_all_reviewed');
|
||||||
|
this.$btn_set_all_reviewed
|
||||||
|
.on('click', $.proxy(this, "_onSetAllAsReviewedClicked"));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show or hide 'set all as reviewed' button in discuss mailbox
|
||||||
|
*
|
||||||
|
* This means in which thread the button should be displayed.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_updateControlPanelButtons: function (thread) {
|
||||||
|
this.$btn_set_all_reviewed
|
||||||
|
.toggleClass(
|
||||||
|
'd-none d-md-none',
|
||||||
|
thread.getID() !== 'mailbox_failed');
|
||||||
|
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to update 'set all as reviewed' button.
|
||||||
|
*
|
||||||
|
* Disabled button if doesn't have more failed messages
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_updateButtonStatus: function (disabled, type) {
|
||||||
|
if (this._thread.getID() === 'mailbox_failed') {
|
||||||
|
this.$btn_set_all_reviewed
|
||||||
|
.toggleClass('disabled', disabled);
|
||||||
|
// Display Rainbowman when all failed messages are reviewed
|
||||||
|
// through 'TOGGLE TRACKING STATUS' or marking last failed
|
||||||
|
// message as reviewed
|
||||||
|
if (disabled && type === 'toggle_tracking_status') {
|
||||||
|
this.trigger_up('show_effect', {
|
||||||
|
message: _t(
|
||||||
|
"Congratulations, your failed mailbox is empty"),
|
||||||
|
type: 'rainbow_man',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to update messages in 'failed' mailbox thread
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_onMessageUpdated: function (message, type) {
|
||||||
|
var self = this;
|
||||||
|
var currentThreadID = this._thread.getID();
|
||||||
|
if (currentThreadID === 'mailbox_failed' && !message.isFailed()) {
|
||||||
|
this._thread.fetchMessages(this.domain)
|
||||||
|
.then(function () {
|
||||||
|
var options = self._getThreadRenderingOptions();
|
||||||
|
self._threadWidget.removeMessageAndRender(
|
||||||
|
message.getID(), self._thread, options)
|
||||||
|
.then(function () {
|
||||||
|
self._updateButtonStatus(
|
||||||
|
!self._thread.hasMessages(), type);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Workaround to avoid calling '_fetchAndRenderThread' and
|
||||||
|
// refetching thread messages because these messages are
|
||||||
|
// actually fetched above.
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide reply feature in the 'failed' mailbox, where it has no sense.
|
||||||
|
* Show instead 'Retry' and 'Set as reviewed' buttons.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_getThreadRenderingOptions: function () {
|
||||||
|
var values = this._super.apply(this, arguments);
|
||||||
|
if (this._thread.getID() === 'mailbox_failed') {
|
||||||
|
values.displayEmailIcons = true;
|
||||||
|
values.displayReplyIcons = false;
|
||||||
|
values.displayRetryButton = true;
|
||||||
|
values.displayReviewedButton = true;
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen also to the event that refreshes thread messages
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_startListening: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.call('mail_service', 'getMailBus')
|
||||||
|
.on('update_failed', this, this._throttledUpdateThreads);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
/**
|
||||||
|
* Open the resend mail.resend.message wizard
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
_onRetryFailedMessage: function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var messageID = $(event.currentTarget).data('message-id');
|
||||||
|
this.do_action('mail.mail_resend_message_action', {
|
||||||
|
additional_context: {
|
||||||
|
mail_message_to_resend: messageID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle 'mail_tracking_needs_action' flag
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Event} event
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
_onMarkFailedMessageReviewed: function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var messageID = $(event.currentTarget).data('message-id');
|
||||||
|
return this._rpc({
|
||||||
|
model: 'mail.message',
|
||||||
|
method: 'toggle_tracking_status',
|
||||||
|
args: [[messageID]],
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inheritable method that call thread implementation
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onSetAllAsReviewedClicked: function () {
|
||||||
|
this._thread.setAllMessagesAsReviewed();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
MailManager.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the 'failed' mailbox
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_updateMailboxesFromServer: function (data) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this._addMailbox({
|
||||||
|
id: 'failed',
|
||||||
|
name: _t("Failed"),
|
||||||
|
mailboxCounter: data.failed_counter || 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Mailbox.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to add domain for 'failed' mailbox thread
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_getThreadDomain: function () {
|
||||||
|
if (this._id === 'mailbox_failed') {
|
||||||
|
return [
|
||||||
|
['mail_tracking_ids.state', 'in', FAILED_STATES],
|
||||||
|
['mail_tracking_needs_action', '=', true],
|
||||||
|
'|',
|
||||||
|
['partner_ids', 'in', [session.partner_id]],
|
||||||
|
['author_id', '=', session.partner_id],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Workaround to avoid throw 'Missing domain' exception. Call _super
|
||||||
|
// without a valid (hard-coded) thread id causes that exeception.
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all messages from the mailbox as reviewed.
|
||||||
|
*
|
||||||
|
* At the moment, this method makes only sense for 'Failed'.
|
||||||
|
*
|
||||||
|
* @returns {$.Promise} resolved when all messages have been marked as
|
||||||
|
* reviewed on the server
|
||||||
|
*/
|
||||||
|
setAllMessagesAsReviewed: function () {
|
||||||
|
if (this._id === 'mailbox_failed' && this.getMailboxCounter() > 0) {
|
||||||
|
return this._rpc({
|
||||||
|
model: 'mail.message',
|
||||||
|
method: 'set_all_as_reviewed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return $.when();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
316
mail_tracking/static/src/js/failed_message/thread.js
Normal file
316
mail_tracking/static/src/js/failed_message/thread.js
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/* Copyright 2019 Alexandre Díaz
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
|
||||||
|
odoo.define('mail_tracking.FailedMessageThread', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var AbstractField = require('web.AbstractField');
|
||||||
|
var BasicModel = require('web.BasicModel');
|
||||||
|
var BasicView = require('web.BasicView');
|
||||||
|
var Chatter = require('mail.Chatter');
|
||||||
|
var MailThread = require('mail.widget.Thread');
|
||||||
|
var utils = require('mail.utils');
|
||||||
|
var core = require('web.core');
|
||||||
|
var field_registry = require('web.field_registry');
|
||||||
|
var time = require('web.time');
|
||||||
|
|
||||||
|
var QWeb = core.qweb;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to fetch failed messages
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} widget
|
||||||
|
* @param {Array} ids
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function _readMessages (widget, ids) {
|
||||||
|
if (!ids.length) {
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
|
var context = widget.record && widget.record.getContext();
|
||||||
|
return widget._rpc({
|
||||||
|
model: 'mail.message',
|
||||||
|
method: 'get_failed_messages',
|
||||||
|
args: [ids],
|
||||||
|
context: context || widget.getSession().user_context,
|
||||||
|
}).then(function (messages) {
|
||||||
|
// Convert date to moment
|
||||||
|
_.each(messages, function (msg) {
|
||||||
|
msg.date = moment(time.auto_str_to_date(msg.date));
|
||||||
|
msg.hour = utils.timeFromNow(msg.date);
|
||||||
|
});
|
||||||
|
return messages;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicModel.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data for the 'mail_failed_message' field widget in form views.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} record
|
||||||
|
* @param {String} fieldName
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
_fetchSpecialFailedMessages: function (record, fieldName) {
|
||||||
|
var localID = record._changes && fieldName in record._changes
|
||||||
|
? record._changes[fieldName] : record.data[fieldName];
|
||||||
|
return _readMessages(this, this.localData[localID].res_ids);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var FailedMessage = AbstractField.extend({
|
||||||
|
className: 'o_mail_failed_message',
|
||||||
|
events: {
|
||||||
|
'click .o_failed_message_retry': '_onRetryFailedMessage',
|
||||||
|
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
|
||||||
|
},
|
||||||
|
specialData: '_fetchSpecialFailedMessages',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to reference failed messages in a easy way
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.failed_messages = this.record.specialData[this.name] || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to listen bus notifications
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
start: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.call(
|
||||||
|
'bus_service', 'onNotification', this, this._onNotification);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paremeters used to render widget
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
_failedItemsQWebParams: function () {
|
||||||
|
return {
|
||||||
|
failed_messages: this.failed_messages,
|
||||||
|
nbFailedMessages: this.failed_messages.length,
|
||||||
|
date_format: time.getLangDateFormat(),
|
||||||
|
datetime_format: time.getLangDatetimeFormat(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_render: function () {
|
||||||
|
if (this.failed_messages.length) {
|
||||||
|
this.$el.html(QWeb.render(
|
||||||
|
'mail_tracking.failed_message_items',
|
||||||
|
this._failedItemsQWebParams()));
|
||||||
|
} else {
|
||||||
|
this.$el.empty();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset widget data using selected record
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
_reset: function (record) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.failed_messages = this.record.specialData[this.name] || [];
|
||||||
|
this.res_id = record.res_id;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger event to reload mail widgets
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Array} fieldsToReload
|
||||||
|
*/
|
||||||
|
_reload: function (fieldsToReload) {
|
||||||
|
this.trigger_up('reload_mail_fields', fieldsToReload);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark failed message as reviewed
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Int} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
_markFailedMessageReviewed: function (id) {
|
||||||
|
return this._rpc({
|
||||||
|
model: 'mail.message',
|
||||||
|
method: 'toggle_tracking_status',
|
||||||
|
args: [[id]],
|
||||||
|
context: this.record.getContext(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
/**
|
||||||
|
* Listen bus notification to launch reload process.
|
||||||
|
* This bus notification is received when the user uses
|
||||||
|
* 'mail.resend.message' wizard.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Array} notifs
|
||||||
|
*/
|
||||||
|
_onNotification: function (notifs) {
|
||||||
|
var self = this;
|
||||||
|
_.each(notifs, function (notif) {
|
||||||
|
var model = notif[0][1];
|
||||||
|
if (model === 'res.partner') {
|
||||||
|
var data = notif[1];
|
||||||
|
if (data.type === 'update_failed_messages') {
|
||||||
|
// Reload 'mail_failed_message' widget
|
||||||
|
self._reload({failed_message: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle retry failed message event to open the mail.resend.message
|
||||||
|
* wizard.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
_onRetryFailedMessage: function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var messageID = $(event.currentTarget).data('message-id');
|
||||||
|
this.do_action('mail.mail_resend_message_action', {
|
||||||
|
additional_context: {
|
||||||
|
mail_message_to_resend: messageID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mark message as reviewed event
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
_onMarkFailedMessageReviewed: function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var messageID = $(event.currentTarget).data('message-id');
|
||||||
|
this._markFailedMessageReviewed(messageID).then(
|
||||||
|
$.proxy(this, "_reload", {failed_message: true}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
field_registry.add('mail_failed_message', FailedMessage);
|
||||||
|
|
||||||
|
var mailWidgets = ['mail_failed_message'];
|
||||||
|
BasicView.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to add 'mail_failed_message' widget as "mail widget" used
|
||||||
|
* in Chatter.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
var fieldsInfo = this.fieldsInfo[this.viewType];
|
||||||
|
for (var fieldName in fieldsInfo) {
|
||||||
|
var fieldInfo = fieldsInfo[fieldName];
|
||||||
|
// Search fields using 'mail_failed_messsage' widget.
|
||||||
|
// Only one field can exists using the widget, the last
|
||||||
|
// found wins.
|
||||||
|
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
||||||
|
// Add field as "mail field" shared with Chatter
|
||||||
|
this.mailFields[fieldInfo.widget] = fieldName;
|
||||||
|
// Avoid fetch x2many data, this will be done by widget
|
||||||
|
fieldInfo.__no_fetch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update renderParmans mailFields to include the found field
|
||||||
|
// using 'mail_failed_messsage' widget. This info is used by the
|
||||||
|
// renderers [In Odoo vanilla by the form renderer to initialize
|
||||||
|
// Chatter widget].
|
||||||
|
_.extend(this.rendererParams.mailFields, this.mailFields);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Chatter.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to initialize 'mail_failed_message' widget.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function (parent, record, mailFields, options) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
// Initialize mail_failed_message widget
|
||||||
|
if (mailFields.mail_failed_message) {
|
||||||
|
this.fields.failed_message = new FailedMessage(
|
||||||
|
this, mailFields.mail_failed_message, record, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects failed messages widget before the chatter
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
_render: function () {
|
||||||
|
var self = this;
|
||||||
|
return this._super.apply(this, arguments).then(function () {
|
||||||
|
if (self.fields.failed_message) {
|
||||||
|
self.fields.failed_message.$el.insertBefore(
|
||||||
|
self.$el.find('.o_mail_thread'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to reload 'mail_failed_message' widget
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_onReloadMailFields: function (event) {
|
||||||
|
if (this.fields.failed_message && event.data.failed_message) {
|
||||||
|
this.trigger_up('reload', {
|
||||||
|
fieldNames: [this.fields.failed_message.name],
|
||||||
|
keepChanges: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Workaround to avoid trigger reload event twice (once for
|
||||||
|
// mail_failed_message and again with empty 'fieldNames'.
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
MailThread.include({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show 'retry' & 'Set as reviewed' buttons in the Chatter
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this._enabledOptions.displayRetryButton = true;
|
||||||
|
this._enabledOptions.displayReviewedButton = true;
|
||||||
|
this._disabledOptions.displayRetryButton = false;
|
||||||
|
this._disabledOptions.displayReviewedButton = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return FailedMessage;
|
||||||
|
|
||||||
|
});
|
@ -14,10 +14,11 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
var _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
AbstractMessage.include({
|
AbstractMessage.include({
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Messages do not have any PartnerTrackings.
|
* Messages do not have any PartnerTrackings.
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasPartnerTrackings: function () {
|
hasPartnerTrackings: function () {
|
||||||
return false;
|
return false;
|
||||||
@ -26,7 +27,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
/**
|
/**
|
||||||
* Messages do not have any email Cc values.
|
* Messages do not have any email Cc values.
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasEmailCc: function () {
|
hasEmailCc: function () {
|
||||||
return false;
|
return false;
|
||||||
@ -34,7 +35,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
});
|
});
|
||||||
|
|
||||||
Message.include({
|
Message.include({
|
||||||
init: function (parent, data, emojis) {
|
init: function (parent, data) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this._partnerTrackings = data.partner_trackings || [];
|
this._partnerTrackings = data.partner_trackings || [];
|
||||||
this._emailCc = data.email_cc || [];
|
this._emailCc = data.email_cc || [];
|
||||||
@ -45,7 +46,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
* State whether this message contains some PartnerTrackings values
|
* State whether this message contains some PartnerTrackings values
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @return {boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasPartnerTrackings: function () {
|
hasPartnerTrackings: function () {
|
||||||
return _.some(this._partnerTrackings);
|
return _.some(this._partnerTrackings);
|
||||||
@ -54,7 +55,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
/**
|
/**
|
||||||
* State whether this message contains some email Cc values
|
* State whether this message contains some email Cc values
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasEmailCc: function () {
|
hasEmailCc: function () {
|
||||||
return _.some(this._emailCc);
|
return _.some(this._emailCc);
|
||||||
@ -65,7 +66,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
* If this message has no PartnerTrackings values, returns []
|
* If this message has no PartnerTrackings values, returns []
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @return {Object[]}
|
* @returns {Object[]}
|
||||||
*/
|
*/
|
||||||
getPartnerTrackings: function () {
|
getPartnerTrackings: function () {
|
||||||
if (!this.hasPartnerTrackings()) {
|
if (!this.hasPartnerTrackings()) {
|
||||||
@ -78,7 +79,7 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
* Get the email Cc values of this message
|
* Get the email Cc values of this message
|
||||||
* If this message has no email Cc values, returns []
|
* If this message has no email Cc values, returns []
|
||||||
*
|
*
|
||||||
* @return {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
getEmailCc: function () {
|
getEmailCc: function () {
|
||||||
if (!this.hasEmailCc()) {
|
if (!this.hasEmailCc()) {
|
||||||
@ -91,7 +92,8 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
* Check if the email is an Cc
|
* Check if the email is an Cc
|
||||||
* If this message has no email Cc values, returns false
|
* If this message has no email Cc values, returns false
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @param {String} email
|
||||||
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
isEmailCc: function (email) {
|
isEmailCc: function (email) {
|
||||||
if (!this.hasEmailCc()) {
|
if (!this.hasEmailCc()) {
|
||||||
@ -113,22 +115,10 @@ odoo.define('mail_tracking.partner_tracking', function(require){
|
|||||||
|
|
||||||
ThreadWidget.include({
|
ThreadWidget.include({
|
||||||
events: _.extend(ThreadWidget.prototype.events, {
|
events: _.extend(ThreadWidget.prototype.events, {
|
||||||
'click .o_mail_action_tracking_partner': 'on_tracking_partner_click',
|
'click .o_mail_action_tracking_partner':
|
||||||
|
'on_tracking_partner_click',
|
||||||
'click .o_mail_action_tracking_status': 'on_tracking_status_click',
|
'click .o_mail_action_tracking_status': 'on_tracking_status_click',
|
||||||
}),
|
}),
|
||||||
_preprocess_message: function () {
|
|
||||||
var msg = this._super.apply(this, arguments);
|
|
||||||
msg.partner_trackings = msg.partner_trackings || [];
|
|
||||||
var needs_action = msg.track_needs_action;
|
|
||||||
var message_track = _.findWhere(messages_tracked_changes, {
|
|
||||||
id: msg.id,
|
|
||||||
});
|
|
||||||
if (message_track) {
|
|
||||||
needs_action = message_track.status;
|
|
||||||
}
|
|
||||||
msg.track_needs_action = needs_action;
|
|
||||||
return msg;
|
|
||||||
},
|
|
||||||
on_tracking_partner_click: function (event) {
|
on_tracking_partner_click: function (event) {
|
||||||
var partner_id = this.$el.find(event.currentTarget).data('partner');
|
var partner_id = this.$el.find(event.currentTarget).data('partner');
|
||||||
var state = {
|
var state = {
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<template>
|
|
||||||
|
|
||||||
<t t-name="mail.chat.SidebarFailed">
|
|
||||||
<span class="o_mail_failed_message_refresh hidden"><i class="fa fa-refresh"/> Outdated</span>
|
|
||||||
<span t-attf-class="o_mail_sidebar_failed badge #{(!counter ? 'hide' : '')}">
|
|
||||||
<t t-esc="counter"/>
|
|
||||||
</span>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
<t t-extend="mail.chat.Sidebar">
|
|
||||||
<t t-jquery="div[class='o_mail_chat_sidebar']>hr[class='mb8']" t-operation="before">
|
|
||||||
<div t-attf-class="o_mail_chat_title_main o_mail_chat_channel_item #{(active_channel_id === 'channel_failed') ? 'o_active': ''}"
|
|
||||||
data-channel-id="channel_failed">
|
|
||||||
<span class="o_channel_name"><i class="fa fa-exclamation mr8"/>Failed</span>
|
|
||||||
<t t-set="counter" t-value="failed_counter"/>
|
|
||||||
<t t-call="mail.chat.SidebarFailed"/>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
<t t-extend="mail.EmptyChannel">
|
|
||||||
<t t-jquery="t:last-child" t-operation="after">
|
|
||||||
<t t-if="options.channel_id==='channel_failed'">
|
|
||||||
<div class="o_thread_title">Congratulations, doesn't have failed messages</div>
|
|
||||||
<div>Failed messages appear here.</div>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<templates xml:space="preserve">
|
|
||||||
<t t-name="mail_tracking.failed_message">
|
|
||||||
<div class="o_thread_message" style="margin-bottom: 10px">
|
|
||||||
<div class="o_thread_message_sidebar">
|
|
||||||
<div class="o_avatar_stack">
|
|
||||||
<img t-attf-src="/web/image#{message.author_id[0] >= 0 ? ('/res.partner/' + message.author_id[0] + '/image_small') : ''}" class="o_thread_message_avatar img-circle mb8" t-att-title="message.author_id[1]"/>
|
|
||||||
<i class="o_avatar_icon fa fa-exclamation bg-danger-full" title="Failed"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="o_thread_message_core">
|
|
||||||
<div class="o_mail_info">
|
|
||||||
<strong class="o_thread_author">
|
|
||||||
<t t-esc="message.author_id[1]"/>
|
|
||||||
</strong>
|
|
||||||
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small><br/>
|
|
||||||
<strong class="text-danger">Failed Recipients:</strong>
|
|
||||||
<t t-set="first_recipient" t-value="true"/>
|
|
||||||
<t t-foreach="message.failed_recipients" t-as="recipient">
|
|
||||||
<t t-if="!first_recipient">
|
|
||||||
-
|
|
||||||
</t>
|
|
||||||
<a class="o_mail_action_tracking_partner"
|
|
||||||
t-att-data-partner="recipient[0]"
|
|
||||||
t-attf-href="#model=res.partner&id=#{recipient[0]}">
|
|
||||||
<t t-esc="recipient[1]"/>
|
|
||||||
</a>
|
|
||||||
<t t-set="first_recipient" t-value="false"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
<div class="o_thread_message_note small">
|
|
||||||
<t t-raw="message.body"/>
|
|
||||||
</div>
|
|
||||||
<div class="o_thread_message_tools btn-group">
|
|
||||||
<a href="#" class="btn btn-link btn-success text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.id">
|
|
||||||
<i class="fa fa-check"/> Mark Reviewed
|
|
||||||
</a>
|
|
||||||
<a href="#" class="btn btn-link btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
|
||||||
<i class="fa fa-retweet"/> Retry
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
<t t-name="mail_tracking.failed_message_items">
|
|
||||||
<div class="o_thread_date_separator o_border_dashed collapsed" data-toggle="collapse" data-target="#o_chatter_failed_message">
|
|
||||||
<span class="o_thread_date btn">
|
|
||||||
<i class="fa fa-fw fa-caret-down"/>
|
|
||||||
Failed messages
|
|
||||||
<small class="o_chatter_failed_message_summary ml8">
|
|
||||||
<span class="label img-circle label-danger"><t t-esc="nbFailedMessages"/></span>
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div id="o_chatter_failed_message" class="collapse">
|
|
||||||
<t t-foreach="failed_messages" t-as="message">
|
|
||||||
<t t-call="mail_tracking.failed_message" />
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
</templates>
|
|
15
mail_tracking/static/src/xml/failed_message/common.xml
Normal file
15
mail_tracking/static/src/xml/failed_message/common.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates>
|
||||||
|
|
||||||
|
<t t-extend="mail.widget.Thread.Message">
|
||||||
|
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
|
||||||
|
<a t-if="message.isFailed() && options.displayRetryButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.getID()">
|
||||||
|
<i class="fa fa-check"/> Set as Reviewed
|
||||||
|
</a>
|
||||||
|
<a t-if="message.isFailed() && options.displayReviewedButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.getID()">
|
||||||
|
<i class="fa fa-retweet"/> Retry
|
||||||
|
</a>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
34
mail_tracking/static/src/xml/failed_message/discuss.xml
Normal file
34
mail_tracking/static/src/xml/failed_message/discuss.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates>
|
||||||
|
|
||||||
|
<t t-name="mail_tracking.SidebarFailedCounter">
|
||||||
|
<span t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}">
|
||||||
|
<t t-esc="counter"/>
|
||||||
|
</span>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="mail_tracking.SidebarFailed">
|
||||||
|
<div t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}"
|
||||||
|
data-thread-id="mailbox_failed">
|
||||||
|
<span class="o_thread_name"><i class="fa fa-exclamation mr8"/>Failed</span>
|
||||||
|
<t t-set="counter" t-value="failedCounter"/>
|
||||||
|
<t t-call="mail_tracking.SidebarFailedCounter"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-extend="mail.widget.Thread.Empty">
|
||||||
|
<t t-jquery="t:last-child" t-operation="after">
|
||||||
|
<t t-if="thread.getID() === 'mailbox_failed'">
|
||||||
|
<div class="o_thread_title">Congratulations, you don't have any failed messages</div>
|
||||||
|
<div>Failed messages appear here.</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-extend="mail.discuss.ControlButtons">
|
||||||
|
<t t-jquery="div" t-operation="append">
|
||||||
|
<button type="button" class="btn btn-secondary o_mail_discuss_button_set_all_reviewed d-none d-md-none d-md-inline-block" title="Mark all as reviewed">Set all as reviewed</button>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
60
mail_tracking/static/src/xml/failed_message/thread.xml
Normal file
60
mail_tracking/static/src/xml/failed_message/thread.xml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="mail_tracking.failed_message_items">
|
||||||
|
<div class="o_thread_date_separator o_border_dashed collapsed" data-toggle="collapse" data-target="#o_chatter_failed_message">
|
||||||
|
<a role="button" class="o_thread_date btn">
|
||||||
|
<i class="fa fa-fw fa-caret-down"/>
|
||||||
|
Failed messages
|
||||||
|
<small class="o_chatter_failed_message_summary ml8">
|
||||||
|
<span class="badge rounded-circle badge-danger"><t t-esc="nbFailedMessages"/></span>
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div id="o_chatter_failed_message" class="in collapse">
|
||||||
|
<t t-foreach="failed_messages" t-as="message">
|
||||||
|
<div class="o_thread_message" style="margin-bottom: 10px">
|
||||||
|
<div class="o_thread_message_sidebar">
|
||||||
|
<div class="o_avatar_stack">
|
||||||
|
<img t-attf-src="/web/image/res.partner/#{message.author[0]}/image_small" class="o_thread_message_avatar rounded-circle mb8" t-att-title="message.author[1]" t-att-alt="message.author[1]"/>
|
||||||
|
<i t-att-class="'o_avatar_icon fa fa-exclamation bg-danger-full'"
|
||||||
|
title="Failed"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="o_thread_message_core">
|
||||||
|
<div class="o_mail_info text-muted">
|
||||||
|
<strong class="o_thread_author">
|
||||||
|
<t t-esc="message.author[1]"/>
|
||||||
|
</strong>
|
||||||
|
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small>
|
||||||
|
<span t-attf-class="o_thread_icons">
|
||||||
|
<a href="#" class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link" t-att-data-message-id="message.id">
|
||||||
|
<i class="fa fa-check"/> Set as Reviewed
|
||||||
|
</a>
|
||||||
|
<a href="#" class="btn btn-link btn-default o_thread_icon text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||||
|
<i class="fa fa-retweet"/> Retry
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<br/>
|
||||||
|
<strong class="text-danger">Failed Recipients:</strong>
|
||||||
|
<t t-foreach="message.failed_recipients" t-as="recipient">
|
||||||
|
<t t-if="!recipient_first">
|
||||||
|
-
|
||||||
|
</t>
|
||||||
|
<a class="o_mail_action_tracking_partner"
|
||||||
|
t-att-data-partner="recipient[0]"
|
||||||
|
t-attf-href="#model=res.partner&id=#{recipient[0]}">
|
||||||
|
<t t-esc="recipient[1]"/>
|
||||||
|
</a>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
<div class="o_thread_message_note small">
|
||||||
|
<t t-raw="message.body"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
@ -22,7 +22,8 @@
|
|||||||
</t>
|
</t>
|
||||||
<t t-elif="tracking['status'] === 'error'">
|
<t t-elif="tracking['status'] === 'error'">
|
||||||
<span class="mail_tracking_error mail_tracking_pointer">
|
<span class="mail_tracking_error mail_tracking_pointer">
|
||||||
<i class="fa fa-remove"></i>
|
<i t-if="tracking['error_type'] === 'no_recipient'" class="fa fa-user-times"></i>
|
||||||
|
<i t-else="" class="fa fa-remove"></i>
|
||||||
</span>
|
</span>
|
||||||
</t>
|
</t>
|
||||||
<t t-elif="tracking['status'] === 'sent'">
|
<t t-elif="tracking['status'] === 'sent'">
|
||||||
@ -51,7 +52,7 @@
|
|||||||
<t t-if="message.hasPartnerTrackings() || message.hasEmailCc()" >
|
<t t-if="message.hasPartnerTrackings() || message.hasEmailCc()" >
|
||||||
<p class="o_mail_tracking">
|
<p class="o_mail_tracking">
|
||||||
<strong>To:</strong>
|
<strong>To:</strong>
|
||||||
<t t-foreach="message.partner_trackings" t-as="tracking">
|
<t t-foreach="message.getPartnerTrackings()" t-as="tracking">
|
||||||
<t t-if="!tracking_first">
|
<t t-if="!tracking_first">
|
||||||
-
|
-
|
||||||
</t>
|
</t>
|
||||||
@ -65,9 +66,11 @@
|
|||||||
<t t-else="">
|
<t t-else="">
|
||||||
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t t-esc="tracking['recipient']"/></span>
|
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t t-esc="tracking['recipient']"/></span>
|
||||||
</t>
|
</t>
|
||||||
|
<t t-if="tracking['status'] === 'error' && tracking['error_type'] === 'no_recipient'" t-set="title_status" t-value="tracking['error_description']" />
|
||||||
|
<t t-else="" t-set="title_status" t-value="tracking['status_human']" />
|
||||||
<span class="mail_tracking o_mail_action_tracking_status"
|
<span class="mail_tracking o_mail_action_tracking_status"
|
||||||
t-att-data-tracking="tracking['tracking_id']"
|
t-att-data-tracking="tracking['tracking_id']"
|
||||||
t-att-title="tracking['status_human']">
|
t-att-title="title_status">
|
||||||
<t t-call="mail.tracking.status"/>
|
<t t-call="mail.tracking.status"/>
|
||||||
</span>
|
</span>
|
||||||
</t>
|
</t>
|
||||||
|
@ -5,6 +5,8 @@ import mock
|
|||||||
from odoo.tools import mute_logger
|
from odoo.tools import mute_logger
|
||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
|
import psycopg2
|
||||||
|
import psycopg2.errorcodes
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase
|
||||||
from ..controllers.main import MailTrackingController, BLANK
|
from ..controllers.main import MailTrackingController, BLANK
|
||||||
@ -36,8 +38,9 @@ class TestMailTracking(TransactionCase):
|
|||||||
})
|
})
|
||||||
self.last_request = http.request
|
self.last_request = http.request
|
||||||
http.request = type('obj', (object,), {
|
http.request = type('obj', (object,), {
|
||||||
'db': self.env.cr.dbname,
|
|
||||||
'env': self.env,
|
'env': self.env,
|
||||||
|
'cr': self.env.cr,
|
||||||
|
'db': self.env.cr.dbname,
|
||||||
'endpoint': type('obj', (object,), {
|
'endpoint': type('obj', (object,), {
|
||||||
'routing': [],
|
'routing': [],
|
||||||
}),
|
}),
|
||||||
@ -116,6 +119,30 @@ class TestMailTracking(TransactionCase):
|
|||||||
tracking_email.event_create('open', metadata)
|
tracking_email.event_create('open', metadata)
|
||||||
self.assertEqual(tracking_email.state, 'opened')
|
self.assertEqual(tracking_email.state, 'opened')
|
||||||
|
|
||||||
|
def test_message_post_partner_no_email(self):
|
||||||
|
# Create message with recipient without defined email
|
||||||
|
self.recipient.write({'email': False})
|
||||||
|
message = self.env['mail.message'].create({
|
||||||
|
'subject': 'Message test',
|
||||||
|
'author_id': self.sender.id,
|
||||||
|
'email_from': self.sender.email,
|
||||||
|
'message_type': 'comment',
|
||||||
|
'model': 'res.partner',
|
||||||
|
'res_id': self.recipient.id,
|
||||||
|
'partner_ids': [(4, self.recipient.id)],
|
||||||
|
'body': '<p>This is a test message</p>',
|
||||||
|
})
|
||||||
|
message._notify(message, {}, force_send=True)
|
||||||
|
# Search tracking created
|
||||||
|
tracking_email = self.env['mail.tracking.email'].search([
|
||||||
|
('mail_message_id', '=', message.id),
|
||||||
|
('partner_id', '=', self.recipient.id),
|
||||||
|
])
|
||||||
|
# No email should generate a error state: no_recipient
|
||||||
|
self.assertEqual(tracking_email.state, 'error')
|
||||||
|
self.assertEqual(tracking_email.error_type, 'no_recipient')
|
||||||
|
self.assertFalse(self.recipient.email_bounced)
|
||||||
|
|
||||||
def _check_partner_trackings(self, message):
|
def _check_partner_trackings(self, message):
|
||||||
message_dict = message.message_format()[0]
|
message_dict = message.message_format()[0]
|
||||||
self.assertEqual(len(message_dict['partner_trackings']), 3)
|
self.assertEqual(len(message_dict['partner_trackings']), 3)
|
||||||
@ -136,18 +163,15 @@ class TestMailTracking(TransactionCase):
|
|||||||
self.assertTrue(foundNoPartner)
|
self.assertTrue(foundNoPartner)
|
||||||
|
|
||||||
def test_email_cc(self):
|
def test_email_cc(self):
|
||||||
message = self.env['mail.message'].create({
|
sender_user = self.env['res.users'].create({
|
||||||
'subject': 'Message test',
|
'name': 'Sender User Test',
|
||||||
'author_id': self.sender.id,
|
'partner_id': self.sender.id,
|
||||||
'email_from': self.sender.email,
|
'login': 'sender-test',
|
||||||
'message_type': 'comment',
|
|
||||||
'model': 'res.partner',
|
|
||||||
'res_id': self.recipient.id,
|
|
||||||
'partner_ids': [(4, self.recipient.id)],
|
|
||||||
'email_cc': 'unnamed@test.com, sender@example.com',
|
|
||||||
'body': '<p>This is a test message</p>',
|
|
||||||
})
|
})
|
||||||
self._check_partner_trackings(message)
|
message = self.recipient.sudo(user=sender_user).message_post(
|
||||||
|
body='<p>This is a test message</p>',
|
||||||
|
cc='unnamed@test.com, sender@example.com'
|
||||||
|
)
|
||||||
# suggested recipients
|
# suggested recipients
|
||||||
recipients = self.recipient.message_get_suggested_recipients()
|
recipients = self.recipient.message_get_suggested_recipients()
|
||||||
suggested_mails = {
|
suggested_mails = {
|
||||||
@ -168,31 +192,37 @@ class TestMailTracking(TransactionCase):
|
|||||||
', recipient@example.com',
|
', recipient@example.com',
|
||||||
'body': '<p>This is another test message</p>',
|
'body': '<p>This is another test message</p>',
|
||||||
})
|
})
|
||||||
|
message._notify(message, {}, force_send=True)
|
||||||
recipients = self.recipient.message_get_suggested_recipients()
|
recipients = self.recipient.message_get_suggested_recipients()
|
||||||
self.assertEqual(len(recipients[self.recipient.id][0]), 3)
|
self.assertEqual(len(recipients[self.recipient.id][0]), 3)
|
||||||
self._check_partner_trackings(message)
|
self._check_partner_trackings(message)
|
||||||
|
|
||||||
def test_failed_message(self):
|
def test_failed_message(self):
|
||||||
|
MailMessageObj = self.env['mail.message']
|
||||||
# Create message
|
# Create message
|
||||||
mail, tracking = self.mail_send(self.recipient.email)
|
mail, tracking = self.mail_send(self.recipient.email)
|
||||||
self.assertFalse(tracking.mail_message_id.mail_tracking_needs_action)
|
self.assertFalse(tracking.mail_message_id.mail_tracking_needs_action)
|
||||||
# Force error state
|
# Force error state
|
||||||
tracking.state = 'error'
|
tracking.state = 'error'
|
||||||
self.assertTrue(tracking.mail_message_id.mail_tracking_needs_action)
|
self.assertTrue(tracking.mail_message_id.mail_tracking_needs_action)
|
||||||
failed_count = self.env['mail.message'].get_failed_count()
|
failed_count = MailMessageObj.get_failed_count()
|
||||||
self.assertTrue(failed_count > 0)
|
self.assertTrue(failed_count > 0)
|
||||||
values = tracking.mail_message_id.get_failed_messages()
|
values = tracking.mail_message_id.get_failed_messages()
|
||||||
self.assertEqual(values[0]['id'], tracking.mail_message_id.id)
|
self.assertEqual(values[0]['id'], tracking.mail_message_id.id)
|
||||||
messages = self.env['mail.message'].message_fetch([])
|
messages = MailMessageObj.search([])
|
||||||
messages_failed = self.env['mail.message'].with_context(
|
messages_failed = MailMessageObj.search(
|
||||||
filter_failed_message=True).message_fetch([])
|
MailMessageObj._get_failed_message_domain())
|
||||||
self.assertTrue(messages)
|
self.assertTrue(messages)
|
||||||
self.assertTrue(messages_failed)
|
self.assertTrue(messages_failed)
|
||||||
self.assertTrue(len(messages) > len(messages_failed))
|
self.assertTrue(len(messages) > len(messages_failed))
|
||||||
tracking.mail_message_id.toggle_tracking_status()
|
tracking.mail_message_id.toggle_tracking_status()
|
||||||
self.assertFalse(tracking.mail_message_id.mail_tracking_needs_action)
|
self.assertFalse(tracking.mail_message_id.mail_tracking_needs_action)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
self.env['mail.message'].get_failed_count() < failed_count)
|
MailMessageObj.get_failed_count() < failed_count)
|
||||||
|
# No author_id
|
||||||
|
tracking.mail_message_id.author_id = False
|
||||||
|
values = tracking.mail_message_id.get_failed_messages()[0]
|
||||||
|
self.assertEqual(values['author'][0], -1)
|
||||||
|
|
||||||
def mail_send(self, recipient):
|
def mail_send(self, recipient):
|
||||||
mail = self.env['mail.mail'].create({
|
mail = self.env['mail.mail'].create({
|
||||||
@ -215,16 +245,53 @@ class TestMailTracking(TransactionCase):
|
|||||||
mail, tracking = self.mail_send(self.recipient.email)
|
mail, tracking = self.mail_send(self.recipient.email)
|
||||||
self.assertEqual(mail.email_to, tracking.recipient)
|
self.assertEqual(mail.email_to, tracking.recipient)
|
||||||
self.assertEqual(mail.email_from, tracking.sender)
|
self.assertEqual(mail.email_from, tracking.sender)
|
||||||
res = controller.mail_tracking_open(db, tracking.id)
|
with mock.patch('odoo.http.db_filter') as mock_client:
|
||||||
|
mock_client.return_value = True
|
||||||
|
res = controller.mail_tracking_open(
|
||||||
|
db, tracking.id, tracking.token)
|
||||||
self.assertEqual(image, res.response[0])
|
self.assertEqual(image, res.response[0])
|
||||||
# Two events: sent and open
|
# Two events: sent and open
|
||||||
self.assertEqual(2, len(tracking.tracking_event_ids))
|
self.assertEqual(2, len(tracking.tracking_event_ids))
|
||||||
# Fake event: tracking_email_id = False
|
# Fake event: tracking_email_id = False
|
||||||
res = controller.mail_tracking_open(db, False)
|
res = controller.mail_tracking_open(db, False, False)
|
||||||
self.assertEqual(image, res.response[0])
|
self.assertEqual(image, res.response[0])
|
||||||
# Two events again because no tracking_email_id found for False
|
# Two events again because no tracking_email_id found for False
|
||||||
self.assertEqual(2, len(tracking.tracking_event_ids))
|
self.assertEqual(2, len(tracking.tracking_event_ids))
|
||||||
|
|
||||||
|
def test_mail_tracking_open(self):
|
||||||
|
controller = MailTrackingController()
|
||||||
|
db = self.env.cr.dbname
|
||||||
|
with mock.patch('odoo.http.db_filter') as mock_client:
|
||||||
|
mock_client.return_value = True
|
||||||
|
mail, tracking = self.mail_send(self.recipient.email)
|
||||||
|
# Tracking is in sent or delivered state. But no token give.
|
||||||
|
# Don't generates tracking event
|
||||||
|
controller.mail_tracking_open(db, tracking.id)
|
||||||
|
self.assertEqual(1, len(tracking.tracking_event_ids))
|
||||||
|
tracking.write({'state': 'opened'})
|
||||||
|
# Tracking isn't in sent or delivered state.
|
||||||
|
# Don't generates tracking event
|
||||||
|
controller.mail_tracking_open(db, tracking.id, tracking.token)
|
||||||
|
self.assertEqual(1, len(tracking.tracking_event_ids))
|
||||||
|
tracking.write({'state': 'sent'})
|
||||||
|
# Tracking is in sent or delivered state and a token is given.
|
||||||
|
# Generates tracking event
|
||||||
|
controller.mail_tracking_open(db, tracking.id, tracking.token)
|
||||||
|
self.assertEqual(2, len(tracking.tracking_event_ids))
|
||||||
|
# Generate new email due concurrent event filter
|
||||||
|
mail, tracking = self.mail_send(self.recipient.email)
|
||||||
|
tracking.write({'token': False})
|
||||||
|
# Tracking is in sent or delivered state but a token is given for a
|
||||||
|
# record that doesn't have a token.
|
||||||
|
# Don't generates tracking event
|
||||||
|
controller.mail_tracking_open(db, tracking.id, 'tokentest')
|
||||||
|
self.assertEqual(1, len(tracking.tracking_event_ids))
|
||||||
|
# Tracking is in sent or delivered state and not token is given for
|
||||||
|
# a record that doesn't have a token.
|
||||||
|
# Generates tracking event
|
||||||
|
controller.mail_tracking_open(db, tracking.id, False)
|
||||||
|
self.assertEqual(2, len(tracking.tracking_event_ids))
|
||||||
|
|
||||||
def test_concurrent_open(self):
|
def test_concurrent_open(self):
|
||||||
mail, tracking = self.mail_send(self.recipient.email)
|
mail, tracking = self.mail_send(self.recipient.email)
|
||||||
ts = time.time()
|
ts = time.time()
|
||||||
@ -400,9 +467,11 @@ class TestMailTracking(TransactionCase):
|
|||||||
def test_db(self):
|
def test_db(self):
|
||||||
db = self.env.cr.dbname
|
db = self.env.cr.dbname
|
||||||
controller = MailTrackingController()
|
controller = MailTrackingController()
|
||||||
not_found = controller.mail_tracking_all('not_found_db')
|
with mock.patch('odoo.http.db_filter') as mock_client:
|
||||||
self.assertEqual(b'NOT FOUND', not_found.response[0])
|
mock_client.return_value = True
|
||||||
none = controller.mail_tracking_all(db)
|
with self.assertRaises(psycopg2.OperationalError):
|
||||||
|
controller.mail_tracking_event('not_found_db')
|
||||||
|
none = controller.mail_tracking_event(db)
|
||||||
self.assertEqual(b'NONE', none.response[0])
|
self.assertEqual(b'NONE', none.response[0])
|
||||||
none = controller.mail_tracking_event(db, 'open')
|
none = controller.mail_tracking_event(db, 'open')
|
||||||
self.assertEqual(b'NONE', none.response[0])
|
self.assertEqual(b'NONE', none.response[0])
|
||||||
|
@ -7,13 +7,15 @@
|
|||||||
inherit_id="web.assets_backend">
|
inherit_id="web.assets_backend">
|
||||||
<xpath expr="." position="inside">
|
<xpath expr="." position="inside">
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="/mail_tracking/static/src/css/mail_tracking.less"/>
|
href="/mail_tracking/static/src/css/mail_tracking.scss"/>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="/mail_tracking/static/src/css/failed_message.less"/>
|
href="/mail_tracking/static/src/css/failed_message.scss"/>
|
||||||
<script type="text/javascript"
|
<script type="text/javascript"
|
||||||
src="/mail_tracking/static/src/js/mail_tracking.js"/>
|
src="/mail_tracking/static/src/js/mail_tracking.js"/>
|
||||||
<script type="text/javascript"
|
<script type="text/javascript"
|
||||||
src="/mail_tracking/static/src/js/failed_message.js"/>
|
src="/mail_tracking/static/src/js/failed_message/discuss.js"/>
|
||||||
|
<script type="text/javascript"
|
||||||
|
src="/mail_tracking/static/src/js/failed_message/thread.js"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="email_compose_message_wizard_form">
|
|
||||||
<field name="model">mail.compose.message</field>
|
|
||||||
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="active_domain" position="after">
|
|
||||||
<field name="hide_followers" invisible="1" />
|
|
||||||
</field>
|
|
||||||
<xpath expr="//div[@groups='base.group_user']/span[2]" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible':['|', '|', ('model', '=', False), ('composition_mode', '=', 'mass_mail'), ('hide_followers', '=', True)]}</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
Loading…
Reference in New Issue
Block a user