Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
52 changes: 52 additions & 0 deletions core/src/main/java/hudson/util/FormApply.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -56,6 +61,7 @@
showNotification(Messages.HttpResponses_Saved(), NotificationType.SUCCESS)
.generateResponse(req, rsp, node);
} else {
setNotificationInSession(req, Messages.HttpResponses_Saved(), NotificationType.SUCCESS);
rsp.sendRedirect(destination);
}
}
Expand Down Expand Up @@ -127,6 +133,52 @@
};
}

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) {

Check warning on line 144 in core/src/main/java/hudson/util/FormApply.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 144 is only partially covered, one branch is missing
return null;

Check warning on line 145 in core/src/main/java/hudson/util/FormApply.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 145 is not covered by tests
}

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) {

Check warning on line 153 in core/src/main/java/hudson/util/FormApply.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 153 is only partially covered, one branch is missing
return null;
}

try {
return new Notification(message, NotificationType.valueOf(notificationType));
} catch (IllegalArgumentException e) {
return null;

Check warning on line 160 in core/src/main/java/hudson/util/FormApply.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 159-160 are not covered by tests
}
}

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 <a href="https://github.com/jenkinsci/jenkins/blob/74610e024a6b8fd8feccdc51b8f7741aa6c30e3b/war/src/main/js/components/notifications/index.js#L13-L25">index.js</a>
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/jenkins/agents/CloudSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
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);

Check warning on line 288 in core/src/main/java/jenkins/agents/CloudSet.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 288 is not covered by tests
}

private static int getIndexOf(List<String> namesList, Cloud cloud) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
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);

Check warning on line 104 in core/src/main/java/jenkins/appearance/AppearanceGlobalConfiguration.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 104 is not covered by tests
}

private boolean configure(StaplerRequest2 req, JSONObject json) throws Descriptor.FormException, IOException {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/jenkins/model/Jenkins.java
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/resources/lib/layout/layout.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ THE SOFTWARE.
</j:if>

<j:set var="_" value="${request2.getSession()}"/>
<j:set var="formApplyNotification" value="${h.formApplyNotification}"/>
<j:set var="extensionsAvailable" value="${h.extensionsAvailable}"/>
<j:if test="${request2.servletPath=='/' || request2.servletPath==''}">
${h.advertiseHeaders(response2)}
Expand Down Expand Up @@ -155,7 +156,9 @@ THE SOFTWARE.
</head>
<body id="jenkins" class="${layoutType} jenkins-${h.version}" data-version="${h.version}" data-model-type="${it.class.name}"
data-search-url="${rootURL + '/search/suggest'}"
data-search-help-url="${%searchBox.url}">
data-search-help-url="${%searchBox.url}"
data-notification-message="${formApplyNotification.message}"
data-notification-type="${formApplyNotification.notificationType}">
<l:command-palette />

<j:if test="${layoutType!='full-screen'}">
Expand Down
27 changes: 3 additions & 24 deletions core/src/main/resources/lib/layout/settings-subpage.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ THE SOFTWARE.
<j:choose>
<j:when test="${newManageJenkins}">
<l:side-panel>
<script src="${resURL}/jsbundles/pages/manage-jenkins.js" type="text/javascript" defer="true" />

<l:app-bar title="${manageJenkinsAction.displayName}" />

<l:tasks>
<l:search-bar placeholder="${%Search}" id="settings-search-bar" />

<script src="${resURL}/jsbundles/pages/manage-jenkins.js" type="text/javascript" />

<j:invokeStatic var="j" className="jenkins.model.Jenkins" method="get" />
<j:forEach var="category" items="${j.categorizedManagementLinks.entrySet()}">
<summary class="jenkins-side-nav__heading">
Expand Down Expand Up @@ -139,28 +139,7 @@ THE SOFTWARE.

<j:out value="${attrs.header}" />

<j:choose>
<j:when test="${!attrs.noDefer}">
<l:defer>
<l:defer.placeholder>
<j:choose>
<j:when test="${attrs.containsKey('placeholder')}">
<j:out value="${attrs.placeholder}" />
</j:when>
<j:otherwise>
<l:skeleton type="form" />
</j:otherwise>
</j:choose>
</l:defer.placeholder>
<l:defer.children>
<d:invokeBody />
</l:defer.children>
</l:defer>
</j:when>
<j:otherwise>
<d:invokeBody />
</j:otherwise>
</j:choose>
<d:invokeBody />
</div>
</div>
</l:main-panel>
Expand Down
14 changes: 14 additions & 0 deletions src/main/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,25 @@ import ConfirmationLink from "@/components/confirmation-link";
import Dialogs from "@/components/dialogs";
import Defer from "@/components/defer";

function showPageLoadNotification() {
const { notificationMessage, notificationType } = document.body.dataset;
if (!notificationMessage) {
return;
}

const options =
(notificationType && window.notificationBar[notificationType]) || undefined;
window.notificationBar.show(notificationMessage, options);
delete document.body.dataset.notificationMessage;
delete document.body.dataset.notificationType;
}

AppBar.init();
Dropdowns.init();
CommandPalette.init();
Defer.init();
Notifications.init();
showPageLoadNotification();
SearchBar.init();
Tooltips.init();
StopButtonLink.init();
Expand Down
Loading
Loading