Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions spp_starter_sp_mis/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ The registry restriction uses:
restrictions
- **MutationObserver**: Monitors DOM changes to re-apply restrictions
dynamically
- **Per-Action Opt-Out**: An action can exempt its own views by setting
``bypass_registry_admin_only_crud: True`` in its context (e.g. an
action that puts a dedicated non-admin role in charge of authoring one
registry list). Like the restriction itself this is client-side only,
not a security boundary — server-side ACLs and record rules remain the
real enforcement. The flag follows Odoo's normal context propagation,
so relational dialogs and actions opened via buttons from inside the
exempt view inherit it; exempt form views also disable the
blur-triggered urgent (beacon) save so partially-filled new records do
not raise validation errors on tab switch

Included Modules
~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -119,6 +129,16 @@ Dependencies
Changelog
=========

19.0.2.1.0
~~~~~~~~~~

- feat(ui): honor ``bypass_registry_admin_only_crud`` in an action's
context to exempt that action's views (including relational dialogs
opened from them) from the registry admin-only CRUD restriction;
exempt form views also disable the blur-triggered urgent (beacon) save
so partially-filled new records do not raise validation errors on tab
switch

19.0.2.0.0
~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion spp_starter_sp_mis/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"name": "OpenSPP Starter: SP-MIS",
"summary": "Complete SP-MIS bundle with Social Registry, Programs, and Service Points",
"category": "OpenSPP",
"version": "19.0.2.0.0",
"version": "19.0.2.1.0",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
Expand Down
9 changes: 9 additions & 0 deletions spp_starter_sp_mis/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ The registry restriction uses:
- **JavaScript Patches**: Modifies `FormController` and `ListController` for `res.partner` model
- **Admin Check**: Users in `spp_security.group_spp_admin` bypass all restrictions
- **MutationObserver**: Monitors DOM changes to re-apply restrictions dynamically
- **Per-Action Opt-Out**: An action can exempt its own views by setting
`bypass_registry_admin_only_crud: True` in its context (e.g. an action that puts a
dedicated non-admin role in charge of authoring one registry list). Like the
restriction itself this is client-side only, not a security boundary — server-side
ACLs and record rules remain the real enforcement. The flag follows Odoo's normal
context propagation, so relational dialogs and actions opened via buttons from
inside the exempt view inherit it; exempt form views also disable the blur-triggered
urgent (beacon) save so partially-filled new records do not raise validation errors
on tab switch

### Included Modules

Expand Down
8 changes: 8 additions & 0 deletions spp_starter_sp_mis/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### 19.0.2.1.0

- feat(ui): honor `bypass_registry_admin_only_crud` in an action's context to exempt
that action's views (including relational dialogs opened from them) from the
registry admin-only CRUD restriction; exempt form views also disable the
blur-triggered urgent (beacon) save so partially-filled new records do not raise
validation errors on tab switch

### 19.0.2.0.0

- Initial migration to OpenSPP2
21 changes: 21 additions & 0 deletions spp_starter_sp_mis/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,16 @@ <h1>Implementation Details</h1>
restrictions</li>
<li><strong>MutationObserver</strong>: Monitors DOM changes to re-apply restrictions
dynamically</li>
<li><strong>Per-Action Opt-Out</strong>: An action can exempt its own views by setting
<tt class="docutils literal">bypass_registry_admin_only_crud: True</tt> in its context (e.g. an
action that puts a dedicated non-admin role in charge of authoring one
registry list). Like the restriction itself this is client-side only,
not a security boundary — server-side ACLs and record rules remain the
real enforcement. The flag follows Odoo’s normal context propagation,
so relational dialogs and actions opened via buttons from inside the
exempt view inherit it; exempt form views also disable the
blur-triggered urgent (beacon) save so partially-filled new records do
not raise validation errors on tab switch</li>
</ul>
</div>
<div class="section" id="included-modules">
Expand Down Expand Up @@ -478,6 +488,17 @@ <h2><a class="toc-backref" href="#toc-entry-1">Changelog</a></h2>
</div>
</div>
<div class="section" id="section-1">
<h1>19.0.2.1.0</h1>
<ul class="simple">
<li>feat(ui): honor <tt class="docutils literal">bypass_registry_admin_only_crud</tt> in an action’s
context to exempt that action’s views (including relational dialogs
opened from them) from the registry admin-only CRUD restriction;
exempt form views also disable the blur-triggered urgent (beacon) save
so partially-filled new records do not raise validation errors on tab
switch</li>
</ul>
</div>
<div class="section" id="section-2">
<h1>19.0.2.0.0</h1>
<ul class="simple">
<li>Initial migration to OpenSPP2</li>
Expand Down
40 changes: 37 additions & 3 deletions spp_starter_sp_mis/static/src/js/registry_restriction.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@
*
* The restriction check is cached and resolved before the form/list setup
* so that modelParams can return mode: "readonly" before the model is created.
*
* Per-action opt-out: an action can exempt its own view from the restriction
* by setting ``bypass_registry_admin_only_crud: True`` in its context (e.g.
* an action that puts a dedicated non-admin role in charge of authoring one
* registry list).
*
* Opt-out scope and caveats:
* - The flag is a UI affordance, NOT a security boundary. Like the
* restriction itself, it changes nothing server-side: ACLs and record
* rules remain the real enforcement, and any user who can write
* ``ir.actions.act_window`` records can set the flag.
* - The flag follows Odoo's normal context propagation: relational dialogs
* (many2one create/edit and search-more, x2many record dialogs), expanded
* dialogs, and actions opened via buttons from inside the exempt view all
* inherit it. Scope bypass actions deliberately.
* - Exempt form views also disable the blur-triggered urgent (beacon) save,
* because authoring actions typically carry ``default_*`` context keys
* that make a new record dirty before the user types.
*/

import {FormController} from "@web/views/form/form_controller";
Expand All @@ -20,6 +38,12 @@ import {onMounted, onPatched, onWillStart, onWillUnmount} from "@odoo/owl";
// Models affected by the registry restriction
const REGISTRY_MODELS = ["res.partner"];

// An action opts its own view out of the restriction by setting this flag
// in its context. See the module docstring for scope and caveats.
function actionBypassesRestriction(controller) {
return Boolean(controller.props.context?.bypass_registry_admin_only_crud);
}

// Cache the restriction check to avoid repeated RPC calls within the same session.
// Resolved once at module load time so it's available synchronously in setup().
let _restrictionResult = null;
Expand Down Expand Up @@ -69,13 +93,16 @@ const BLOCKED_ACTIONS = ["delete", "archive", "unarchive", "duplicate"];
patch(FormController.prototype, {
setup() {
const modelName = this.props.resModel;
const bypassed = actionBypassesRestriction(this);
// Check synchronous cache BEFORE super.setup() creates the model
this._registryRestricted =
REGISTRY_MODELS.includes(modelName) && _restrictionResult === true;
REGISTRY_MODELS.includes(modelName) &&
!bypassed &&
_restrictionResult === true;

super.setup(...arguments);

if (!REGISTRY_MODELS.includes(modelName)) {
if (!REGISTRY_MODELS.includes(modelName) || bypassed) {
return;
}

Expand Down Expand Up @@ -156,6 +183,13 @@ patch(FormController.prototype, {
if (this._registryRestricted) {
params.config.mode = "readonly";
}
if (actionBypassesRestriction(this)) {
// Bypass actions typically set default_* keys that make a new
// record dirty before the user types; the blur-triggered urgent
// (beacon) save would then raise validation errors on every tab
// refocus. Let the user decide when to save.
params.useSendBeaconToSaveUrgently = false;
}
return params;
},

Expand All @@ -182,7 +216,7 @@ patch(ListController.prototype, {
super.setup(...arguments);

const modelName = this.props.resModel;
if (!REGISTRY_MODELS.includes(modelName)) {
if (!REGISTRY_MODELS.includes(modelName) || actionBypassesRestriction(this)) {
return;
}

Expand Down
Loading