Repository navigation
Document that readOnly is only a control in the relation configuration - #269
Merged
Merged
Conversation
`relationRender('comments', ['readOnly' => true])` reads as a way to forbid writes, but the value goes to the browser and comes back with each relation AJAX request, so the client decides whether it returns at all. It can only ever make a relation more restricted than its configuration says. The configuration key, and `relationExtendConfig()`, are the forms that are enforced.
Also documents what a read only relation actually does now that the handlers refuse, and the preview-mode contract form widgets have to honour in their own AJAX handlers.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
applyExtraConfig() keeps only recordUrl and recordOnClick, so the value is dropped in both directions rather than restricting the render further.
LukeTowers
added a commit
to wintercms/winter
that referenced
this pull request
Oct 30, 2026
… write paths (#1554) Three controller behaviours decide what a user may act on, then resolve the ids posted back without consulting that decision. Relation. Posted ids (manage_id, record_id, foreign_id, checked[]) resolved against the related model rather than through the relation, so a user managing one parent's relation could reach another parent's records, and the handlers that attach an existing record had no server-side notion of the selectable set — ignoring the manage.conditions and manage.scope options that narrow it. Separately readOnly was consulted only while rendering, so every write handler still wrote, including the AJAX handlers of field widgets inside the relation's forms. List. Lists::getSelectionQuery() refused a whole-query selection when the list renders no checkboxes but accepted explicit ids on the same list, so index_onDelete could be handed ids for a list offering no selection. In core that reaches the authentication audit trail: AccessLogs declares no showCheckboxes, renders only a Refresh button, and is gated by system.access_logs — "View system logs". Reorder. onReorder() resolved record_ids and sourceNode/targetNode against the bare model, so reorderExtendQuery() narrowed the listing but not the write. Each now resolves through the query it already uses to decide what to show. A readOnly passed to relationRender() is presentational and cannot be enforced — the client decides whether it returns — so only the relation's configuration is a control; documented in wintercms/docs#269. Upgrade notes: A read-only relation refuses every write handler with a 403, including drag-and-drop reordering and the FileUpload and Repeater handlers inside its forms. Rendering, paging and searching are unchanged. readOnly counts only from the relation configuration or relationExtendConfig(); a value passed to relationRender() no longer does, and the behaviour's $readOnly property is always a boolean. Posted record ids the relation manager does not offer are ignored, and the manage and pivot forms raise the usual "record not found". A record only the backend.list.extendRecords event injects is displayed but not manageable. A list declaring no showCheckboxes refuses a selection however the ids arrive, so a controller driving index_onDelete from its own UI must declare it. reorderExtendQuery() narrows what may be reordered as well as what is listed.
bennothommo
pushed a commit
to wintercms/wn-system-module
that referenced
this pull request
Oct 30, 2026
… write paths (#1554) Three controller behaviours decide what a user may act on, then resolve the ids posted back without consulting that decision. Relation. Posted ids (manage_id, record_id, foreign_id, checked[]) resolved against the related model rather than through the relation, so a user managing one parent's relation could reach another parent's records, and the handlers that attach an existing record had no server-side notion of the selectable set — ignoring the manage.conditions and manage.scope options that narrow it. Separately readOnly was consulted only while rendering, so every write handler still wrote, including the AJAX handlers of field widgets inside the relation's forms. List. Lists::getSelectionQuery() refused a whole-query selection when the list renders no checkboxes but accepted explicit ids on the same list, so index_onDelete could be handed ids for a list offering no selection. In core that reaches the authentication audit trail: AccessLogs declares no showCheckboxes, renders only a Refresh button, and is gated by system.access_logs — "View system logs". Reorder. onReorder() resolved record_ids and sourceNode/targetNode against the bare model, so reorderExtendQuery() narrowed the listing but not the write. Each now resolves through the query it already uses to decide what to show. A readOnly passed to relationRender() is presentational and cannot be enforced — the client decides whether it returns — so only the relation's configuration is a control; documented in wintercms/docs#269. Upgrade notes: A read-only relation refuses every write handler with a 403, including drag-and-drop reordering and the FileUpload and Repeater handlers inside its forms. Rendering, paging and searching are unchanged. readOnly counts only from the relation configuration or relationExtendConfig(); a value passed to relationRender() no longer does, and the behaviour's $readOnly property is always a boolean. Posted record ids the relation manager does not offer are ignored, and the manage and pivot forms raise the usual "record not found". A record only the backend.list.extendRecords event injects is displayed but not manageable. A list declaring no showCheckboxes refuses a selection however the ids arrive, so a controller driving index_onDelete from its own UI must declare it. reorderExtendQuery() narrows what may be reordered as well as what is listed.
bennothommo
pushed a commit
to wintercms/wn-backend-module
that referenced
this pull request
Oct 30, 2026
… write paths (#1554) Three controller behaviours decide what a user may act on, then resolve the ids posted back without consulting that decision. Relation. Posted ids (manage_id, record_id, foreign_id, checked[]) resolved against the related model rather than through the relation, so a user managing one parent's relation could reach another parent's records, and the handlers that attach an existing record had no server-side notion of the selectable set — ignoring the manage.conditions and manage.scope options that narrow it. Separately readOnly was consulted only while rendering, so every write handler still wrote, including the AJAX handlers of field widgets inside the relation's forms. List. Lists::getSelectionQuery() refused a whole-query selection when the list renders no checkboxes but accepted explicit ids on the same list, so index_onDelete could be handed ids for a list offering no selection. In core that reaches the authentication audit trail: AccessLogs declares no showCheckboxes, renders only a Refresh button, and is gated by system.access_logs — "View system logs". Reorder. onReorder() resolved record_ids and sourceNode/targetNode against the bare model, so reorderExtendQuery() narrowed the listing but not the write. Each now resolves through the query it already uses to decide what to show. A readOnly passed to relationRender() is presentational and cannot be enforced — the client decides whether it returns — so only the relation's configuration is a control; documented in wintercms/docs#269. Upgrade notes: A read-only relation refuses every write handler with a 403, including drag-and-drop reordering and the FileUpload and Repeater handlers inside its forms. Rendering, paging and searching are unchanged. readOnly counts only from the relation configuration or relationExtendConfig(); a value passed to relationRender() no longer does, and the behaviour's $readOnly property is always a boolean. Posted record ids the relation manager does not offer are ignored, and the manage and pivot forms raise the usual "record not found". A record only the backend.list.extendRecords event injects is displayed but not manageable. A list declaring no showCheckboxes refuses a selection however the ids arrive, so a controller driving index_onDelete from its own UI must declare it. reorderExtendQuery() narrows what may be reordered as well as what is listed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
relationRender('comments', ['readOnly' => true])reads as a way to forbid writes, but the value goes to the browser and comes back with each relation AJAX request, so the client decides whether it returns at all. It can only ever make a relation more restricted than its configuration says. The configuration key, andrelationExtendConfig(), are the forms that are enforced.Also documents what a read only relation actually does now that the handlers refuse, and the preview-mode contract form widgets have to honour in their own AJAX handlers.