From 81b71bde02949a7979a23aa1580de1a510039915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20-=20Le=20Filament?= <30716308+remi-filament@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:48:40 +0200 Subject: [PATCH] [ADD] group_user_management: Module creation --- group_user_management/README.rst | 122 +++++ group_user_management/__init__.py | 1 + group_user_management/__manifest__.py | 17 + group_user_management/i18n/fr.po | 54 ++ .../i18n/user_management_role.pot | 54 ++ group_user_management/models/__init__.py | 2 + group_user_management/models/res_groups.py | 70 +++ group_user_management/models/res_partner.py | 24 + group_user_management/readme/CONTEXT.md | 17 + group_user_management/readme/CONTRIBUTORS.md | 1 + group_user_management/readme/DESCRIPTION.md | 8 + group_user_management/readme/USAGE.md | 6 + .../readme/newsfragments/.gitkeep | 0 .../security/ir.model.access.csv | 8 + group_user_management/security/ir_ui_menu.xml | 13 + group_user_management/security/res_groups.xml | 21 + .../static/description/icon.png | Bin 0 -> 18048 bytes .../static/description/index.html | 471 ++++++++++++++++++ .../views/res_users_view.xml | 16 + .../odoo/addons/group_user_management | 1 + setup/group_user_management/setup.py | 6 + 21 files changed, 912 insertions(+) create mode 100644 group_user_management/README.rst create mode 100644 group_user_management/__init__.py create mode 100644 group_user_management/__manifest__.py create mode 100644 group_user_management/i18n/fr.po create mode 100644 group_user_management/i18n/user_management_role.pot create mode 100644 group_user_management/models/__init__.py create mode 100644 group_user_management/models/res_groups.py create mode 100644 group_user_management/models/res_partner.py create mode 100644 group_user_management/readme/CONTEXT.md create mode 100644 group_user_management/readme/CONTRIBUTORS.md create mode 100644 group_user_management/readme/DESCRIPTION.md create mode 100644 group_user_management/readme/USAGE.md create mode 100644 group_user_management/readme/newsfragments/.gitkeep create mode 100644 group_user_management/security/ir.model.access.csv create mode 100644 group_user_management/security/ir_ui_menu.xml create mode 100644 group_user_management/security/res_groups.xml create mode 100644 group_user_management/static/description/icon.png create mode 100644 group_user_management/static/description/index.html create mode 100644 group_user_management/views/res_users_view.xml create mode 120000 setup/group_user_management/odoo/addons/group_user_management create mode 100644 setup/group_user_management/setup.py diff --git a/group_user_management/README.rst b/group_user_management/README.rst new file mode 100644 index 000000000..eea09d306 --- /dev/null +++ b/group_user_management/README.rst @@ -0,0 +1,122 @@ +===================== +User management Group +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e31aad4f0cb22a489d70478ebdebae4e94acfb5af11813f659b869be1a32f7ab + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github + :target: https://github.com/OCA/server-backend/tree/16.0/group_user_management + :alt: OCA/server-backend +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-backend-16-0/server-backend-16-0-group_user_management + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-backend&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a group (a res_groups for granting access rights) "User +Management" which allows to read / create / update / delete users from +your companies : + +- including access rights management +- excluding Administration group access rights management + +The purpose of this new group is to grant user authorization to manage +users but without allowing configuration of other stuff of the instance +(as default Administration > Access Rights can do) + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +[ This file is optional but strongly suggested to allow end-users to +evaluate the module's usefulness in their context. ] The purpose of this +module is to add a new group for user management to grant user +authorization to manage users but without allowing configuration of +other stuff of the instance (as default Administration > Access Rights +can do) + +In other terms, if you want to get a user administrator on your instance +that will : + +- Create new users +- Update user rights +- Update passwords or generate renew password links +- Archive or remove users + +But is not authorized to : + +- Grant himself (or anyone else) Administration / Access Rights or + Administration / Settings role +- Update advanced configuration of the instance +- Update access rights / ir rules +- Create or Update existing groups + +Usage +===== + +To user this module, you need to: + +1. Activate debug mode +2. Go to Settings > Users & Companies > Users +3. Open the form view of the User to whom you want to grant User + Management access +4. Tick User Management (in Extra Rights Section) + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Le Filament + +Contributors +------------ + +- Rémi - Le Filament (https://le-filament.com) + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-backend `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/group_user_management/__init__.py b/group_user_management/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/group_user_management/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/group_user_management/__manifest__.py b/group_user_management/__manifest__.py new file mode 100644 index 000000000..6700c8d7f --- /dev/null +++ b/group_user_management/__manifest__.py @@ -0,0 +1,17 @@ +{ + "name": "User management Group", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "Le Filament, Odoo Community Association (OCA)", + "license": "LGPL-3", + "development_status": "Beta", + "website": "https://github.com/OCA/server-backend", + "depends": ["base", "auth_signup"], + "data": [ + "security/res_groups.xml", + "security/ir_ui_menu.xml", + "security/ir.model.access.csv", + "views/res_users_view.xml", + ], + "installable": True, +} diff --git a/group_user_management/i18n/fr.po b/group_user_management/i18n/fr.po new file mode 100644 index 000000000..52203aec1 --- /dev/null +++ b/group_user_management/i18n/fr.po @@ -0,0 +1,54 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * group_user_management +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-11 08:28+0000\n" +"PO-Revision-Date: 2024-06-11 08:28+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: group_user_management +#: model:ir.model,name:group_user_management.model_res_groups +msgid "Access Groups" +msgstr "Groupes" + +#. module: group_user_management +#: model:ir.model,name:group_user_management.model_res_partner +msgid "Contact" +msgstr "Contact" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_expiration +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_expiration +msgid "Signup Expiration" +msgstr "Expiration de la session de connexion" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_token +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_token +msgid "Signup Token" +msgstr "Jeton de connexion" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_type +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_type +msgid "Signup Token Type" +msgstr "Type de jeton de connexion" + +#. module: group_user_management +#: model:res.groups,comment:group_user_management.group_user_manager +msgid "This group allows to manage users and related access rights" +msgstr "Ce groupe permet de gérer les utilisateurs et leurs droits d'accès" + +#. module: group_user_management +#: model:res.groups,name:group_user_management.group_user_manager +msgid "User Management" +msgstr "Gestion des Utilisateurs" diff --git a/group_user_management/i18n/user_management_role.pot b/group_user_management/i18n/user_management_role.pot new file mode 100644 index 000000000..9f5cf0240 --- /dev/null +++ b/group_user_management/i18n/user_management_role.pot @@ -0,0 +1,54 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * group_user_management +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-11 08:28+0000\n" +"PO-Revision-Date: 2024-06-11 08:28+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: group_user_management +#: model:ir.model,name:group_user_management.model_res_groups +msgid "Access Groups" +msgstr "" + +#. module: group_user_management +#: model:ir.model,name:group_user_management.model_res_partner +msgid "Contact" +msgstr "" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_expiration +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_expiration +msgid "Signup Expiration" +msgstr "" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_token +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_token +msgid "Signup Token" +msgstr "" + +#. module: group_user_management +#: model:ir.model.fields,field_description:group_user_management.field_res_partner__signup_type +#: model:ir.model.fields,field_description:group_user_management.field_res_users__signup_type +msgid "Signup Token Type" +msgstr "" + +#. module: group_user_management +#: model:res.groups,comment:group_user_management.group_user_manager +msgid "This group allows to manage users and related access rights" +msgstr "" + +#. module: group_user_management +#: model:res.groups,name:group_user_management.group_user_manager +msgid "User Management" +msgstr "" diff --git a/group_user_management/models/__init__.py b/group_user_management/models/__init__.py new file mode 100644 index 000000000..d1fe480ca --- /dev/null +++ b/group_user_management/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_groups +from . import res_partner diff --git a/group_user_management/models/res_groups.py b/group_user_management/models/res_groups.py new file mode 100644 index 000000000..0da434484 --- /dev/null +++ b/group_user_management/models/res_groups.py @@ -0,0 +1,70 @@ +# Copyright (c) 2024- Le Filament (https://le-filament.com) +# Copyright (c) 2004- Odoo S.A. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from lxml import etree +from lxml.builder import E + +from odoo import api, models + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + + +class GroupsView(models.Model): + _inherit = "res.groups" + + # The following lines are copied / modified from Odoo base module, + # res_users.py file, same function _update_user_groups_view(self) + @api.model + def _update_user_groups_view(self): + """Modify the view with xmlid ``base.user_groups_view``, which inherits + the user form view, and introduces the reified group fields. + """ + super()._update_user_groups_view() + + # remove the language to avoid translations, + # it will be handled at the view level + self = self.with_context(lang=None) + + # We have to try-catch this, because at first init the view does not + # exist but we are already creating some basic groups. + view = self.env.ref( + "group_user_management.user_groups_view_admin_limited", + raise_if_not_found=False, + ) + if not (view and view._name == "ir.ui.view"): + return + + if self._context.get("install_filename") or self._context.get( + MODULE_UNINSTALL_FLAG + ): + # use a dummy view during install/upgrade/uninstall + xml = E.field(name="parent_id", position="after") + + else: + admin_categories = [ + category + for category in self.get_groups_by_application() + if category[0].xml_id + == "base.module_category_administration_administration" + ] + for _app, _kind, gs, _category_name in admin_categories: + field_name = str( + "sel_groups_" + "_".join(str(it) for it in sorted(gs.ids)) + ) + xpath_expr = "//group[field[@name='" + field_name + "']]" + xml = E.xpath(expr=xpath_expr, position="attributes") + group_attribute = E.attribute(name="groups") + group_attribute.text = "base.group_erp_manager" + xml.append(group_attribute) + xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS")) + + # serialize and update the view + xml_content = etree.tostring(xml, pretty_print=True, encoding="unicode") + if xml_content != view.arch: # avoid useless xml validation if no change + new_context = dict(view._context) + new_context.pop( + "install_filename", None + ) # don't set arch_fs for this computed view + new_context["lang"] = None + view.with_context(**new_context).write({"arch": xml_content}) diff --git a/group_user_management/models/res_partner.py b/group_user_management/models/res_partner.py new file mode 100644 index 000000000..a98906fa2 --- /dev/null +++ b/group_user_management/models/res_partner.py @@ -0,0 +1,24 @@ +# Copyright (c) 2024- Le Filament (https://le-filament.com) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + signup_token = fields.Char( + copy=False, + groups="base.group_erp_manager, group_user_management.group_user_manager", + compute="_compute_token", + inverse="_inverse_token", + ) + signup_type = fields.Char( + string="Signup Token Type", + copy=False, + groups="base.group_erp_manager, group_user_management.group_user_manager", + ) + signup_expiration = fields.Datetime( + copy=False, + groups="base.group_erp_manager, group_user_management.group_user_manager", + ) diff --git a/group_user_management/readme/CONTEXT.md b/group_user_management/readme/CONTEXT.md new file mode 100644 index 000000000..8938bf838 --- /dev/null +++ b/group_user_management/readme/CONTEXT.md @@ -0,0 +1,17 @@ +[ This file is optional but strongly suggested to allow end-users to evaluate the +module's usefulness in their context. ] +The purpose of this module is to add a new group for user management to grant user +authorization to manage users but without allowing configuration of other stuff of the instance +(as default Administration > Access Rights can do) + +In other terms, if you want to get a user administrator on your instance that will : + - Create new users + - Update user rights + - Update passwords or generate renew password links + - Archive or remove users + +But is not authorized to : + - Grant himself (or anyone else) Administration / Access Rights or Administration / Settings role + - Update advanced configuration of the instance + - Update access rights / ir rules + - Create or Update existing groups diff --git a/group_user_management/readme/CONTRIBUTORS.md b/group_user_management/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..40513934e --- /dev/null +++ b/group_user_management/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Rémi - Le Filament (https://le-filament.com) diff --git a/group_user_management/readme/DESCRIPTION.md b/group_user_management/readme/DESCRIPTION.md new file mode 100644 index 000000000..7d2227a23 --- /dev/null +++ b/group_user_management/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This module adds a group (a res_groups for granting access rights) "User Management" +which allows to read / create / update / delete users from your companies : + * including access rights management + * excluding Administration group access rights management + +The purpose of this new group is to grant user authorization to manage users but without +allowing configuration of other stuff of the instance +(as default Administration > Access Rights can do) diff --git a/group_user_management/readme/USAGE.md b/group_user_management/readme/USAGE.md new file mode 100644 index 000000000..41a603e85 --- /dev/null +++ b/group_user_management/readme/USAGE.md @@ -0,0 +1,6 @@ +To user this module, you need to: + +1. Activate debug mode +1. Go to Settings > Users & Companies > Users +1. Open the form view of the User to whom you want to grant User Management access +1. Tick User Management (in Extra Rights Section) diff --git a/group_user_management/readme/newsfragments/.gitkeep b/group_user_management/readme/newsfragments/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/group_user_management/security/ir.model.access.csv b/group_user_management/security/ir.model.access.csv new file mode 100644 index 000000000..082a9ece8 --- /dev/null +++ b/group_user_management/security/ir.model.access.csv @@ -0,0 +1,8 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_ir_model_access_group_user_manager","ir_model_access_group_user_manager","base.model_ir_model_access","group_user_manager",1,0,0,0 +"access_ir_rule_group_user_manager","ir_rule group_user_manager","base.model_ir_rule","group_user_manager",1,0,0,0 +"access_res_company_group_user_manager","res_company group_user_manager","base.model_res_company","group_user_manager",1,1,0,0 +"access_res_users_group_user_manager","res_users group_user_manager","base.model_res_users","group_user_manager",1,1,1,1 +"access_res_users_deletion_group_user_manager","res_users_deletion group_user_manager","base.model_res_users_deletion","group_user_manager",1,1,1,1 +"access_change_password_wizard","access.change.password.wizard","base.model_change_password_wizard","group_user_manager",1,1,1,0 +"access_change_password_user","access.change.password.user","base.model_change_password_user","group_user_manager",1,1,1,0 diff --git a/group_user_management/security/ir_ui_menu.xml b/group_user_management/security/ir_ui_menu.xml new file mode 100644 index 000000000..cd225993f --- /dev/null +++ b/group_user_management/security/ir_ui_menu.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/group_user_management/security/res_groups.xml b/group_user_management/security/res_groups.xml new file mode 100644 index 000000000..1acf3f358 --- /dev/null +++ b/group_user_management/security/res_groups.xml @@ -0,0 +1,21 @@ + + + + + User Management + + + This group allows to manage users and related access rights + + + + + diff --git a/group_user_management/static/description/icon.png b/group_user_management/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8a6d74ee31c70efd2540873b14293f2f95acf9 GIT binary patch literal 18048 zcmXVXWk6Kl7ww$@h90`RySs)GkQAi5;YX{K)X*g*Qqm|AA|NHrP=d68(%s!%&;NVx z!#(%oI(MJ7_gQyGtd5onE*2#g006jZs!F>5-pKzwF!X1}oKk2g9QATU5UMKJ@Gn@+ zCnN-9*cqEo-5mxW4e#$Cs5Jnlf@(w8VvB8U6AMGFPpzUJi>k-^GsW@e_F~IlPdC!5 zfa>^+|G$haZGcKK(Av=j_hYLrJ58cLNy9W?tNQ4auZeK(<>KlgmSXZC(q$>G>R@OF zDk^toz6uzl+qm+CK86tf^&c%pBJyI+D5-8vlZ_56M0b&_6^|!I!1PtB{26wtDA#yx}1fE&gZ~ycOu@ zGH6;|_u4t-+Mhj4-V+UNbLGe)EaZ;k6~tDh`HBQ+&_A@6Y>9|y!=d0;3&Lj-CNP83 ze*LV*|J{c%M`WLS`jczYIKe~fCztd^z9imgGSN2RwaFGTODvw=GYmu@f6zp^wp@R(Q@z%2; zy0^Zdv(mhxMEQ6EI>iCJlp4gC+VsSgU7{N+eU@g-|BRWpLHlLp&MUnwrrSS9Gcd#3 zn+d~*!}Hd?q`SaXbfLg@lhIa;OGbB+`HBPWE|19>fLvbe|+ia9Hg>D zmnnI@8vopCY=-SAVSZP`)nzw6Y?%uXA2c6QTkC7-Si4Zo{zG)KaBo6TigfvD4r#gZ zU89suBW%!qDbmT0Qz6+BW-FjFt6gE^JhLRv2Kr}#^5 zk8_{J$DHFESG-qE)Z0<~yFUz=f6AM=lR5E{LBOhl5ZE1dw0Oa6o^y17;Uac!td>?F z8@$niAMzd+26U}D=4x{*+shn2Bt1I*drcK1d0PDIK-n>2vT;jr_lL%i{x_c3 z*ibuJ+3H)+w(1*V&@wbM?s*}ep_Rz;mr?XiL*TktIglbvh9XTit!=Y*kx`al>*Mz+ zL{>{h#C06KtT`qQE5To@h@?KkWVPazN;Uk;n=`qG<>$w@t#B9`Ih=VFcyC9Pe-oW! zp;(dP3jOje?WEBy~0fT{S6Cd{UsP|`qYl_RI1Yt9bJJjMaK>&+GJBBMf zr)_SAUsU*S`i+vOaszb`=}FTBzI{ifA|)nW{7;5l)$WHr=?O=o$~=ErKZpe^T5X%#bqol=`AhMDPW+;E>S!ky#Vt-$f|2$_?8kuT^CW|>Mw1j=RFXz+$ou89GZZIHzx_lWJ z!BU`GW~8T3msfFt68hx^Z_WjYZtRmKDcl@|m(L0wuj$7i2?6o{I=REb5Y@cj@fMwe zKlP(QVcYc}fon1i#NywuzoCgH@_a9V#N)?j9QzE|W zWVUt*qhVOC%!hFDZub}Wk63MeqAOHW7!eMZ7ZMCEkV50hW`ZX$uXt?n?AJ<^$hOEu z4BqoQv4ab!C6MLxX`gkAsQwZHJlN#DTTrmDWP!jR7+qbE4l$(Ij6R`ZSbQ440RijS zY|`beb^$FL?R&7izc*(-5jfVH#&);=K5_2$~nk><;K(fHNs$Dnj{LF`b+}$wmYF|H( zFXzrh&x`*>uPqrOkZOu*0MPEU2Jeo9JN4_Gr3$9w!) z_akqB4s3ID+EzHAhbB%zVQo$dGs7#o0;3UOn?aRk$Hy8O&~srCKPug~YmHTwt&yxAq>sAm z;1)ap&vx&Qb4WqgGHPJy@E9_8PBcIlCa=IrXXek}s6$a3%ljJM$j#qqhrv&6xC&*B zLyn{q3s)p5Vt`lL_DpVF{}Sk7$deETPxsTm#8bdSDIt}eK3%2hpA0pRPB0c0cF&JA zS&fJ1nIVZ|^h?3AaSkglu3vVu2{rOo?*YrI%puq#M`_C{*?t6 zbXsUA_D1tp3}AkBaTV@f$OH$H1P$%L%X)>QMuw!>5g&}Wwr9;4gK^^|D$K=&0>%&g?2N~5jsitu?kbiNgS0(_r z2LuC*ryJ2VI~%Bn9+c}wzt9A#ke9odRnL*rfl&$6h)n@H($Lz(nS5_qh}L~$UTD-k zX$(n?Rm`r;z1@9l3UDkMI}=KGN|9(0>n7&A+*RiuL5>RJ;<} z#zxb5$<>}j9!`a!*_}l!dJj*Bh!5JE62ls*B&R#ZFxVrw7R{0AU#OKB(XQ7Gf5~Py zj1C90bLm;@O7M6%x&37V8Q;?-O5z?1Tdo>0T%G8AuLj6-1z$2#K z@)AD{o(gVfk9JVqR{$V+t4iNAr3j3uq;AtU#z`FJ0`yu4fWBxID9`Q3O#U<=9)pEf zuHv)eI+eWrzWZ>iBHtfuk4J4nFjTCm%KAolP{T+@WX^Hor^VK@T0L+G(3@ar!xhR+ zJ6RnTo=~KcoE^&g3Mp;(j(Yn#8{LHOnS^W=LQ;=TN!4E)7&}8drk6(RP-uV|h?*nH|&)-D*xXqN$ zt8w(iwvc`)$6aZ3tL`K0AW^p-e0^Ffq#ViyRM3dyI&GM$Z?ZatJ7Gb_zth(r_^o#o zr#qIe1{}A|7D=wENC2q$kyWaIU~v1tsDOf}BM3m^LLQy^ngN2KkVbw<#<88Q^L_<1 z_c;n`iQziRRdjdGhY{_pfEKdqyL%rX&=IRvib$YTNm8#P#y$o?z=pwIaGwueLbbra z^pNa=;|_eFgXL*%#;gn(ZH*^e#tA|$12t$!gMu{SF#_O*?Wq=2U>-po3-F`QnYe(E zF+2)Cp+Mut5@tqsr{94BRm^IhF9?8U{>TU5D8sa4ac=9*NygbJjwei@U)CH3*pmEp zpq_Mm>IQkz#uYM5A1;u9>2t4^?0EgkxI&clUN-H*?;^BdC2M~ZNFrE8A)!K4pM;saXi7QlTUU9nJ zD=q=EZgD+V7$(-ejP!NP+|+Nyqlah{>Bk@Ul1HW$^{zn?EcBc~UTj0*Uw^}isS92i zE(4rn;D8rUGn}dJ7so+TsnOGiZY(}ll~$8Ee|V$0rmN@M)3J?Gf6!8?rlH>Vb!fAt z+^u>aOtypgZzVC!rX{QBI;Of~XSp6nn@6b<#pUl!H?b}+J<;?CB$3m)yo4G<5j*VQ zqs|sldOY|d2~Yr_&5)#_h*qRL@#gKejtKxbh3s~*u^Eo88gpjxSLDcs_0|A=8!O)X zcLlkTSavv7gTV`80PDS3m=48kNooJh;5E)jmW%+ewS2 z*2^TDHiPT7K5+P)Ki}Et7_!gHFIxXiwpp&#p%fiqwWEHBaBZR*N*#v z-c>^ui4j`vY}Papm!+iic71=q7;AuP3KY-d>@Cv0mAARf1K7W!!W{TY05E~Tej*}V z*O1us@5toe@lzUwV*E-6Y2HIwmh1_&j|vU&h*nHV6gH;PC@&bGplIuq4*gK<%x?yo z>a-K@C56Zcy#-lrdV^8up}lDH&+(IuiV&>88~UV$2nEtA?2uV8;IczKjK8bKK^$BV zH+LfO=|i<~{Itzw27IZR6uJU>Tax?fmbMigzw2p*W>(Y*uNe+Rns*ZQt37|8cW(bO zL&x!vvOIJORW;or^ZM2}q;pl77z23w3R+)E)<`1>+5wz}nWTQl#jJU+Xv;!?D^6lFor5yk#h8p*E}P3LP!L@+d_c;Tdl8QR2CSKXz-mjQRq4JM1(kvR1BRimzz54}yS74G8wOpr2vIR(y2)1+v%h>K`qRGEniCYA`o{ z!z?s_;PfDKg@sz4FM+_WH)7mOSxcui;^Wg<>CN%XC=c)|X{Y7661+OXgO?iK&a7cm zfc~tr`xuTFF-R-CW%Y3tKR$v~iBAA+gYm@JkDP!vDix0e#cd&%bxL7?qGR@C;(jOv z{$p^TBYVdPd#k^&ie*se`3o;8FKeK5J6SPz>v_&qonj;~7JJVhI(jzU_k@GT|1f2! z#`|OH>>z(Wm9`)so@hA^i6AG(nOa3rqxIEjpARkUC71k%uaHq4)YRyKl!%OBVSC~5 ziM+oTlx{Kej~;K~DY2Ka7&9HaKY(S}R|c%yr$T)cFdv@zo0$xfe?F2G;+iavyzrVG^kdzk{BRTg0fr*d*M#yt`@Qw_ zW*ARYo*PIdr*|nx4_yXdiw2U7>`p{AIBn0fvKq;HLAENb!N{yl8${pU?%*9dYFdR^ zr#0t6ofOESLc~rhFpGD09nea|E)hXPqR4bkJ7B#tOOZ!x-?cBnCt4uXCo%JCzQ?ir z+L@ulRSJTogOf1q*wOLN&b&Hhgs@{7wG}R=FV+b8sn@(2K1Tv%i+t;*T_9|lgZoThY>enyp8|Q24??EE0K3=%IQPP}*efrU zYWmh`8g7J6*J*AdHyiNLipVVPVLWS`%|VVvH|1wAaRn`|^glzb#nrz5KCKs3c<1_j zdEcjLVcEwYp}5*IPm2d#h4LnV>$1J^l2Ti}!GR-Q%P4 z99?aIeg|C7kH?C7@J!v{-_5O``-M65&6R)*tDxQx2_Vc+Jucn$j4tfo)`_PD8FI1Y zXHd+_k=98oe}*09>W3B~R^o_dBVta5b|J+kB0${E6Zf9d0DPe7fhKSVO6#zRSVlpB z*pL^Dl>Qn;i-ZEWhaU{=sGXUcvtil@xM5NsdU(0B>+m0TV*2QwQHR>(?*EA2HHpur zYPFtk&ie?fiAOp$-pm4IXn#CKsqr%kjJMB=$qL@Q=q+U8|2CdQhu}S=DaZs9Id;4Q z{DP0^tY_G;tsbL~5GpECc7nzY=X)pD1r@D?2Kp6IcILE20Su_1-pnV3BqjLev-Uh8 zi@lSN%v)g8;YDQ61d1x*o>!?v^_ySOq&tuAb0SBE9(J* zj#P1PrnjQJoelf8U&c*R9uYBWFZhlK4oHjdiGaYXFxs!p3;QBL7C*#+2XoeJYs2R~ z?1Opg+IPPXk~~z-pk(xLI2pXCfD)qy>=yRbN{)Oqsz0k_DXZK(2KrS{W(bT*PID)J z;KPr=mMGPIt&^2^i5)F+9@PEJ+GTTdMf9?u4bmxv1;uoytL6KC)-B?XY}? z8yM4E_&i(6;7b3Vu(}I>9LG1)9yv0XsTv zBf!hwEzMr)^*c=MHEVH`8n=TuJ_LM*Hkp4uaCz4|vH#@*XV2v?u;=dpjmwryHPtLZ zyS%q48{I@{J5cc!-B@Vo2AQL|+=b!XZw+f{31#^-eXe6QwD|PJPb@ zooQ`zv$Xfy?!UA7i2rrDg#9M<-5))-AOAtc_eK?7c;a#cv>i8HTtYkNkjdDWk`Nx6 zh2<;%!qysUM+Is^qaX`>Xpk%n9Ri0%rySfN^ohT)Rg2>aLhRc?sm~NkaORZ)D+mWa z#TPthg~M+yww6w}OAlPjGYz3(R`~D_D^d)TnC%cYRHth*7EOU;HH++$qd)G4j!ej~ zi&Y1Bd89;sxEK}R!}d=+I7eeGmcQjU2lW?yU!sVGyi)s{EMG4&w)h(}GR#7Q!-M66 zh3_j;1I>47vMP34R~))-syOJW7czwETyGpsxUf!k&M9KRAuQ)yX`GrPV2KsE)Am zLe4q?mu+)NL4(MtBMb=UQP-v5>Kfg3a?v0CaGl4ms_S^F>p1Gvp1jNWP^+pFTdTi) z*q(&_ED22TWGBC?FT61ZcJV@%4jtzIenwr6sRM!eR;E?#OTL)=DBU}IE6{Pj8(IHb z#ta4K7E`Gf>+wR2Zh4G@$l5GwDN5CHDr2d{`qd~uMm)OFk7(FiBi7GpzXuH$hsF1R zcF|?4$O41uIOAy3gLCfzY15it=a@--*uPcD@T`t!&)og5+Yel8gamamqwGlmcE5=8 zZ~!aJ`j|@``P*pJlW<$1A=k&A7``06gmrQzwW~3(^Xjldm*6)PErUNO{nnkz_5sb9a zr!}uiODn>3lqGht-S+_W-2B$UE$f8=ZR?YH|9$!>t5TdM$cX6h+1{B)$n_?TQ^Od> z+gD*&dz9ez-pO#2P6AH`>TX^2)#}?8VexCW;NxhS($Y=x=%Ur1S!Oi6(F%2`v0!in z)m=)Tu}5Rraup>wYS-F*;cQfnEbneYz1piA{iW-fsarwC!0|1e$?qb5p<^iJTW!=` zzu7q6YW$40p}<+>_>Z&kUOC@B{KSN3j6#ylw(l;E1l*x2^<9{Jwk11xIF3HdWKyik zD{B7CUW@;oe1Q5azb~s*@`HkFlS3;Tc#eO{Jw@7d4JO)lR=r)hH>G=<9yp# zW@bTu^6!0%{5Oq6h2`axq&#K-U;8;_U0nk|m!pgQpX>8-5uvpZ`9F!UyzepELTz*9 zFXFF94hr&{z7|`8j`t5T@@W#3@;#2?P|hXP_}x)|`F$O|E(6HL=nXTujhm)s+;L*_ z^H^eO2!Zs_A-j%cFzYLjOg3^uJ-w@@IA75IMM@ezU+cq3)rI0&F1@EIl#G$VRlkNj za9^mulvTU5S$=q3_?yiGUyX>|EGkf;)x{YEW*+2*p38xb>KkRN~jfQQ@) z@$PL(IXrZ1VH6PGn`2Y-R))oD*DQ5A%7K6xxwCL}gGcY!yL%Qd%a`Fiz%~x>A!0@t z+saO_|90(o0Gm%h?-v~ox03oYGI%!(2(8A8nU=?ZL)nfQwoXEV@@rc3e{jUIz9t&6 zb6A+dlcPL;L#T`ZC(7 z5ZfwTvO>u|a$oA5peVre`8648>(H|!P44M3ddufcom{r6!}5@jmO|E{W38nEHi7{t z?RnJ4xZC=J4(Y#CGr6zh2`9*l2dR_wm!O6mtv0EI3XA=*p$eT%WcR1vV;`n*3(2wK zvz5Y@K+tA%bi=oobYk;C;hVe_T}n(gXYtH}z_EGEb4wh6-$@=_?nF)T zhD43ysQ8)b@)>NHP6FTv(RiFN>Cm;i5(S9kue-{IUmtzKb&3rI2WY-Url@z7lekzd z=14ekKo{G#FZ8R1q*)+_7}gn z_6Wt$G|hsI##DK&no=jf1}cx>fW|%>M1nIl_u#55566~l!*n_wZT5hTEI{z6R@28GzSI<7bhcWkymxd zu(t?i(&&)(Q_h!DlhR`U?U%;mNgsUdN~{n5s!VRth0oR#=Ia`CTy7n{PWN73(SEEz zl3eOP-|3JcRdLS_NalM^%vCO>z&(qaMx@BOYt}Rv*b#r3pnzwrH&G;8Z~4esiIfxRL+tT6>ODZ3zVtQwP<}_U7pST)_inE*L4+Zn%h2?2isVo6k2h=ys@&fd@22i>fwRX~TO{SK z(v9uknFLD9x%qw0`E_Ep!GP%oAma`ELJuXu>(M|I!UWY(tAHe@VXY%ygmw6U{Kf5) z$jaxh+SK$wwbyBY%y@O@#{0^x59uR~x|Rn|oy-5(mKQzu+!tU|3{nOXZ)c}&ZWLm$ zjNxY9X#kK($bR2g6ym>0B0>SY5+yTgd4+a=>wBfpo*b=%qH`WCum9+}JJV|2TJIUu z!OXiY25`T%-;FHIk)f!l6nl7XG5#|0TY!24%yUK(QAWsaEcn|R{LdJY;-SZAaNwOQ zAy$*H>&!S-`CPzo-cs}CBM1s`rLr9|G>Mp>Hj+3mEvN*C{B6b0(>E8)c{Oz6d5g&= zfY0h8!_*LOioiPZ#T4_5kr=tf7F|hcomL6X*XmhLHw}Rd5A*0)%6(p2R>aQ_Tp6Xi zu9Lf)^Z<;mZ&SmI?Oam%%sA5SL}DF8^zOOo5HB$6Dv)^&1qT>#hXS4CxW9m2`Y9N| z>@Sp<9$!Rz)cNyho7D2=Hv)qAAQk-P%T`NC&ZNl3&%M?U@a00OU<`W_5+6EOfFFMR z)m2XKhI|Z^qH)H?LWZfeM}q@34(^?j1mB;q<%M3!W+pgn6ng$Oc6{`nOxeo4pu+?> z)h% zF=(U{^s3-f``Kp4QiS8cK%BDuTqD!}M5Np3!4)Uk>}Fc7Mm z<@6ikbORPA8*^nT?Z4z{;tNFsPncYJZG&*2XbrQ^910SF7;j&B+Q|SPxyOM$U)mdg zrC|_6L!X{4&Kh;w# z=H2kVTXLa0M!R1UKR(}cxO9}eB>`LF76C0E$DB!;*S5Iy@96)k~N^&AuMU{z2msTLtckPd^1Z z#iKzL;uQ7T+PzpWCW5Di9~Ipnc|;pNK?+uK`}%rqeDsKEQ{zHsv71H(RFi^vY z-^6{ShM(RjCw~vo1=KJQyhz5C)biM^eJLI2qqO}(%P0@l_DPVw*yZoFTIrwx1EcoA zyodMOVn$l@pQ6b<#ly)}`st@@DOA-CUq91mcX}IgWsL|^$d>h!+)u~a^Wx_eU%3JL zIfuc)Ja`fOXoadzt8vwB^maz4DWQM_aua(VzIb;og%MSkU*&qeoTXkLc=#fzB3#J~ zX(S73?zt>6_Hce`pfch#5s&J|cKX*0@Q|ho{hXRSNG2)tMfy$8?+kn2)<4Des7tiE zkn5qN6casSYyk11T{K{FnxO1`HSoMHTx~uo9lX$GxW>d zM!919>z|1FhxiW-_<0{bo4qRDKTS-N4sM?=_SEWg>}X4x*O~Bd}O6ka2y6QWlfyi|0XLopUg0 znw$$k3_7#+;Nn0|x#Fmd9sS&cvU=ZL5}J3z#z?ezV;Vi*lN_Z8DBWlED-4JCT# zDKVo9i)f;EYbja}(Y{OH-YvNGr0C`6UM(W(0#Kl8|I4#G_L*x1WdpkrG-7n<2&q5R zAeIlMdp|8FA4r+301|a~SEoJCrGh{fHV-)5j%2AL0vkXQ2#OtWjV@G@t$ki`>R%~m zMF-tGIDe3TKQvyi*B)^DQdb!CgoQ||BoVR<|}noL@JAtXySv*;W0{V64RU}lYF z!j<>=d{gIUklFg;wtmwsNrv~g61lE<+tWnVH=5+$%3Lr~f={!hc%Wp7^gO_S*Fyh0 znby7^P}cvbNx<2=AJUH1Xk`!ie75_P8~o=$Zid2bF>n0(=fiuObpvPGC-N_F-TQEw z6K-O2b_ee|1nL501!8z6iT2BhZ2o#Ve2gNY`sf>0AQvinoK@7$uW4&R`aHim=M!5n z&^Pdp-^C7fMGFr**qMK8-3(0JP8g*g_ZeDA#Pw@aX+PGQ3n{@v*dIjm z(f)8yT*7*kv zuI|WnFr;bR`<(Zuz>ok2XabUxTi47vpZj?&c~mYH`DafQwdc`Z95P<_f*{}L>uwp} z@Gfm~@DVW}-1hX+7^<*Pmt}kJE}}#ASwq#Ii?%=*58?Dv_%#+9WYM${>pm^q`iFJ5 z6CG+gDCy=gWB1{%qUN+%s?zOlypS99fBv)NvFqW z@oKk5r{rcDpM9SIsg0vm4JFD6!4DRkomW4~`m1}mc%&Lunt{CQ)A)lYJ=5_4jS{j7 zE0-=)6BFyj8eAOt^elkiSt*1*oJtV&+tk)I2CWyRf{IhhiZxwbdkEB?A>9L|Am z`j()E>_%qloLhCu5K%PKSxGB(L!5zo+Uu=%VnfALhI7)8y(EiAaS1p2*_H`ltoi;S zP@bROcs-sHu5nAS0V65A{YDB1mdtqxLrlj;0bq7M~0|Z>>xQf1$sj&wDQaxdq#f z2|s)QTsqWLfz+{bKXekr9X9tO6|^*X5o^X9!)irM8?Y~6^uwO$E~n=UWd3SPV#now zyFddH)a6R?(1P5)^k1b8D@ZTmey@b}*eq}_V5dHmV*43c0Mr!-pw+MVgFsx@rDA{Yd?Bjv_%`e(?F_(w`c*vJ8!$N+1pVy$X$9-ap_uh*+m>K+A zRg9VEUpJ0wES-}M3`$E^{8G=jLWYN)1o8Mg<{^<5Zai&Sd~x86%`*HWUrAz_qs1|i z(A0jq!Q=Ux7h^X&cpnH2*Pq$MexIi$Wg~ELi7fMd!jfF0F#{v-4Uo08c5v z7Z3Q${g!Dxz5RvNSAQsyo7qP7-U+%21E+9BV-166ofUxsgnI9Dn!yHh_1);Wj@wde z+spYp<`js|Uu|=3*@gqFNEHAe~q4e>Z9J75x6T8K;LG|F6+Uq88l_N z8}Q?#e)V5Uf;NcGCNhZ`08(WJfu+_GZ#8b{3Ti- z4%7k)%8L77{1W$1Xv-^TjZXOGuWwT?cdYG7MxzxUc_6=|zop?Xy=@aM*m)f*XWSRkT8GSk1yL8;r#LJ2`xOuozP7t|{w*#*MK#Ov!*oU{ zLuh1h>jL}uMcU@-2>y?36%g0Sv$2WXQgRkJK%lB*3y)VhIOlxnBzm2Eai{Z`iiaBi z6&)4!!=Wx0F&l3d_Fi%MXkAn9t*ODgl7)|#VWV4%hvqe=T(2G4?h=$(LjpN-CG_?G zFo@gA6l#PEjFY7ucn3=zch3^3Mlfevu0~(Si9E{sx|@1Md=T4N%R1_c4{0Xz2J;cu z+~2=L=*y^^I%sz^X1~ZuGjBoF?x?iwVp}%Ggewgk!AO%1H5#NU!lkA}2fh!9Q%}6BK@Gphqdl9|^PEa7?gEo9h!;G%!7Ul-o!_Zn z$-924fAbUGkZ0X{7k)!Q4S;Mj1zYbYiTFb<_t+l?=u@{FFxRPCrQTJ3i_#mFNs1kM zvtU>Hwd&|XKU=gSyyoW=hgdkn4d>xVsLxt86W`2w4d!66^|mdAyE7LlZ{%dh$aNRJB}r61`xXCDNpxpumwh1m6?D^3IsKL3(-%>4BV zy+wvny@=hdxamu;32p!#$kT1kZmpS?q|BL}Gp}f}G4NvJTAc>BnWY$8!zSvds z2RcUJ-m7U!Abr>$J4)TuXi;vn$wsZ(Z#^mZU}zj;`jRhwVp&KDiI!u=iV+fc9BkC` zAFZD!p`9&_XiAv(8bdVEX55|!XqYZyeYx9ey+5U`-T5L;yO33ojF~7jLvh38LPlmg z(}j1Q0pK=mn&DEr?R6@9s!kT`bNa~KG$!jiVU5ol$>AKwZ9LNv7jRF0@{KD?QDe^j zD#uMIA2|}MIl8S)qFginbAZ(E)}8T5s*)#OZF3!FK(D~KSS+ag7*XN(YTqMwE^zjJK1+wO7a7rXWBkKWo;3_zH9}!KN$hYNuxxm|%)=rubYmu8Lqe&Xg`A3-WbQ#Fl=$l`y?1y(?3n9A9UmwA%IxKMS}>v-g~wF+{rb z6!O2>9<#8mas8!GD)zS)^B35;R&5pP{L->-D)8Y{(-QWtvq~(`_bmNzYk^m@tQ3Y2 z-&X88qZkdBK!Okr4w7p?ja_JA=H*NHb5fxqx_IZ}R7iYnrjHH>m0|k7@ty?x=@%*= ziyrloJY-pqxej z%B8WKE_8EwW2KYs;bxoiFO8;&!7l z5$TC^-XBn7UdgU*5ffEy*t>WVnE$!B1SIs0_nuidHl_GeHsL8SIJ}UgKh6(ldnz@g z%yk1Vr)pENiPXPokE}KOT8@LTH%@jw6^7;X=g0_Mdb|dgF(1mW4>M(P8@+vkpz1;} zDwrlU2`n9n#VA67upw_e=JAP{i=!nB^RHV>C;Gn*9CPAjJDNGrKjF#IjA3nG1YmCt zg$W#zR?-;_WHTGTp2V_E?LW5-xbrI&yVG3_e#351{T?l@nr5WTofh*$;%|30zdbsS z&aRm!hX+r6yhv9=p|#4WU*AkgG!>5xP>tblPX5ACF*Fr zGQ&B1Ll_hO<;gK*bcvvsKT_Gs%1{9lQz?plImf@B`;yKg-)zFG}Hsy^wH?EY!*J#*|<^%nY+Aj8{J2)FBR zsQPwui~n3;t4`%{^iC;6UOva^S!mzz!gu$V*Vh$-6f3_*9B2LMgt8qvbYQ*) z)(k#&!5_8S3dlYGGo{IL=NBDW@64vJARziH(q}#P$mX8bJ8}~Pj12`*uLgvw2&vbs z^6Q0Sv}A4?pc^Yi&GLjRF@v-h&ZxEzUBjTyBN-I;=w}ay{o$`G1X1@Jfd{!6lKP`!;r%inxL%*o3{{exinF>%8M^Wl;XT{= z%$HUhfHhCgg+!O%QB)WE)|uhUc#_+p9t;cLAm#t|5@N=ST~zJhmWA&S_&PSkTF(?~ zfQ=fW7=^QQvG96pC~Bep83wRVSDzUETIunl!qN5qWrJu#5t^qESPVk~lg$DJDf5f) zC{`>^!OJXCh>F`k?VJ&N09}4OAGTPkUtYUCSQ=)wl;HAGkl^H>KNq(nEb8e9a~}y`506iy^WoOPhry|*l2^IL z$Nv(48m;J@DMl|E5)nW_w`k1}&(IuQ1vL&v@EVTzL3GFA#oU?PTe6=wa{;kttO`z% zFW!%q<-N(ubu9QCsXir!c6QP=I1~7ose>8}JhaP*f4F1bYNZXX4!3)r?#*;D^L?~t z4Wk?;_T4=)Gn+Jk>}X~_cF{Srv&g3>z_ht`nZg@7>Crp!cX}GO`31i~aQ)T&bYi_o z{$Tgus5G`ZSx|rbb8LM@-oGRL|75;Vs0OEVJwD3Ppx1`Q!>?IbH)mG%sN2`7wZoQ6 zl8g)z8F+cqnOY@jC|zGB`-wM=1Wn%RHU8)F z5qvI}dJF*Zw6)$dft?TDo5|BT>ijyA8DZ2O)oHyv=Elbi7}FnNF@0-e{9E?qFV>z` zac7^!diO1_v>Zja4<1g9>Gt=wG-{)H^O=y>fPi&ktXj1C7E4R)cB`^t!KY7GR=yrC z_%y)&!S-8L0)dwv8wF})bB-@+PFMDe9}xxjQ7?IeYX4&`XA!!p#SZeOX*|**0G~{A7fGPgZNMg8-%kTQ7?cmJUf{lRUVWWT~kwI zv7}}&Tytd+<`r(1uhPNghJT*N3GMpvC<|H4IT3>kpx!h)h0YJ5K!`ZYzaZd}gZhbx z?;evnWJH8z>4)EAA3yCk;^LC`|0Q6UfIkyI)$GR5&Eplx90l=DZH(Wm6iS?zo9dYd z$3Mj91X+o^J4S3;viE}&fPzkz_+|C=^EujMLICNOl-(eQ+NBb;aIpmI+7Q)}|NY@; zH5SZP&AyNq2~;~AX7fwtY*Ze#Q=z2e-%sDT9$@lcte$wzydljCTE{f1e;aFGrmBiA zhMi5+!%BdNwvU?vlTT>|U_R4em*KMg)z!qmxPSq$D{$mLRy&-2sG0#XMM9K~((303 z5)6q>+AK1&7<|Y~_(<76 zCND-_qW7CJd2X{Nw|Q?)kbfQNacP1L4{vn;ig)zUWfSL?|W|xv<0X91^;aN_%^$&^~6kK zgtcaY!o%;1i1*j?qu@G?(`K_zURIA0F|zc?{iO|q0QjUkWhzf_K2=ySe(JXr)i|E1 z=DKh9%Nc4Ro1~(Rcvi8r|qyb!vX2Q{w zD~dpJYRq#--yBf5^6otbi8cCW#Wf`0-*{g{dIVGTSIS*sDp_>j1`5)uBviz&r9%>Y z`hh4BF6$3#G%8QU(4R&@7uT-dI?pgeBcN@a-z&A;DKPRsxX($%LK+r{7OOVcMlJHcR{CHzK(a>~QNHu!-GWy@*N{Qh@x8t8sKNb~kR z#B>#oW8B~vbRx}crN*mAXAi$rx_ruF{NvxxzjWR)(J)wiyTCTsO9d)OUMv!Ip85=&2a`Hv^R zUma~mC7SM{r5L}sjH}N7^=QY5tvPJ2qet@$dqvK6 zFVFKldA|h4T-^u?!Ot}2GVSD2QUVJS*mW2AqOzTly% zZg4Ydq5LTK$fY5D4ZsS>`!{f6STFij2G6R7bCzyOyuUIH@=4cMdJnkCIkemFcs%9t zsU!@A<|dphO}<2DJ(O^7>?oS&JL#thhsd)t|9!>z4fjJK3moQmjB#51y$~YeU?P4R zG*iciM~^M{X;Khv4ugKaPB8nTb@$qH=2Y|9*)7R|G*a@cMSbJjY5-kN=dH;$FgQbZuO~YxC*q;D!==f|mpsax*iPJ|kwm*V& z5@P*Sx8{SO{xvi-z?|o9g^5R;P{Ie8;ng& zoyO(c9HB1E4}{KEVZ#Y`!|q41uNvtUIVn3uk(4`T)MI` z^U0EpEwOlNVWg#N#jpx}aHX>0$jf7-4jG!e(|C ze`3(*9x<%0Cb-+MDt9>Kmjw|C#WXO;DcCIPjwH~G$w4sSEqpTxBpC`kdg)odAt z-J~AxSpQ4K{6UWATw4|~fI|B>Ip;!9vZPejHzG$cH#T+FM zEm4M(6yos&M7q0;M0c!x-Gl$)1yF0o!w{#@i_a9#qJ9ytv=Gt3-(&t`@ zUR`+pg*TOKRt=+#KPsKg>~9$IzNV>6#z<~us0 zsrIf!w7oO30Wq)|Om&H-n2RLIv%1`GL@v4LID2QxRoDEV(%KMpb#*6vsV*bzsrFY5 zt@hUqt@TWF`CPjV8Cq6fUG5(3^|`|e+P`j(NAh^wY?Eg(RIa#6TI|Et&|;RFqfoS7 zXlF5{$DvIi2udTH&Lk6wbjy~u)Y|UOR7Wb2U9+(4Oid z%U^fnWuH5V#x^lL{l!^QJS}UgnF{KKR82moj?PQkC@}M*@a=k+OBoyr`m6nZSHRF&InKh%t-&PX%hqN}5s4)8 zS;$C@+P<02Do(z#R^T@b~@LZ+A_lRv!wvMyW2TT2ocO z>e4)cpxaX(QY+C~3LwYuQlK*eRHdd{;gd2pxqy8~E7i;*C^Oldo{A>&9hoF*RoQGh zn>8ZYOm0g@w6M0TE8UD^mdYp6>14c+tQZnZ)z_D$Q!zbv_@ohq9IE&~`RRGvwI=zw znb=d$|5I@XL#l4bzK&$xuf!9T2>-#Eyi}uU`ASol2cgPXg%l|>bt8!L(~o9|PtjBl z63vaX!)4Opi>Tos5G3X#<&&7TgRBilCQb67nv@iReNbTsBNh-Oxx8*JG6Lv&z;Xp z1oF3MZjI7M(lTty)M(0&i|N7TP8#qx+WdG)me|k>8 zZH(P+DfY_0=ixx8NP)DRlD&#Qo^l~|JgHqA=r0)e8pNP6mseCYE8MVvaMWfWuZWL8VzzAgL$muC(e?Qt4!pM|G$D zzEDb5vswJAEY75S*cZwwE`$go48PYr<-`NOlpVzP-1VGfi2;doEH0w#pjoh3Jxhm7 omKJJ2W>Lmpf5Z08=Itr}5BhI7;vNo#0000007*qoM6N<$f_*dH!~g&Q literal 0 HcmV?d00001 diff --git a/group_user_management/static/description/index.html b/group_user_management/static/description/index.html new file mode 100644 index 000000000..38ce64a97 --- /dev/null +++ b/group_user_management/static/description/index.html @@ -0,0 +1,471 @@ + + + + + + +User management Group + + + +
+

User management Group

+ + +

Beta License: LGPL-3 OCA/server-backend Translate me on Weblate Try me on Runboat

+

This module adds a group (a res_groups for granting access rights) “User +Management” which allows to read / create / update / delete users from +your companies :

+
    +
  • including access rights management
  • +
  • excluding Administration group access rights management
  • +
+

The purpose of this new group is to grant user authorization to manage +users but without allowing configuration of other stuff of the instance +(as default Administration > Access Rights can do)

+

Table of contents

+ +
+

Use Cases / Context

+

[ This file is optional but strongly suggested to allow end-users to +evaluate the module’s usefulness in their context. ] The purpose of this +module is to add a new group for user management to grant user +authorization to manage users but without allowing configuration of +other stuff of the instance (as default Administration > Access Rights +can do)

+

In other terms, if you want to get a user administrator on your instance +that will :

+
    +
  • Create new users
  • +
  • Update user rights
  • +
  • Update passwords or generate renew password links
  • +
  • Archive or remove users
  • +
+

But is not authorized to :

+
    +
  • Grant himself (or anyone else) Administration / Access Rights or +Administration / Settings role
  • +
  • Update advanced configuration of the instance
  • +
  • Update access rights / ir rules
  • +
  • Create or Update existing groups
  • +
+
+
+

Usage

+

To user this module, you need to:

+
    +
  1. Activate debug mode
  2. +
  3. Go to Settings > Users & Companies > Users
  4. +
  5. Open the form view of the User to whom you want to grant User +Management access
  6. +
  7. Tick User Management (in Extra Rights Section)
  8. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Le Filament
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-backend project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/group_user_management/views/res_users_view.xml b/group_user_management/views/res_users_view.xml new file mode 100644 index 000000000..99315a390 --- /dev/null +++ b/group_user_management/views/res_users_view.xml @@ -0,0 +1,16 @@ + + + + + res.users.groups.admin.limited + res.users + + + + + + + diff --git a/setup/group_user_management/odoo/addons/group_user_management b/setup/group_user_management/odoo/addons/group_user_management new file mode 120000 index 000000000..db3887389 --- /dev/null +++ b/setup/group_user_management/odoo/addons/group_user_management @@ -0,0 +1 @@ +../../../../group_user_management \ No newline at end of file diff --git a/setup/group_user_management/setup.py b/setup/group_user_management/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/group_user_management/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)