From c3cc59cec76295717c7ffaafd52652637408c808 Mon Sep 17 00:00:00 2001 From: clementthomas Date: Mon, 22 May 2023 15:04:33 +0200 Subject: [PATCH] [ADD] project_visibility_followers_portal: Add project visibility to "some employees and some portal users" --- .../README.rst | 45 +++++++++ .../__init__.py | 3 + .../__manifest__.py | 92 +++++++++++++++++++ .../models/__init__.py | 2 + .../models/project_project.py | 30 ++++++ .../models/project_task.py | 89 ++++++++++++++++++ .../security/project_security.xml | 54 +++++++++++ .../views/project_views.xml | 20 ++++ 8 files changed, 335 insertions(+) create mode 100644 project_visibility_followers_portal/README.rst create mode 100644 project_visibility_followers_portal/__init__.py create mode 100644 project_visibility_followers_portal/__manifest__.py create mode 100644 project_visibility_followers_portal/models/__init__.py create mode 100644 project_visibility_followers_portal/models/project_project.py create mode 100644 project_visibility_followers_portal/models/project_task.py create mode 100644 project_visibility_followers_portal/security/project_security.xml create mode 100644 project_visibility_followers_portal/views/project_views.xml diff --git a/project_visibility_followers_portal/README.rst b/project_visibility_followers_portal/README.rst new file mode 100644 index 0000000..36b50f2 --- /dev/null +++ b/project_visibility_followers_portal/README.rst @@ -0,0 +1,45 @@ +================= +project_visibility_followers_portal +================= + +Add project visibility : some employees and some portal users + +Installation +============ + +Use Odoo normal procedure to install add-ons to install +``project_visibility_followers_portal``. + +Known issues / Roadmap +====================== + +Bug Tracker +=========== + +Bugs are tracked on `our issues website +`_. In case of +trouble, please check there if your issue has already been +reported. If you spotted it first, help us smashing it by providing a +detailed and welcomed feedback. + +Credits +======= + +Images +------ +* Elabore: `Icon `_. + +Contributors +------------ +* Clément Thomas + +Funders +------- +The development of this module has been financially supported by: +* Elabore (https://elabore.coop) + + +Maintainer +---------- + +This module is maintained by Elabore. \ No newline at end of file diff --git a/project_visibility_followers_portal/__init__.py b/project_visibility_followers_portal/__init__.py new file mode 100644 index 0000000..cde864b --- /dev/null +++ b/project_visibility_followers_portal/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/project_visibility_followers_portal/__manifest__.py b/project_visibility_followers_portal/__manifest__.py new file mode 100644 index 0000000..4bea689 --- /dev/null +++ b/project_visibility_followers_portal/__manifest__.py @@ -0,0 +1,92 @@ +# Copyright 2022 Stéphan Sainléger (Elabore) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "project_visibility_followers_portal", + "version": "14.0.1.0.0", + "author": "Elabore", + "website": "https://github.com/elabore-coop/project-tools", + "maintainer": "Clément Thomas", + "license": "AGPL-3", + "category": "Tools", + "summary": "Add project visibility : some employees and some portal users", + "description": """ + :image: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +================= +project_visibility_followers_portal +================= + +Add project visibility : some employees and some portal users + +Installation +============ + +Install ``project_visibility_followers_portal``, all dependencies will be installed by default. + +Known issues / Roadmap +====================== + +None yet. + +Bug Tracker +=========== + +Bugs are tracked on `our issues website +`_. In case of +trouble, please check there if your issue has already been +reported. If you spotted it first, help us smashing it by providing a +detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Elabore: `Icon `_. + +Contributors +------------ + +* Clément Thomas + +Funders +------- + +The development of this module has been financially supported by: +* Elabore (https://elabore.coop) + + +Maintainer +---------- +This module is maintained by Elabore. + +""", + # any module necessary for this one to work correctly + "depends": [ + "base", + "project", + ], + "qweb": [ + # "static/src/xml/*.xml", + ], + "external_dependencies": { + "python": [], + }, + # always loaded + "data": [ + "views/project_views.xml", + "security/project_security.xml" + ], + # only loaded in demonstration mode + "demo": [], + "js": [], + "css": [], + "installable": True, + # Install this module automatically if all dependency have been previously + # and independently installed. Used for synergetic or glue modules. + "auto_install": False, + "application": False, +} diff --git a/project_visibility_followers_portal/models/__init__.py b/project_visibility_followers_portal/models/__init__.py new file mode 100644 index 0000000..a2e968a --- /dev/null +++ b/project_visibility_followers_portal/models/__init__.py @@ -0,0 +1,2 @@ + +from . import project_project, project_task \ No newline at end of file diff --git a/project_visibility_followers_portal/models/project_project.py b/project_visibility_followers_portal/models/project_project.py new file mode 100644 index 0000000..dd53048 --- /dev/null +++ b/project_visibility_followers_portal/models/project_project.py @@ -0,0 +1,30 @@ + +from odoo import models, fields, api + + +class Project(models.Model): + _inherit = "project.project" + + privacy_visibility = fields.Selection( + selection_add=[('followers_portal','Invited portal users and invited internal users')], + ondelete={'followers_portal': 'set default'}) + + @api.model + def create(self, vals): + """In case of "followers_portal" privacy visibility, add current user to list of allowed users. + Same behaviour than "portal" privacy visibility. + """ + project = super(Project, self).create(vals) + if project.privacy_visibility == 'followers_portal' and project.partner_id.user_ids: + project.allowed_user_ids |= project.partner_id.user_ids + return project + + def write(self, vals): + """In case of "followers_portal" privacy visibility, add current user to list of allowed users. + Same behaviour than "portal" privacy visibility. + """ + res = super(Project, self).write(vals) + if vals.get('partner_id') or vals.get('privacy_visibility'): + for project in self.filtered(lambda project: project.privacy_visibility == 'followers_portal'): + project.allowed_user_ids |= project.partner_id.user_ids + return res \ No newline at end of file diff --git a/project_visibility_followers_portal/models/project_task.py b/project_visibility_followers_portal/models/project_task.py new file mode 100644 index 0000000..97bfedb --- /dev/null +++ b/project_visibility_followers_portal/models/project_task.py @@ -0,0 +1,89 @@ + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + + +class Task(models.Model): + _inherit = "project.task" + + @api.constrains('allowed_user_ids') + def _check_no_portal_allowed(self): + for task in self.filtered(lambda t: t.project_id.privacy_visibility not in ('portal','followers_portal')): + portal_users = task.allowed_user_ids.filtered('share') + if portal_users: + user_names = ', '.join(portal_users[:10].mapped('name')) + raise ValidationError(_("The project visibility setting doesn't allow portal users to see the project's tasks. (%s)", user_names)) + + @api.depends('project_id.allowed_user_ids', 'project_id.privacy_visibility') + def _compute_allowed_user_ids(self): + for task in self.with_context(prefetch_fields=False): + portal_users = task.allowed_user_ids.filtered('share') + internal_users = task.allowed_user_ids - portal_users + + if task.project_id.privacy_visibility == 'followers': + task.allowed_user_ids |= task.project_id.allowed_internal_user_ids + task.allowed_user_ids -= portal_users + elif task.project_id.privacy_visibility == 'portal': + task.allowed_user_ids |= task.project_id.allowed_portal_user_ids + elif task.project_id.privacy_visibility == 'followers_portal': + task.allowed_user_ids |= task.project_id.allowed_internal_user_ids + task.allowed_user_ids |= task.project_id.allowed_portal_user_ids + + if task.project_id.privacy_visibility not in ('portal','followers_portal'): + task.allowed_user_ids -= portal_users + elif task.project_id.privacy_visibility not in ('followers','followers_portal'): + task.allowed_user_ids -= internal_users + + def _compute_access_warning(self): + for task in self.filtered(lambda x: x.project_id.privacy_visibility not in ('portal','followers_portal')): + task.access_warning = _( + "The task cannot be shared with the recipient(s) because the privacy of the project is too restricted. Set the privacy of the project to 'Visible by following customers' in order to make it accessible by the recipient(s).") + + def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None): + """ + Add the users subscribed to allowed portal users + """ + res = super(Task, self).message_subscribe(partner_ids=partner_ids, channel_ids=channel_ids, subtype_ids=subtype_ids) + if partner_ids: + new_allowed_users = self.env['res.partner'].browse(partner_ids).user_ids.filtered('share') + tasks = self.filtered(lambda task: task.project_id.privacy_visibility == 'followers_portal') + tasks.sudo().allowed_user_ids |= new_allowed_users + return res + + @api.model_create_multi + def create(self, vals_list): + tasks = super(Task, self).create(vals_list) + for task in tasks: + if task.project_id.privacy_visibility == 'followers_portal': + task._portal_ensure_token() + return tasks + + def _notify_get_groups(self, msg_vals=None): + groups = super(Task, self)._notify_get_groups(msg_vals=msg_vals) + """ Handle project users and managers recipients that can assign + tasks and create new one directly from notification emails. Also give + access button to portal users and portal customers. If they are notified + they should probably have access to the document. """ + self.ensure_one() + + project_user_group_id = self.env.ref('project.group_project_user').id + project_manager_group_id = self.env.ref('project.group_project_manager').id + + group_func = lambda pdata: pdata['type'] == 'user' and project_user_group_id in pdata['groups'] + if self.project_id.privacy_visibility == 'followers_portal': + allowed_user_ids = self.project_id.allowed_internal_user_ids.partner_id.ids + group_func = lambda pdata:\ + pdata['type'] == 'user'\ + and ( + project_manager_group_id in pdata['groups']\ + or (project_user_group_id in pdata['groups'] and pdata['id'] in allowed_user_ids) + ) + groups = [('group_project_user', group_func, {})]+groups + allowed_user_ids = self.project_id.allowed_portal_user_ids.partner_id.ids + groups.insert(0, ( + 'allowed_portal_users', + lambda pdata: pdata['type'] == 'portal' and pdata['id'] in allowed_user_ids, + {} + )) + + return groups \ No newline at end of file diff --git a/project_visibility_followers_portal/security/project_security.xml b/project_visibility_followers_portal/security/project_security.xml new file mode 100644 index 0000000..0cae1df --- /dev/null +++ b/project_visibility_followers_portal/security/project_security.xml @@ -0,0 +1,54 @@ + + + + + + + Project: Invited internal users and invited portal users + + [ + ('privacy_visibility', '=', 'followers_portal'), + '|', ('allowed_portal_user_ids', 'in', user.ids), + ('allowed_internal_user_ids', 'in', user.ids), + ] + + + + + Task: Invited internal users and invited portal users + + [ + ('project_id.privacy_visibility', '=', 'followers_portal'), + ('allowed_user_ids', 'in', user.ids), + ] + + + + + + [ + '|', + ('privacy_visibility', '!=', 'followers_portal'), + ('allowed_internal_user_ids', 'in', user.ids), + '|', + ('privacy_visibility', '!=', 'followers'), + ('allowed_internal_user_ids', 'in', user.ids), + ] + + + + [ + '|', + ('project_id.privacy_visibility', '!=', 'followers_portal'), + ('allowed_user_ids', 'in', user.ids), + '|', + ('project_id.privacy_visibility', '!=', 'followers'), + ('allowed_user_ids', 'in', user.ids), + ] + + + + + + + diff --git a/project_visibility_followers_portal/views/project_views.xml b/project_visibility_followers_portal/views/project_views.xml new file mode 100644 index 0000000..bcc815b --- /dev/null +++ b/project_visibility_followers_portal/views/project_views.xml @@ -0,0 +1,20 @@ + + + + + + project.form.visibility.followers.portal + project.project + + + + {'invisible': [('privacy_visibility', '!=', 'followers'),('privacy_visibility', '!=', 'followers_portal')]} + + + {'invisible': [('privacy_visibility', '!=', 'portal'),('privacy_visibility', '!=', 'followers_portal')]} + + + + + +