Skip to content

Implement snooze functionality for administrative monitors with…#26244

Open
LakshyaBagani wants to merge 8 commits intojenkinsci:masterfrom
LakshyaBagani:reate-snooze-feature-for-administrativeMonitors
Open

Implement snooze functionality for administrative monitors with…#26244
LakshyaBagani wants to merge 8 commits intojenkinsci:masterfrom
LakshyaBagani:reate-snooze-feature-for-administrativeMonitors

Conversation

@LakshyaBagani
Copy link
Contributor

@LakshyaBagani LakshyaBagani commented Feb 2, 2026

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.properties for presence of Snooze, Snooze.duration.label, etc.

Verified Accessibility:
Inspected Jelly files (BuiltInNodeMigration, ControllerExecutorsAgents, ControllerExecutorsNoAgents) to ensure <label>, id, aria-label, and title attributes were correctly added to input fields.

Verified Javadoc:
Confirmed AdministrativeMonitor.java Javadoc formatting matches master branch (no diff noise).

Screenshots (UI changes only)

Before

Screenshot from 2026-02-02 16-58-00

After

Screenshot from 2026-02-02 16-50-38

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

  • The issue, if it exists, is well-described.
  • The changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developers, depending on the change) and are in the imperative mood.
  • There is automated testing or an explanation as to why this change has no tests.
  • New public classes, fields, and methods are annotated with @Restricted or have @since TODO Javadocs, as appropriate.
  • New deprecations are annotated with @Deprecated(since = "TODO") or @Deprecated(forRemoval = true, since = "TODO"), if applicable.
  • UI changes do not introduce regressions when enforcing the current default rules of Content Security Policy Plugin. In particular, new or substantially changed JavaScript is not defined inline and does not call eval to ease future introduction of Content Security Policy (CSP) directives.
  • For dependency updates, there are links to external changelogs and, if possible, full differentials.
  • For new APIs and extension points, there is a link to at least one consumer.

Desired reviewers

@timja @mawinter69 @MarkEWaite

@comment-ops-bot comment-ops-bot bot added the rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted label Feb 2, 2026
Copy link
Contributor

@mawinter69 mawinter69 left a comment

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

That is not accessible from jelly files. You can see in your screenshot that the values are not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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;">
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks identical in all 3 places. So that might be better placed in a taglib for easy reuse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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;">
Copy link
Contributor

Choose a reason for hiding this comment

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

Would prefer that this is defined in a css file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have replaced the inline styles with core utility classes.

String duration = req.getParameter("duration");
long durationMs;
try {
if ("custom".equals(duration)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't that always true as the 'duration' field has a default of custom and there is no code that will ever change this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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()}">
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

@mawinter69 mawinter69 left a comment

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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"
Copy link
Contributor

Choose a reason for hiding this comment

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

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"
Copy link
Contributor

Choose a reason for hiding this comment

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

Use <f:form

</select>
</div>

<div class="jenkins-form-item">
Copy link
Contributor

Choose a reason for hiding this comment

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

That could be just <f:number ...

private final Map<String, Long> snoozedAdministrativeMonitors = new HashMap<>();

/**
* @since 2.549
Copy link
Contributor

Choose a reason for hiding this comment

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

-> TODO

}

/**
* @since 2.549
Copy link
Contributor

Choose a reason for hiding this comment

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

-> TODO

}

/**
* @since 2.549
Copy link
Contributor

Choose a reason for hiding this comment

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

-> TODO

}

/**
* @since 2.549
Copy link
Contributor

Choose a reason for hiding this comment

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

-> TODO

<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%;"
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

@mawinter69 mawinter69 left a comment

Choose a reason for hiding this comment

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

Seems you've never really tested this.

Image Image

Uses AJAX to avoid page reload.
</st:documentation>

<st:adjunct includes="lib.hudson.snoozeForm"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Contributor

Choose a reason for hiding this comment

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

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

@LakshyaBagani LakshyaBagani force-pushed the reate-snooze-feature-for-administrativeMonitors branch from da9f705 to 9105aae Compare February 7, 2026 07:15
@LakshyaBagani
Copy link
Contributor Author

LakshyaBagani commented Feb 7, 2026

Seems you've never really tested this.

Image Image

I have checked the code and also change the UI as per the requirement
Screenshot from 2026-02-07 12-22-50
Screenshot from 2026-02-06 18-14-28

@LakshyaBagani
Copy link
Contributor Author

@mawinter69 any updates on this PR?

@LakshyaBagani
Copy link
Contributor Author

@mawinter69 @MarkEWaite any update on this PR it is open from more than 2 weeks ?

Copy link
Contributor

@MarkEWaite MarkEWaite left a comment

Choose a reason for hiding this comment

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

Several items that need attention before a detailed review.

}

/**
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
/**
/**

* ============================================================================================================== */

/**
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove this white space change.

}

/**
* @since TODO
Copy link
Contributor

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

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>
*
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove this extraneous change

/*
* The MIT License
*
* Copyright (c) 2025 Jenkins Contributors
Copy link
Contributor

Choose a reason for hiding this comment

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

Copyright year should be 2026 as far as I can tell.

Suggested change
* Copyright (c) 2025 Jenkins Contributors
* Copyright (c) 2026 Jenkins Contributors

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

LakshyaBagani and others added 8 commits February 19, 2026 23:31
- 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>
@LakshyaBagani LakshyaBagani force-pushed the reate-snooze-feature-for-administrativeMonitors branch from 89a4e2c to 6ba42fa Compare February 19, 2026 18:02
@LakshyaBagani
Copy link
Contributor Author

@MarkEWaite I ahve made the change as per u said . Please recheck the code and tell me whether I had implemented correctly or not ?

@MarkEWaite
Copy link
Contributor

@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.

@LakshyaBagani
Copy link
Contributor Author

@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.

@LakshyaBagani
Copy link
Contributor Author

@MarkEWaite have to check the changes I had changed the javaDocs as u said before ?

@LakshyaBagani
Copy link
Contributor Author

@MarkEWaite @mawinter69 have u found the PR good or it need some changes ?

@MarkEWaite
Copy link
Contributor

@LakshyaBagani I am on vacation for two weeks. I will not look at this until after I return

@LakshyaBagani
Copy link
Contributor Author

Thanks for addressing @MarkEWaite

@mawinter69
Copy link
Contributor

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.
This is also in the context of #18321 and #18417
Right now each monitor can decide if it adds a dismiss button and needs to do this on it's own placing this at will (some make it the first some the last I think). And each monitor has implemented that differently, either by using the doDisable endpoint but also frequently by implementing a doAct with different parameters.
Formalizing this and getting a more consistent UI would be good I think. With #18321 and when it really looks like in the repeatables, the snooze could also be just a small icon in the upper right.

@LakshyaBagani
Copy link
Contributor Author

Sounds good. I had love to hear your thoughts on implementing the snooze feature - happy to take this forward and work on it further.

@timja
Copy link
Member

timja commented Feb 26, 2026

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?

@LakshyaBagani
Copy link
Contributor Author

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?

@timja
Copy link
Member

timja commented Feb 27, 2026

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.

l:adminMonitor

That takes values like:

  • dismissable
  • dismissValue - default to something sensible so most don't have to set this
  • actions - actions (buttons) that people can take
  • The body would take html content

@LakshyaBagani
Copy link
Contributor Author

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.

l:adminMonitor

That takes values like:

  • dismissable
  • dismissValue - default to something sensible so most don't have to set this
  • actions - actions (buttons) that people can take
  • The body would take html content

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.

l:adminMonitor

That takes values like:

  • dismissable
  • dismissValue - default to something sensible so most don't have to set this
  • actions - actions (buttons) that people can take
  • The body would take html content

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 .

@timja
Copy link
Member

timja commented Feb 27, 2026

I wouldn't wait for a meeting where it may or may not get discussed.

@LakshyaBagani
Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[JENKINS-75222] Create a snooze feature for AdministrativeMonitors

5 participants