Skip to content

Commit 23ff4bc

Browse files
janfaraciktimja
andauthored
Add experimental Manage Jenkins layout (#11222)
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
1 parent 2a6f83d commit 23ff4bc

File tree

34 files changed

+995
-570
lines changed

34 files changed

+995
-570
lines changed

core/src/main/java/hudson/diagnosis/OldDataMonitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ public String getIconFileName() {
460460

461461
@Override
462462
public String getUrlName() {
463-
return "administrativeMonitor/OldData/";
463+
return "administrativeMonitor/OldData/manage";
464464
}
465465

466466
@Override

core/src/main/java/hudson/model/ManageJenkinsAction.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@
2828
import hudson.Util;
2929
import hudson.util.HudsonIsLoading;
3030
import hudson.util.HudsonIsRestarting;
31+
import jakarta.servlet.ServletException;
3132
import java.io.IOException;
3233
import java.util.logging.Level;
3334
import java.util.logging.Logger;
3435
import jenkins.management.Badge;
3536
import jenkins.model.Jenkins;
3637
import jenkins.model.ModelObjectWithContextMenu;
38+
import jenkins.model.experimentalflags.NewManageJenkinsUserExperimentalFlag;
3739
import org.apache.commons.jelly.JellyException;
3840
import org.jenkinsci.Symbol;
3941
import org.kohsuke.accmod.Restricted;
4042
import org.kohsuke.accmod.restrictions.NoExternalUse;
43+
import org.kohsuke.stapler.HttpRedirect;
4144
import org.kohsuke.stapler.Stapler;
4245
import org.kohsuke.stapler.StaplerFallback;
4346
import org.kohsuke.stapler.StaplerRequest2;
@@ -76,6 +79,21 @@ public boolean isPrimaryAction() {
7679
return true;
7780
}
7881

82+
public HttpRedirect doIndex(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException {
83+
try {
84+
var newUiEnabled = new NewManageJenkinsUserExperimentalFlag().getFlagValue();
85+
86+
if (newUiEnabled) {
87+
return new HttpRedirect("configure");
88+
}
89+
90+
req.getView(this, "index.jelly").forward(req, rsp);
91+
} catch (ServletException e) {
92+
throw new RuntimeException(e);
93+
}
94+
return null;
95+
}
96+
7997
@Override
8098
public Object getStaplerFallback() {
8199
return Jenkins.get();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2025, Jan Faracik
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package jenkins.model.experimentalflags;
26+
27+
import edu.umd.cs.findbugs.annotations.Nullable;
28+
import hudson.Extension;
29+
import org.kohsuke.accmod.Restricted;
30+
import org.kohsuke.accmod.restrictions.NoExternalUse;
31+
32+
@Extension
33+
@Restricted(NoExternalUse.class)
34+
public class NewManageJenkinsUserExperimentalFlag extends BooleanUserExperimentalFlag {
35+
public NewManageJenkinsUserExperimentalFlag() {
36+
super("new-manage-jenkins.flag");
37+
}
38+
39+
@Override
40+
public String getDisplayName() {
41+
return "New Manage Jenkins UI";
42+
}
43+
44+
@Nullable
45+
@Override
46+
public String getShortDescription() {
47+
return "Enables a sidebar for the Manage Jenkins pages for easier navigation.";
48+
}
49+
}

core/src/main/resources/hudson/AboutJenkins/index.jelly

Lines changed: 95 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,9 @@ THE SOFTWARE.
2525
<!-- About Jenkins page -->
2626
<?jelly escape-by-default='true'?>
2727
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
28-
<l:layout type="one-column" permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%about(app.VERSION)}">
29-
<l:header>
30-
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
31-
</l:header>
32-
33-
<l:main-panel>
34-
<div class="app-about-branding">
35-
<div class="app-about-branding__aurora"></div>
36-
<img src="${imagesURL}/svgs/logo.svg" alt="${%logo}" />
37-
</div>
38-
39-
<div class="jenkins-app-bar">
28+
<j:set var="header">
29+
<l:view>
30+
<div class="jenkins-app-bar jenkins-!-margin-top-2 jenkins-!-margin-bottom-4">
4031
<div class="jenkins-app-bar__content">
4132
<h1 class="app-about-heading">Jenkins</h1>
4233
<p class="app-about-version">
@@ -50,93 +41,104 @@ THE SOFTWARE.
5041
</a>
5142
</div>
5243
</div>
44+
</l:view>
45+
</j:set>
5346

54-
<p class="app-about-paragraph">${%blurb}</p>
47+
<l:settings-subpage header="${header}"
48+
placeholder="${null}"
49+
permissions="${app.MANAGE_AND_SYSTEM_READ}">
50+
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
5551

56-
<div class="jenkins-tab-pane">
57-
<h2 class="jenkins-tab-pane__title">${%maven.dependencies}</h2>
58-
<j:set var="uri" value="${it.licensesURL}"/>
59-
<j:choose>
60-
<j:when test="${uri != null}">
61-
<t:thirdPartyLicenses>
62-
<j:include uri="${uri}"/>
63-
</t:thirdPartyLicenses>
64-
</j:when>
65-
<j:otherwise>
66-
<p>${%No information recorded}</p>
67-
</j:otherwise>
68-
</j:choose>
69-
</div>
52+
<div class="app-about-branding">
53+
<div class="app-about-branding__aurora"></div>
54+
<img src="${imagesURL}/svgs/logo.svg" alt="${%logo}" />
55+
</div>
7056

71-
<div class="jenkins-tab-pane">
72-
<h2 class="jenkins-tab-pane__title">${%static.dependencies}</h2>
73-
<table class="jenkins-table sortable">
74-
<thead>
75-
<tr>
76-
<th>${%Name}</th>
77-
<th>${%Author}</th>
78-
<th>${%Licence}</th>
79-
</tr>
80-
</thead>
81-
<tbody>
82-
<tr>
83-
<td>
84-
<a class="jenkins-table__link" href="https://github.com/jenkins-contrib-themes/jenkins-core-theme">
85-
Jenkins Contrib Themes
86-
</a>
87-
</td>
88-
<td>
89-
<a class="jenkins-table__link" href="https://github.com/afonsof">Afonso Franca</a>
90-
</td>
91-
<td>
92-
<a class="jenkins-table__link" href="https://opensource.org/licenses/MIT">MIT License</a>
93-
</td>
94-
</tr>
57+
<p class="jenkins-page-description jenkins-!-text-color">${%blurb}</p>
58+
59+
<div class="jenkins-tab-pane">
60+
<h2 class="jenkins-tab-pane__title">${%maven.dependencies}</h2>
61+
<j:set var="uri" value="${it.licensesURL}"/>
62+
<j:choose>
63+
<j:when test="${uri != null}">
64+
<t:thirdPartyLicenses>
65+
<j:include uri="${uri}"/>
66+
</t:thirdPartyLicenses>
67+
</j:when>
68+
<j:otherwise>
69+
<p>${%No information recorded}</p>
70+
</j:otherwise>
71+
</j:choose>
72+
</div>
73+
74+
<div class="jenkins-tab-pane">
75+
<h2 class="jenkins-tab-pane__title">${%static.dependencies}</h2>
76+
<table class="jenkins-table sortable">
77+
<thead>
78+
<tr>
79+
<th>${%Name}</th>
80+
<th>${%Author}</th>
81+
<th>${%Licence}</th>
82+
</tr>
83+
</thead>
84+
<tbody>
85+
<tr>
86+
<td>
87+
<a class="jenkins-table__link" href="https://github.com/jenkins-contrib-themes/jenkins-core-theme">
88+
Jenkins Contrib Themes
89+
</a>
90+
</td>
91+
<td>
92+
<a class="jenkins-table__link" href="https://github.com/afonsof">Afonso Franca</a>
93+
</td>
94+
<td>
95+
<a class="jenkins-table__link" href="https://opensource.org/licenses/MIT">MIT License</a>
96+
</td>
97+
</tr>
98+
<tr>
99+
<td>
100+
<a class="jenkins-table__link" href="https://ionic.io/ionicons">Ionicons</a>
101+
</td>
102+
<td>
103+
<a class="jenkins-table__link" href="https://github.com/ionic-team">Ionic</a>
104+
</td>
105+
<td>
106+
<a class="jenkins-table__link" href="https://github.com/ionic-team/ionicons/blob/master/LICENSE">MIT
107+
License
108+
</a>
109+
</td>
110+
</tr>
111+
</tbody>
112+
</table>
113+
</div>
114+
115+
<div class="jenkins-tab-pane">
116+
<h2 class="jenkins-tab-pane__title">${%plugin.dependencies}</h2>
117+
<table class="jenkins-table sortable">
118+
<thead>
119+
<tr>
120+
<th>${%Name}</th>
121+
</tr>
122+
</thead>
123+
<tbody>
124+
<j:forEach var="p" items="${app.pluginManager.plugins}">
95125
<tr>
96126
<td>
97-
<a class="jenkins-table__link" href="https://ionic.io/ionicons">Ionicons</a>
98-
</td>
99-
<td>
100-
<a class="jenkins-table__link" href="https://github.com/ionic-team">Ionic</a>
101-
</td>
102-
<td>
103-
<a class="jenkins-table__link" href="https://github.com/ionic-team/ionicons/blob/master/LICENSE">MIT
104-
License
127+
<a class="jenkins-table__link" href="${rootURL}/plugin/${p.shortName}/wrapper/thirdPartyLicenses">
128+
<j:choose>
129+
<j:when test="${p.active}">
130+
${p.displayName}
131+
</j:when>
132+
<j:otherwise>
133+
<strike>${p.displayName}</strike>
134+
</j:otherwise>
135+
</j:choose>
105136
</a>
106137
</td>
107138
</tr>
108-
</tbody>
109-
</table>
110-
</div>
111-
112-
<div class="jenkins-tab-pane">
113-
<h2 class="jenkins-tab-pane__title">${%plugin.dependencies}</h2>
114-
<table class="jenkins-table sortable">
115-
<thead>
116-
<tr>
117-
<th>${%Name}</th>
118-
</tr>
119-
</thead>
120-
<tbody>
121-
<j:forEach var="p" items="${app.pluginManager.plugins}">
122-
<tr>
123-
<td>
124-
<a class="jenkins-table__link" href="${rootURL}/plugin/${p.shortName}/wrapper/thirdPartyLicenses">
125-
<j:choose>
126-
<j:when test="${p.active}">
127-
${p.displayName}
128-
</j:when>
129-
<j:otherwise>
130-
<strike>${p.displayName}</strike>
131-
</j:otherwise>
132-
</j:choose>
133-
</a>
134-
</td>
135-
</tr>
136-
</j:forEach>
137-
</tbody>
138-
</table>
139-
</div>
140-
</l:main-panel>
141-
</l:layout>
139+
</j:forEach>
140+
</tbody>
141+
</table>
142+
</div>
143+
</l:settings-subpage>
142144
</j:jelly>

core/src/main/resources/hudson/cli/CLIAction/index.jelly

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@ THE SOFTWARE.
2424

2525
<?jelly escape-by-default='true'?>
2626
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
27-
<j:new var="managementLink" className="jenkins.management.CliLink" />
28-
29-
<l:layout title="${managementLink.displayName}" permission="${app.READ}" type="one-column">
30-
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
31-
32-
<l:main-panel>
33-
<l:app-bar title="${managementLink.displayName}">
27+
<j:set var="header">
28+
<l:view>
29+
<l:app-bar title="${it.displayName}">
3430
<t:help href="https://www.jenkins.io/redirect/cli" />
3531
</l:app-bar>
3632

3733
<div class="jenkins-page-description">
3834
${%description}
3935
</div>
36+
</l:view>
37+
</j:set>
4038

39+
<l:settings-subpage header="${header}" permission="${app.READ}">
40+
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
4141
<f:section title="${%Getting started}">
4242
<ol class="jenkins-instructions">
4343
<li>
@@ -79,6 +79,5 @@ THE SOFTWARE.
7979
</tbody>
8080
</table>
8181
</f:section>
82-
</l:main-panel>
83-
</l:layout>
82+
</l:settings-subpage>
8483
</j:jelly>

core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ THE SOFTWARE.
2424

2525
<?jelly escape-by-default='true'?>
2626
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
27-
<l:layout title="${%Manage Old Data}" type="one-column">
28-
<!-- no need for additional breadcrumb here as we're on an "index" page already including breadcrumb -->
29-
<l:main-panel>
30-
<h1>${%Manage Old Data}</h1>
31-
<p>${%blurb.1}</p>
27+
<j:set var="previousIt" value="${it}" />
28+
<j:new var="it" className="hudson.diagnosis.OldDataMonitor$ManagementLinkImpl" />
29+
30+
<l:settings-subpage>
31+
<j:set var="it" value="${previousIt}" />
32+
33+
<p class="jenkins-!-margin-top-0">${%blurb.1}</p>
3234
<p>${%blurb.2}</p>
3335

3436
<!-- First set a flag to check if we'll have any valid data -->
@@ -132,6 +134,5 @@ THE SOFTWARE.
132134
<f:submit value="${%Discard Unreadable Data}"/>
133135
</form>
134136
</j:if>
135-
</l:main-panel>
136-
</l:layout>
137+
</l:settings-subpage>
137138
</j:jelly>

0 commit comments

Comments
 (0)