Skip to content

[JENKINS-62218] Disable Default View select for read-only folder config viewers - #747

Merged
jtnord merged 7 commits into
jenkinsci:masterfrom
Racknaraock:JENKINS-62218-readonly-folder-config
Aug 3, 2026
Merged

[JENKINS-62218] Disable Default View select for read-only folder config viewers#747
jtnord merged 7 commits into
jenkinsci:masterfrom
Racknaraock:JENKINS-62218-readonly-folder-config

Conversation

@Racknaraock

@Racknaraock Racknaraock commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

[JENKINS-62218] Disable Default View select for read-only folder config viewers

Problem

AbstractFolder/configure.jelly sets a readOnlyMode variable (<j:set var="readOnlyMode" value="${!it.hasPermission(it.CONFIGURE)}" />) and already hides the Save/Apply buttons for a user without Item/Configure. Most fields inherit readOnlyMode automatically through Jenkins core's form tags, but the Default View selector is a hand-rolled <select> (not f:select), so it never inherited readOnlyMode at all — a read-only viewer could change the selection client-side (it just couldn't be saved, since Save/Apply are already correctly hidden).

This is part of the JENKINS-12548 epic (read-only system configuration browsing / JEP-224).

Note on prior scope: earlier revisions of this PR also touched Health metrics (auto-expanding it out of f:advanced for read-only viewers) and added capture="readOnlyMode" to the Views Tab Bar / Icon f:dropdownDescriptorSelector calls. Per review discussion:

  • The capture="readOnlyMode" gap is a generic Jenkins core bug (affects every dropdownDescriptorSelector consumer, e.g. config-scm.jelly's scmCheckoutStrategy), so it was reverted here and pursued instead as jenkinsci/jenkins#27167.
  • Per feedback from @jtnord and @timja, advanced configuration sections should render the same regardless of readOnlyMode — if a section was previously collapsed behind f:advanced, it should stay that way in read-only mode too. That change (and its test) has been reverted.

This PR's scope is now just the Default View select fix.

Changes

In src/main/resources/com/cloudbees/hudson/plugins/folder/AbstractFolder/configure.jelly:

  • Default View: add disabled="${readOnlyMode ? 'true' : null}" to the <select name="primaryView">, matching the idiom used by Jenkins core's own f:dropdownList (and by core's own GlobalDefaultViewConfiguration/config.jelly, which solves the identical "pick a default view" problem the same way).

Screenshots

Default View — Viewer

Verified on a local mvn hpi:run instance with a Matrix-Authorization-Strategy viewer (Item.READ+Item.EXTENDED_READ+View.READ, no Item.CONFIGURE) against a folder with 2 views:

Before After
before after

Before the fix, the select had no disabled attribute and its value could be changed via a normal change event (confirmed via script: setting .value and dispatching change succeeded). After the fix, disabled is true and the control no longer accepts input.

Admin, for reference (no regression — still fully editable):

admin

Testing

  • Manual verification on a local mvn hpi:run instance, comparing an admin against a read-only viewer on the same folder's configure page (screenshots above), including live DOM/interactivity checks (disabled attribute state, attempted change events) for the Default View select.

Checklist

  • Referenced the Jira issue (JENKINS-62218) in the commit messages
  • Interactively tested the change on a local Jenkins instance

Proposed changelog entries

  • JENKINS-62218: The "Default View" selector on a folder's configuration page is now correctly disabled for read-only viewers.

f:advanced collapses the Health metrics section behind a click-to-expand
toggle regardless of permissions. A user with Item.READ/EXTENDED_READ but
not Item.CONFIGURE has no way to interact with the form anyway, so hiding
already-configured health metrics behind an extra click only makes the
read-only view less informative, contrary to the read-only rendering
convention described in JENKINS-12548 (every advanced section should be
expanded out of the box for read-only browsing).

When readOnlyMode is true, render the health metrics hetero-list directly
instead of wrapping it in f:advanced. Behavior for users with CONFIGURE
is unchanged.
…ring

Covers both cases: a viewer with Item.READ/EXTENDED_READ but not
Item.CONFIGURE sees the configured health metric directly without an
Advanced toggle, while a user with CONFIGURE still gets the collapsible
Advanced section as before.
@Racknaraock
Racknaraock requested a review from a team as a code owner July 25, 2026 12:58
The Appearance section's Default View <select name="primaryView"> was
hand-rolled HTML with no readOnlyMode awareness, unlike the rest of the
configure page. A user with Item.READ + Item.EXTENDED_READ but without
Item.CONFIGURE could interact with and change this dropdown client-side
even though they could not save, since it lacked a disabled attribute.

Add disabled="${readOnlyMode ? 'true' : null}" to the select, matching
the idiom used elsewhere in Jenkins core (e.g. lib/form/dropdownList.jelly)
and consistent with how the rest of this page already respects
readOnlyMode.
…escriptor fragments

f:dropdownDescriptorSelector renders the config fragment of the currently
selected Views Tab Bar / Icon descriptor inline, but every other candidate
descriptor's fragment is rendered lazily via l:renderOnDemand. That closure
only snapshots jelly variables named in the tag's capture attribute, so the
outer readOnlyMode variable set in configure.jelly was invisible to those
lazy fragments, leaving any field they render fully editable for a read-only
viewer (Item.READ+Item.EXTENDED_READ without Item.CONFIGURE) if the outer,
already-disabled <select> is bypassed client-side or the render-on-demand
endpoint is hit directly.

Add capture="readOnlyMode" to both dropdownDescriptorSelector call sites
(viewsTabBar and icon).

Add a regression test that registers a second fake FolderIcon descriptor via
@TestExtension so the Icon dropdown has a non-selected entry to lazily
render, fetches that lazy fragment through the same stapler-bound-proxy
protocol the browser's own JavaScript uses, and asserts a read-only viewer
sees the read-only placeholder instead of an editable <input>. Verified the
test fails without the capture attribute and passes with it.
@Racknaraock Racknaraock changed the title [JENKINS-62218] Expand Health metrics by default for read-only viewers [JENKINS-62218] Fix remaining read-only gaps in folder configuration (health metrics, default view, tab bar/icon) Jul 27, 2026
@jtnord
jtnord removed the request for review from a team July 27, 2026 10:14

@jtnord jtnord left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Health metrics was wrapped in f:advanced, Jenkins core's collapsible "Advanced..." toggle. That toggle renders identically for every user regardless of permissions, so a read-only viewer has to click it just to discover whether any health metric is even configured.

Seems appropriate, not a bug. Otherwise a config page can be full of loads of things you need to scroll through.
If this is an issue (which I do not beleive it is) it should not be fixed here, but in Jenkins core so that the behaviour is the same for all consumers of f:advanced in all of the Jenkins UI.

Views Tab Bar / Icon (f:dropdownDescriptorSelector) render their currently selected descriptor's config fragment inline, where readOnlyMode is visible normally — but any other descriptor's fragment is deferred via Jenkins core's l:renderOnDemand, which only exposes variables explicitly named in the tag's capture attribute. Neither call declared capture="readOnlyMode", so an extra config field from a third-party Icon/TabBar descriptor would come back fully editable to a read-only viewer once loaded.

Would appear to be a generic bug in Jenkins core and would affect all users of dropdownDescriptorSelector across the project so should be fixed in Jenkins core if so?

<j:if test="${it.folderViews.primaryModifiable and views.size()>1}">
<f:entry title="${%Default View}" field="defaultView">
<div class="jenkins-select">
<select class="jenkins-select__input" name="primaryView">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to adapt this to f:select?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sooo... Choosing f:select would require refactoring the control according to the AJAX-fill model; I decided that this change was better, since it is a pattern already used in the core

https://github.com/jenkinsci/jenkins/blob/master/core/src/main/resources/hudson/views/GlobalDefaultViewConfiguration/config.jelly#L26

oneEach="true" hasHeader="true" honorOrder="true"/>
</f:block>
</f:advanced>
<j:choose>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if advanced blocks are not supposed to be expanded by default in read only mode then the change should go in Jenkins core so that all configuration items behave the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strategy followed to implement these changes in other Epic tasks has been to implement them internally within the plugins—I understand this is to keep the components as generic primitives without any kind of permission context (such as read-only). Implementing the change in the core would mean altering all the components that use it, with the problems that could entail...

I also understand your point—not including it in the core would lead to UX inconsistencies, but that’s more of a design decision that the project maintainers should make.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also understand your point—not including it in the core would lead to UX inconsistencies, but that’s more of a design decision that the project maintainers should make.

FTR I think the current behaviour in this regard is correct. To me a readonly view should display the same as a read/write view with the exception of the disabled state of components, and the lack of save/apply buttons.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kohsuke was the creator of JENKINS-12548 ; perhaps he can provide some more information. I'm just trying to fix the reported issue. If the decision is to leave it as is, perhaps the issue should be closed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kohsuke is no longer active in this project. JEP-224 does not explicitly state that the layout should or should not change only

Many views have been modified to hide information that is not relevent to read only users.
Jelly tags in the Jenkins core have been modified to display a read only state, i.e. text instead of inputs, icons for checkboxes

However I maintain that nothing should be changing advanced configuration to standard configuration just because it becomes read only, it is either always advanced, or never advanced), but will summon @timja as the author of the JEP for a second opinion.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advanced configuration does not change anything here, if it was previously advanced then it should still be under that as part of read only change.

(Advanced can be evaluated as part of separate UX review, it should only be used for non standard or uncommon configurations, as it will hide options and users may not see them)

@Racknaraock Racknaraock Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means, this statement in the epic : ''every "advanced" sections should be expanded out of the box.''

Is not longer valid? Just trying to confirm if we are only expecting the fix on the Default view select or there is something else to fix that im not noticing.

Once I get the confirmation I can push the revert of the advanced sections.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means, this statement in the epic : ''every "advanced" sections should be expanded out of the box.''
Is not longer valid?

Correct.

Just trying to confirm if we are only expecting the fix on the Default view select or there is something else to fix that im not noticing.

that should be all that is needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert applied.

It would also be good to clarify the incorrect statement in the epic. I'll continue working on the remaining tasks in that epic, so I'll keep that in mind, but if any other contributors join, they might run into the same problem.

<j:choose>
<j:when test="${descriptor.tabBarConfigurable}">
<f:dropdownDescriptorSelector title="${%Views Tab Bar}" field="viewsTabBar"/>
<f:dropdownDescriptorSelector title="${%Views Tab Bar}" field="viewsTabBar" capture="readOnlyMode"/>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks strange.
if this is needed why is this not more prevelant elsewhere. e.g. any project that defines an SCM -> https://github.com/jenkinsci/jenkins/blob/master/core/src/main/resources/lib/hudson/project/config-scm.jelly#L45-L48 would appear to have issues?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like so... I've reverted the change in the plugin. I'll open an issue in the core later to try to fix it there.

That was a good catch—thanks for letting me know.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…descriptors

The underlying gap (l:renderOnDemand's RenderOnDemandClosure only exposes
variables named in `capture`, so readOnlyMode set in an ancestor scope is
invisible to lazily-rendered descriptor fragments) is not specific to this
plugin: Jenkins core's own hudson/model/Job/configure.jelly sets readOnlyMode
the same way and includes config-scm.jelly, whose dropdownDescriptorSelector
for scmCheckoutStrategy has the identical gap. Per review feedback, fixing
this locally in cloudbees-folder-plugin wouldn't address the same issue on
every other Job configuration page, so this should be fixed in Jenkins core
instead. Reverts the capture="readOnlyMode" attribute additions and the
associated FakeCaptureFolderIcon test.

This is a revert of e6eed4f and 88edadd; the Default View select fix
(c9aed80) and Health metrics fix (0bc0fda) are unaffected.
@Racknaraock Racknaraock changed the title [JENKINS-62218] Fix remaining read-only gaps in folder configuration (health metrics, default view, tab bar/icon) [JENKINS-62218] Fix remaining read-only gaps in folder configuration (health metrics, default view) Jul 27, 2026
…ly viewers

Per review feedback from jtnord and timja, advanced configuration
sections should render the same regardless of readOnlyMode: if a
section was previously collapsed behind f:advanced, it should stay
that way in read-only mode too, rather than auto-expanding just
because the viewer lacks CONFIGURE. Only the Default View select's
disabled attribute (c9aed80) remains from this PR's original scope.

This is a revert of 0bc0fda and 67b5274.
@Racknaraock

Copy link
Copy Markdown
Contributor Author

@jtnord @timja Agreed on the Health metrics point — reverted in 853d481. The section now stays behind f:advanced regardless of readOnlyMode, and the associated test (readOnlyViewerSeesExpandedHealthMetrics) was removed. This PR's scope is now just the Default View select disabled fix.

@Racknaraock Racknaraock changed the title [JENKINS-62218] Fix remaining read-only gaps in folder configuration (health metrics, default view) [JENKINS-62218] Disable Default View select for read-only folder config viewers Aug 3, 2026
@jtnord jtnord added the bug label Aug 3, 2026
@jtnord
jtnord merged commit 3ad9a6d into jenkinsci:master Aug 3, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants