[ADD] project_visibility_followers_portal: Add project visibility to "some employees and some portal users"

This commit is contained in:
clementthomas
2023-05-22 15:04:33 +02:00
parent cd663ec6e5
commit c3cc59cec7
8 changed files with 335 additions and 0 deletions

View File

@@ -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
<https://github.com/elabore-coop/project-tools/issues>`_. 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 <https://elabore.coop/web/image/res.company/1/logo?unique=f3db262>`_.
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.

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -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
<https://github.com/elabore-coop/project-tools/issues>`_. 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 <https://elabore.coop/web/image/res.company/1/logo?unique=f3db262>`_.
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,
}

View File

@@ -0,0 +1,2 @@
from . import project_project, project_task

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record model="ir.rule" id="project_project_rule_followers_portal">
<field name="name">Project: Invited internal users and invited portal users</field>
<field name="model_id" ref="model_project_project"/>
<field name="domain_force">[
('privacy_visibility', '=', 'followers_portal'),
'|', ('allowed_portal_user_ids', 'in', user.ids),
('allowed_internal_user_ids', 'in', user.ids),
]</field>
<field name="groups" eval="[(4, ref('base.group_user')),(4, ref('base.group_portal'))]"/>
</record>
<record model="ir.rule" id="project_task_rule_followers_portal">
<field name="name">Task: Invited internal users and invited portal users</field>
<field name="model_id" ref="model_project_task"/>
<field name="domain_force">[
('project_id.privacy_visibility', '=', 'followers_portal'),
('allowed_user_ids', 'in', user.ids),
]</field>
<field name="groups" eval="[(4, ref('base.group_user')),(4, ref('base.group_portal'))]"/>
</record>
<!-- change existing rule -->
<record model="ir.rule" id="project.project_public_members_rule">
<field name="domain_force">[
'|',
('privacy_visibility', '!=', 'followers_portal'),
('allowed_internal_user_ids', 'in', user.ids),
'|',
('privacy_visibility', '!=', 'followers'),
('allowed_internal_user_ids', 'in', user.ids),
]</field>
</record>
<record model="ir.rule" id="project.task_visibility_rule">
<field name="domain_force">[
'|',
('project_id.privacy_visibility', '!=', 'followers_portal'),
('allowed_user_ids', 'in', user.ids),
'|',
('project_id.privacy_visibility', '!=', 'followers'),
('allowed_user_ids', 'in', user.ids),
]</field>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,20 @@
<!-- todo: remove the date_start and date_end from the Extra Info Tab -->
<odoo>
<data>
<record id="view_project_form_visibility_followers_portal" model="ir.ui.view">
<field name="name">project.form.visibility.followers.portal</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='allowed_internal_user_ids']" position="attributes">
<attribute name="attrs">{'invisible': [('privacy_visibility', '!=', 'followers'),('privacy_visibility', '!=', 'followers_portal')]}</attribute>
</xpath>
<xpath expr="//field[@name='allowed_portal_user_ids']" position="attributes">
<attribute name="attrs">{'invisible': [('privacy_visibility', '!=', 'portal'),('privacy_visibility', '!=', 'followers_portal')]}</attribute>
</xpath>
</field>
</record>
</data>
</odoo>