mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 16:52:02 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
25
odoo-bringout-oca-ocb-mail/mail/tests/discuss/__init__.py
Normal file
25
odoo-bringout-oca-ocb-mail/mail/tests/discuss/__init__.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_avatar_acl
|
||||
from . import test_discuss_attachment_controller
|
||||
from . import test_discuss_binary_controller
|
||||
from . import test_discuss_channel_invite
|
||||
from . import test_discuss_action
|
||||
from . import test_discuss_channel
|
||||
from . import test_discuss_channel_access
|
||||
from . import test_discuss_channel_as_guest
|
||||
from . import test_discuss_channel_member
|
||||
from . import test_discuss_mail_presence
|
||||
from . import test_discuss_mention_suggestions
|
||||
from . import test_discuss_message_update_controller
|
||||
from . import test_discuss_reaction_controller
|
||||
from . import test_discuss_res_role
|
||||
from . import test_discuss_sub_channels
|
||||
from . import test_discuss_thread_controller
|
||||
from . import test_guest
|
||||
from . import test_message_controller
|
||||
from . import test_guest_feature
|
||||
from . import test_toggle_upload
|
||||
from . import test_load_messages
|
||||
from . import test_rtc
|
||||
from . import test_ui
|
||||
BIN
odoo-bringout-oca-ocb-mail/mail/tests/discuss/files/test_AES.pdf
Normal file
BIN
odoo-bringout-oca-ocb-mail/mail/tests/discuss/files/test_AES.pdf
Normal file
Binary file not shown.
Binary file not shown.
204
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_avatar_acl.py
Normal file
204
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_avatar_acl.py
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, Command
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestAvatarAcl(HttpCase):
|
||||
def get_avatar_url(self, record, add_token=False):
|
||||
access_token = ""
|
||||
if add_token:
|
||||
access_token = f"&access_token={record._get_avatar_128_access_token()}"
|
||||
return f"/web/image?field=avatar_128&id={record.id}&model={record._name}&unique={fields.Datetime.to_string(record.write_date)}{access_token}"
|
||||
|
||||
def test_partner_open_guest_avatar(self):
|
||||
self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
self.authenticate("testuser", "testuser")
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
res = self.url_open(url=self.get_avatar_url(guest))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=Guest.svg")
|
||||
|
||||
def test_partner_open_guest_avatar_with_channel(self):
|
||||
testuser = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
self.authenticate("testuser", "testuser")
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
channel._add_members(guests=guest, users=testuser)
|
||||
res = self.url_open(url=self.get_avatar_url(guest))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=Guest.svg")
|
||||
|
||||
def test_guest_open_partner_avatar(self):
|
||||
self.authenticate(None, None)
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
self.opener.cookies[guest._cookie_name] = guest._format_auth_cookie()
|
||||
testuser = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
partner = self.env["res.users"].browse(testuser.id).partner_id
|
||||
res = self.url_open(url=self.get_avatar_url(partner))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
|
||||
def test_guest_open_partner_avatar_with_channel(self):
|
||||
self.authenticate(None, None)
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
self.opener.cookies[guest._cookie_name] = guest._format_auth_cookie()
|
||||
testuser = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
channel._add_members(guests=guest, users=testuser)
|
||||
res = self.url_open(url=self.get_avatar_url(testuser.partner_id))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
res = self.url_open(url=self.get_avatar_url(testuser.partner_id, add_token=True))
|
||||
self.assertEqual(res.headers["Content-Disposition"], f'inline; filename="{testuser.partner_id.name}.svg"')
|
||||
|
||||
def test_partner_open_partner_avatar(self):
|
||||
testuser = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
self.authenticate("testuser", "testuser")
|
||||
testuser2 = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser2@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User 2",
|
||||
"login": "testuser 2",
|
||||
"password": "testuser 2",
|
||||
}
|
||||
)
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
channel._add_members(users=testuser | testuser2)
|
||||
res = self.url_open(url=self.get_avatar_url(testuser2.partner_id))
|
||||
self.assertEqual(res.headers["Content-Disposition"], f'inline; filename="{testuser2.partner_id.name}.svg"')
|
||||
|
||||
def test_guest_open_guest_avatar(self):
|
||||
self.authenticate(None, None)
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
self.opener.cookies[guest._cookie_name] = guest._format_auth_cookie()
|
||||
guest2 = self.env["mail.guest"].create({"name": "Guest 2"})
|
||||
res = self.url_open(url=self.get_avatar_url(guest2))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
|
||||
def test_guest_open_guest_avatar_with_channel(self):
|
||||
self.authenticate(None, None)
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
self.opener.cookies[guest._cookie_name] = guest._format_auth_cookie()
|
||||
guest2 = self.env["mail.guest"].create({"name": "Guest 2"})
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
channel._add_members(guests=guest | guest2)
|
||||
res = self.url_open(url=self.get_avatar_url(guest2))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
res = self.url_open(url=self.get_avatar_url(guest2, add_token=True))
|
||||
self.assertEqual(res.headers["Content-Disposition"], 'inline; filename="Guest 2.svg"')
|
||||
|
||||
def test_portal_open_partner_avatar(self):
|
||||
self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_portal")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
self.authenticate("testuser", "testuser")
|
||||
testuser2 = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser2@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User 2",
|
||||
"login": "testuser 2",
|
||||
"password": "testuser 2",
|
||||
}
|
||||
)
|
||||
partner2 = self.env["res.users"].browse(testuser2.id).partner_id
|
||||
res = self.url_open(url=self.get_avatar_url(partner2))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
|
||||
def test_portal_open_partner_avatar_with_channel(self):
|
||||
testuser = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_portal")])],
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
"password": "testuser",
|
||||
}
|
||||
)
|
||||
self.authenticate("testuser", "testuser")
|
||||
testuser2 = self.env["res.users"].create(
|
||||
{
|
||||
"email": "testuser2@testuser.com",
|
||||
"group_ids": [Command.set([self.ref("base.group_user")])],
|
||||
"name": "Test User 2",
|
||||
"login": "testuser 2",
|
||||
"password": "testuser 2",
|
||||
}
|
||||
)
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
channel._add_members(users=testuser | testuser2)
|
||||
res = self.url_open(url=self.get_avatar_url(testuser2.partner_id))
|
||||
self.assertEqual(res.headers["Content-Disposition"], "inline; filename=placeholder.png")
|
||||
res = self.url_open(url=self.get_avatar_url(testuser2.partner_id, add_token=True))
|
||||
self.assertEqual(res.headers["Content-Disposition"], f'inline; filename="{testuser2.partner_id.name}.svg"')
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
|
||||
try:
|
||||
import websocket as ws
|
||||
except ImportError:
|
||||
ws = None
|
||||
|
||||
from odoo.tests import tagged, new_test_user
|
||||
from odoo.addons.bus.tests.common import WebsocketCase
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.addons.bus.models.bus import channel_with_db, json_dump
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestBusPresence(WebsocketCase, MailCommon):
|
||||
def _receive_presence(self, sender, recipient):
|
||||
self._reset_bus()
|
||||
sent_from_user = isinstance(sender, self.env.registry["res.users"])
|
||||
receive_to_user = isinstance(recipient, self.env.registry["res.users"])
|
||||
if receive_to_user:
|
||||
session = self.authenticate(recipient.login, recipient.login)
|
||||
auth_cookie = f"session_id={session.sid};"
|
||||
else:
|
||||
self.authenticate(None, None)
|
||||
auth_cookie = f"{recipient._cookie_name}={recipient._format_auth_cookie()};"
|
||||
websocket = self.websocket_connect(cookie=auth_cookie)
|
||||
sender_bus_target = sender.partner_id if sent_from_user else sender
|
||||
self.subscribe(
|
||||
websocket,
|
||||
[f"odoo-presence-{sender_bus_target._name}_{sender_bus_target.id}"],
|
||||
self.env["bus.bus"]._bus_last_id(),
|
||||
)
|
||||
self.env["bus.presence"].create(
|
||||
{"user_id" if sent_from_user else "guest_id": sender.id, "status": "online"}
|
||||
)
|
||||
self.trigger_notification_dispatching([(sender_bus_target, "presence")])
|
||||
notifications = json.loads(websocket.recv())
|
||||
self._close_websockets()
|
||||
bus_record = self.env["bus.bus"].search([("id", "=", int(notifications[0]["id"]))])
|
||||
self.assertEqual(
|
||||
bus_record.channel,
|
||||
json_dump(channel_with_db(self.env.cr.dbname, (sender_bus_target, "presence"))),
|
||||
)
|
||||
self.assertEqual(notifications[0]["message"]["type"], "bus.bus/im_status_updated")
|
||||
self.assertEqual(notifications[0]["message"]["payload"]["im_status"], "online")
|
||||
self.assertEqual(notifications[0]["message"]["payload"]["presence_status"], "online")
|
||||
self.assertEqual(
|
||||
notifications[0]["message"]["payload"]["partner_id" if sent_from_user else "guest_id"],
|
||||
sender_bus_target.id,
|
||||
)
|
||||
|
||||
def test_receive_presences_as_guest(self):
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
bob = new_test_user(self.env, login="bob_user", groups="base.group_user")
|
||||
# Guest should not receive users's presence: no common channel.
|
||||
with self.assertRaises(ws._exceptions.WebSocketTimeoutException):
|
||||
self._receive_presence(sender=bob, recipient=guest)
|
||||
channel = self.env["discuss.channel"].channel_create(group_id=None, name="General")
|
||||
channel.add_members(guest_ids=[guest.id], partner_ids=[bob.partner_id.id])
|
||||
# Now that they share a channel, guest should receive users's presence.
|
||||
self._receive_presence(sender=bob, recipient=guest)
|
||||
|
||||
other_guest = self.env["mail.guest"].create({"name": "OtherGuest"})
|
||||
# Guest should not receive guest's presence: no common channel.
|
||||
with self.assertRaises(ws._exceptions.WebSocketTimeoutException):
|
||||
self._receive_presence(sender=other_guest, recipient=guest)
|
||||
channel.add_members(guest_ids=[other_guest.id])
|
||||
# Now that they share a channel, guest should receive guest's presence.
|
||||
self._receive_presence(sender=other_guest, recipient=guest)
|
||||
|
||||
def test_receive_presences_as_portal(self):
|
||||
portal = new_test_user(self.env, login="portal_user", groups="base.group_portal")
|
||||
bob = new_test_user(self.env, login="bob_user", groups="base.group_user")
|
||||
# Portal should not receive users's presence: no common channel.
|
||||
with self.assertRaises(ws._exceptions.WebSocketTimeoutException):
|
||||
self._receive_presence(sender=bob, recipient=portal)
|
||||
channel = self.env["discuss.channel"].channel_create(group_id=None, name="General")
|
||||
channel.add_members(partner_ids=[portal.partner_id.id, bob.partner_id.id])
|
||||
# Now that they share a channel, portal should receive users's presence.
|
||||
self._receive_presence(sender=bob, recipient=portal)
|
||||
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
# Portal should not receive guest's presence: no common channel.
|
||||
with self.assertRaises(ws._exceptions.WebSocketTimeoutException):
|
||||
self._receive_presence(sender=guest, recipient=portal)
|
||||
channel.add_members(guest_ids=[guest.id])
|
||||
# Now that they share a channel, portal should receive guest's presence.
|
||||
self._receive_presence(sender=guest, recipient=portal)
|
||||
|
||||
def test_receive_presences_as_internal(self):
|
||||
internal = new_test_user(self.env, login="internal_user", groups="base.group_user")
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
# Internal can access guest's presence regardless of their channels.
|
||||
self._receive_presence(sender=guest, recipient=internal)
|
||||
# Internal can access users's presence regardless of their channels.
|
||||
bob = new_test_user(self.env, login="bob_user", groups="base.group_user")
|
||||
self._receive_presence(sender=bob, recipient=internal)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install", "discuss_action")
|
||||
class TestDiscussAction(HttpCase, MailCommon):
|
||||
def test_go_back_to_thread_from_breadcrumbs(self):
|
||||
self.start_tour(
|
||||
"/odoo/discuss?active_id=mail.box_inbox",
|
||||
"discuss_go_back_to_thread_from_breadcrumbs.js",
|
||||
login="admin",
|
||||
)
|
||||
|
||||
def test_join_call_with_client_action(self):
|
||||
inviting_user = self.env['res.users'].sudo().create({'name': "Inviting User", 'login': 'inviting'})
|
||||
invited_user = self.env['res.users'].sudo().create({'name': "Invited User", 'login': 'invited'})
|
||||
channel = self.env['discuss.channel'].with_user(inviting_user)._get_or_create_chat(partners_to=invited_user.partner_id.ids)
|
||||
channel_member = channel.sudo().channel_member_ids.filtered(
|
||||
lambda channel_member: channel_member.partner_id == inviting_user.partner_id)
|
||||
self._reset_bus()
|
||||
channel_member._rtc_join_call()
|
||||
self.start_tour(
|
||||
f"/odoo/{channel.id}/action-mail.action_discuss?call=accept",
|
||||
"discuss_channel_call_action.js",
|
||||
login=invited_user.login,
|
||||
)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
from odoo.addons.mail.tests.common_controllers import MailControllerAttachmentCommon
|
||||
from odoo.tools.misc import file_open
|
||||
|
||||
|
||||
@odoo.tests.tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestDiscussAttachmentController(MailControllerAttachmentCommon):
|
||||
def test_attachment_allowed_upload_public_channel(self):
|
||||
"""Test access to upload an attachment on an allowed upload public channel"""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"group_public_id": None, "name": "public channel"}
|
||||
)
|
||||
channel._add_members(guests=self.guest)
|
||||
channel = channel.with_context(guest=self.guest)
|
||||
self._execute_subtests_upload(
|
||||
channel,
|
||||
(
|
||||
(self.guest, True),
|
||||
(self.user_admin, True),
|
||||
(self.user_employee, True),
|
||||
(self.user_portal, True),
|
||||
(self.user_public, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_attachment_delete_linked_to_public_channel(self):
|
||||
"""Test access to delete an attachment associated with a public channel
|
||||
whether or not limited `ownership_token` is sent"""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"group_public_id": None, "name": "public channel"}
|
||||
)
|
||||
self._execute_subtests_delete(self.all_users, token=True, allowed=True, thread=channel)
|
||||
self._execute_subtests_delete(
|
||||
(self.user_admin, self.user_employee),
|
||||
token=False,
|
||||
allowed=True,
|
||||
thread=channel,
|
||||
)
|
||||
self._execute_subtests_delete(
|
||||
(self.guest, self.user_portal, self.user_public),
|
||||
token=False,
|
||||
allowed=False,
|
||||
thread=channel,
|
||||
)
|
||||
|
||||
def test_attachment_delete_linked_to_private_channel(self):
|
||||
"""Test access to delete an attachment associated with a private channel
|
||||
whether or not limited `ownership_token` is sent"""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"name": "Private Channel", "channel_type": "group"}
|
||||
)
|
||||
self._execute_subtests_delete(self.all_users, token=True, allowed=True, thread=channel)
|
||||
self._execute_subtests_delete(self.user_admin, token=False, allowed=True, thread=channel)
|
||||
self._execute_subtests_delete(
|
||||
(self.guest, self.user_employee, self.user_portal, self.user_public),
|
||||
token=False,
|
||||
allowed=False,
|
||||
thread=channel,
|
||||
)
|
||||
|
||||
def test_first_page_access_of_mail_attachment_pdf(self):
|
||||
"""Test accessing the first page of a PDF that is encrypted(test_AES.pdf) or has invalid encoding(test_unicode.pdf)."""
|
||||
attachments = []
|
||||
for pdf in (
|
||||
'mail/tests/discuss/files/test_AES.pdf',
|
||||
'mail/tests/discuss/files/test_unicode.pdf',
|
||||
):
|
||||
with file_open(pdf, "rb") as file:
|
||||
attachments.append({
|
||||
'name': pdf,
|
||||
'raw': file.read(),
|
||||
'mimetype': 'application/pdf',
|
||||
})
|
||||
attachments = self.env['ir.attachment'].create(attachments)
|
||||
|
||||
self.authenticate("admin", "admin")
|
||||
|
||||
for attachment in attachments:
|
||||
ownership_token = attachment._get_ownership_token()
|
||||
url = f'/mail/attachment/pdf_first_page/{attachment.id}?access_token={ownership_token}'
|
||||
response = self.url_open(url)
|
||||
# Depending on the environment, the response status_code may vary:
|
||||
# - 200 if PyPDF2 and PyCryptodome are installed (PDF successfully parsed)
|
||||
# - 415 if those libs are missing (PDF cannot be processed)
|
||||
self.assertIn(response.status_code, [415, 200])
|
||||
|
|
@ -0,0 +1,390 @@
|
|||
from odoo.addons.mail.tests.common_controllers import MailControllerBinaryCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestDiscussBinaryController(MailControllerBinaryCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.private_channel = cls.env["discuss.channel"].create(
|
||||
{"name": "Private Channel", "channel_type": "group"}
|
||||
)
|
||||
cls.public_channel = cls.env["discuss.channel"]._create_channel(
|
||||
name="Public Channel", group_id=None
|
||||
)
|
||||
cls.users = (
|
||||
cls.user_public + cls.user_portal + cls.user_employee + cls.user_admin
|
||||
)
|
||||
|
||||
def test_open_guest_avatar(self):
|
||||
"""Test access to open the avatar of a guest.
|
||||
There is no common channel or any interaction from the guest."""
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_01_guest_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: False"""
|
||||
self.private_channel._add_members(users=self.users, guests=self.guest | self.guest_2)
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_01_partner_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: False"""
|
||||
self.private_channel._add_members(
|
||||
users=self.users | self.user_employee_nopartner, guests=self.guest
|
||||
)
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_02_guest_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: True"""
|
||||
self.private_channel._add_members(users=self.users, guests=self.guest | self.guest_2)
|
||||
self._post_message(self.private_channel, self.guest_2)
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_02_partner_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: True"""
|
||||
self.private_channel._add_members(
|
||||
users=self.users | self.user_employee_nopartner, guests=self.guest
|
||||
)
|
||||
self._post_message(self.private_channel, self.user_employee_nopartner)
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_03_guest_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: False
|
||||
- target leaves the channel: True"""
|
||||
self.private_channel._add_members(users=self.users, guests=self.guest | self.guest_2)
|
||||
self.env["discuss.channel.member"].search(
|
||||
[("guest_id", "=", self.guest_2.id), ("channel_id", "=", self.private_channel.id)]
|
||||
).unlink()
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_03_partner_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: False
|
||||
- target leaves the channel: True"""
|
||||
self.private_channel._add_members(
|
||||
users=self.users | self.user_employee_nopartner, guests=self.guest
|
||||
)
|
||||
self.env["discuss.channel.member"].search(
|
||||
[
|
||||
("partner_id", "=", self.user_employee_nopartner.partner_id.id),
|
||||
("channel_id", "=", self.private_channel.id),
|
||||
]
|
||||
).unlink()
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_04_guest_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: True
|
||||
- target leaves the channel: True"""
|
||||
self.private_channel._add_members(users=self.users, guests=self.guest | self.guest_2)
|
||||
self._post_message(self.private_channel, self.guest_2)
|
||||
self.env["discuss.channel.member"].search(
|
||||
[("guest_id", "=", self.guest_2.id), ("channel_id", "=", self.private_channel.id)]
|
||||
).unlink()
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_04_partner_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: group
|
||||
- target joins the channel: True
|
||||
- other users join the channel: True
|
||||
- target sends a message: True
|
||||
- target leaves the channel: True"""
|
||||
self.private_channel._add_members(
|
||||
users=self.users | self.user_employee_nopartner, guests=self.guest
|
||||
)
|
||||
self._post_message(self.private_channel, self.user_employee_nopartner)
|
||||
self.env["discuss.channel.member"].search(
|
||||
[
|
||||
("partner_id", "=", self.user_employee_nopartner.partner_id.id),
|
||||
("channel_id", "=", self.private_channel.id),
|
||||
]
|
||||
).unlink()
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_05_guest_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: group
|
||||
- target joins the channel: False
|
||||
- other users join the channel: False
|
||||
- target sends a message: True"""
|
||||
self.private_channel.with_user(self.user_public).with_context(
|
||||
guest=self.guest_2
|
||||
).sudo().message_post(body="Test", subtype_xmlid="mail.mt_comment", message_type="comment")
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_05_partner_avatar_private_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: group
|
||||
- target joins the channel: False
|
||||
- other users join the channel: False
|
||||
- target sends a message: True"""
|
||||
self.private_channel.message_post(
|
||||
body="Test",
|
||||
subtype_xmlid="mail.mt_comment",
|
||||
message_type="comment",
|
||||
author_id=self.user_employee_nopartner.partner_id.id,
|
||||
)
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_01_guest_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: public
|
||||
- target joins the channel: False
|
||||
- other users join the channel: False
|
||||
- target sends a message: True"""
|
||||
self.public_channel.with_user(self.user_public).with_context(
|
||||
guest=self.guest_2
|
||||
).sudo().message_post(body="Test", subtype_xmlid="mail.mt_comment", message_type="comment")
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_01_partner_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: public
|
||||
- target joins the channel: False
|
||||
- other users join the channel: False
|
||||
- target sends a message: True"""
|
||||
self._post_message(self.public_channel, self.user_employee_nopartner)
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_02_guest_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: public
|
||||
- target joins the channel: True
|
||||
- other users join the channel: False
|
||||
- target sends a message: False
|
||||
- target leaves the channel: True"""
|
||||
target_member = self.public_channel._add_members(guests=self.guest_2)
|
||||
target_member.unlink()
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_02_partner_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: public
|
||||
- target joins the channel: True
|
||||
- other users join the channel: False
|
||||
- target sends a message: False
|
||||
- target leaves the channel: True"""
|
||||
target_member = self.public_channel._add_members(users=self.user_employee_nopartner)
|
||||
target_member.unlink()
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_03_guest_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: guest
|
||||
- channel type: public
|
||||
- target joins the channel: True
|
||||
- other users join the channel: False
|
||||
- target sends a message: True
|
||||
- target leaves the channel: True"""
|
||||
target_member = self.public_channel._add_members(guests=self.guest_2)
|
||||
self._post_message(self.public_channel, self.guest_2)
|
||||
target_member.unlink()
|
||||
self._execute_subtests(
|
||||
self.guest_2,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_03_partner_avatar_public_channel(self):
|
||||
"""Test access to open the avatar:
|
||||
- target type: partner
|
||||
- channel type: public
|
||||
- target joins the channel: True
|
||||
- other users join the channel: False
|
||||
- target sends a message: True
|
||||
- target leaves the channel: True"""
|
||||
target_member = self.public_channel._add_members(users=self.user_employee_nopartner)
|
||||
self._post_message(self.public_channel, self.user_employee_nopartner)
|
||||
target_member.unlink()
|
||||
self._execute_subtests(
|
||||
self.user_employee_nopartner.partner_id,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,572 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from psycopg2.errors import UniqueViolation
|
||||
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.tests.common import tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestDiscussChannelAccess(MailCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls._channel_type_channel_access_cases = [
|
||||
("public", "no_group", "member", "read", True),
|
||||
("public", "no_group", "member", "write", False),
|
||||
("public", "no_group", "member", "unlink", False),
|
||||
("public", "no_group", "outside", "create", False),
|
||||
("public", "no_group", "outside", "read", True),
|
||||
("public", "no_group", "outside", "write", False),
|
||||
("public", "no_group", "outside", "unlink", False),
|
||||
("public", "group_matching", "member", "read", True),
|
||||
("public", "group_matching", "member", "write", False),
|
||||
("public", "group_matching", "member", "unlink", False),
|
||||
("public", "group_matching", "outside", "create", False),
|
||||
("public", "group_matching", "outside", "read", True),
|
||||
("public", "group_matching", "outside", "write", False),
|
||||
("public", "group_matching", "outside", "unlink", False),
|
||||
("public", "group_failing", "member", "read", False),
|
||||
("public", "group_failing", "member", "write", False),
|
||||
("public", "group_failing", "member", "unlink", False),
|
||||
("public", "group_failing", "outside", "create", False),
|
||||
("public", "group_failing", "outside", "read", False),
|
||||
("public", "group_failing", "outside", "write", False),
|
||||
("public", "group_failing", "outside", "unlink", False),
|
||||
("portal", "no_group", "member", "read", True),
|
||||
("portal", "no_group", "member", "write", False),
|
||||
("portal", "no_group", "member", "unlink", False),
|
||||
("portal", "no_group", "outside", "create", False),
|
||||
("portal", "no_group", "outside", "read", True),
|
||||
("portal", "no_group", "outside", "write", False),
|
||||
("portal", "no_group", "outside", "unlink", False),
|
||||
("portal", "group_matching", "member", "read", True),
|
||||
("portal", "group_matching", "member", "write", False),
|
||||
("portal", "group_matching", "member", "unlink", False),
|
||||
("portal", "group_matching", "outside", "create", False),
|
||||
("portal", "group_matching", "outside", "read", True),
|
||||
("portal", "group_matching", "outside", "write", False),
|
||||
("portal", "group_matching", "outside", "unlink", False),
|
||||
("portal", "group_failing", "member", "read", False),
|
||||
("portal", "group_failing", "member", "write", False),
|
||||
("portal", "group_failing", "member", "unlink", False),
|
||||
("portal", "group_failing", "outside", "create", False),
|
||||
("portal", "group_failing", "outside", "read", False),
|
||||
("portal", "group_failing", "outside", "write", False),
|
||||
("portal", "group_failing", "outside", "unlink", False),
|
||||
("user", "no_group", "member", "read", True),
|
||||
("user", "no_group", "member", "write", True),
|
||||
("user", "no_group", "member", "unlink", False),
|
||||
("user", "no_group", "outside", "create", True),
|
||||
("user", "no_group", "outside", "read", True),
|
||||
("user", "no_group", "outside", "write", True),
|
||||
("user", "no_group", "outside", "unlink", False),
|
||||
("user", "group_matching", "member", "read", True),
|
||||
("user", "group_matching", "member", "write", True),
|
||||
("user", "group_matching", "member", "unlink", False),
|
||||
("user", "group_matching", "outside", "create", True),
|
||||
("user", "group_matching", "outside", "read", True),
|
||||
("user", "group_matching", "outside", "write", True),
|
||||
("user", "group_matching", "outside", "unlink", False),
|
||||
("user", "group_failing", "member", "read", False),
|
||||
("user", "group_failing", "member", "write", False),
|
||||
("user", "group_failing", "member", "unlink", False),
|
||||
("user", "group_failing", "outside", "create", False),
|
||||
("user", "group_failing", "outside", "read", False),
|
||||
("user", "group_failing", "outside", "write", False),
|
||||
("user", "group_failing", "outside", "unlink", False),
|
||||
]
|
||||
cls._channel_type_channel_member_access_cases = [
|
||||
("public", "no_group", "member", "self", "create", False),
|
||||
("public", "no_group", "member", "self", "read", True),
|
||||
("public", "no_group", "member", "self", "write", True),
|
||||
("public", "no_group", "member", "self", "unlink", True),
|
||||
("public", "no_group", "member", "other", "create", False),
|
||||
("public", "no_group", "member", "other", "read", True),
|
||||
("public", "no_group", "member", "other", "write", False),
|
||||
("public", "no_group", "member", "other", "unlink", False),
|
||||
("public", "no_group", "outside", "self", "create", True),
|
||||
("public", "no_group", "outside", "other", "create", False),
|
||||
("public", "no_group", "outside", "other", "read", True),
|
||||
("public", "no_group", "outside", "other", "write", False),
|
||||
("public", "no_group", "outside", "other", "unlink", False),
|
||||
("public", "group_matching", "member", "self", "create", False),
|
||||
("public", "group_matching", "member", "self", "read", True),
|
||||
("public", "group_matching", "member", "self", "write", True),
|
||||
("public", "group_matching", "member", "self", "unlink", True),
|
||||
("public", "group_matching", "member", "other", "create", False),
|
||||
("public", "group_matching", "member", "other", "read", True),
|
||||
("public", "group_matching", "member", "other", "write", False),
|
||||
("public", "group_matching", "member", "other", "unlink", False),
|
||||
("public", "group_matching", "outside", "self", "create", True),
|
||||
("public", "group_matching", "outside", "other", "create", False),
|
||||
("public", "group_matching", "outside", "other", "read", True),
|
||||
("public", "group_matching", "outside", "other", "write", False),
|
||||
("public", "group_matching", "outside", "other", "unlink", False),
|
||||
("public", "group_failing", "member", "self", "create", False),
|
||||
("public", "group_failing", "member", "self", "read", False),
|
||||
("public", "group_failing", "member", "self", "write", False),
|
||||
("public", "group_failing", "member", "self", "unlink", False),
|
||||
("public", "group_failing", "member", "other", "create", False),
|
||||
("public", "group_failing", "member", "other", "read", False),
|
||||
("public", "group_failing", "member", "other", "write", False),
|
||||
("public", "group_failing", "member", "other", "unlink", False),
|
||||
("public", "group_failing", "outside", "self", "create", False),
|
||||
("public", "group_failing", "outside", "other", "create", False),
|
||||
("public", "group_failing", "outside", "other", "read", False),
|
||||
("public", "group_failing", "outside", "other", "write", False),
|
||||
("public", "group_failing", "outside", "other", "unlink", False),
|
||||
("portal", "no_group", "member", "self", "create", False),
|
||||
("portal", "no_group", "member", "self", "read", True),
|
||||
("portal", "no_group", "member", "self", "write", True),
|
||||
("portal", "no_group", "member", "self", "unlink", True),
|
||||
("portal", "no_group", "member", "other", "create", False),
|
||||
("portal", "no_group", "member", "other", "read", True),
|
||||
("portal", "no_group", "member", "other", "write", False),
|
||||
("portal", "no_group", "member", "other", "unlink", False),
|
||||
("portal", "no_group", "outside", "self", "create", True),
|
||||
("portal", "no_group", "outside", "other", "create", False),
|
||||
("portal", "no_group", "outside", "other", "read", True),
|
||||
("portal", "no_group", "outside", "other", "write", False),
|
||||
("portal", "no_group", "outside", "other", "unlink", False),
|
||||
("portal", "group_matching", "member", "self", "create", False),
|
||||
("portal", "group_matching", "member", "self", "read", True),
|
||||
("portal", "group_matching", "member", "self", "write", True),
|
||||
("portal", "group_matching", "member", "self", "unlink", True),
|
||||
("portal", "group_matching", "member", "other", "create", False),
|
||||
("portal", "group_matching", "member", "other", "read", True),
|
||||
("portal", "group_matching", "member", "other", "write", False),
|
||||
("portal", "group_matching", "member", "other", "unlink", False),
|
||||
("portal", "group_matching", "outside", "self", "create", True),
|
||||
("portal", "group_matching", "outside", "other", "create", False),
|
||||
("portal", "group_matching", "outside", "other", "read", True),
|
||||
("portal", "group_matching", "outside", "other", "write", False),
|
||||
("portal", "group_matching", "outside", "other", "unlink", False),
|
||||
("portal", "group_failing", "member", "self", "create", False),
|
||||
("portal", "group_failing", "member", "self", "read", False),
|
||||
("portal", "group_failing", "member", "self", "write", False),
|
||||
("portal", "group_failing", "member", "self", "unlink", False),
|
||||
("portal", "group_failing", "member", "other", "create", False),
|
||||
("portal", "group_failing", "member", "other", "read", False),
|
||||
("portal", "group_failing", "member", "other", "write", False),
|
||||
("portal", "group_failing", "member", "other", "unlink", False),
|
||||
("portal", "group_failing", "outside", "self", "create", False),
|
||||
("portal", "group_failing", "outside", "other", "create", False),
|
||||
("portal", "group_failing", "outside", "other", "read", False),
|
||||
("portal", "group_failing", "outside", "other", "write", False),
|
||||
("portal", "group_failing", "outside", "other", "unlink", False),
|
||||
("user", "no_group", "member", "self", "create", False),
|
||||
("user", "no_group", "member", "self", "read", True),
|
||||
("user", "no_group", "member", "self", "write", True),
|
||||
("user", "no_group", "member", "self", "unlink", True),
|
||||
("user", "no_group", "member", "other", "create", True),
|
||||
("user", "no_group", "member", "other", "read", True),
|
||||
("user", "no_group", "member", "other", "write", False),
|
||||
("user", "no_group", "member", "other", "unlink", False),
|
||||
("user", "no_group", "outside", "self", "create", True),
|
||||
("user", "no_group", "outside", "other", "create", True),
|
||||
("user", "no_group", "outside", "other", "read", True),
|
||||
("user", "no_group", "outside", "other", "write", False),
|
||||
("user", "no_group", "outside", "other", "unlink", False),
|
||||
("user", "group_matching", "member", "self", "create", False),
|
||||
("user", "group_matching", "member", "self", "read", True),
|
||||
("user", "group_matching", "member", "self", "write", True),
|
||||
("user", "group_matching", "member", "self", "unlink", True),
|
||||
("user", "group_matching", "member", "other", "create", True),
|
||||
("user", "group_matching", "member", "other", "read", True),
|
||||
("user", "group_matching", "member", "other", "write", False),
|
||||
("user", "group_matching", "member", "other", "unlink", False),
|
||||
("user", "group_matching", "outside", "self", "create", True),
|
||||
("user", "group_matching", "outside", "other", "create", True),
|
||||
("user", "group_matching", "outside", "other", "read", True),
|
||||
("user", "group_matching", "outside", "other", "write", False),
|
||||
("user", "group_matching", "outside", "other", "unlink", False),
|
||||
("user", "group_failing", "member", "self", "create", False),
|
||||
("user", "group_failing", "member", "self", "read", False),
|
||||
("user", "group_failing", "member", "self", "write", False),
|
||||
("user", "group_failing", "member", "self", "unlink", False),
|
||||
("user", "group_failing", "member", "other", "create", False),
|
||||
("user", "group_failing", "member", "other", "read", False),
|
||||
("user", "group_failing", "member", "other", "write", False),
|
||||
("user", "group_failing", "member", "other", "unlink", False),
|
||||
("user", "group_failing", "outside", "self", "create", False),
|
||||
("user", "group_failing", "outside", "other", "create", False),
|
||||
("user", "group_failing", "outside", "other", "read", False),
|
||||
("user", "group_failing", "outside", "other", "write", False),
|
||||
("user", "group_failing", "outside", "other", "unlink", False),
|
||||
]
|
||||
cls._group_type_channel_access_cases = [
|
||||
("public", "group", "member", "read", True),
|
||||
("public", "group", "member", "write", False),
|
||||
("public", "group", "member", "unlink", False),
|
||||
("public", "group", "outside", "create", False),
|
||||
("public", "group", "outside", "read", False),
|
||||
("public", "group", "outside", "write", False),
|
||||
("public", "group", "outside", "unlink", False),
|
||||
("portal", "group", "member", "read", True),
|
||||
("portal", "group", "member", "write", False),
|
||||
("portal", "group", "member", "unlink", False),
|
||||
("portal", "group", "outside", "create", False),
|
||||
("portal", "group", "outside", "read", False),
|
||||
("portal", "group", "outside", "write", False),
|
||||
("portal", "group", "outside", "unlink", False),
|
||||
("user", "group", "member", "read", True),
|
||||
("user", "group", "member", "write", True),
|
||||
("user", "group", "member", "unlink", False),
|
||||
("user", "group", "outside", "create", True),
|
||||
("user", "group", "outside", "read", False),
|
||||
("user", "group", "outside", "write", False),
|
||||
("user", "group", "outside", "unlink", False),
|
||||
]
|
||||
cls._group_type_channel_member_access_cases = [
|
||||
("public", "group", "member", "self", "create", False),
|
||||
("public", "group", "member", "self", "read", True),
|
||||
("public", "group", "member", "self", "write", True),
|
||||
("public", "group", "member", "self", "unlink", True),
|
||||
("public", "group", "member", "other", "create", False),
|
||||
("public", "group", "member", "other", "read", True),
|
||||
("public", "group", "member", "other", "write", False),
|
||||
("public", "group", "member", "other", "unlink", False),
|
||||
("public", "group", "outside", "self", "create", False),
|
||||
("public", "group", "outside", "other", "create", False),
|
||||
("public", "group", "outside", "other", "read", False),
|
||||
("public", "group", "outside", "other", "write", False),
|
||||
("public", "group", "outside", "other", "unlink", False),
|
||||
("portal", "group", "member", "self", "create", False),
|
||||
("portal", "group", "member", "self", "read", True),
|
||||
("portal", "group", "member", "self", "write", True),
|
||||
("portal", "group", "member", "self", "unlink", True),
|
||||
("portal", "group", "member", "other", "create", False),
|
||||
("portal", "group", "member", "other", "read", True),
|
||||
("portal", "group", "member", "other", "write", False),
|
||||
("portal", "group", "member", "other", "unlink", False),
|
||||
("portal", "group", "outside", "self", "create", False),
|
||||
("portal", "group", "outside", "other", "create", False),
|
||||
("portal", "group", "outside", "other", "read", False),
|
||||
("portal", "group", "outside", "other", "write", False),
|
||||
("portal", "group", "outside", "other", "unlink", False),
|
||||
("user", "group", "member", "self", "create", False),
|
||||
("user", "group", "member", "self", "read", True),
|
||||
("user", "group", "member", "self", "write", True),
|
||||
("user", "group", "member", "self", "unlink", True),
|
||||
("user", "group", "member", "other", "create", True),
|
||||
("user", "group", "member", "other", "read", True),
|
||||
("user", "group", "member", "other", "write", False),
|
||||
("user", "group", "member", "other", "unlink", False),
|
||||
("user", "group", "outside", "self", "create", False),
|
||||
("user", "group", "outside", "other", "create", False),
|
||||
("user", "group", "outside", "other", "read", False),
|
||||
("user", "group", "outside", "other", "write", False),
|
||||
("user", "group", "outside", "other", "unlink", False),
|
||||
]
|
||||
cls.secret_group = cls.env["res.groups"].create({"name": "Secret User Group"})
|
||||
cls.env["ir.model.data"].create(
|
||||
{
|
||||
"name": "secret_group",
|
||||
"module": "mail",
|
||||
"model": cls.secret_group._name,
|
||||
"res_id": cls.secret_group.id,
|
||||
}
|
||||
)
|
||||
cls.guest = cls.env["mail.guest"].create({"name": "A Guest"}).sudo(False)
|
||||
cls.users = {
|
||||
"public": mail_new_test_user(
|
||||
cls.env,
|
||||
login="public1",
|
||||
name="A Public User",
|
||||
groups="base.group_public,mail.secret_group",
|
||||
),
|
||||
"portal": mail_new_test_user(
|
||||
cls.env,
|
||||
login="portal1",
|
||||
name="A Portal User",
|
||||
groups="base.group_portal,mail.secret_group",
|
||||
),
|
||||
"user": mail_new_test_user(
|
||||
cls.env,
|
||||
login="user1",
|
||||
name="An Internal User",
|
||||
groups="base.group_user,mail.secret_group",
|
||||
),
|
||||
}
|
||||
cls.other_user = mail_new_test_user(
|
||||
cls.env,
|
||||
login="other1",
|
||||
name="Another User 1",
|
||||
groups="base.group_user,mail.secret_group",
|
||||
)
|
||||
cls.other_user_2 = mail_new_test_user(
|
||||
cls.env,
|
||||
login="other2",
|
||||
name="Another User 2",
|
||||
groups="base.group_user,mail.secret_group",
|
||||
)
|
||||
|
||||
def _test_discuss_channel_access(self, cases, for_sub_channel):
|
||||
"""
|
||||
Executes a list of operations on channels in various setups and checks whether the outcomes
|
||||
match the expected results.
|
||||
|
||||
:param cases: A list of test cases, where each tuple contains:
|
||||
|
||||
- user_key (``"portal"`` | ``"public"`` | ``"user"``): The user performing the operation.
|
||||
- group_key (``"chat"`` | ``"group"`` | ``"no_group"`` | ``"group_matching"`` |
|
||||
``"group_failing"``): The group specification to use. ``chat`` and ``group`` define the
|
||||
channel type, while the others configure group setups for the channels.
|
||||
- membership (``"member"`` | ``"outside"``): Whether the user is a member of the channel.
|
||||
- operation (``"create"`` | ``"read"`` | ``"write"`` | ``"unlink"``): The action being tested.
|
||||
- expected_result (bool): Whether the action is expected to be allowed (``True``) or denied
|
||||
(``False``).
|
||||
:type cases: List[Tuple[str, str, str, str, bool]]
|
||||
:param for_sub_channel: Whether the operation is being tested on a sub-channel. In this case, the
|
||||
``cases`` parameter is used to configure the parent channel.
|
||||
"""
|
||||
for user_key, channel_key, membership, operation, result in cases:
|
||||
if result:
|
||||
try:
|
||||
self._execute_action_channel(
|
||||
user_key, channel_key, membership, operation, result, for_sub_channel
|
||||
)
|
||||
except Exception as e: # noqa: BLE001 - re-raising, just with a more contextual message
|
||||
raise AssertionError(
|
||||
f"{user_key, channel_key, membership, operation} should not raise"
|
||||
) from e
|
||||
else:
|
||||
try:
|
||||
with self.assertRaises(AccessError), mute_logger("odoo.sql_db"), mute_logger(
|
||||
"odoo.addons.base.models.ir_model"
|
||||
), mute_logger("odoo.addons.base.models.ir_rule"), mute_logger(
|
||||
"odoo.models.unlink"
|
||||
):
|
||||
self._execute_action_channel(
|
||||
user_key, channel_key, membership, operation, result, for_sub_channel
|
||||
)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(
|
||||
f"{user_key, channel_key, membership, operation} should raise"
|
||||
) from e
|
||||
|
||||
def test_01_discuss_channel_access(self):
|
||||
cases = [
|
||||
*self._channel_type_channel_access_cases,
|
||||
*self._group_type_channel_access_cases,
|
||||
("public", "chat", "outside", "create", False),
|
||||
("public", "chat", "outside", "read", False),
|
||||
("public", "chat", "outside", "write", False),
|
||||
("public", "chat", "outside", "unlink", False),
|
||||
("portal", "chat", "member", "read", True),
|
||||
("portal", "chat", "member", "write", False),
|
||||
("portal", "chat", "member", "unlink", False),
|
||||
("portal", "chat", "outside", "create", False),
|
||||
("portal", "chat", "outside", "read", False),
|
||||
("portal", "chat", "outside", "write", False),
|
||||
("portal", "chat", "outside", "unlink", False),
|
||||
("user", "chat", "member", "read", True),
|
||||
("user", "chat", "member", "write", True),
|
||||
("user", "chat", "member", "unlink", False),
|
||||
("user", "chat", "outside", "create", True),
|
||||
("user", "chat", "outside", "read", False),
|
||||
("user", "chat", "outside", "write", False),
|
||||
("user", "chat", "outside", "unlink", False),
|
||||
]
|
||||
self._test_discuss_channel_access(cases, for_sub_channel=False)
|
||||
|
||||
def test_02_discuss_sub_channel_access(self):
|
||||
cases = [
|
||||
*self._channel_type_channel_access_cases,
|
||||
*self._group_type_channel_access_cases,
|
||||
]
|
||||
self._test_discuss_channel_access(cases, for_sub_channel=True)
|
||||
|
||||
def _test_discuss_channel_member_access(self, cases, for_sub_channel):
|
||||
"""
|
||||
Executes a list of operations on channel members in various setups and checks whether the
|
||||
outcomes match the expected results.
|
||||
|
||||
:param cases: A list of test cases, where each tuple contains:
|
||||
- user_key (``"portal"`` | ``"public"`` | ``"user"``):
|
||||
The user performing the operation.
|
||||
- group_key (``"chat"`` | ``"group"`` | ``"no_group"`` | ``"group_matching"`` |
|
||||
``"group_failing"``):
|
||||
The group specification to use. ``chat`` and ``group`` define the channel type, while the
|
||||
others configure group setups for the channels.
|
||||
- membership (``"member"`` | ``"outside"``):
|
||||
Whether the user is a member of the channel.
|
||||
- target (``"self"`` | ``"other"``):
|
||||
Whether the operation is executed on the self-member or another member.
|
||||
- operation (``"create"`` | ``"read"`` | ``"write"`` | ``"unlink"``):
|
||||
The action being tested.
|
||||
- expected_result (bool):
|
||||
Whether the action is expected to be allowed (``True``) or denied (``False``).
|
||||
:type cases: List[Tuple[str, str, str, str, str, bool]]
|
||||
:param for_sub_channel: Whether the operation is being tested on a sub-channel. In this case, the
|
||||
``cases`` parameter is used to configure the parent channel's member.
|
||||
"""
|
||||
for user_key, channel_key, membership, target, operation, result in cases:
|
||||
channel_id = self._get_channel_id(user_key, channel_key, membership, for_sub_channel)
|
||||
if result:
|
||||
try:
|
||||
self._execute_action_member(channel_id, user_key, target, operation, result)
|
||||
except Exception as e: # noqa: BLE001 - re-raising, just with a more contextual message
|
||||
raise AssertionError(
|
||||
f"{user_key, channel_key, membership, target, operation} should not raise"
|
||||
) from e
|
||||
else:
|
||||
try:
|
||||
with self.assertRaises(AccessError), mute_logger("odoo.sql_db"), mute_logger(
|
||||
"odoo.addons.base.models.ir_model"
|
||||
), mute_logger("odoo.addons.base.models.ir_rule"), mute_logger(
|
||||
"odoo.models.unlink"
|
||||
):
|
||||
try:
|
||||
self._execute_action_member(
|
||||
channel_id, user_key, target, operation, result
|
||||
)
|
||||
except (UniqueViolation, UserError) as e:
|
||||
raise AccessError("expected errors as access error") from e
|
||||
except AssertionError as e:
|
||||
raise AssertionError(
|
||||
f"{user_key, channel_key, membership, target, operation} should raise access error"
|
||||
) from e
|
||||
|
||||
def test_10_discuss_channel_member_access(self):
|
||||
cases = [
|
||||
*self._channel_type_channel_member_access_cases,
|
||||
*self._group_type_channel_member_access_cases,
|
||||
("public", "chat", "outside", "self", "create", False),
|
||||
("public", "chat", "outside", "other", "create", False),
|
||||
("public", "chat", "outside", "other", "read", False),
|
||||
("public", "chat", "outside", "other", "write", False),
|
||||
("public", "chat", "outside", "other", "unlink", False),
|
||||
("portal", "chat", "member", "self", "create", False),
|
||||
("portal", "chat", "member", "self", "read", True),
|
||||
("portal", "chat", "member", "self", "write", True),
|
||||
("portal", "chat", "member", "self", "unlink", True),
|
||||
("portal", "chat", "member", "other", "create", False),
|
||||
("portal", "chat", "member", "other", "read", True),
|
||||
("portal", "chat", "member", "other", "write", False),
|
||||
("portal", "chat", "member", "other", "unlink", False),
|
||||
("portal", "chat", "outside", "self", "create", False),
|
||||
("portal", "chat", "outside", "other", "create", False),
|
||||
("portal", "chat", "outside", "other", "read", False),
|
||||
("portal", "chat", "outside", "other", "write", False),
|
||||
("portal", "chat", "outside", "other", "unlink", False),
|
||||
("user", "chat", "member", "self", "create", False),
|
||||
("user", "chat", "member", "self", "read", True),
|
||||
("user", "chat", "member", "self", "write", True),
|
||||
("user", "chat", "member", "self", "unlink", True),
|
||||
("user", "chat", "member", "other", "create", False),
|
||||
("user", "chat", "member", "other", "read", True),
|
||||
("user", "chat", "member", "other", "write", False),
|
||||
("user", "chat", "member", "other", "unlink", False),
|
||||
("user", "chat", "outside", "self", "create", False),
|
||||
("user", "chat", "outside", "other", "create", False),
|
||||
("user", "chat", "outside", "other", "read", False),
|
||||
("user", "chat", "outside", "other", "write", False),
|
||||
("user", "chat", "outside", "other", "unlink", False),
|
||||
]
|
||||
self._test_discuss_channel_member_access(cases, for_sub_channel=False)
|
||||
|
||||
def test_11_discuss_sub_channel_member_access(self):
|
||||
cases = [
|
||||
*self._channel_type_channel_member_access_cases,
|
||||
*self._group_type_channel_member_access_cases,
|
||||
]
|
||||
self._test_discuss_channel_member_access(cases, for_sub_channel=True)
|
||||
|
||||
def _get_channel_id(self, user_key, channel_key, membership, sub_channel):
|
||||
user = self.env["res.users"] if user_key == "public" else self.users[user_key]
|
||||
partner = user.partner_id
|
||||
guest = self.guest if user_key == "public" else self.env["mail.guest"]
|
||||
partners = self.other_user.partner_id
|
||||
if membership == "member":
|
||||
partners += partner
|
||||
DiscussChannel = self.env["discuss.channel"].with_user(self.other_user)
|
||||
if channel_key == "group":
|
||||
channel = DiscussChannel._create_group(partners.ids)
|
||||
if membership == "member":
|
||||
channel._add_members(users=user, guests=guest)
|
||||
elif channel_key == "chat":
|
||||
channel = DiscussChannel._get_or_create_chat(partners.ids)
|
||||
else:
|
||||
channel = DiscussChannel._create_channel("Channel", group_id=None)
|
||||
if membership == "member":
|
||||
channel._add_members(users=user, guests=guest)
|
||||
if channel_key == "no_group":
|
||||
channel.group_public_id = None
|
||||
elif channel_key == "group_matching":
|
||||
channel.group_public_id = self.secret_group
|
||||
elif channel_key == "group_failing":
|
||||
channel.group_public_id = self.env.ref("base.group_system")
|
||||
if sub_channel:
|
||||
channel.sudo()._create_sub_channel()
|
||||
channel = channel.sub_channel_ids[0]
|
||||
if membership == "member":
|
||||
channel.sudo()._add_members(users=user, guests=guest)
|
||||
return channel.id
|
||||
|
||||
def _execute_action_channel(self, user_key, channel_key, membership, operation, result, for_sub_channel):
|
||||
current_user = self.users[user_key]
|
||||
guest = self.guest if user_key == "public" else self.env["mail.guest"]
|
||||
ChannelAsUser = self.env["discuss.channel"].with_user(current_user).with_context(guest=guest)
|
||||
if operation == "create":
|
||||
group_public_id = None
|
||||
if channel_key == "group_matching":
|
||||
group_public_id = self.secret_group.id
|
||||
elif channel_key == "group_failing":
|
||||
group_public_id = self.env.ref("base.group_system").id
|
||||
data = {
|
||||
"name": "Test Channel",
|
||||
"channel_type": channel_key if channel_key in ("group", "chat") else "channel",
|
||||
"group_public_id": group_public_id,
|
||||
}
|
||||
ChannelAsUser.create(data)
|
||||
else:
|
||||
channel = ChannelAsUser.browse(
|
||||
self._get_channel_id(user_key, channel_key, membership, for_sub_channel)
|
||||
)
|
||||
self.assertEqual(len(channel), 1, "should find the channel")
|
||||
if operation == "read":
|
||||
self.assertEqual(len(ChannelAsUser.search([("id", "=", channel.id)])), 1 if result else 0)
|
||||
channel.read(["name"])
|
||||
elif operation == "write":
|
||||
channel.write({"name": "new name"})
|
||||
elif operation == "unlink":
|
||||
channel.unlink()
|
||||
|
||||
def _execute_action_member(self, channel_id, user_key, target, operation, result):
|
||||
current_user = self.users[user_key]
|
||||
partner = self.env["res.partner"] if user_key == "public" else current_user.partner_id
|
||||
guest = self.guest if user_key == "public" else self.env["mail.guest"]
|
||||
ChannelMemberAsUser = self.env["discuss.channel.member"].with_user(current_user).with_context(guest=guest)
|
||||
if operation == "create":
|
||||
create_data = {"channel_id": channel_id}
|
||||
if target == "self":
|
||||
if guest:
|
||||
create_data["guest_id"] = guest.id
|
||||
else:
|
||||
create_data["partner_id"] = partner.id
|
||||
else:
|
||||
create_data["partner_id"] = self.other_user_2.partner_id.id
|
||||
ChannelMemberAsUser.create(create_data)
|
||||
else:
|
||||
domain = [("channel_id", "=", channel_id)]
|
||||
if target == "self":
|
||||
if guest:
|
||||
domain.append(("guest_id", "=", guest.id))
|
||||
else:
|
||||
domain.append(("partner_id", "=", partner.id))
|
||||
else:
|
||||
domain.append(("partner_id", "=", self.other_user.partner_id.id))
|
||||
member = ChannelMemberAsUser.sudo().search(domain).sudo(False)
|
||||
self.assertEqual(len(member), 1, "should find the target member")
|
||||
if operation == "read":
|
||||
self.assertEqual(len(ChannelMemberAsUser.search(domain)), 1 if result else 0)
|
||||
member.read(["custom_channel_name"])
|
||||
elif operation == "write":
|
||||
member.write({"custom_channel_name": "new name"})
|
||||
elif operation == "unlink":
|
||||
member.unlink()
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.tests.common import tagged
|
||||
from odoo.addons.base.tests.common import HttpCaseWithUserPortal, HttpCaseWithUserDemo
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install", "is_tour")
|
||||
class TestMailPublicPage(HttpCaseWithUserPortal, HttpCaseWithUserDemo):
|
||||
"""Checks that the invite page redirects to the channel and that all
|
||||
modules load correctly on the welcome and channel page when authenticated as various users"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
portal_user = mail_new_test_user(
|
||||
self.env,
|
||||
name='Portal Bowser',
|
||||
login='portal_bowser',
|
||||
email='portal_bowser@example.com',
|
||||
groups='base.group_portal',
|
||||
)
|
||||
internal_user = mail_new_test_user(
|
||||
self.env,
|
||||
name='Internal Luigi',
|
||||
login='internal_luigi',
|
||||
email='internal_luigi@example.com',
|
||||
groups='base.group_user',
|
||||
)
|
||||
guest = self.env['mail.guest'].create({'name': 'Guest Mario'})
|
||||
|
||||
self.channel = self.env['discuss.channel']._create_channel(group_id=None, name='Test channel')
|
||||
self.channel._add_members(users=portal_user)
|
||||
self.channel._add_members(users=internal_user)
|
||||
self.channel._add_members(guests=guest)
|
||||
internal_member = self.channel.channel_member_ids.filtered(lambda m: internal_user.partner_id == m.partner_id)
|
||||
internal_member._rtc_join_call()
|
||||
|
||||
self.group = self.env['discuss.channel']._create_group(partners_to=(internal_user + portal_user).partner_id.ids, name="Test group")
|
||||
self.group._add_members(guests=guest)
|
||||
self.tour = "discuss_channel_public_tour.js"
|
||||
|
||||
def _open_channel_page_as_user(self, login):
|
||||
self.start_tour(self.channel.invitation_url, self.tour, login=login)
|
||||
# Update the body to a unique value to ensure the second run does not confuse the 2 messages.
|
||||
self.channel._get_last_messages().body = "a-very-unique-body-in-channel"
|
||||
# Second run of the tour as the first call has side effects, like creating user settings or adding members to
|
||||
# the channel, so we need to run it again to test different parts of the code.
|
||||
self.start_tour(self.channel.invitation_url, self.tour, login=login)
|
||||
|
||||
def _open_group_page_as_user(self, login):
|
||||
self.start_tour(self.group.invitation_url, self.tour, login=login)
|
||||
# Update the body to a unique value to ensure the second run does not confuse the 2 messages.
|
||||
self.channel._get_last_messages().body = "a-very-unique-body-in-group"
|
||||
# Second run of the tour as the first call has side effects, like creating user settings or adding members to
|
||||
# the channel, so we need to run it again to test different parts of the code.
|
||||
self.start_tour(self.group.invitation_url, self.tour, login=login)
|
||||
|
||||
def test_discuss_channel_public_page_as_admin(self):
|
||||
self._open_channel_page_as_user('admin')
|
||||
|
||||
def test_mail_group_public_page_as_admin(self):
|
||||
self._open_group_page_as_user('admin')
|
||||
|
||||
def test_discuss_channel_public_page_as_guest(self):
|
||||
self.start_tour(self.channel.invitation_url, "discuss_channel_as_guest_tour.js")
|
||||
guest = self.env['mail.guest'].search([('channel_ids', 'in', self.channel.id)], limit=1, order='id desc')
|
||||
self.start_tour(self.channel.invitation_url, self.tour, cookies={guest._cookie_name: guest._format_auth_cookie()})
|
||||
|
||||
def test_discuss_channel_public_page_call_public(self):
|
||||
self.channel.default_display_mode = 'video_full_screen'
|
||||
self.start_tour(self.channel.invitation_url, "discuss_channel_call_public_tour.js")
|
||||
|
||||
def test_mail_group_public_page_as_guest(self):
|
||||
self.start_tour(self.group.invitation_url, "discuss_channel_as_guest_tour.js")
|
||||
guest = self.env['mail.guest'].search([('channel_ids', 'in', self.channel.id)], limit=1, order='id desc')
|
||||
self.start_tour(self.group.invitation_url, self.tour, cookies={guest._cookie_name: guest._format_auth_cookie()})
|
||||
|
||||
def test_discuss_channel_public_page_as_internal(self):
|
||||
self._open_channel_page_as_user('demo')
|
||||
|
||||
def test_mail_group_public_page_as_internal(self):
|
||||
self._open_group_page_as_user('demo')
|
||||
|
||||
def test_discuss_channel_public_page_as_portal(self):
|
||||
self._open_channel_page_as_user('portal')
|
||||
|
||||
def test_mail_group_public_page_as_portal(self):
|
||||
self._open_group_page_as_user('portal')
|
||||
|
||||
def test_chat_from_token_as_guest(self):
|
||||
self.env['ir.config_parameter'].set_param('mail.chat_from_token', True)
|
||||
self.url_open('/chat/xyz')
|
||||
channel = self.env['discuss.channel'].search([('uuid', '=', 'xyz')])
|
||||
self.assertEqual(len(channel), 1)
|
||||
|
||||
def test_channel_invitation_from_token(self):
|
||||
public_channel = self.env["discuss.channel"]._create_channel(name="Public Channel", group_id=None)
|
||||
internal_channel = self.env["discuss.channel"]._create_channel(name="Internal Channel", group_id=self.env.ref("base.group_user").id)
|
||||
|
||||
public_response = self.url_open(public_channel.invitation_url)
|
||||
self.assertEqual(public_response.status_code, 200)
|
||||
|
||||
internal_response = self.url_open(internal_channel.invitation_url)
|
||||
self.assertEqual(internal_response.status_code, 404)
|
||||
|
||||
def test_sidebar_in_public_page(self):
|
||||
guest = self.env['mail.guest'].create({'name': 'Guest'})
|
||||
channel_1 = self.env["discuss.channel"]._create_channel(name="Channel 1", group_id=None)
|
||||
channel_2 = self.env["discuss.channel"]._create_channel(name="Channel 2", group_id=None)
|
||||
channel_1._add_members(guests=guest)
|
||||
channel_2._add_members(guests=guest)
|
||||
self.start_tour(f"/discuss/channel/{channel_1.id}", "sidebar_in_public_page_tour", cookies={guest._cookie_name: guest._format_auth_cookie()})
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from lxml import html
|
||||
from itertools import product
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import HttpCase, new_test_user, tagged, users
|
||||
from odoo.tools.misc import hash_sign
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestDiscussChannelInvite(HttpCase, MailCommon):
|
||||
def test_01_invite_by_email_flow(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com")
|
||||
john = new_test_user(self.env, "john", groups="base.group_user", email="john@test.com")
|
||||
group_chat = (
|
||||
self.env["discuss.channel"].with_user(bob)._create_group(partners_to=bob.partner_id.ids)
|
||||
)
|
||||
with self.mock_mail_gateway():
|
||||
self.start_tour(
|
||||
f"/odoo/discuss?active_id={group_chat.id}", "discuss.invite_by_email", login="bob"
|
||||
)
|
||||
self.assertIn(john.partner_id, group_chat.channel_member_ids.partner_id)
|
||||
self.assertNoMail(self.env["res.partner"], "john@test.com")
|
||||
self.assertMailMail(
|
||||
self.env["res.partner"],
|
||||
status=None,
|
||||
email_to_all=["unknown_email@test.com"],
|
||||
author=bob.partner_id,
|
||||
email_values={
|
||||
"subject": f"{bob.name} has invited you to a channel",
|
||||
},
|
||||
)
|
||||
mail = self.env["mail.mail"].search(
|
||||
[("model", "=", "discuss.channel"), ("res_id", "=", group_chat.id)]
|
||||
)
|
||||
body_html = html.fromstring(mail.body_html)
|
||||
join_link = body_html.xpath('//a[normalize-space(text())="Join Channel"]')
|
||||
self.assertTrue(join_link)
|
||||
self.assertEqual(
|
||||
join_link[0].get("href"),
|
||||
f"{self.env['ir.config_parameter'].get_base_url()}{group_chat.invitation_url}?email_token={hash_sign(self.env, 'mail.invite_email', 'unknown_email@test.com')}",
|
||||
)
|
||||
|
||||
def test_02_invite_by_email_excludes_member_emails(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com")
|
||||
group_chat = (
|
||||
self.env["discuss.channel"].with_user(bob)._create_group(partners_to=bob.partner_id.ids)
|
||||
)
|
||||
alfred_guest = self.env["mail.guest"].create({"email": "alfred@test.com", "name": "Alfred"})
|
||||
group_chat._add_members(guests=alfred_guest)
|
||||
with self.mock_mail_gateway():
|
||||
group_chat.invite_by_email(["alfred@test.com", "bob@test.com", "other@test.com"])
|
||||
self.assertMailMail(
|
||||
self.env["res.partner"],
|
||||
status=None,
|
||||
email_to_all=["other@test.com"],
|
||||
author=bob.partner_id,
|
||||
)
|
||||
self.assertNoMail(self.env["res.partner"], "bob@test.com")
|
||||
self.assertNoMail(self.env["res.partner"], "alfred@test.com")
|
||||
|
||||
def test_03_only_invite_by_email_on_allowed_channel_types(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user")
|
||||
john = new_test_user(self.env, "john", groups="base.group_user")
|
||||
chat = (
|
||||
self.env["discuss.channel"]
|
||||
.with_user(bob)
|
||||
._get_or_create_chat(partners_to=john.partner_id.ids)
|
||||
)
|
||||
group_chat = (
|
||||
self.env["discuss.channel"]
|
||||
.with_user(bob)
|
||||
._create_group(partners_to=john.partner_id.ids)
|
||||
)
|
||||
public_channel = self.env["discuss.channel"].create(
|
||||
{"name": "public community", "group_public_id": False}
|
||||
)
|
||||
private_channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"name": "user restricted channel",
|
||||
"channel_type": "channel",
|
||||
"group_public_id": self.env.ref("base.group_user").id,
|
||||
}
|
||||
)
|
||||
for channel in [chat, private_channel]:
|
||||
with self.assertRaises(UserError) as exc:
|
||||
channel.invite_by_email(["some@email.com"])
|
||||
self.assertEqual(
|
||||
exc.exception.args[0],
|
||||
f"Inviting by email is not allowed for this channel type ({channel.channel_type}).",
|
||||
)
|
||||
with self.mock_mail_gateway():
|
||||
for channel in [group_chat, public_channel]:
|
||||
channel.invite_by_email(["some@email.com"])
|
||||
self.assertMailMail(
|
||||
self.env["res.partner"],
|
||||
status=None,
|
||||
email_to_all=["some@email.com"],
|
||||
email_values={"model": "discuss.channel", "res_id": channel.id},
|
||||
)
|
||||
|
||||
def test_04_guest_email_updated_when_invited_from_email(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com")
|
||||
group_chat = (
|
||||
self.env["discuss.channel"].with_user(bob)._create_group(partners_to=bob.partner_id.ids)
|
||||
)
|
||||
# Guest email is filled at create
|
||||
self.url_open(
|
||||
f"{group_chat.invitation_url}?email_token={hash_sign(self.env, 'mail.invite_email', 'alfred@test.com')}"
|
||||
)
|
||||
self.assertEqual(group_chat.channel_member_ids.guest_id.email, "alfred@test.com")
|
||||
self.assertEqual(group_chat.channel_member_ids.guest_id.name, "alfred@test.com")
|
||||
# Guest email is updated if empty when invited from email
|
||||
guest = self.env["mail.guest"].create({"name": "Alice"})
|
||||
self.assertFalse(guest.email)
|
||||
self.url_open(
|
||||
f"{group_chat.invitation_url}?email_token={hash_sign(self.env, 'mail.invite_email', 'alice@test.com')}",
|
||||
cookies={
|
||||
guest._cookie_name: f"{guest.id}{guest._cookie_separator}{guest.access_token}",
|
||||
},
|
||||
)
|
||||
self.assertEqual(guest.email, "alice@test.com")
|
||||
self.assertEqual(guest.name, "Alice")
|
||||
# Guest email is not overwriten if already filled
|
||||
guest = self.env["mail.guest"].create({"name": "John", "email": "john@test.com"})
|
||||
self.url_open(
|
||||
f"{group_chat.invitation_url}?email_token={hash_sign(self.env, 'mail.invite_email', 'john_other_email@test.com')}",
|
||||
cookies={
|
||||
guest._cookie_name: f"{guest.id}{guest._cookie_separator}{guest.access_token}",
|
||||
},
|
||||
)
|
||||
self.assertEqual(guest.email, "john@test.com")
|
||||
self.assertEqual(guest.name, "John")
|
||||
|
||||
def test_05_search_for_channel_invite_selectable_email(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com")
|
||||
john = new_test_user(self.env, "john", groups="base.group_user", email="john@test.com")
|
||||
alfred_guest = self.env["mail.guest"].create({"email": "alfred@test.com", "name": "Alfred"})
|
||||
chat = (
|
||||
self.env["discuss.channel"]
|
||||
.with_user(bob)
|
||||
._get_or_create_chat(partners_to=john.partner_id.ids)
|
||||
)
|
||||
group_chat = (
|
||||
self.env["discuss.channel"]
|
||||
.with_user(bob)
|
||||
._create_group(partners_to=john.partner_id.ids)
|
||||
)
|
||||
group_chat._add_members(guests=alfred_guest)
|
||||
public_channel = self.env["discuss.channel"].create(
|
||||
{"name": "public community", "group_public_id": False},
|
||||
)
|
||||
public_channel._add_members(guests=alfred_guest)
|
||||
private_channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"name": "user restricted channel",
|
||||
"channel_type": "channel",
|
||||
"group_public_id": self.env.ref("base.group_user").id,
|
||||
},
|
||||
)
|
||||
cases = [
|
||||
*product(
|
||||
[chat, private_channel, group_chat, public_channel],
|
||||
["foo@bar"],
|
||||
[False],
|
||||
),
|
||||
# Channel types that do not allow inviting by email, not selectable.
|
||||
*product(
|
||||
[chat, private_channel],
|
||||
["bob@odoo.com", "alfred@odoo.com", "jane@odoo.com"],
|
||||
[False],
|
||||
),
|
||||
# Channel types that allow inviting by email, valid email, selectable.
|
||||
*product(
|
||||
[group_chat, public_channel],
|
||||
["bob@odoo.com", "alfred@odoo.com", "jane@odoo.com"],
|
||||
[True],
|
||||
),
|
||||
]
|
||||
for channel, search_term, is_selectable in cases:
|
||||
with self.subTest(
|
||||
f"channel={channel.channel_type}_{channel.display_name}, search_term={search_term}, is_selectable={is_selectable}"
|
||||
):
|
||||
result = self.env["res.partner"].search_for_channel_invite(
|
||||
search_term, channel_id=channel.id
|
||||
)
|
||||
if is_selectable:
|
||||
self.assertEqual(result["selectable_email"], search_term)
|
||||
continue
|
||||
self.assertFalse(result["selectable_email"])
|
||||
|
||||
@users("employee")
|
||||
def test_06_invite_by_email_posts_user_notification(self):
|
||||
group_chat = self.env["discuss.channel"]._create_group(partners_to=self.user_employee.partner_id.ids)
|
||||
with self.mock_mail_gateway():
|
||||
group_chat.invite_by_email(["alfred@test.com"])
|
||||
last_message = group_chat._get_last_messages()
|
||||
self.assertEqual(last_message.message_type, "user_notification")
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
from odoo.tests.common import new_test_user, tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestDiscussChannelMember(MailCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.secret_group = cls.env['res.groups'].create({
|
||||
'name': 'Secret User Group',
|
||||
})
|
||||
cls.env['ir.model.data'].create({
|
||||
'name': 'secret_group',
|
||||
'module': 'mail',
|
||||
'model': cls.secret_group._name,
|
||||
'res_id': cls.secret_group.id,
|
||||
})
|
||||
cls.user_1 = new_test_user(
|
||||
cls.env, login="user_1", name="User 1", groups="base.group_user,mail.secret_group"
|
||||
)
|
||||
cls.user_2 = new_test_user(
|
||||
cls.env, login="user_2", name="User 2", groups="base.group_user,mail.secret_group"
|
||||
)
|
||||
cls.user_3 = new_test_user(
|
||||
cls.env, login="user_3", name="User 3", groups="base.group_user,mail.secret_group"
|
||||
)
|
||||
cls.user_portal = new_test_user(
|
||||
cls.env, login="user_portal", name="User Portal", groups="base.group_portal"
|
||||
)
|
||||
cls.user_public = new_test_user(
|
||||
cls.env, login="user_public", name="User Public", groups="base.group_public"
|
||||
)
|
||||
|
||||
|
||||
cls.group = cls.env['discuss.channel'].create({
|
||||
'name': 'Group',
|
||||
'channel_type': 'group',
|
||||
})
|
||||
cls.group_restricted_channel = cls.env['discuss.channel'].create({
|
||||
'name': 'Group restricted channel',
|
||||
'channel_type': 'channel',
|
||||
'group_public_id': cls.secret_group.id,
|
||||
})
|
||||
cls.public_channel = cls.env['discuss.channel']._create_channel(group_id=None, name='Public channel of user 1')
|
||||
(cls.group | cls.group_restricted_channel | cls.public_channel).channel_member_ids.unlink()
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# GROUP
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def test_group_01(self):
|
||||
"""Test access on group."""
|
||||
res = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertFalse(res)
|
||||
|
||||
# User 1 can join group with SUDO
|
||||
self.group.with_user(self.user_1).sudo()._add_members(users=self.user_1)
|
||||
res = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(res.partner_id, self.user_1.partner_id)
|
||||
|
||||
# User 2 can not join group
|
||||
with self.assertRaises(AccessError):
|
||||
self.group.with_user(self.user_2)._add_members(users=self.user_2)
|
||||
|
||||
# User 2 can not create a `discuss.channel.member` to join the group
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['discuss.channel.member'].with_user(self.user_2).create({
|
||||
'partner_id': self.user_2.partner_id.id,
|
||||
'channel_id': self.group.id,
|
||||
})
|
||||
|
||||
# User 2 can not write on `discuss.channel.member` to join the group
|
||||
channel_member = self.env['discuss.channel.member'].with_user(self.user_2).search([('is_self', '=', True)])[0]
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member.channel_id = self.group.id
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member.write({'channel_id': self.group.id})
|
||||
|
||||
# Even with SUDO, channel_id of channel.member should not be changed.
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member.sudo().channel_id = self.group.id
|
||||
|
||||
# User 2 can not write on the `partner_id` of `discuss.channel.member`
|
||||
# of an other partner to join a group
|
||||
channel_member_1 = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id), ('partner_id', '=', self.user_1.partner_id.id)])
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member_1.with_user(self.user_2).partner_id = self.user_2.partner_id
|
||||
self.assertEqual(channel_member_1.partner_id, self.user_1.partner_id)
|
||||
|
||||
# Even with SUDO, partner_id of channel.member should not be changed.
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member_1.with_user(self.user_2).sudo().partner_id = self.user_2.partner_id
|
||||
|
||||
def test_group_members(self):
|
||||
"""Test invitation in group part 1 (invite using crud methods)."""
|
||||
self.group.with_user(self.user_1).sudo()._add_members(users=self.user_1)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(len(channel_members), 1)
|
||||
|
||||
# User 2 is not in the group, they can not invite user 3
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['discuss.channel.member'].with_user(self.user_2).create({
|
||||
'partner_id': self.user_portal.partner_id.id,
|
||||
'channel_id': self.group.id,
|
||||
})
|
||||
|
||||
# User 1 is in the group, they can invite other users
|
||||
self.env['discuss.channel.member'].with_user(self.user_1).create({
|
||||
'partner_id': self.user_portal.partner_id.id,
|
||||
'channel_id': self.group.id,
|
||||
})
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id | self.user_portal.partner_id)
|
||||
|
||||
# But User 3 can not write on the `discuss.channel.member` of other user
|
||||
channel_member_1 = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id), ('partner_id', '=', self.user_1.partner_id.id)])
|
||||
channel_member_3 = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id), ('partner_id', '=', self.user_portal.partner_id.id)])
|
||||
channel_member_3.with_user(self.user_portal).custom_channel_name = 'Test'
|
||||
with self.assertRaises(AccessError):
|
||||
channel_member_1.with_user(self.user_2).custom_channel_name = 'Blabla'
|
||||
self.assertNotEqual(channel_member_1.custom_channel_name, 'Blabla')
|
||||
|
||||
def test_group_invite(self):
|
||||
"""Test invitation in group part 2 (use `invite` action)."""
|
||||
self.group.with_user(self.user_1).sudo()._add_members(users=self.user_1)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# User 2 is not in the group, they can not invite user_portal
|
||||
with self.assertRaises(AccessError):
|
||||
self.group.with_user(self.user_2)._add_members(users=self.user_portal)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# User 1 is in the group, they can invite user_portal
|
||||
self.group.with_user(self.user_1)._add_members(users=self.user_portal)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id | self.user_portal.partner_id)
|
||||
|
||||
def test_group_leave(self):
|
||||
"""Test kick/leave channel."""
|
||||
self.group.with_user(self.user_1).sudo()._add_members(users=self.user_1)
|
||||
self.group.with_user(self.user_portal).sudo()._add_members(users=self.user_portal)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group.id)])
|
||||
self.assertEqual(len(channel_members), 2)
|
||||
|
||||
# User 2 is not in the group, they can not kick user 1
|
||||
with self.assertRaises(AccessError):
|
||||
channel_members.with_user(self.user_2).unlink()
|
||||
|
||||
# User 3 is in the group, but not admin/owner, they can not kick user 1
|
||||
with self.assertRaises(AccessError):
|
||||
channel_members.with_user(self.user_portal).unlink()
|
||||
|
||||
def test_group_subchannel_join(self):
|
||||
"""Test join subchannel."""
|
||||
self.group.add_members((self.user_1 | self.user_2).partner_id.ids)
|
||||
group_subchannel = self.group.with_user(self.user_1)._create_sub_channel()
|
||||
group_subchannel.with_user(self.user_2).add_members(self.user_2.partner_id.id)
|
||||
self.assertEqual(group_subchannel.channel_member_ids.partner_id, (self.user_1 | self.user_2).partner_id)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# GROUP BASED CHANNELS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def test_group_restricted_channel(self):
|
||||
"""Test basics on group channel."""
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
self.assertFalse(channel_members)
|
||||
|
||||
# user 1 is in the channel, they can join the channel
|
||||
self.group_restricted_channel.with_user(self.user_1)._add_members(users=self.user_1)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# user 3 is not in the channel, they can not join
|
||||
with self.assertRaises(AccessError):
|
||||
self.group_restricted_channel.with_user(self.user_portal)._add_members(users=self.user_portal)
|
||||
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
with self.assertRaises(AccessError):
|
||||
channel_members.with_user(self.user_portal).partner_id = self.user_portal.partner_id
|
||||
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
self.group_restricted_channel.with_user(self.user_1)._add_members(users=self.user_portal)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id | self.user_portal.partner_id)
|
||||
|
||||
# but user 2 is in the channel and can be invited by user 1
|
||||
self.group_restricted_channel.with_user(self.user_1)._add_members(users=self.user_2)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.group_restricted_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id | self.user_2.partner_id | self.user_portal.partner_id)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# PUBLIC CHANNELS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def test_public_channel(self):
|
||||
""" Test access on public channels """
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.public_channel.id)])
|
||||
self.assertFalse(channel_members)
|
||||
|
||||
self.public_channel.with_user(self.user_1)._add_members(users=self.user_1)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.public_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
self.public_channel.with_user(self.user_2)._add_members(users=self.user_2)
|
||||
channel_members = self.env['discuss.channel.member'].search([('channel_id', '=', self.public_channel.id)])
|
||||
self.assertEqual(channel_members.mapped('partner_id'), self.user_1.partner_id | self.user_2.partner_id)
|
||||
|
||||
self.public_channel.with_user(self.user_portal)._add_members(users=self.user_portal)
|
||||
with self.assertRaises(ValidationError): # public cannot join without having a guest
|
||||
self.public_channel.with_user(self.user_public)._add_members(users=self.user_public)
|
||||
|
||||
def test_channel_member_invite_with_guest(self):
|
||||
guest = self.env['mail.guest'].create({'name': 'Guest'})
|
||||
partner = self.env['res.partner'].create({
|
||||
'name': 'ToInvite',
|
||||
'active': True,
|
||||
'type': 'contact',
|
||||
'user_ids': self.user_1,
|
||||
})
|
||||
self.public_channel._add_members(guests=guest)
|
||||
data = self.env["res.partner"].search_for_channel_invite(
|
||||
partner.name, channel_id=self.public_channel.id
|
||||
)["store_data"]
|
||||
self.assertEqual(len(data["res.partner"]), 1)
|
||||
self.assertEqual(data["res.partner"][0]["id"], partner.id)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# UNREAD COUNTER TESTS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def test_unread_counter_with_message_post(self):
|
||||
channel_as_user_1 = self.env['discuss.channel'].with_user(self.user_1)._create_channel(group_id=None, name='Public channel')
|
||||
channel_as_user_1.with_user(self.user_1)._add_members(users=self.user_1)
|
||||
channel_as_user_1.with_user(self.user_1)._add_members(users=self.user_2)
|
||||
channel_1_rel_user_2 = self.env['discuss.channel.member'].search([
|
||||
('channel_id', '=', channel_as_user_1.id),
|
||||
('partner_id', '=', self.user_2.partner_id.id)
|
||||
])
|
||||
self.assertEqual(channel_1_rel_user_2.message_unread_counter, 0, "should not have unread message initially as notification type is ignored")
|
||||
|
||||
channel_as_user_1.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
channel_1_rel_user_2 = self.env['discuss.channel.member'].search([
|
||||
('channel_id', '=', channel_as_user_1.id),
|
||||
('partner_id', '=', self.user_2.partner_id.id)
|
||||
])
|
||||
self.assertEqual(channel_1_rel_user_2.message_unread_counter, 1, "should have 1 unread message after someone else posted a message")
|
||||
|
||||
def test_unread_counter_with_message_post_multi_channel(self):
|
||||
channel_1_as_user_1 = self.env['discuss.channel'].with_user(self.user_1)._create_channel(group_id=None, name='wololo channel')
|
||||
channel_2_as_user_2 = self.env['discuss.channel'].with_user(self.user_2)._create_channel(group_id=None, name='walala channel')
|
||||
channel_1_as_user_1._add_members(users=self.user_2)
|
||||
channel_2_as_user_2._add_members(users=self.user_1)
|
||||
channel_2_as_user_2._add_members(users=self.user_3)
|
||||
channel_1_as_user_1.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
channel_1_as_user_1.message_post(body='Test 2', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
channel_2_as_user_2.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
members = self.env['discuss.channel.member'].search([('channel_id', 'in', (channel_1_as_user_1 + channel_2_as_user_2).ids)], order="id")
|
||||
self.assertEqual(members.mapped('message_unread_counter'), [
|
||||
0, # channel 1 user 1: posted last message
|
||||
0, # channel 2 user 2: posted last message
|
||||
2, # channel 1 user 2: received 2 messages (from message post)
|
||||
1, # channel 2 user 1: received 1 message (from message post)
|
||||
1, # channel 2 user 3: received 1 message (from message post)
|
||||
])
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
|
||||
try:
|
||||
import websocket as ws
|
||||
except ImportError:
|
||||
ws = None
|
||||
|
||||
from itertools import product
|
||||
|
||||
from odoo.tests import tagged, new_test_user
|
||||
from odoo.addons.bus.tests.common import WebsocketCase
|
||||
from odoo.addons.mail.tests.common import MailCommon, freeze_all_time
|
||||
from odoo.addons.bus.models.bus import channel_with_db, json_dump
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestMailPresence(WebsocketCase, MailCommon):
|
||||
def _receive_presence(self, requested_by, target, has_token=False):
|
||||
self.env["mail.presence"].search([]).unlink()
|
||||
target_user = isinstance(target, self.env.registry["res.users"])
|
||||
if isinstance(requested_by, self.env.registry["res.users"]):
|
||||
session = self.authenticate(requested_by.login, requested_by.login)
|
||||
auth_cookie = f"session_id={session.sid};"
|
||||
else:
|
||||
self.authenticate(None, None)
|
||||
auth_cookie = f"{requested_by._cookie_name}={requested_by._format_auth_cookie()};"
|
||||
websocket = self.websocket_connect(cookie=auth_cookie)
|
||||
target_channel = target.partner_id if target_user else target
|
||||
channel_parts = ["odoo-presence", f"{target_channel._name}_{target_channel.id}"]
|
||||
if has_token:
|
||||
channel_parts.append(target_channel._get_im_status_access_token())
|
||||
self.subscribe(websocket, ["-".join(channel_parts)], self.env["bus.bus"]._bus_last_id())
|
||||
self.env["mail.presence"]._update_presence(target)
|
||||
self.trigger_notification_dispatching([(target_channel, "presence")])
|
||||
notifications = json.loads(websocket.recv())
|
||||
self._close_websockets()
|
||||
bus_record = self.env["bus.bus"].search([("id", "=", int(notifications[0]["id"]))])
|
||||
self.assertEqual(
|
||||
bus_record.channel,
|
||||
json_dump(channel_with_db(self.env.cr.dbname, (target_channel, "presence"))),
|
||||
)
|
||||
self.assertEqual(notifications[0]["message"]["type"], "bus.bus/im_status_updated")
|
||||
self.assertEqual(notifications[0]["message"]["payload"]["im_status"], "online")
|
||||
self.assertEqual(notifications[0]["message"]["payload"]["presence_status"], "online")
|
||||
self.assertEqual(
|
||||
notifications[0]["message"]["payload"]["partner_id" if target_user else "guest_id"],
|
||||
target_channel.id,
|
||||
)
|
||||
|
||||
@freeze_all_time()
|
||||
def test_presence_access(self):
|
||||
internal = new_test_user(self.env, login="internal_user", groups="base.group_user")
|
||||
other_internal = new_test_user(
|
||||
self.env, login="other_internal_user", groups="base.group_user"
|
||||
)
|
||||
portal = new_test_user(self.env, login="portal_user", groups="base.group_portal")
|
||||
other_portal = new_test_user(
|
||||
self.env, login="other_portal_user", groups="base.group_portal"
|
||||
)
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
other_guest = self.env["mail.guest"].create({"name": "Other Guest"})
|
||||
for requested_by, target, has_token, allowed in [
|
||||
*product([internal], [guest, other_internal, portal], [True, False], [True]),
|
||||
*product([guest, portal], [internal, other_guest, other_portal], [False], [False]),
|
||||
*product([guest, portal], [internal, other_guest, other_portal], [True], [True]),
|
||||
]:
|
||||
with self.subTest(
|
||||
f"test presence access, requested_by={requested_by.name}, target={target.name}, has_token={has_token}, allowed={allowed}"
|
||||
):
|
||||
if allowed:
|
||||
self._receive_presence(requested_by, target, has_token=has_token)
|
||||
else:
|
||||
with self.assertRaises(ws._exceptions.WebSocketTimeoutException):
|
||||
self._receive_presence(requested_by, target, has_token=has_token)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests.common import HttpCase, new_test_user, tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestDiscussMentionSuggestions(HttpCase):
|
||||
def test_mention_suggestions_group_restricted_channel(self):
|
||||
user_admin = self.env.ref("base.user_admin")
|
||||
user_group = self.env.ref("base.group_user")
|
||||
rd_group = self.env["res.groups"].create({"name": "R&D Group"})
|
||||
new_test_user(self.env, login="dev", name="Dev User", group_ids=[user_group.id, rd_group.id])
|
||||
# have a user that is not channel member and not in group -> should not be suggested as mention
|
||||
new_test_user(self.env, login="sales", name="Sales User", groups="base.group_user")
|
||||
consultant_user = new_test_user(self.env, login="consultant", name="Consultant User", groups="base.group_user")
|
||||
rd_channel = self.env['discuss.channel'].with_user(user_admin).create({
|
||||
"name": "R&D Channel",
|
||||
"channel_type": "channel",
|
||||
"group_public_id": rd_group.id,
|
||||
"channel_member_ids": [
|
||||
Command.create({"partner_id": consultant_user.partner_id.id}),
|
||||
Command.create({"partner_id": user_admin.partner_id.id}),
|
||||
],
|
||||
})
|
||||
self.start_tour(
|
||||
f"/odoo/discuss?active_id=discuss.channel_{rd_channel.id}",
|
||||
"discuss_mention_suggestions_group_restricted_channel.js",
|
||||
login="admin",
|
||||
)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from odoo.addons.mail.tests.common_controllers import MailControllerUpdateCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestDiscussMessageUpdateController(MailControllerUpdateCommon):
|
||||
|
||||
def test_message_update_guest_as_owner(self):
|
||||
"""Test only admin user and message author can update the message content in a channel."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"group_public_id": None, "name": "public channel"}
|
||||
)
|
||||
channel._add_members(guests=self.guest)
|
||||
# sudo: discuss.channel: posting a message as guest in a test is acceptable
|
||||
message = (
|
||||
channel.with_user(self.user_public)
|
||||
.with_context(guest=self.guest)
|
||||
.sudo()
|
||||
.message_post(body=self.message_body, message_type="comment")
|
||||
)
|
||||
self._execute_subtests(
|
||||
message,
|
||||
(
|
||||
(self.guest, True),
|
||||
(self.user_admin, True),
|
||||
(self.user_employee, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_public, False),
|
||||
),
|
||||
)
|
||||
|
||||
def test_message_update_public_channel(self):
|
||||
"""Test only admin user can update the message content of other authors in a channel."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"group_public_id": None, "name": "public channel"}
|
||||
)
|
||||
message = channel.message_post(
|
||||
body=self.message_body,
|
||||
message_type="comment",
|
||||
)
|
||||
self._execute_subtests(
|
||||
message,
|
||||
(
|
||||
(self.guest, False),
|
||||
(self.user_admin, True),
|
||||
(self.user_employee, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_public, False),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
from odoo.addons.mail.tests.common_controllers import MailControllerReactionCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestMessageReactionController(MailControllerReactionCommon):
|
||||
|
||||
def test_message_reaction_public_channel(self):
|
||||
"""Test access of message reaction for a public channel."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"group_public_id": None, "name": "public channel"}
|
||||
)
|
||||
message = channel.message_post(body="public message")
|
||||
self._execute_subtests(
|
||||
message,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, True),
|
||||
(self.user_portal, True),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_message_reaction_channel_as_member(self):
|
||||
"""Test access of message reaction for a channel as member."""
|
||||
channel = self.env["discuss.channel"]._create_group(
|
||||
partners_to=(self.user_portal + self.user_employee).partner_id.ids
|
||||
)
|
||||
channel._add_members(guests=self.guest)
|
||||
message = channel.message_post(body="invite message")
|
||||
self._execute_subtests(
|
||||
message,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, True),
|
||||
(self.user_portal, True),
|
||||
(self.user_employee, True),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_message_reaction_channel_as_non_member(self):
|
||||
"""Test access of message reaction for a channel as non-member."""
|
||||
channel = self.env["discuss.channel"]._create_group(partners_to=[])
|
||||
message = channel.message_post(body="private message")
|
||||
self._execute_subtests(
|
||||
message,
|
||||
(
|
||||
(self.user_public, False),
|
||||
(self.guest, False),
|
||||
(self.user_portal, False),
|
||||
(self.user_employee, False),
|
||||
(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.addons.mail.tests.test_res_role import TestResRole
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestDiscussResRole(TestResRole):
|
||||
def test_only_mention_by_role_when_channel_is_accessible(self):
|
||||
self.authenticate("admin", "admin")
|
||||
role = self.env["res.role"].create({"name": "rd-Discuss"})
|
||||
for idx, test_case in enumerate([
|
||||
# channel_type, channel_grp, user_grp, is_member, mentionned
|
||||
("channel", None, "base.group_user", False, True),
|
||||
("channel", None, "base.group_user", True, True),
|
||||
("channel", "base.group_system", "base.group_user", False, False),
|
||||
("channel", "base.group_system", "base.group_system", True, True),
|
||||
("group", None, "base.group_user", False, False),
|
||||
("group", None, "base.group_user", True, True),
|
||||
]):
|
||||
channel_type, channel_grp, user_grp, is_member, mentionned = test_case
|
||||
with self.subTest(
|
||||
channel_type=channel_type,
|
||||
channel_grp=channel_grp,
|
||||
user_grp=user_grp,
|
||||
is_member=is_member,
|
||||
notified=mentionned,
|
||||
):
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{
|
||||
"name": f"channel_{channel_grp}_{user_grp}_{mentionned}",
|
||||
"channel_type": channel_type,
|
||||
"group_public_id": self.env.ref(channel_grp).id if channel_grp else None,
|
||||
}
|
||||
)
|
||||
user = mail_new_test_user(
|
||||
self.env, login=f"user_{user_grp}_{idx}", role_ids=role.ids, groups=user_grp
|
||||
)
|
||||
if is_member:
|
||||
channel.add_members(partner_ids=user.partner_id.ids)
|
||||
data = self.make_jsonrpc_request(
|
||||
"/mail/message/post",
|
||||
{
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": channel.id,
|
||||
"post_data": {
|
||||
"body": "irrelevant",
|
||||
"message_type": "comment",
|
||||
"role_ids": role.ids,
|
||||
"subtype_xmlid": "mail.mt_note",
|
||||
},
|
||||
},
|
||||
)
|
||||
formatted_partner = user.partner_id.id
|
||||
message = next(filter(lambda m: m["id"] == data["message_id"], data["store_data"]["mail.message"]))
|
||||
if mentionned:
|
||||
self.assertIn(formatted_partner, message["partner_ids"])
|
||||
else:
|
||||
self.assertNotIn(formatted_partner, message["partner_ids"])
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from freezegun import freeze_time
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.tests.common import HttpCase, new_test_user, tagged
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestDiscussSubChannels(HttpCase):
|
||||
def test_01_gc_unpin_outdated_sub_channels(self):
|
||||
bob = new_test_user(self.env, "bob_user", groups="base.group_user")
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent._create_sub_channel()
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
sub_channel._add_members(users=self.env.user)
|
||||
sub_channel.channel_pin(pinned=True)
|
||||
self_member = sub_channel.channel_member_ids.filtered(lambda m: m.is_self)
|
||||
self.assertTrue(self_member.is_pinned)
|
||||
# Last interrest of the member is older than 2 days, no activity on the
|
||||
# channel: should be unpinned.
|
||||
two_days_later_dt = datetime.now() + timedelta(days=3)
|
||||
with freeze_time(two_days_later_dt) as frozen_time:
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertFalse(self_member.is_pinned)
|
||||
unpin_dt = self_member.unpin_dt
|
||||
# The member isn't unpinned again when GC re-runs.
|
||||
frozen_time.tick(delta=timedelta(days=1))
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertEqual(self_member.unpin_dt, unpin_dt)
|
||||
sub_channel.channel_pin(pinned=True)
|
||||
with freeze_time(two_days_later_dt) as frozen_time:
|
||||
# Last interrest older than 2 days, activity on the channel: should be kept.
|
||||
message = sub_channel.with_user(bob).message_post(body="Hey!", message_type="comment")
|
||||
self_member._mark_as_read(message.id)
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertTrue(self_member.is_pinned)
|
||||
# Unread messages: should be kept regardless of last interrest.
|
||||
message = sub_channel.with_user(bob).message_post(body="Another message!", message_type="comment")
|
||||
frozen_time.tick(delta=timedelta(days=3))
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertTrue(self_member.is_pinned)
|
||||
self_member._mark_as_read(message.id)
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertFalse(self_member.is_pinned)
|
||||
# Ensure regular channels are not impacted.
|
||||
channel = self.env["discuss.channel"].create({"name": "General"})
|
||||
channel.channel_pin(pinned=True)
|
||||
with freeze_time(two_days_later_dt):
|
||||
self.env["discuss.channel.member"]._gc_unpin_outdated_sub_channels()
|
||||
self.assertTrue(channel.channel_member_ids.filtered("is_self").is_pinned)
|
||||
|
||||
def test_02_sub_channel_members_sync_with_parent(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent.action_unfollow()
|
||||
self.assertFalse(any(m.is_self for m in parent.channel_member_ids))
|
||||
parent._create_sub_channel()
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
# Member created for sub channel (_create_sub_channel): should also be
|
||||
# created for the parent channel.
|
||||
self.assertTrue(any(m.is_self for m in parent.channel_member_ids))
|
||||
self.assertTrue(any(m.is_self for m in sub_channel.channel_member_ids))
|
||||
# Member removed from parent channel: should also be removed from the sub
|
||||
# channel.
|
||||
parent.action_unfollow()
|
||||
self.assertFalse(any(m.is_self for m in parent.channel_member_ids))
|
||||
self.assertFalse(any(m.is_self for m in sub_channel.channel_member_ids))
|
||||
# Member created for sub channel (add_members): should also be created
|
||||
# for parent.
|
||||
sub_channel._add_members(users=self.env.user)
|
||||
self.assertTrue(any(m.is_self for m in parent.channel_member_ids))
|
||||
self.assertTrue(any(m.is_self for m in sub_channel.channel_member_ids))
|
||||
|
||||
def test_03_cannot_create_recursive_sub_channel(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent._create_sub_channel()
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
with self.assertRaises(ValidationError):
|
||||
sub_channel._create_sub_channel()
|
||||
|
||||
def test_04_sub_channel_panel_search(self):
|
||||
bob_user = new_test_user(self.env, "bob_user", groups="base.group_user")
|
||||
self.authenticate("bob_user", "bob_user")
|
||||
channel = self.env["discuss.channel"]._create_channel(name="General", group_id=None)
|
||||
channel._add_members(users=bob_user)
|
||||
for i in range(100):
|
||||
channel._create_sub_channel(name=f"Sub Channel {i}")
|
||||
self.start_tour(
|
||||
f"/odoo/discuss?active_id=discuss.channel_{channel.id}",
|
||||
"test_discuss_sub_channel_search",
|
||||
login="bob_user",
|
||||
)
|
||||
|
||||
def test_05_cannot_upate_first_message_nor_parent_channel(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent.message_post(body="Hello there!")
|
||||
parent._create_sub_channel(from_message_id=parent.message_ids[0].id)
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
random_channel = self.env["discuss.channel"].create({"name": "Random"})
|
||||
parent.message_post(body="Random message")
|
||||
with self.assertRaises(UserError, msg="Cannot change initial message nor parent channel of: Hello there!."):
|
||||
sub_channel.parent_channel_id = random_channel
|
||||
with self.assertRaises(UserError, msg="Cannot change initial message nor parent channel of: Hello there!."):
|
||||
sub_channel.from_message_id = parent.message_ids[0]
|
||||
|
||||
def test_06_initial_message_must_belong_to_parent_channel(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
random_channel = self.env["discuss.channel"].create({"name": "Random"})
|
||||
random_channel.message_post(body="Hello world!")
|
||||
with self.assertRaises(
|
||||
ValidationError,
|
||||
msg="Cannot create Hello world!: initial message should belong to parent channel.",
|
||||
):
|
||||
parent._create_sub_channel(from_message_id=random_channel.message_ids[0].id)
|
||||
|
||||
def test_07_unlink_sub_channel(self):
|
||||
bob_user = new_test_user(self.env, "bob_user", groups="base.group_user")
|
||||
baz_user = new_test_user(self.env, "baz_user", groups="base.group_user")
|
||||
parent_1 = self.env["discuss.channel"].with_user(bob_user).create({"name": "Parent 1"})
|
||||
parent_1_baz_member = parent_1._add_members(users=baz_user)
|
||||
parent_1_sub_channel_1 = parent_1._create_sub_channel(name="Parent 1 Sub 1")
|
||||
parent_1_sub_channel_1._add_members(users=baz_user)
|
||||
parent_1_sub_channel_2 = parent_1._create_sub_channel(name="Parent 1 Sub 2")
|
||||
parent_1_sub_channel_2._add_members(users=baz_user)
|
||||
parent_2 = self.env["discuss.channel"].with_user(baz_user).create({"name": "Parent 2"})
|
||||
parent_2_bob_member = parent_2._add_members(users=bob_user)
|
||||
parent_2_sub_channel = parent_2._create_sub_channel(name="Parent 2 Sub")
|
||||
parent_2_sub_channel._add_members(users=bob_user)
|
||||
parent_3 = self.env["discuss.channel"].with_user(bob_user).create({"name": "Parent 3"})
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
parent_3_guest_member = parent_3._add_members(guests=guest)
|
||||
parent_3_sub_channel = parent_3._create_sub_channel(name="Parent 3 Sub")
|
||||
parent_3_sub_channel._add_members(guests=guest)
|
||||
members_to_unlink = parent_1_baz_member + parent_2_bob_member + parent_3_guest_member
|
||||
members_to_unlink.sudo().unlink()
|
||||
self.assertNotIn(
|
||||
baz_user.partner_id,
|
||||
parent_1.channel_member_ids.partner_id
|
||||
| parent_1.sub_channel_ids.channel_member_ids.partner_id,
|
||||
)
|
||||
self.assertNotIn(
|
||||
bob_user.partner_id,
|
||||
parent_2.channel_member_ids.partner_id
|
||||
| parent_2.sub_channel_ids.channel_member_ids.partner_id,
|
||||
)
|
||||
self.assertNotIn(
|
||||
guest,
|
||||
parent_3.channel_member_ids.guest_id
|
||||
| parent_3.sub_channel_ids.channel_member_ids.guest_id,
|
||||
)
|
||||
self.assertIn(bob_user.partner_id, parent_1_sub_channel_1.channel_member_ids.partner_id)
|
||||
self.assertIn(bob_user.partner_id, parent_1_sub_channel_2.channel_member_ids.partner_id)
|
||||
self.assertIn(baz_user.partner_id, parent_2_sub_channel.channel_member_ids.partner_id)
|
||||
self.assertIn(bob_user.partner_id, parent_3_sub_channel.channel_member_ids.partner_id)
|
||||
|
||||
def test_08_group_public_id_synced_with_parent(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent._create_sub_channel()
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
self.assertEqual(parent.group_public_id, self.env.ref("base.group_user"))
|
||||
self.assertEqual(sub_channel.group_public_id, parent.group_public_id)
|
||||
parent.group_public_id = self.env.ref("base.group_system")
|
||||
self.assertEqual(parent.group_public_id, self.env.ref("base.group_system"))
|
||||
self.assertEqual(sub_channel.group_public_id, parent.group_public_id)
|
||||
parent.group_public_id = None
|
||||
self.assertEqual(parent.group_public_id, self.env["res.groups"])
|
||||
self.assertEqual(sub_channel.group_public_id, parent.group_public_id)
|
||||
|
||||
def test_09_cannot_change_group_public_id_of_sub_channel(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
parent._create_sub_channel()
|
||||
sub_channel = parent.sub_channel_ids[0]
|
||||
with self.assertRaises(UserError):
|
||||
sub_channel.group_public_id = self.env.ref("base.group_system")
|
||||
|
||||
def test_10_sub_channel_message_author_member(self):
|
||||
bob_user = new_test_user(self.env, "bob_user", groups="base.group_user")
|
||||
parent = self.env["discuss.channel"].create({
|
||||
"name": "General",
|
||||
"channel_member_ids": [Command.create({"partner_id": bob_user.partner_id.id})],
|
||||
})
|
||||
message = parent.with_user(bob_user).message_post(body="Hello there!")
|
||||
sub_channel = parent._create_sub_channel(from_message_id=message.id)
|
||||
self.assertIn(bob_user.partner_id, sub_channel.channel_member_ids.partner_id)
|
||||
self.assertEqual(len(sub_channel.channel_member_ids), 2)
|
||||
|
||||
def test_11_sub_channel_fallback_name_on_empty_message(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
message = parent.message_post(body="Hello there!", message_type="comment")
|
||||
parent._message_update_content(message, body="")
|
||||
sub_channel = parent._create_sub_channel(from_message_id=message.id)
|
||||
self.assertEqual(sub_channel.name, "This message has been removed")
|
||||
|
||||
def test_12_unlink_children_members_only_once(self):
|
||||
parent = self.env["discuss.channel"].create({"name": "General"})
|
||||
child = parent._create_sub_channel()
|
||||
|
||||
og_unlink = self.env.registry["discuss.channel.member"].unlink
|
||||
unlinked_member_ids = []
|
||||
expected_unlinked_member_ids = sorted((parent.self_member_id | child.self_member_id).ids)
|
||||
|
||||
def _patched_unlink(records):
|
||||
unlinked_member_ids.extend(records.ids)
|
||||
og_unlink(records)
|
||||
|
||||
with patch.object(self.env.registry["discuss.channel.member"], "unlink", _patched_unlink):
|
||||
(parent | child).channel_member_ids.unlink()
|
||||
self.assertEqual(expected_unlinked_member_ids, sorted(unlinked_member_ids))
|
||||
|
||||
def test_13_mentioned_user_becomes_sub_channel_member(self):
|
||||
alice_user = new_test_user(self.env, "alice_user", groups="base.group_user")
|
||||
bob_user = new_test_user(self.env, "bob_user", groups="base.group_user")
|
||||
parent = self.env["discuss.channel"].create({
|
||||
"name": "General",
|
||||
"channel_member_ids": [
|
||||
Command.create({"partner_id": alice_user.partner_id.id}),
|
||||
Command.create({"partner_id": bob_user.partner_id.id}),
|
||||
],
|
||||
})
|
||||
message = parent.with_user(bob_user).message_post(body="Hello there!")
|
||||
sub_channel = parent._create_sub_channel(from_message_id=message.id)
|
||||
self.assertNotIn(alice_user.partner_id, sub_channel.channel_member_ids.partner_id)
|
||||
sub_channel.with_user(bob_user).message_post(
|
||||
body="Check this out @Alice",
|
||||
partner_ids=[alice_user.partner_id.id],
|
||||
)
|
||||
self.assertIn(alice_user.partner_id, sub_channel.channel_member_ids.partner_id)
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
from odoo.addons.mail.tests.common_controllers import MailControllerThreadCommon, MessagePostSubTestData
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestDiscussThreadController(MailControllerThreadCommon):
|
||||
|
||||
def test_internal_channel_message_post_access(self):
|
||||
"""Test access of message_post on internal channel."""
|
||||
channel = self.env["discuss.channel"].create({"name": "Internal Channel"})
|
||||
|
||||
def test_access(user, allowed):
|
||||
return MessagePostSubTestData(user, allowed)
|
||||
|
||||
self._execute_message_post_subtests(
|
||||
channel,
|
||||
(
|
||||
test_access(self.user_public, False),
|
||||
test_access(self.guest, False),
|
||||
test_access(self.user_portal, False),
|
||||
test_access(self.user_employee, True),
|
||||
test_access(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_public_channel_message_post_access(self):
|
||||
"""Test access of message_post on public channel."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"name": "Public Channel", "group_public_id": None}
|
||||
)
|
||||
|
||||
def test_access(user, allowed, exp_author=None):
|
||||
return MessagePostSubTestData(user, allowed, exp_author=exp_author)
|
||||
|
||||
self._execute_message_post_subtests(
|
||||
channel,
|
||||
(
|
||||
test_access(self.user_public, True),
|
||||
test_access(self.guest, True),
|
||||
test_access(self.user_portal, True),
|
||||
test_access(self.user_employee, True),
|
||||
test_access(self.user_admin, True),
|
||||
),
|
||||
)
|
||||
|
||||
def test_public_channel_message_post_partner_ids(self):
|
||||
"""Test partner_ids of message_post on public channel.
|
||||
Non-internal users cannot use mentions without mention_token."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"name": "Public Channel", "group_public_id": None}
|
||||
)
|
||||
channel._add_members(users=self.user_employee_nopartner)
|
||||
partners = (
|
||||
self.user_portal + self.user_employee + self.user_employee_nopartner + self.user_admin
|
||||
).partner_id
|
||||
|
||||
def test_partners(user, allowed, exp_partners):
|
||||
return MessagePostSubTestData(
|
||||
user, allowed, partners=partners, exp_partners=exp_partners
|
||||
)
|
||||
|
||||
self._execute_message_post_subtests(
|
||||
channel,
|
||||
(
|
||||
test_partners(self.user_public, True, self.env["res.partner"]),
|
||||
test_partners(self.guest, True, self.env["res.partner"]),
|
||||
test_partners(self.user_portal, True, self.env["res.partner"]),
|
||||
test_partners(self.user_employee, True, partners),
|
||||
test_partners(self.user_employee_nopartner, True, partners),
|
||||
test_partners(self.user_admin, True, partners),
|
||||
),
|
||||
)
|
||||
|
||||
def test_public_channel_message_post_partner_emails(self):
|
||||
"""Test partner_emails of message_post on public channel can only be
|
||||
used by users of base.group_partner_manager."""
|
||||
channel = self.env["discuss.channel"].create(
|
||||
{"name": "Public Channel", "group_public_id": None}
|
||||
)
|
||||
no_emails = []
|
||||
existing_emails = [self.user_employee.email]
|
||||
partner_emails = [self.user_employee.email, "test@example.com"]
|
||||
|
||||
def test_emails(user, allowed, exp_emails, exp_author=None):
|
||||
return MessagePostSubTestData(
|
||||
user,
|
||||
allowed,
|
||||
partner_emails=partner_emails,
|
||||
exp_author=exp_author,
|
||||
exp_emails=exp_emails,
|
||||
)
|
||||
|
||||
self._execute_message_post_subtests(
|
||||
channel,
|
||||
(
|
||||
test_emails(self.user_public, True, no_emails),
|
||||
test_emails(self.guest, True, no_emails),
|
||||
test_emails(self.user_portal, True, no_emails),
|
||||
# restricted because not base.group_partner_manager: find existing only
|
||||
test_emails(self.user_employee_nopartner, True, existing_emails),
|
||||
test_emails(self.user_employee, True, partner_emails),
|
||||
test_emails(self.user_admin, True, partner_emails),
|
||||
),
|
||||
)
|
||||
47
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_guest.py
Normal file
47
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_guest.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from odoo import fields
|
||||
from odoo.addons.mail.tests.common import MailCase
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestGuest(MailCase):
|
||||
|
||||
def test_updating_guest_name_linked_to_multiple_channels(self):
|
||||
"""This test ensures that when a guest is linked to multiple channels,
|
||||
the guest's name is updated correctly and the appropriate bus notifications are sent.
|
||||
"""
|
||||
guest = self.env['mail.guest'].create({'name': 'Guest'})
|
||||
channel_1 = self.env["discuss.channel"]._create_channel(name="Channel 1", group_id=None)
|
||||
channel_2 = self.env["discuss.channel"]._create_channel(name="Channel 2", group_id=None)
|
||||
channel_1._add_members(guests=guest)
|
||||
channel_2._add_members(guests=guest)
|
||||
|
||||
def get_guest_bus_params():
|
||||
guest_write_date = fields.Datetime.to_string(guest.write_date)
|
||||
message = {
|
||||
"type": "mail.record/insert",
|
||||
"payload": {
|
||||
"mail.guest": [
|
||||
{
|
||||
"avatar_128_access_token": guest._get_avatar_128_access_token(),
|
||||
"id": guest.id,
|
||||
"name": "Guest Name Updated",
|
||||
"write_date": guest_write_date,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
[
|
||||
(self.cr.dbname, "discuss.channel", channel_1.id),
|
||||
(self.cr.dbname, "discuss.channel", channel_2.id),
|
||||
(self.cr.dbname, "mail.guest", guest.id),
|
||||
],
|
||||
[message, message, message],
|
||||
)
|
||||
|
||||
self._reset_bus()
|
||||
with self.assertBus(get_params=get_guest_bus_params):
|
||||
guest._update_name("Guest Name Updated")
|
||||
self.assertEqual(guest.name, "Guest Name Updated")
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
from odoo.tests import tagged
|
||||
from odoo.addons.bus.tests.common import WebsocketCase
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestGuestFeature(WebsocketCase, MailCommon):
|
||||
def test_mark_as_read_as_guest(self):
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
partner = self.env["res.partner"].create({"name": "John"})
|
||||
channel = self.env["discuss.channel"]._create_channel(
|
||||
group_id=None, name="General"
|
||||
)
|
||||
channel._add_members(guests=guest, partners=partner)
|
||||
channel.message_post(
|
||||
body="Hello World!", message_type="comment", subtype_xmlid="mail.mt_comment"
|
||||
)
|
||||
guest_member = channel.channel_member_ids.filtered(
|
||||
lambda m: m.guest_id == guest
|
||||
)
|
||||
self.assertEqual(guest_member.seen_message_id, self.env["mail.message"])
|
||||
self.make_jsonrpc_request(
|
||||
"/discuss/channel/mark_as_read",
|
||||
{
|
||||
"channel_id": channel.id,
|
||||
"last_message_id": channel.message_ids[0].id,
|
||||
},
|
||||
cookies={guest._cookie_name: guest._format_auth_cookie()}
|
||||
)
|
||||
self.assertEqual(guest_member.seen_message_id, channel.message_ids[0])
|
||||
|
||||
def test_subscribe_to_guest_channel(self):
|
||||
self._reset_bus()
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
guest_websocket = self.websocket_connect()
|
||||
self.subscribe(guest_websocket, [f"mail.guest_{guest._format_auth_cookie()}"], guest.id)
|
||||
guest._bus_send("lambda", {"foo": "bar"})
|
||||
self.trigger_notification_dispatching([guest])
|
||||
notifications = json.loads(guest_websocket.recv())
|
||||
self.assertEqual(1, len(notifications))
|
||||
self.assertEqual(notifications[0]["message"]["type"], "lambda")
|
||||
self.assertEqual(notifications[0]["message"]["payload"], {"foo": "bar"})
|
||||
|
||||
def test_subscribe_to_discuss_channel(self):
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
channel = self.env["discuss.channel"]._create_channel(
|
||||
group_id=None, name="General"
|
||||
)
|
||||
channel._add_members(guests=guest)
|
||||
self._reset_bus()
|
||||
guest_websocket = self.websocket_connect()
|
||||
self.subscribe(guest_websocket, [f"mail.guest_{guest._format_auth_cookie()}"], guest.id)
|
||||
channel._bus_send("lambda", {"foo": "bar"})
|
||||
self.trigger_notification_dispatching([channel])
|
||||
notifications = json.loads(guest_websocket.recv())
|
||||
self.assertEqual(1, len(notifications))
|
||||
self.assertEqual(notifications[0]["message"]["type"], "lambda")
|
||||
self.assertEqual(notifications[0]["message"]["payload"], {"foo": "bar"})
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
import odoo.tests
|
||||
from odoo import Command
|
||||
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
|
||||
|
||||
|
||||
@odoo.tests.tagged('post_install', '-at_install')
|
||||
class TestLoadMessages(HttpCaseWithUserDemo):
|
||||
def test_01_mail_message_load_order_tour(self):
|
||||
partner_admin = self.env.ref('base.partner_admin')
|
||||
channel_id = self.env["discuss.channel"].create({
|
||||
"name": "MyTestChannel",
|
||||
"channel_member_ids": [Command.create({"partner_id": partner_admin.id})],
|
||||
})
|
||||
self.env["mail.message"].create([{
|
||||
"body": str(n),
|
||||
"model": "discuss.channel",
|
||||
"pinned_at": odoo.fields.Datetime.now() if n == 1 else None,
|
||||
"res_id": channel_id.id,
|
||||
"author_id": partner_admin.id,
|
||||
"message_type": "comment",
|
||||
} for n in range(1, 61)])
|
||||
channel_id.channel_member_ids.filtered(
|
||||
lambda m: m.partner_id == partner_admin
|
||||
)._mark_as_read(channel_id.message_ids[0].id)
|
||||
self.start_tour("/odoo/action-mail.action_discuss", "mail_message_load_order_tour", login="admin")
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
|
||||
import odoo
|
||||
from odoo.tests import tagged, users
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.addons.base.tests.common import HttpCase, HttpCaseWithUserDemo
|
||||
from odoo.addons.mail.tests.common import MailCommon, mail_new_test_user
|
||||
from odoo.http import STATIC_CACHE_LONG
|
||||
from odoo import Command, fields
|
||||
|
||||
|
||||
@odoo.tests.tagged("-at_install", "post_install", "mail_controller")
|
||||
class TestMessageController(HttpCaseWithUserDemo):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.channel = cls.env["discuss.channel"].create(
|
||||
{
|
||||
"group_public_id": None,
|
||||
"name": "Test channel",
|
||||
}
|
||||
)
|
||||
cls.public_user = cls.env.ref("base.public_user")
|
||||
cls.attachments = (
|
||||
cls.env["ir.attachment"]
|
||||
.with_user(cls.public_user)
|
||||
.sudo()
|
||||
.create(
|
||||
[
|
||||
{
|
||||
"name": "File 1",
|
||||
"res_id": 0,
|
||||
"res_model": "mail.compose.message",
|
||||
},
|
||||
{
|
||||
"name": "File 2",
|
||||
"res_id": 0,
|
||||
"res_model": "mail.compose.message",
|
||||
},
|
||||
]
|
||||
)
|
||||
)
|
||||
cls.guest = cls.env["mail.guest"].create({"name": "Guest"})
|
||||
cls.channel._add_members(guests=cls.guest)
|
||||
|
||||
@mute_logger("odoo.addons.http_routing.models.ir_http", "odoo.http")
|
||||
def test_channel_message_attachments(self):
|
||||
self.authenticate(None, None)
|
||||
self.opener.cookies[self.guest._cookie_name] = self.guest._format_auth_cookie()
|
||||
# test message post: token error
|
||||
res1 = self.url_open(
|
||||
url="/mail/message/post",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": self.channel.id,
|
||||
"post_data": {
|
||||
"body": "test",
|
||||
"attachment_ids": [self.attachments[0].id],
|
||||
"attachment_tokens": ["wrong token"],
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res1.status_code, 200)
|
||||
self.assertIn(
|
||||
"One or more attachments do not exist, or you do not have the rights to access them.",
|
||||
res1.text,
|
||||
"guest should not be allowed to add attachment without token when posting message",
|
||||
)
|
||||
# test message post: token ok
|
||||
res2 = self.url_open(
|
||||
url="/mail/message/post",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": self.channel.id,
|
||||
"post_data": {
|
||||
"body": "test",
|
||||
"attachment_ids": [self.attachments[0].id],
|
||||
"attachment_tokens": [self.attachments[0]._get_ownership_token()],
|
||||
"message_type": "comment",
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res2.status_code, 200)
|
||||
data1 = res2.json()["result"]
|
||||
self.assertEqual(
|
||||
data1["store_data"]["ir.attachment"],
|
||||
[
|
||||
{
|
||||
"checksum": False,
|
||||
"create_date": fields.Datetime.to_string(self.attachments[0].create_date),
|
||||
"file_size": 0,
|
||||
"has_thumbnail": False,
|
||||
"id": self.attachments[0].id,
|
||||
"mimetype": "application/octet-stream",
|
||||
"name": "File 1",
|
||||
"ownership_token": self.attachments[0]._get_ownership_token(),
|
||||
"raw_access_token": self.attachments[0]._get_raw_access_token(),
|
||||
"res_name": "Test channel",
|
||||
"res_model": self.attachments[0].res_model,
|
||||
"thread": {"id": self.channel.id, "model": "discuss.channel"},
|
||||
"thumbnail_access_token": self.attachments[0]._get_thumbnail_token(),
|
||||
"voice_ids": [],
|
||||
'type': 'binary',
|
||||
'url': False,
|
||||
},
|
||||
],
|
||||
"guest should be allowed to add attachment with token when posting message",
|
||||
)
|
||||
# test message update: token error
|
||||
res3 = self.url_open(
|
||||
url="/mail/message/update_content",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"message_id": data1["message_id"],
|
||||
"update_data": {
|
||||
"body": "test",
|
||||
"attachment_ids": [self.attachments[1].id],
|
||||
"attachment_tokens": ["wrong token"],
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res3.status_code, 200)
|
||||
self.assertIn(
|
||||
"One or more attachments do not exist, or you do not have the rights to access them.",
|
||||
res3.text,
|
||||
"guest should not be allowed to add attachment without token when updating message",
|
||||
)
|
||||
# test message update: token ok
|
||||
res4 = self.url_open(
|
||||
url="/mail/message/update_content",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"message_id": data1["message_id"],
|
||||
"update_data": {
|
||||
"body": "test",
|
||||
"attachment_ids": [self.attachments[1].id],
|
||||
"attachment_tokens": [self.attachments[1]._get_ownership_token()],
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res4.status_code, 200)
|
||||
data2 = res4.json()["result"]
|
||||
self.assertEqual(
|
||||
data2["ir.attachment"],
|
||||
[
|
||||
{
|
||||
"checksum": False,
|
||||
"create_date": fields.Datetime.to_string(self.attachments[0].create_date),
|
||||
"file_size": 0,
|
||||
"has_thumbnail": False,
|
||||
"id": self.attachments[0].id,
|
||||
"mimetype": "application/octet-stream",
|
||||
"name": "File 1",
|
||||
"ownership_token": self.attachments[0]._get_ownership_token(),
|
||||
"raw_access_token": self.attachments[0]._get_raw_access_token(),
|
||||
"res_name": "Test channel",
|
||||
"res_model": self.attachments[0].res_model,
|
||||
"thread": {"id": self.channel.id, "model": "discuss.channel"},
|
||||
"thumbnail_access_token": self.attachments[0]._get_thumbnail_token(),
|
||||
"voice_ids": [],
|
||||
'type': 'binary',
|
||||
'url': False,
|
||||
},
|
||||
{
|
||||
"checksum": False,
|
||||
"create_date": fields.Datetime.to_string(self.attachments[1].create_date),
|
||||
"file_size": 0,
|
||||
"has_thumbnail": False,
|
||||
"id": self.attachments[1].id,
|
||||
"mimetype": "application/octet-stream",
|
||||
"name": "File 2",
|
||||
"ownership_token": self.attachments[1]._get_ownership_token(),
|
||||
"raw_access_token": self.attachments[1]._get_raw_access_token(),
|
||||
"res_name": "Test channel",
|
||||
"res_model": self.attachments[1].res_model,
|
||||
"thread": {"id": self.channel.id, "model": "discuss.channel"},
|
||||
"thumbnail_access_token": self.attachments[1]._get_thumbnail_token(),
|
||||
"voice_ids": [],
|
||||
'type': 'binary',
|
||||
'url': False,
|
||||
},
|
||||
],
|
||||
"guest should be allowed to add attachment with token when updating message",
|
||||
)
|
||||
|
||||
@mute_logger("odoo.addons.http_routing.models.ir_http", "odoo.http")
|
||||
def test_mail_partner_from_email_unauthenticated(self):
|
||||
self.authenticate(None, None)
|
||||
self.opener.cookies[self.guest._cookie_name] = self.guest._format_auth_cookie()
|
||||
res1 = self.url_open(
|
||||
url="/mail/partner/from_email",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": self.channel.id,
|
||||
"emails": ["john@test.be"],
|
||||
},
|
||||
}
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res1.status_code, 200)
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.env["res.partner"].search_count([('email', '=', "john@test.be")]),
|
||||
"guest should not be allowed to create a partner from an email",
|
||||
)
|
||||
|
||||
self.authenticate(None, None)
|
||||
self.opener.cookies[self.guest._cookie_name] = self.guest._format_auth_cookie()
|
||||
res1 = self.url_open(
|
||||
url="/mail/partner/from_email",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": self.channel.id,
|
||||
"emails": ["john@test.be"],
|
||||
},
|
||||
}
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res1.status_code, 200)
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.env["res.partner"].search_count([('email', '=', "john@test.be")]),
|
||||
"guest should not be allowed to create a partner from an email",
|
||||
)
|
||||
res2 = self.url_open(
|
||||
url="/mail/message/post",
|
||||
data=json.dumps(
|
||||
{
|
||||
"params": {
|
||||
"thread_model": "discuss.channel",
|
||||
"thread_id": self.channel.id,
|
||||
"post_data": {
|
||||
"body": "test",
|
||||
"partner_emails": ["john@test.be"],
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
self.assertEqual(res2.status_code, 200)
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.env["res.partner"].search_count([('email', '=', "john@test.be")]),
|
||||
"guest should not be allowed to create a partner from an email from message_post",
|
||||
)
|
||||
|
||||
def test_mail_cache_control_header(self):
|
||||
testuser = self.env['res.users'].create({
|
||||
'email': 'testuser@testuser.com',
|
||||
'group_ids': [Command.set([self.ref('base.group_portal')])],
|
||||
'name': 'Test User',
|
||||
'login': 'testuser',
|
||||
'password': 'testuser',
|
||||
})
|
||||
test_user = self.authenticate("testuser", "testuser")
|
||||
partner = self.env["res.users"].browse(test_user.uid).partner_id
|
||||
self.channel._add_members(users=testuser)
|
||||
res = self.url_open(
|
||||
url=f"/web/image/?field=avatar_128&id={self.channel.id}&model=discuss.channel&unique={self.channel.avatar_cache_key}"
|
||||
)
|
||||
self.assertIn(f"max-age={STATIC_CACHE_LONG}", res.headers["Cache-Control"])
|
||||
|
||||
res = self.url_open(
|
||||
url=f"/web/image/?field=avatar_128&id={self.channel.id}&model=discuss.channel"
|
||||
)
|
||||
self.assertIn("no-cache", res.headers["Cache-Control"])
|
||||
|
||||
res = self.url_open(
|
||||
url=f"/web/image?field=avatar_128&id={partner.id}&model=res.partner&unique={fields.Datetime.to_string(partner.write_date)}"
|
||||
)
|
||||
self.assertIn(f"max-age={STATIC_CACHE_LONG}", res.headers["Cache-Control"])
|
||||
|
||||
res = self.url_open(
|
||||
url=f"/web/image?field=avatar_128&id={partner.id}&model=res.partner"
|
||||
)
|
||||
self.assertIn("no-cache", res.headers["Cache-Control"])
|
||||
|
||||
res = self.url_open(
|
||||
url=f"/web/image?field=avatar_128&id={self.guest.id}&model=mail.guest&unique={fields.Datetime.to_string(partner.write_date)}"
|
||||
)
|
||||
self.assertIn(f"max-age={STATIC_CACHE_LONG}", res.headers["Cache-Control"])
|
||||
|
||||
res = self.url_open(
|
||||
url=f"/web/image?field=avatar_128&id={self.guest.id}&model=mail.guest"
|
||||
)
|
||||
self.assertIn("no-cache", res.headers["Cache-Control"])
|
||||
|
||||
|
||||
@tagged("mail_message")
|
||||
class TestMessageLinks(MailCommon, HttpCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.user_employee_1 = mail_new_test_user(cls.env, login='tao1', groups='base.group_user', name='Tao Lee')
|
||||
cls.public_channel = cls.env['discuss.channel']._create_channel(name='Public Channel1', group_id=None)
|
||||
cls.private_group = cls.env['discuss.channel']._create_group(partners_to=cls.user_employee_1.partner_id.ids, name="Group")
|
||||
|
||||
@users('employee')
|
||||
def test_message_link_by_employee(self):
|
||||
channel_message = self.public_channel.message_post(body='Public Channel Message', message_type='comment')
|
||||
private_message_id = self.private_group.with_user(self.user_employee_1).message_post(
|
||||
body='Private Message',
|
||||
message_type='comment',
|
||||
).id
|
||||
self.authenticate('employee', 'employee')
|
||||
with self.subTest(channel_message=channel_message):
|
||||
expected_url = self.base_url() + f'/odoo/action-mail.action_discuss?active_id={channel_message.res_id}&highlight_message_id={channel_message.id}'
|
||||
res = self.url_open(f'/mail/message/{channel_message.id}')
|
||||
self.assertEqual(res.url, expected_url)
|
||||
with self.subTest(private_message_id=private_message_id):
|
||||
res = self.url_open(f'/mail/message/{private_message_id}')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@users('employee')
|
||||
def test_message_link_by_public(self):
|
||||
message = self.public_channel.message_post(
|
||||
body='Public Channel Message',
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment'
|
||||
)
|
||||
res = self.url_open(f'/mail/message/{message.id}')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
1295
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_rtc.py
Normal file
1295
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_rtc.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,28 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from odoo import Command, http
|
||||
from odoo.tests.common import tagged, HttpCase
|
||||
from odoo.tools import file_open, mute_logger
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestToggleUpload(HttpCase):
|
||||
def test_upload_allowed(self):
|
||||
self.authenticate(None, None)
|
||||
channel = self.env["discuss.channel"].create({"name": "General", "group_public_id": None})
|
||||
guest = self.env["mail.guest"].create({"name": "Guest"})
|
||||
channel.write({"channel_member_ids": [Command.create({"guest_id": guest.id})]})
|
||||
with file_open("addons/web/__init__.py") as file:
|
||||
response = self.url_open(
|
||||
"/mail/attachment/upload",
|
||||
{
|
||||
"csrf_token": http.Request.csrf_token(self),
|
||||
"thread_id": channel.id,
|
||||
"thread_model": "discuss.channel",
|
||||
},
|
||||
files={"ufile": file},
|
||||
cookies={guest._cookie_name: guest._format_auth_cookie()}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
48
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_ui.py
Normal file
48
odoo-bringout-oca-ocb-mail/mail/tests/discuss/test_ui.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
from odoo import Command
|
||||
from odoo.addons.base.tests.common import HttpCaseWithUserDemo, new_test_user
|
||||
|
||||
|
||||
@odoo.tests.tagged('post_install', '-at_install')
|
||||
class TestUi(HttpCaseWithUserDemo):
|
||||
|
||||
def test_01_mail_tour(self):
|
||||
self.start_tour("/odoo", 'discuss_channel_tour', login="admin")
|
||||
|
||||
def test_02_mail_create_channel_no_mail_tour(self):
|
||||
self.env['res.users'].create({
|
||||
'email': '', # User should be able to create a channel even if no email is defined
|
||||
'group_ids': [Command.set([self.ref('base.group_user')])],
|
||||
'name': 'Test User',
|
||||
'login': 'testuser',
|
||||
'password': 'testuser',
|
||||
})
|
||||
self.start_tour("/odoo", 'discuss_channel_tour', login='testuser')
|
||||
|
||||
# basic rendering test of the configuration menu in Discuss
|
||||
def test_03_mail_discuss_configuration_tour(self):
|
||||
self.start_tour("/odoo", "discuss_configuration_tour", login="admin")
|
||||
|
||||
def test_04_meeting_view_tour(self):
|
||||
bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com")
|
||||
john = new_test_user(self.env, "john", groups="base.group_user", email="john@test.com")
|
||||
group_chat = (
|
||||
self.env["discuss.channel"]
|
||||
.with_user(bob)
|
||||
._create_group(
|
||||
partners_to=john.partner_id.ids, default_display_mode="video_full_screen"
|
||||
)
|
||||
)
|
||||
self.authenticate("bob", "bob")
|
||||
self.make_jsonrpc_request("/mail/rtc/channel/join_call", {"channel_id": group_chat.id})
|
||||
self.start_tour(
|
||||
f"/odoo/discuss?active_id=discuss.channel_{group_chat.id}&fullscreen=1",
|
||||
"discuss.meeting_view_tour",
|
||||
login="john",
|
||||
)
|
||||
self.start_tour(group_chat.invitation_url, "discuss.meeting_view_public_tour", login="john")
|
||||
|
||||
def test_05_can_create_channel_tour(self):
|
||||
self.start_tour("odoo/discuss", "can_create_channel_from_form_view", login="demo")
|
||||
Loading…
Add table
Add a link
Reference in a new issue