Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[15.0][FIX] mail_tracking: forward ports #1057

Merged
merged 5 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/** @odoo-module **/
import {patch} from "web.utils";
import {ChatterTopbar} from "@mail/components/chatter_topbar/chatter_topbar";
import {patch} from "web.utils";
const components = {ChatterTopbar};
// Import {rpc}
import rpc from "web.rpc";

patch(
Expand Down
1 change: 0 additions & 1 deletion mail_tracking/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

from . import models
from . import controllers
from .hooks import pre_init_hook
1 change: 0 additions & 1 deletion mail_tracking/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,4 @@
],
},
"demo": ["demo/demo.xml"],
"pre_init_hook": "pre_init_hook",
}
7 changes: 5 additions & 2 deletions mail_tracking/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def _request_metadata(self):
"ua_family": request.user_agent.browser or False,
}

# TODO Remove useless controller
@http.route(
[
"/mail/tracking/all/<string:db>",
Expand Down Expand Up @@ -80,8 +81,10 @@ def mail_tracking_open(self, db, tracking_email_id, token=False, **kw):
metadata = self._request_metadata()
with db_env(db) as env:
try:
tracking_email = env["mail.tracking.email"].search(
[("id", "=", tracking_email_id), ("token", "=", token)]
tracking_email = (
env["mail.tracking.email"]
.sudo()
.search([("id", "=", tracking_email_id), ("token", "=", token)])
)
if not tracking_email:
_logger.warning(
Expand Down
32 changes: 0 additions & 32 deletions mail_tracking/hooks.py

This file was deleted.

1 change: 1 addition & 0 deletions mail_tracking/models/mail_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class MailMessage(models.Model):
mail_tracking_ids = fields.One2many(
comodel_name="mail.tracking.email",
inverse_name="mail_message_id",
auto_join=True,
string="Mail Trackings",
)
mail_tracking_needs_action = fields.Boolean(
Expand Down
89 changes: 85 additions & 4 deletions mail_tracking/models/mail_tracking_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import uuid
from datetime import datetime

from odoo import api, fields, models, tools
from odoo import _, api, fields, models, tools
from odoo.exceptions import AccessError
from odoo.tools import email_split

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -128,11 +129,90 @@ def write(self, vals):
self.mapped("mail_message_id").write({"mail_tracking_needs_action": True})
return res

def _find_allowed_tracking_ids(self):
"""Filter trackings based on related records ACLs"""
# Admins passby this filter
if not self or self.env.user.has_group("base.group_system"):
return self.ids
# Override ORM to get the values directly
self._cr.execute(
"""
SELECT id, mail_message_id, partner_id
FROM mail_tracking_email WHERE id IN %s
""",
(tuple(self.ids),),
)
msg_linked = self._cr.fetchall()
if not msg_linked:
return []
_, msg_ids, partner_ids = zip(*msg_linked)
# Filter messages with their ACL rules avoiding False values fetched in the set
msg_ids = self.env["mail.message"]._search(
[("id", "in", [x for x in msg_ids if x])]
)
partner_ids = self.env["res.partner"]._search(
[("id", "in", [x for x in partner_ids if x])]
)
return [
x[0]
for x in msg_linked
if (x[1] in msg_ids) # We can read the linked message
or (
not any({x[1], x[2]}) and x[3] in partner_ids
) # No linked msg/mail but we can read the linked partner
or (not any({x[1], x[2], x[3]})) # No linked record
]

@api.model
def _search(
self,
args,
offset=0,
limit=None,
order=None,
count=False,
access_rights_uid=None,
):
"""Filter ids based on related records ACLs"""
ids = super()._search(
args, offset, limit, order, count=count, access_rights_uid=access_rights_uid
)
if not self.env.user.has_group("base.group_system") and not count:
ids = self.browse(ids)._find_allowed_tracking_ids()
return ids

def check_access_rule(self, operation):
"""Rely on related messages ACLs"""
super().check_access_rule(operation)
allowed_ids = self._search([("id", "in", self._find_allowed_tracking_ids())])
disallowed_ids = set(self.exists().ids).difference(set(allowed_ids))
if not disallowed_ids:
return
raise AccessError(
_(
"The requested operation cannot be completed due to security "
"restrictions. Please contact your system administrator.\n\n"
"(Document type: %(desc)s, Operation: %(operation)s)"
)
% {"desc": self._description, "operation": operation}
+ " - ({} {}, {} {})".format(
_("Records:"), list(disallowed_ids), _("User:"), self._uid
)
)

def read(self, fields=None, load="_classic_read"):
"""Override to explicitly call check_access_rule, that is not called
by the ORM. It instead directly fetches ir.rules and apply them.
"""
if not self.env.user.has_group("base.group_system"):
self.check_access_rule("read")
return super().read(fields=fields, load=load)

@api.model
def email_is_bounced(self, email):
if not email:
return False
res = self._email_last_tracking_state(email)
res = self.sudo()._email_last_tracking_state(email)
return res and res[0].get("state", "") in {
"rejected",
"error",
Expand All @@ -153,13 +233,13 @@ def _email_last_tracking_state(self, email):
def email_score_from_email(self, email):
if not email:
return 0.0
data = self.read_group(
data = self.sudo().read_group(
[("recipient_address", "=", email.lower())],
["recipient_address", "state"],
["state"],
)
mapped_data = {state["state"]: state["state_count"] for state in data}
return self.with_context(mt_states=mapped_data).email_score()
return self.with_context(mt_states=mapped_data).sudo().email_score()

@api.model
def _email_score_weights(self):
Expand Down Expand Up @@ -372,6 +452,7 @@ def event_create(self, event_type, metadata):
_logger.debug("Concurrent event '%s' discarded", event_type)
return event_ids

# TODO Remove useless method
@api.model
def event_process(self, request, post, metadata, event_type=None):
# Generic event process hook, inherit it and
Expand Down
4 changes: 4 additions & 0 deletions mail_tracking/models/mail_tracking_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ def _process_bounce(self, tracking_email, metadata, event_type, state):
)
return self._process_data(tracking_email, metadata, event_type, state)

@api.model
def process_sent(self, tracking_email, metadata):
return self._process_status(tracking_email, metadata, "sent", "sent")

@api.model
def process_delivered(self, tracking_email, metadata):
return self._process_status(tracking_email, metadata, "delivered", "delivered")
Expand Down
18 changes: 10 additions & 8 deletions mail_tracking/models/res_partner.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ class ResPartner(models.Model):

@api.depends("email")
def _compute_email_score_and_count(self):
self.email_score = 50.0
self.tracking_emails_count = 0
partners_mail = self.filtered("email")
mail_tracking_obj = self.env["mail.tracking.email"]
mt_obj = self.env["mail.tracking.email"].sudo()
for partner in partners_mail:
partner.email_score = self.env[
"mail.tracking.email"
].email_score_from_email(partner.email)
partner.tracking_emails_count = mail_tracking_obj.search_count(
[("recipient_address", "=", partner.email.lower())]
partner.email_score = mt_obj.email_score_from_email(partner.email)
# We don't want performance issues due to heavy ACLs check for large
# recordsets. Our option is to hide the number for regular users.
if not self.env.user.has_group("base.group_system"):
continue
partner.tracking_emails_count = len(
mt_obj._search([("recipient_address", "=", partner.email.lower())])
)
partners_no_mail = self - partners_mail
partners_no_mail.update({"email_score": 50.0, "tracking_emails_count": 0})
3 changes: 1 addition & 2 deletions mail_tracking/static/src/js/chatter.esm.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/** @odoo-module **/

import {attr} from "@mail/model/model_field";
import {
registerFieldPatchModel,
registerInstancePatchModel,
} from "@mail/model/model_core";
import {attr} from "@mail/model/model_field";

registerInstancePatchModel(
"mail.chatter",
Expand Down
3 changes: 2 additions & 1 deletion mail_tracking/static/src/js/discuss/discuss.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ registerInstancePatchModel(
} else {
this.messaging.update({
userSetting: insertAndReplace({
id: -1, // Fake id for guest
// Fake id for guest
id: -1,
}),
});
}
Expand Down
10 changes: 0 additions & 10 deletions mail_tracking/static/src/js/failed_message/mail_failed_box.esm.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
/** @odoo-module **/

// const chatter = require("mail/static/src/models/chatter/chatter.js");
// const useStore = require("mail/static/src/component_hooks/use_store/use_store.js");
import {registerMessagingComponent} from "@mail/utils/messaging_component";
const {Component} = owl;

export class MessageFailedBox extends Component {
constructor(...args) {
super(...args);
}

// Get chatter() {
// return this.env.models["mail.chatter"].get(this.props.chatterLocalId);
// }

_onClickTitle() {
this.chatter.toggleMessageFailedBoxVisibility();
}
Expand Down
8 changes: 4 additions & 4 deletions mail_tracking/static/src/js/models/thread.esm.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/** @odoo-module **/
import {registerNewModel} from "@mail/model/model_core";
import {attr, many2one} from "@mail/model/model_field";
import {registerNewModel} from "@mail/model/model_core";

function factory(dependencies) {
class MessageFailed extends dependencies["mail.model"] {
static convertData(data) {
const data2 = {};
if ("author" in data) {
if (!data.author) {
data2.author = [["unlink-all"]];
} else {
if (data.author) {
data2.author = data.author[1];
data2.author_id = data.author[0];
} else {
data2.author = [["unlink-all"]];
}
}
if ("body" in data) {
Expand Down
28 changes: 17 additions & 11 deletions mail_tracking/tests/test_mail_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import psycopg2.errorcodes

from odoo import http
from odoo.tests import users
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger

Expand Down Expand Up @@ -74,19 +75,24 @@ def test_recipient_address_compute(self):
tracking.write({"recipient": False})
self.assertEqual(False, tracking.recipient_address)

@users("admin", "demo")
def test_message_post(self):
# This message will generate a notification for recipient
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 = (
self.env["mail.message"]
.sudo()
.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>",
}
)
)
if message.is_thread_message():
self.env[message.model].browse(message.res_id)._notify_thread(message)
Expand Down
6 changes: 1 addition & 5 deletions mail_tracking/views/mail_tracking_email_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,7 @@
<field name="name" string="Subject" />
<field name="time" string="Time" />
<field name="date" string="Date" />
<filter
name="sent"
string="Sent"
domain="[('state', 'in', ('sent',))]"
/>
<filter name="sent" string="Sent" domain="[('state', '=', 'sent')]" />
<filter
name="deferred"
string="Deferred"
Expand Down
1 change: 1 addition & 0 deletions mail_tracking/views/res_partner_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
name="tracking_emails_count"
widget="statinfo"
string="Tracking emails"
attrs="{'invisible': [('tracking_emails_count', '=', False)]}"
/>
</button>
</div>
Expand Down