diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 391ed0389ea7..08c1cce3c327 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -86,6 +86,7 @@ import hudson.tasks.Publisher; import hudson.tasks.UserAvatarResolver; import hudson.util.Area; +import hudson.util.FormApply; import hudson.util.FormValidation.CheckMethod; import hudson.util.HudsonIsLoading; import hudson.util.HudsonIsRestarting; @@ -217,6 +218,11 @@ public class Functions { public Functions() { } + @Restricted(NoExternalUse.class) + public @CheckForNull FormApply.Notification getFormApplyNotification() { + return FormApply.getAndClearNotification(Stapler.getCurrentRequest2()); + } + /** * Generates an unique ID. */ diff --git a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java index ae90b0428c7b..d1fd4bdcca6d 100644 --- a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java +++ b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java @@ -108,7 +108,7 @@ public synchronized void doConfigure(StaplerRequest2 req, StaplerResponse2 rsp) boolean result = configure(req, json); LOGGER.log(Level.FINE, "security saved: " + result); Jenkins.get().save(); - FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null); + FormApply.success(req.getContextPath() + "/manage/" + getUrlName()).generateResponse(req, rsp, null); } catch (JSONException x) { LOGGER.warning(() -> "Bad JSON:\n" + json.toString(2)); throw x; diff --git a/core/src/main/java/hudson/util/FormApply.java b/core/src/main/java/hudson/util/FormApply.java index 3d5f458ec126..ed6d63931288 100644 --- a/core/src/main/java/hudson/util/FormApply.java +++ b/core/src/main/java/hudson/util/FormApply.java @@ -24,8 +24,10 @@ package hudson.util; +import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.Functions; import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import jenkins.model.Jenkins; import org.kohsuke.stapler.HttpResponses.HttpResponseException; @@ -40,6 +42,9 @@ * @since 1.453 */ public class FormApply { + private static final String NOTIFICATION_MESSAGE_SESSION_ATTRIBUTE = FormApply.class.getName() + ".notificationMessage"; + private static final String NOTIFICATION_TYPE_SESSION_ATTRIBUTE = FormApply.class.getName() + ".notificationType"; + /** * Generates the response for the form submission in such a way that it handles the "apply" button * correctly. @@ -56,6 +61,7 @@ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object n showNotification(Messages.HttpResponses_Saved(), NotificationType.SUCCESS) .generateResponse(req, rsp, node); } else { + setNotificationInSession(req, Messages.HttpResponses_Saved(), NotificationType.SUCCESS); rsp.sendRedirect(destination); } } @@ -127,6 +133,52 @@ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object n }; } + private static void setNotificationInSession(StaplerRequest2 req, String message, NotificationType notificationType) { + HttpSession session = req.getSession(); + session.setAttribute(NOTIFICATION_MESSAGE_SESSION_ATTRIBUTE, message); + session.setAttribute(NOTIFICATION_TYPE_SESSION_ATTRIBUTE, notificationType.name()); + } + + public static @CheckForNull Notification getAndClearNotification(StaplerRequest2 req) { + HttpSession session = req.getSession(false); + if (session == null) { + return null; + } + + String message = (String) session.getAttribute(NOTIFICATION_MESSAGE_SESSION_ATTRIBUTE); + String notificationType = (String) session.getAttribute(NOTIFICATION_TYPE_SESSION_ATTRIBUTE); + session.removeAttribute(NOTIFICATION_MESSAGE_SESSION_ATTRIBUTE); + session.removeAttribute(NOTIFICATION_TYPE_SESSION_ATTRIBUTE); + + if (message == null || notificationType == null) { + return null; + } + + try { + return new Notification(message, NotificationType.valueOf(notificationType)); + } catch (IllegalArgumentException e) { + return null; + } + } + + public static final class Notification { + private final String message; + private final NotificationType notificationType; + + private Notification(String message, NotificationType notificationType) { + this.message = message; + this.notificationType = notificationType; + } + + public String getMessage() { + return message; + } + + public NotificationType getNotificationType() { + return notificationType; + } + } + /** * Corresponds to types declared in index.js diff --git a/core/src/main/java/jenkins/agents/CloudSet.java b/core/src/main/java/jenkins/agents/CloudSet.java index cbd5cc975d89..d458d7ac549c 100644 --- a/core/src/main/java/jenkins/agents/CloudSet.java +++ b/core/src/main/java/jenkins/agents/CloudSet.java @@ -285,7 +285,7 @@ public void doReorder(StaplerRequest2 req, StaplerResponse2 rsp) throws IOExcept var clouds = new ArrayList<>(Jenkins.get().clouds); clouds.sort(Comparator.comparingInt(c -> getIndexOf(namesList, c))); Jenkins.get().clouds.replaceBy(clouds); - FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null); + FormApply.success(req.getContextPath() + "/manage/" + getUrlName()).generateResponse(req, rsp, null); } private static int getIndexOf(List namesList, Cloud cloud) { diff --git a/core/src/main/java/jenkins/appearance/AppearanceGlobalConfiguration.java b/core/src/main/java/jenkins/appearance/AppearanceGlobalConfiguration.java index 1208043b60cd..33d764d0cc04 100644 --- a/core/src/main/java/jenkins/appearance/AppearanceGlobalConfiguration.java +++ b/core/src/main/java/jenkins/appearance/AppearanceGlobalConfiguration.java @@ -101,7 +101,7 @@ public Category getCategory() { public synchronized void doConfigure(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException, Descriptor.FormException { boolean result = configure(req, req.getSubmittedForm()); LOGGER.log(Level.FINE, "appearance saved: " + result); - FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null); + FormApply.success(req.getContextPath() + "/manage/" + getUrlName()).generateResponse(req, rsp, null); } private boolean configure(StaplerRequest2 req, JSONObject json) throws Descriptor.FormException, IOException { diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 7cfac6f3b3c6..f4c009f9e397 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -4045,7 +4045,7 @@ public synchronized void doConfigSubmit(StaplerRequest2 req, StaplerResponse2 rs save(); updateComputers(this); if (result) - FormApply.success(req.getContextPath() + '/').generateResponse(req, rsp, null); + FormApply.success(req.getContextPath() + "/manage/configure").generateResponse(req, rsp, null); else FormApply.success("configure").generateResponse(req, rsp, null); // back to config diff --git a/core/src/main/java/jenkins/model/experimentalflags/NewManageJenkinsUserExperimentalFlag.java b/core/src/main/java/jenkins/model/experimentalflags/NewManageJenkinsUserExperimentalFlag.java index e04f7c60314a..7528aa512cb8 100644 --- a/core/src/main/java/jenkins/model/experimentalflags/NewManageJenkinsUserExperimentalFlag.java +++ b/core/src/main/java/jenkins/model/experimentalflags/NewManageJenkinsUserExperimentalFlag.java @@ -24,6 +24,7 @@ package jenkins.model.experimentalflags; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import hudson.Extension; import org.kohsuke.accmod.Restricted; @@ -46,4 +47,9 @@ public String getDisplayName() { public String getShortDescription() { return "Enables a sidebar for the Manage Jenkins pages for easier navigation."; } + + @Override + public @NonNull Boolean getDefaultValue() { + return true; + } } diff --git a/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java b/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java index 47208f942958..d00906363b09 100644 --- a/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java +++ b/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java @@ -84,7 +84,7 @@ public Category getCategory() { public synchronized void doConfigure(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException, Descriptor.FormException { boolean result = configure(req, req.getSubmittedForm()); LOGGER.log(Level.FINE, "tools saved: " + result); - FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null); + FormApply.success(req.getContextPath() + "/manage/" + getUrlName()).generateResponse(req, rsp, null); } private boolean configure(StaplerRequest2 req, JSONObject json) throws Descriptor.FormException, IOException { diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly index 0e2dac0f47e8..399aade7341e 100644 --- a/core/src/main/resources/lib/layout/layout.jelly +++ b/core/src/main/resources/lib/layout/layout.jelly @@ -91,6 +91,7 @@ THE SOFTWARE. + ${h.advertiseHeaders(response2)} @@ -155,7 +156,9 @@ THE SOFTWARE. + data-search-help-url="${%searchBox.url}" + data-notification-message="${formApplyNotification.message}" + data-notification-type="${formApplyNotification.notificationType}"> diff --git a/core/src/main/resources/lib/layout/settings-subpage.jelly b/core/src/main/resources/lib/layout/settings-subpage.jelly index f77a06e7ddd3..747fa0757228 100644 --- a/core/src/main/resources/lib/layout/settings-subpage.jelly +++ b/core/src/main/resources/lib/layout/settings-subpage.jelly @@ -101,13 +101,13 @@ THE SOFTWARE. +