Implement snooze functionality for administrative monitors with…#26244
Implement snooze functionality for administrative monitors with…#26244LakshyaBagani wants to merge 8 commits intojenkinsci:masterfrom
Conversation
mawinter69
left a comment
There was a problem hiding this comment.
Do we really need minute precision for this? It will be hard to know how many minutes you have to enter when you want to snooze for a couple of days or even weeks. Hours or even days might be sufficient. Maybe instead use an input with a datetime picker.
Having the input field next to the button doesn't look that good. Already in your screenshot the placeholder is cut off. Might be worth to check if using a dialog where you choose the snooze time is better. There you could easily provide a drop down with some predefined values and a custom value.
You could just do some javascript that does a post to the doSnooze method and on success just hides the message. That would avoid a page reload.
|
|
||
| UserPreferencesProperty.DisplayName=Preferences | ||
|
|
||
| snooze=Snooze |
There was a problem hiding this comment.
That is not accessible from jelly files. You can see in your screenshot that the values are not used.
There was a problem hiding this comment.
You are right the previous package mismatch caused the translation lookup to fail. I have now moved the form into a dedicated t:snooze taglib at lib/hudson/snooze.jelly with its own snooze.properties file in the same folder
| <f:submit name="yes" value="${%Manage}"/> | ||
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> |
There was a problem hiding this comment.
Looks identical in all 3 places. So that might be better placed in a taglib for easy reuse.
There was a problem hiding this comment.
Refactored the duplicate code into a reusable t:snooze tag in lib/hudson.
| <f:submit name="yes" value="${%Manage}"/> | ||
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> |
There was a problem hiding this comment.
Would prefer that this is defined in a css file
There was a problem hiding this comment.
I have replaced the inline styles with core utility classes.
| String duration = req.getParameter("duration"); | ||
| long durationMs; | ||
| try { | ||
| if ("custom".equals(duration)) { |
There was a problem hiding this comment.
Isn't that always true as the 'duration' field has a default of custom and there is no code that will ever change this?
There was a problem hiding this comment.
I included that for future flexibility, but you are right it is redundant since the current UI only sends custom. I will simplify the logic to handle the custom path directly and remove the unnecessary check.
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> | ||
| <j:if test="${h.isCrumbEncoded()}"> |
There was a problem hiding this comment.
I manually added the CSRF crumb logic to ensure the snooze action was secure, but I have since realized this is redundant as Jenkins global JavaScript automatically injects crumbs into all POST forms. I will remove these lines along with the manual flex styling to keep the implementation clean and consistent with the existing patterns in the codebase.
mawinter69
left a comment
There was a problem hiding this comment.
You might want to take a look at #23859 where I implemented token expiration. This also has a dialog with a select for preset expirations and a custom input field for a date select.
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Please don't set a version yet but write TODO. You don't know how fast your change will be merged. The version will be filled in later automatically.
There was a problem hiding this comment.
Please use the built-in support for dialogs. See https://weekly.ci.jenkins.io/design-library/dialogs/ how to use it
| <label for="snooze-duration-${it.id}" class="jenkins-form-label" style="display:block; margin-bottom: 5px;"> | ||
| ${%snooze.duration.label} | ||
| </label> | ||
| <select id="snooze-duration-${it.id}" name="durationPreset" |
There was a problem hiding this comment.
The select will not be styled like other selects in Jenkins
| </button> | ||
|
|
||
| <dialog id="snooze-dialog-${it.id}" class="snooze-dialog"> | ||
| <form id="snooze-form-${it.id}" method="post" |
| </select> | ||
| </div> | ||
|
|
||
| <div class="jenkins-form-item"> |
There was a problem hiding this comment.
That could be just <f:number ...
| private final Map<String, Long> snoozedAdministrativeMonitors = new HashMap<>(); | ||
|
|
||
| /** | ||
| * @since 2.549 |
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
| <input type="number" id="snooze-minutes-${it.id}" name="customMinutes" | ||
| placeholder="${%snooze.duration.placeholder}" min="1" max="525600" | ||
| class="jenkins-input snooze-custom-input" | ||
| style="display: none; width: 100%;" |
There was a problem hiding this comment.
In general when you want to hide something, set the class jenkins-hidden, to later show it just remove it from the element. That avoids that you need to know which exact value is set for display when it is shown.
| Uses AJAX to avoid page reload. | ||
| </st:documentation> | ||
|
|
||
| <st:adjunct includes="lib.hudson.snoozeForm"/> |
There was a problem hiding this comment.
You need to rename the .js and the .css files. What happens now is that the adjunct will also include the snoozeForm.jelly, leading to a duplication of things
There was a problem hiding this comment.
By using the built-in dialog support you will most likely not need any css. It will work out of the box also with dark theme or other themes
da9f705 to
9105aae
Compare
|
@mawinter69 any updates on this PR? |
|
@mawinter69 @MarkEWaite any update on this PR it is open from more than 2 weeks ? |
MarkEWaite
left a comment
There was a problem hiding this comment.
Several items that need attention before a detailed review.
| } | ||
|
|
||
| /** | ||
| /** |
There was a problem hiding this comment.
Please remove this change that is only white space. It wastes maintainer time to see white space changes that will then be reverted by someone else making a white space change to restore consistency.
| /** | |
| /** |
| * ============================================================================================================== */ | ||
|
|
||
| /** | ||
| /** |
There was a problem hiding this comment.
Please remove this white space change.
| } | ||
|
|
||
| /** | ||
| * @since TODO |
There was a problem hiding this comment.
Please provide a complete Javadoc comment. This is adding a new public API and new public API's need a comment that describes their purpose.
| } | ||
|
|
||
| /** | ||
| * @since TODO |
There was a problem hiding this comment.
Please add a complete Javadoc comment that describes the method, its parameters, and the exception that is thrown.
| * Form UI elements that change system state, e.g. toggling a feature on or off, need to be hidden from users | ||
| * lacking Administer permission. | ||
| * </p> | ||
| * |
There was a problem hiding this comment.
Please remove this extraneous change
| /* | ||
| * The MIT License | ||
| * | ||
| * Copyright (c) 2025 Jenkins Contributors |
There was a problem hiding this comment.
Copyright year should be 2026 as far as I can tell.
| * Copyright (c) 2025 Jenkins Contributors | |
| * Copyright (c) 2026 Jenkins Contributors |
… cleanup and validation
- Replaced custom minutes input with HTML5 date picker for snooze duration - Migrated from custom dialog to Jenkins dialog.form() API - Simplified snoozeForm.jelly by removing inline dialog markup - Renamed snoozeForm.js to snooze.js following Jenkins adjunct naming - Removed snoozeForm.css (now uses built-in Jenkins styles) - Updated doSnooze() to parse date instead of custom minutes - Changed @SInCE version to TODO for new methods
Moved submitButton variable declaration to function scope to avoid multiple redeclarations within the same function.
…ight year - Revert whitespace-only changes in AbstractCIBase.java - Add complete Javadoc for new public API methods (isSnoozed, snooze, doSnooze, getSnoozedAdministrativeMonitors, setSnoozedAdministrativeMonitors) - Remove extraneous blank line in getRequiredPermission Javadoc - Update copyright year from 2025 to 2026 in snooze.js and snoozeForm.properties Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89a4e2c to
6ba42fa
Compare
|
@MarkEWaite I ahve made the change as per u said . Please recheck the code and tell me whether I had implemented correctly or not ? |
Please don't force push changes. That breaks the connection between previous review comments and the code. |
Sorry about that - I had to rebase on latest master and remove an accidentally committed .vscode/settings.json, I will use fixup commits going forward instead of force pushing. |
|
@MarkEWaite have to check the changes I had changed the javaDocs as u said before ? |
|
@MarkEWaite @mawinter69 have u found the PR good or it need some changes ? |
|
@LakshyaBagani I am on vacation for two weeks. I will not look at this until after I return |
|
Thanks for addressing @MarkEWaite |
|
I think we should discuss this in the next UX-SIG meeting, while the idea to allow snoozing the monitors is good, it requires an additional button. Monitors can already have several buttons and adding another might be too much for some of them. |
|
Sounds good. I had love to hear your thoughts on implementing the snooze feature - happy to take this forward and work on it further. |
|
I think it should be part of dismiss, a dialog pops up and asks you if you want to dismiss forever, for a period of time, and if you want to dismiss just for you or for all users potentially? |
Interesting idea . If we go with a dialog on dismiss, how would that work for monitors that already have their own custom doAct implementations with different dismiss behaviors would we override those, or would the dialog need to be aware of each monitor's existing logic? |
Some sort of jelly tag would need to be created for admin monitors e.g.
That takes values like:
|
That makes sense. I will wait for the UX-SIG discussion before prototyping further. In the meantime, I will look into how the l:adminMonitor tag could work . |
|
I wouldn't wait for a meeting where it may or may not get discussed. |
Then I will go ahead and start prototyping the l:adminMonitor tag |






Fixes #16641
Testing done
Automated Testing:
Created core/src/test/java/hudson/model/AdministrativeMonitorTest.java with 8 new unit tests covering:
Snooze expiry logic (testSnoozeExpiry).
Cleanup of expired monitors (testCleanupRemovesOnlyThisMonitor).
Persistence of snoozed state across restarts (testSnoozePersistence).
Isolation between multiple monitors (testMultipleMonitorsIndependent).
Validation of duration input (negative, zero, excessive duration checks).
Ran mvn test -Dtest=AdministrativeMonitorTest (Passed: 8/8 tests).
Ran mvn clean verify to ensure no regressions in established tests.
Manual Verification:
Verified Internationalization:
Checked
core/src/main/resources/hudson/model/Messages.propertiesfor presence ofSnooze,Snooze.duration.label, etc.Verified Accessibility:
Inspected Jelly files (
BuiltInNodeMigration,ControllerExecutorsAgents,ControllerExecutorsNoAgents) to ensure<label>,id,aria-label, andtitleattributes were correctly added to input fields.Verified Javadoc:
Confirmed
AdministrativeMonitor.javaJavadoc formatting matches master branch (no diff noise).Screenshots (UI changes only)
Before
After
Proposed changelog entries
Allow administrative monitors to be snoozed for a specific duration (up to 1 year).
Proposed changelog category
/label rfe
Proposed upgrade guidelines
N/A
Submitter checklist
@Restrictedor have@since TODOJavadocs, as appropriate.@Deprecated(since = "TODO")or@Deprecated(forRemoval = true, since = "TODO"), if applicable.evalto ease future introduction of Content Security Policy (CSP) directives.Desired reviewers
@timja @mawinter69 @MarkEWaite