19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:56 +01:00
parent a2f74aefd8
commit 4a4d12c333
844 changed files with 212348 additions and 270090 deletions

View file

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from werkzeug.exceptions import Forbidden
from odoo.http import request, route
from odoo.http import request
from odoo.addons.portal.controllers.mail import PortalChatter
from odoo.addons.portal.controllers.portal_thread import PortalChatter
from .portal import ProjectCustomerPortal
@ -25,77 +24,7 @@ class ProjectSharingChatter(PortalChatter):
can_access = project_sudo and res_model == 'project.task' and project_sudo.with_user(request.env.user)._check_project_sharing_access()
task = None
if can_access:
task = request.env['project.task'].sudo().search([('id', '=', res_id), ('project_id', '=', project_sudo.id)])
task = request.env['project.task'].sudo().with_context(active_test=False).search([('id', '=', res_id), ('project_id', '=', project_sudo.id)])
if not can_access or not task:
raise Forbidden()
return task[task._mail_post_token_field]
# ============================================================ #
# Note concerning the methods portal_chatter_(init/post/fetch)
# ============================================================ #
#
# When the project is shared to a portal user with the edit rights,
# he has the read/write access to the related tasks. So it could be
# possible to call directly the message_post method on a task.
#
# This change is considered as safe, as we only willingly expose
# records, for some assumed fields only, and this feature is
# optional and opt-in. (like the public employee model for example).
# It doesn't allow portal users to access other models, like
# a timesheet or an invoice.
#
# It could seem odd to use those routes, and converting the project
# access token into the task access token, as the user has actually
# access to the records.
#
# However, it has been decided that it was the less hacky way to
# achieve this, as:
#
# - We're reusing the existing routes, that convert all the data
# into valid arguments for the methods we use (message_post, ...).
# That way, we don't have to reinvent the wheel, duplicating code
# from mail/portal that surely will lead too desynchronization
# and inconsistencies over the time.
#
# - We don't define new routes, to do the exact same things than portal,
# considering that the portal user can use message_post for example
# because he has access to the record.
# Let's suppose that we remove this in a future development, those
# new routes won't be valid anymore.
#
# - We could have reused the mail widgets, as we already reuse the
# form/list/kanban views, etc. However, we only want to display
# the messages and allow to post. We don't need the next activities
# the followers system, etc. This required to override most of the
# mail.thread basic methods, without being sure that this would
# work with other installed applications or customizations
@route()
def portal_chatter_init(self, res_model, res_id, domain=False, limit=False, **kwargs):
project_sharing_id = kwargs.get('project_sharing_id')
if project_sharing_id:
# if there is a token in `kwargs` then it should be the access_token of the project shared
token = self._check_project_access_and_get_token(project_sharing_id, res_model, res_id, kwargs.get('token'))
if token:
del kwargs['project_sharing_id']
kwargs['token'] = token
return super().portal_chatter_init(res_model, res_id, domain=domain, limit=limit, **kwargs)
@route()
def portal_chatter_post(self, res_model, res_id, message, attachment_ids=None, attachment_tokens=None, **kw):
project_sharing_id = kw.get('project_sharing_id')
if project_sharing_id:
token = self._check_project_access_and_get_token(project_sharing_id, res_model, res_id, kw.get('token'))
if token:
del kw['project_sharing_id']
kw['token'] = token
return super().portal_chatter_post(res_model, res_id, message, attachment_ids=attachment_ids, attachment_tokens=attachment_tokens, **kw)
@route()
def portal_message_fetch(self, res_model, res_id, domain=False, limit=10, offset=0, **kw):
project_sharing_id = kw.get('project_sharing_id')
if project_sharing_id:
token = self._check_project_access_and_get_token(project_sharing_id, res_model, res_id, kw.get('token'))
if token is not None:
kw['token'] = token # Update token (either string which contains token value or False)
return super().portal_message_fetch(res_model, res_id, domain=domain, limit=limit, offset=offset, **kw)