[MERGE][IMP] base, *: hide non-relevant fields for portal users
Purpose ======= Hide non-relevant fields for a portal user. E.G. we want to hide the notification type, the menu customization... Because those fields make no sense for a portal user. In this PR we make onchange works on reified field to allow fields depending on groups to be modified as regular onchange in form view. Force the non-internal user to receive notifications by emails since they can not open Discuss. Task-2508521 closes odoo/odoo#77766 Related: odoo/upgrade#2890 Related: odoo/enterprise#21420 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -38,11 +38,9 @@
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="attributes">
|
||||
<attribute name="decoration-info">state == 'new'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='login_date']" position="after">
|
||||
<field name="state" invisible="1"/>
|
||||
<xpath expr="//field[@name='company_id']" position="after">
|
||||
<field name="state" widget="badge"
|
||||
decoration-info="state == 'new'" decoration-success="state == 'active'"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<xpath expr="//search" position="inside">
|
||||
<separator/>
|
||||
<filter name="totp_enabled" string="Two-factor authentication Enabled" domain="[('totp_secret','!=',False)]"/>
|
||||
<separator/>
|
||||
<filter name="totp_disabled" string="Two-factor authentication Disabled" domain="[('totp_secret','=',False)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -346,7 +346,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<notebook colspan="4" position="inside">
|
||||
<!-- Placeholder container to hold information about external accounts (Google calendar, Microsoft calendar, ...) -->
|
||||
<page string="Calendar" name="calendar" invisible="1" groups="base.group_system">
|
||||
<page string="Calendar" name="calendar" invisible="1" groups="base.group_system"
|
||||
attrs="{'invisible': [('share', '=', True)]}">
|
||||
<group name="calendar_accounts"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
@@ -16,14 +16,14 @@ class TestLivechatLead(TestCrmCommon):
|
||||
cls.env, login='user_anonymous',
|
||||
name='Anonymous Website', email=False,
|
||||
company_id=cls.company_main.id,
|
||||
notification_type='inbox',
|
||||
notification_type='email',
|
||||
groups='base.group_public',
|
||||
)
|
||||
cls.user_portal = mail_new_test_user(
|
||||
cls.env, login='user_portal',
|
||||
name='Paulette Portal', email='user_portal@test.example.com',
|
||||
company_id=cls.company_main.id,
|
||||
notification_type='inbox',
|
||||
notification_type='email',
|
||||
groups='base.group_portal',
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='tz']" position="after">
|
||||
<field name="livechat_username" string="Online Chat Name" readonly="0"/>
|
||||
<field name="livechat_username" string="Online Chat Name" readonly="0"
|
||||
attrs="{'invisible': [('share', '=', True)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -21,7 +22,8 @@
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='messaging']" position="after">
|
||||
<group name="livechat" string="Livechat">
|
||||
<group name="livechat" string="Livechat"
|
||||
attrs="{'invisible': [('share', '=', True)]}">
|
||||
<field name="livechat_username"/>
|
||||
</group>
|
||||
</xpath>
|
||||
|
||||
@@ -22,11 +22,25 @@ class Users(models.Model):
|
||||
('email', 'Handle by Emails'),
|
||||
('inbox', 'Handle in Odoo')],
|
||||
'Notification', required=True, default='email',
|
||||
compute='_compute_notification_type', store=True, readonly=False,
|
||||
help="Policy on how to handle Chatter notifications:\n"
|
||||
"- Handle by Emails: notifications are sent to your email address\n"
|
||||
"- Handle in Odoo: notifications appear in your Odoo Inbox")
|
||||
res_users_settings_ids = fields.One2many('res.users.settings', 'user_id')
|
||||
|
||||
_sql_constraints = [(
|
||||
"notification_type",
|
||||
"CHECK (notification_type = 'email' OR NOT share)",
|
||||
"Only internal user can receive notifications in Odoo",
|
||||
)]
|
||||
|
||||
@api.depends('share')
|
||||
def _compute_notification_type(self):
|
||||
for user in self:
|
||||
# Only the internal users can receive notifications in Odoo
|
||||
if user.share or not user.notification_type:
|
||||
user.notification_type = 'email'
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CRUD
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@@ -9,6 +9,7 @@ from . import test_mail_render
|
||||
from . import test_mail_template
|
||||
from . import test_mail_tools
|
||||
from . import test_res_partner
|
||||
from . import test_res_users
|
||||
from . import test_res_users_settings
|
||||
from . import test_rtc
|
||||
from . import test_update_notification
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from psycopg2 import IntegrityError
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon, mail_new_test_user
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestUser(MailCommon):
|
||||
|
||||
@mute_logger('odoo.sql_db')
|
||||
def test_notification_type_constraint(self):
|
||||
with self.assertRaises(IntegrityError, msg='Portal user can not receive notification in Odoo'):
|
||||
mail_new_test_user(
|
||||
self.env,
|
||||
login='user_test_constraint_2',
|
||||
name='Test User 2',
|
||||
email='user_test_constraint_2@test.example.com',
|
||||
notification_type='inbox',
|
||||
groups='base.group_portal',
|
||||
)
|
||||
@@ -27,7 +27,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<field name="signature" position="before">
|
||||
<field name="notification_type" widget="radio"/>
|
||||
<field name="notification_type" widget="radio"
|
||||
attrs="{'invisible': [('share', '=', True)]}"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<field name="notification_type" position="after">
|
||||
<field name="odoobot_state" readonly="0" groups="base.group_no_one"/>
|
||||
<field name="odoobot_state" readonly="0" groups="base.group_no_one"
|
||||
attrs="{'invisible': [('share', '=', True)]}"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
@@ -22,7 +23,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<field name="signature" position="before">
|
||||
<field name="odoobot_state" readonly="0" groups="base.group_no_one"/>
|
||||
<field name="odoobot_state" readonly="0" groups="base.group_no_one"
|
||||
attrs="{'invisible': [('share', '=', True)]}"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestWebsiteBlogCommon(common.TransactionCase):
|
||||
'name': 'Cedric Public',
|
||||
'login': 'cedric',
|
||||
'email': 'cedric.public@example.com',
|
||||
'notification_type': 'inbox',
|
||||
'notification_type': 'email',
|
||||
'groups_id': [(6, 0, [group_public_id])]
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestWebsiteBlogFlow(TestWebsiteBlogCommon):
|
||||
'name': 'Dorian Portal',
|
||||
'login': 'portal_user',
|
||||
'email': 'portal_user@example.com',
|
||||
'notification_type': 'inbox',
|
||||
'notification_type': 'email',
|
||||
'groups_id': [(6, 0, [group_portal.id])]
|
||||
})
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestPartnerLeadPortal(TestCrmCommon):
|
||||
company_id=self.env.ref("base.main_company").id,
|
||||
grade_id=self.grade.id,
|
||||
user_id=self.user_sales_manager.id,
|
||||
notification_type='inbox',
|
||||
notification_type='email',
|
||||
groups='base.group_portal',
|
||||
)
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ class Users(models.Model):
|
||||
'image_1024', 'image_512', 'image_256', 'image_128', 'lang', 'tz',
|
||||
'tz_offset', 'groups_id', 'partner_id', '__last_update', 'action_id',
|
||||
'avatar_1920', 'avatar_1024', 'avatar_512', 'avatar_256', 'avatar_128',
|
||||
'share',
|
||||
]
|
||||
|
||||
@property
|
||||
@@ -1235,7 +1236,8 @@ class GroupsView(models.Model):
|
||||
user_type_field_name = field_name
|
||||
user_type_readonly = str({'readonly': [(user_type_field_name, '!=', group_employee.id)]})
|
||||
attrs['widget'] = 'radio'
|
||||
attrs['groups'] = 'base.group_no_one'
|
||||
# Trigger the on_change of this "virtual field"
|
||||
attrs['on_change'] = '1'
|
||||
xml1.append(E.field(name=field_name, **attrs))
|
||||
xml1.append(E.newline())
|
||||
|
||||
@@ -1273,9 +1275,9 @@ class GroupsView(models.Model):
|
||||
xml2.append(E.group(*(xml_by_category[xml_cat]), col="2", string=master_category_name))
|
||||
|
||||
xml = E.field(
|
||||
E.group(*(xml1), col="2"),
|
||||
E.group(*(xml1), col="2", groups="base.group_no_one"),
|
||||
E.group(*(xml2), col="2", attrs=str(user_type_attrs)),
|
||||
E.group(*(xml3), col="4", attrs=str(user_type_attrs)), name="groups_id", position="replace")
|
||||
E.group(*(xml3), col="4", attrs=str(user_type_attrs), groups="base.group_no_one"), name="groups_id", position="replace")
|
||||
xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
|
||||
|
||||
# serialize and update the view
|
||||
@@ -1431,6 +1433,22 @@ class UsersView(models.Model):
|
||||
return values
|
||||
|
||||
def onchange(self, values, field_name, field_onchange):
|
||||
# field_name can be either a string, a list or Falsy
|
||||
if isinstance(field_name, list):
|
||||
names = field_name
|
||||
elif field_name:
|
||||
names = [field_name]
|
||||
else:
|
||||
names = []
|
||||
|
||||
if any(is_reified_group(field) for field in names):
|
||||
field_name = (
|
||||
['groups_id']
|
||||
+ [field for field in names if not is_reified_group(field)]
|
||||
)
|
||||
values.pop('groups_id', None)
|
||||
values.update(self._remove_reified_groups(values))
|
||||
|
||||
field_onchange['groups_id'] = ''
|
||||
result = super().onchange(values, field_name, field_onchange)
|
||||
if not field_name: # merged default_get
|
||||
|
||||
@@ -171,3 +171,28 @@ class TestUsers2(TransactionCase):
|
||||
user.write({fname: group2.id})
|
||||
self.assertEqual(user.groups_id & groups, groups)
|
||||
self.assertEqual(user.read([fname])[0][fname], group2.id)
|
||||
|
||||
def test_reified_groups_on_change(self):
|
||||
"""Test that a change on a reified fields trigger the onchange of groups_id."""
|
||||
group_public = self.env.ref('base.group_public')
|
||||
group_portal = self.env.ref('base.group_portal')
|
||||
group_user = self.env.ref('base.group_user')
|
||||
|
||||
# Build the reified group field name
|
||||
user_groups = group_public | group_portal | group_user
|
||||
user_groups_ids = [str(group_id) for group_id in sorted(user_groups.ids)]
|
||||
group_field_name = f"sel_groups_{'_'.join(user_groups_ids)}"
|
||||
|
||||
user_form = Form(self.env['res.users'], view='base.view_users_form')
|
||||
user_form.name = "Test"
|
||||
user_form.login = "Test"
|
||||
self.assertFalse(user_form.share)
|
||||
|
||||
setattr(user_form, group_field_name, group_portal.id)
|
||||
self.assertTrue(user_form.share, 'The groups_id onchange should have been triggered')
|
||||
|
||||
setattr(user_form, group_field_name, group_user.id)
|
||||
self.assertFalse(user_form.share, 'The groups_id onchange should have been triggered')
|
||||
|
||||
setattr(user_form, group_field_name, group_public.id)
|
||||
self.assertTrue(user_form.share, 'The groups_id onchange should have been triggered')
|
||||
|
||||
@@ -223,6 +223,7 @@
|
||||
<group>
|
||||
<field name="partner_id" readonly="1" required="0" groups="base.group_no_one"
|
||||
attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<field name="share" invisible="1"/>
|
||||
</group>
|
||||
</div>
|
||||
<notebook colspan="4">
|
||||
@@ -251,7 +252,8 @@
|
||||
<field name="tz" widget="timezone_mismatch" options="{'tz_offset_field': 'tz_offset'}" />
|
||||
<field name="tz_offset" invisible="1"/>
|
||||
</group>
|
||||
<group string="Menus Customization" groups="base.group_no_one">
|
||||
<group string="Menus Customization" groups="base.group_no_one"
|
||||
attrs="{'invisible': [('share', '=', True)]}">
|
||||
<field name="action_id"/>
|
||||
</group>
|
||||
</group>
|
||||
@@ -320,7 +322,8 @@
|
||||
<field name="name" filter_domain="['|', '|', ('name','ilike',self), ('login','ilike',self), ('email','ilike',self)]" string="User"/>
|
||||
<field name="company_ids" string="Company" groups="base.group_multi_company"/>
|
||||
<field name="share"/>
|
||||
<filter name="no_share" string="Internal Users" domain="[('share','=',False)]"/>
|
||||
<filter name="filter_no_share" string="Internal Users" domain="[('share', '=', False)]"/>
|
||||
<filter name="filter_share" string="Portal Users" domain="[('share', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter name="Inactive" string="Inactive Users" domain="[('active','=',False)]"/>
|
||||
</search>
|
||||
@@ -453,6 +456,7 @@
|
||||
<field name="tz" widget="timezone_mismatch" options="{'tz_offset_field': 'tz_offset'}" readonly="0"/>
|
||||
<field name="tz_offset" invisible="1"/>
|
||||
</group>
|
||||
<field name="share" invisible="1"/>
|
||||
</group>
|
||||
<group name="signature">
|
||||
<field name="signature" readonly="0" options="{'style-inline': true}"/>
|
||||
|
||||
Reference in New Issue
Block a user