mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-10-26 19:15:09 +00:00
Add experimental Manage Jenkins layout (#11222)
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
This commit is contained in:
1 parent
2a6f83dccb
commit
23ff4bcb63
34 files changed
+1001
-576
No files matched your search
@@ -460,7 +460,7 @@ public class OldDataMonitor extends AdministrativeMonitor {
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "administrativeMonitor/OldData/";
|
||||
return "administrativeMonitor/OldData/manage";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,16 +28,19 @@ import hudson.Extension;
|
||||
import hudson.Util;
|
||||
import hudson.util.HudsonIsLoading;
|
||||
import hudson.util.HudsonIsRestarting;
|
||||
import jakarta.servlet.ServletException;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.management.Badge;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.ModelObjectWithContextMenu;
|
||||
import jenkins.model.experimentalflags.NewManageJenkinsUserExperimentalFlag;
|
||||
import org.apache.commons.jelly.JellyException;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
import org.kohsuke.stapler.Stapler;
|
||||
import org.kohsuke.stapler.StaplerFallback;
|
||||
import org.kohsuke.stapler.StaplerRequest2;
|
||||
@@ -76,6 +79,21 @@ public class ManageJenkinsAction implements RootAction, StaplerFallback, ModelOb
|
||||
return true;
|
||||
}
|
||||
|
||||
public HttpRedirect doIndex(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException {
|
||||
try {
|
||||
var newUiEnabled = new NewManageJenkinsUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (newUiEnabled) {
|
||||
return new HttpRedirect("configure");
|
||||
}
|
||||
|
||||
req.getView(this, "index.jelly").forward(req, rsp);
|
||||
} catch (ServletException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getStaplerFallback() {
|
||||
return Jenkins.get();
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model.experimentalflags;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.Extension;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class NewManageJenkinsUserExperimentalFlag extends BooleanUserExperimentalFlag {
|
||||
public NewManageJenkinsUserExperimentalFlag() {
|
||||
super("new-manage-jenkins.flag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "New Manage Jenkins UI";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return "Enables a sidebar for the Manage Jenkins pages for easier navigation.";
|
||||
}
|
||||
}
|
||||
@@ -25,18 +25,9 @@ THE SOFTWARE.
|
||||
<!-- About Jenkins page -->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
|
||||
<l:layout type="one-column" permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%about(app.VERSION)}">
|
||||
<l:header>
|
||||
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
|
||||
</l:header>
|
||||
|
||||
<l:main-panel>
|
||||
<div class="app-about-branding">
|
||||
<div class="app-about-branding__aurora"></div>
|
||||
<img src="${imagesURL}/svgs/logo.svg" alt="${%logo}" />
|
||||
</div>
|
||||
|
||||
<div class="jenkins-app-bar">
|
||||
<j:set var="header">
|
||||
<l:view>
|
||||
<div class="jenkins-app-bar jenkins-!-margin-top-2 jenkins-!-margin-bottom-4">
|
||||
<div class="jenkins-app-bar__content">
|
||||
<h1 class="app-about-heading">Jenkins</h1>
|
||||
<p class="app-about-version">
|
||||
@@ -50,93 +41,104 @@ THE SOFTWARE.
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<p class="app-about-paragraph">${%blurb}</p>
|
||||
<l:settings-subpage header="${header}"
|
||||
placeholder="${null}"
|
||||
permissions="${app.MANAGE_AND_SYSTEM_READ}">
|
||||
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%maven.dependencies}</h2>
|
||||
<j:set var="uri" value="${it.licensesURL}"/>
|
||||
<j:choose>
|
||||
<j:when test="${uri != null}">
|
||||
<t:thirdPartyLicenses>
|
||||
<j:include uri="${uri}"/>
|
||||
</t:thirdPartyLicenses>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%No information recorded}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</div>
|
||||
<div class="app-about-branding">
|
||||
<div class="app-about-branding__aurora"></div>
|
||||
<img src="${imagesURL}/svgs/logo.svg" alt="${%logo}" />
|
||||
</div>
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%static.dependencies}</h2>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${%Name}</th>
|
||||
<th>${%Author}</th>
|
||||
<th>${%Licence}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<p class="jenkins-page-description jenkins-!-text-color">${%blurb}</p>
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%maven.dependencies}</h2>
|
||||
<j:set var="uri" value="${it.licensesURL}"/>
|
||||
<j:choose>
|
||||
<j:when test="${uri != null}">
|
||||
<t:thirdPartyLicenses>
|
||||
<j:include uri="${uri}"/>
|
||||
</t:thirdPartyLicenses>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%No information recorded}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</div>
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%static.dependencies}</h2>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${%Name}</th>
|
||||
<th>${%Author}</th>
|
||||
<th>${%Licence}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/jenkins-contrib-themes/jenkins-core-theme">
|
||||
Jenkins Contrib Themes
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/afonsof">Afonso Franca</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://opensource.org/licenses/MIT">MIT License</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://ionic.io/ionicons">Ionicons</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/ionic-team">Ionic</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/ionic-team/ionicons/blob/master/LICENSE">MIT
|
||||
License
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%plugin.dependencies}</h2>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${%Name}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="p" items="${app.pluginManager.plugins}">
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/jenkins-contrib-themes/jenkins-core-theme">
|
||||
Jenkins Contrib Themes
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/afonsof">Afonso Franca</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://opensource.org/licenses/MIT">MIT License</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://ionic.io/ionicons">Ionicons</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/ionic-team">Ionic</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="https://github.com/ionic-team/ionicons/blob/master/LICENSE">MIT
|
||||
License
|
||||
<a class="jenkins-table__link" href="${rootURL}/plugin/${p.shortName}/wrapper/thirdPartyLicenses">
|
||||
<j:choose>
|
||||
<j:when test="${p.active}">
|
||||
${p.displayName}
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<strike>${p.displayName}</strike>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="jenkins-tab-pane">
|
||||
<h2 class="jenkins-tab-pane__title">${%plugin.dependencies}</h2>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${%Name}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="p" items="${app.pluginManager.plugins}">
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="${rootURL}/plugin/${p.shortName}/wrapper/thirdPartyLicenses">
|
||||
<j:choose>
|
||||
<j:when test="${p.active}">
|
||||
${p.displayName}
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<strike>${p.displayName}</strike>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -24,20 +24,20 @@ THE SOFTWARE.
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<j:new var="managementLink" className="jenkins.management.CliLink" />
|
||||
|
||||
<l:layout title="${managementLink.displayName}" permission="${app.READ}" type="one-column">
|
||||
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
|
||||
|
||||
<l:main-panel>
|
||||
<l:app-bar title="${managementLink.displayName}">
|
||||
<j:set var="header">
|
||||
<l:view>
|
||||
<l:app-bar title="${it.displayName}">
|
||||
<t:help href="https://www.jenkins.io/redirect/cli" />
|
||||
</l:app-bar>
|
||||
|
||||
<div class="jenkins-page-description">
|
||||
${%description}
|
||||
</div>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<l:settings-subpage header="${header}" permission="${app.READ}">
|
||||
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
|
||||
<f:section title="${%Getting started}">
|
||||
<ol class="jenkins-instructions">
|
||||
<li>
|
||||
@@ -79,6 +79,5 @@ THE SOFTWARE.
|
||||
</tbody>
|
||||
</table>
|
||||
</f:section>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -24,11 +24,13 @@ THE SOFTWARE.
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<l:layout title="${%Manage Old Data}" type="one-column">
|
||||
<!-- no need for additional breadcrumb here as we're on an "index" page already including breadcrumb -->
|
||||
<l:main-panel>
|
||||
<h1>${%Manage Old Data}</h1>
|
||||
<p>${%blurb.1}</p>
|
||||
<j:set var="previousIt" value="${it}" />
|
||||
<j:new var="it" className="hudson.diagnosis.OldDataMonitor$ManagementLinkImpl" />
|
||||
|
||||
<l:settings-subpage>
|
||||
<j:set var="it" value="${previousIt}" />
|
||||
|
||||
<p class="jenkins-!-margin-top-0">${%blurb.1}</p>
|
||||
<p>${%blurb.2}</p>
|
||||
|
||||
<!-- First set a flag to check if we'll have any valid data -->
|
||||
@@ -132,6 +134,5 @@ THE SOFTWARE.
|
||||
<f:submit value="${%Discard Unreadable Data}"/>
|
||||
</form>
|
||||
</j:if>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -27,22 +27,30 @@ THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<l:layout title="${%Log Recorders}" type="one-column">
|
||||
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
|
||||
<l:main-panel xmlns:local="local">
|
||||
<l:app-bar title="${%Log Recorders}">
|
||||
<l:isAdmin>
|
||||
<a href="new" class="jenkins-button jenkins-button--primary">
|
||||
<l:icon src="symbol-add" />
|
||||
${%Add recorder}
|
||||
</a>
|
||||
</l:isAdmin>
|
||||
<a href="levels" class="jenkins-button">
|
||||
${%Log levels}
|
||||
</a>
|
||||
<t:help tooltip="${%Additional information on log recorders}" href="https://www.jenkins.io/redirect/log-recorders" />
|
||||
</l:app-bar>
|
||||
<j:set var="header">
|
||||
<j:new var="it" className="jenkins.management.SystemLogLink" />
|
||||
|
||||
<l:view>
|
||||
<l:app-bar title="${it.displayName}">
|
||||
<l:isAdmin>
|
||||
<a href="new" class="jenkins-button jenkins-button--primary">
|
||||
<l:icon src="symbol-add" />
|
||||
${%Add recorder}
|
||||
</a>
|
||||
</l:isAdmin>
|
||||
<a href="levels" class="jenkins-button">
|
||||
${%Log levels}
|
||||
</a>
|
||||
<t:help tooltip="${%Additional information on log recorders}" href="https://www.jenkins.io/redirect/log-recorders" />
|
||||
</l:app-bar>
|
||||
|
||||
<div class="jenkins-page-description">
|
||||
<j:out value="${it.description}" />
|
||||
</div>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<l:settings-subpage header="${header}" xmlns:local="local">
|
||||
<d:taglib uri="local">
|
||||
<d:tag name="row">
|
||||
<tr>
|
||||
@@ -76,6 +84,5 @@ THE SOFTWARE.
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -27,32 +27,40 @@ THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:s="/lib/form">
|
||||
<l:layout title="${%Nodes}" type="one-column">
|
||||
<!-- no need for additional breadcrumb here as we're on an index page already including breadcrumb -->
|
||||
<l:main-panel>
|
||||
<l:app-bar title="${%Nodes}">
|
||||
<j:getStatic var="createPermission" className="hudson.model.Computer" field="CREATE"/>
|
||||
<j:if test="${h.hasPermission(createPermission)}">
|
||||
<a class="jenkins-button jenkins-button--primary" href="new">
|
||||
<l:icon src="symbol-add" />
|
||||
${%New Node}
|
||||
</a>
|
||||
</j:if>
|
||||
<j:set var="hasPermission" value="${h.hasAnyPermission(it, app.MANAGE_AND_SYSTEM_READ)}" />
|
||||
<j:if test="${hasPermission}">
|
||||
<a class="jenkins-button" href="configure" tooltip="${%Configure Node Monitors}">
|
||||
${%Configure Monitors}
|
||||
</a>
|
||||
</j:if>
|
||||
<l:hasAdministerOrManage>
|
||||
<form method="post" action="updateNow" class="jenkins-!-display-contents">
|
||||
<button tooltip="${%Refresh status}" class="jenkins-button">
|
||||
<l:icon src="symbol-refresh" />
|
||||
</button>
|
||||
</form>
|
||||
</l:hasAdministerOrManage>
|
||||
</l:app-bar>
|
||||
<j:set var="header">
|
||||
<l:view>
|
||||
<j:new var="it" className="jenkins.management.NodesLink" />
|
||||
|
||||
<l:app-bar title="${it.displayName}">
|
||||
<j:getStatic var="createPermission" className="hudson.model.Computer" field="CREATE"/>
|
||||
<j:if test="${h.hasPermission(createPermission)}">
|
||||
<a class="jenkins-button jenkins-button--primary" href="new">
|
||||
<l:icon src="symbol-add" />
|
||||
${%New Node}
|
||||
</a>
|
||||
</j:if>
|
||||
<j:set var="hasPermission" value="${h.hasAnyPermission(it, app.MANAGE_AND_SYSTEM_READ)}" />
|
||||
<j:if test="${hasPermission}">
|
||||
<a class="jenkins-button" href="configure" tooltip="${%Configure Node Monitors}">
|
||||
${%Configure Monitors}
|
||||
</a>
|
||||
</j:if>
|
||||
<l:hasAdministerOrManage>
|
||||
<form method="post" action="updateNow" class="jenkins-!-display-contents">
|
||||
<button tooltip="${%Refresh status}" class="jenkins-button">
|
||||
<l:icon src="symbol-refresh" />
|
||||
</button>
|
||||
</form>
|
||||
</l:hasAdministerOrManage>
|
||||
</l:app-bar>
|
||||
|
||||
<div class="jenkins-page-description">
|
||||
${it.description}
|
||||
</div>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<l:settings-subpage header="${header}">
|
||||
<j:set var="monitors" value="${it._monitors}"/>
|
||||
<j:set var="tableWidth" value="${3}"/>
|
||||
<t:setIconSize/>
|
||||
@@ -133,6 +141,5 @@ THE SOFTWARE.
|
||||
<st:include page="_legend.jelly"/>
|
||||
</template>
|
||||
<script src="${resURL}/jsbundles/pages/computer-set.js" type="text/javascript" defer="true" />
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -24,11 +24,12 @@ THE SOFTWARE.
|
||||
|
||||
<!-- renders an HTML fragment that shows trend graph -->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:f="/lib/form">
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:f="/lib/form" xmlns:l="/lib/layout">
|
||||
<st:adjunct includes="hudson.model.LoadStatistics.resources"/>
|
||||
<h1>
|
||||
${%title(it.displayName)}
|
||||
</h1>
|
||||
|
||||
<j:if test="${it != app}">
|
||||
<l:app-bar title="${%title(it.displayName)}" />
|
||||
</j:if>
|
||||
|
||||
<f:entry title="${%Timespan}" class="jenkins-form-item--small">
|
||||
<div class="jenkins-select">
|
||||
|
||||
@@ -10,64 +10,60 @@ def f=namespace(lib.FormTagLib)
|
||||
def l=namespace(lib.LayoutTagLib)
|
||||
def st=namespace("jelly:stapler")
|
||||
|
||||
l.layout(permission:app.SYSTEM_READ, title:my.displayName, cssclass:request2.getParameter('decorate'), type:"one-column") {
|
||||
l.main_panel {
|
||||
l.app_bar(title: my.displayName)
|
||||
l.'settings-subpage'(permission: app.SYSTEM_READ) {
|
||||
set("readOnlyMode", !app.hasPermission(app.ADMINISTER))
|
||||
|
||||
set("readOnlyMode", !app.hasPermission(app.ADMINISTER))
|
||||
l.skeleton()
|
||||
f.form(method:"post", name:"config", action:"configure", class: "jenkins-form") {
|
||||
set("instance", my)
|
||||
set("descriptor", my.descriptor)
|
||||
|
||||
l.skeleton()
|
||||
f.form(method:"post", name:"config", action:"configure", class: "jenkins-form") {
|
||||
set("instance", my)
|
||||
set("descriptor", my.descriptor)
|
||||
|
||||
f.section(title:_("Authentication")) {
|
||||
f.entry(help: '/descriptor/hudson.security.GlobalSecurityConfiguration/help/disableRememberMe') {
|
||||
f.checkbox(title:_("Disable remember me"), field: "disableRememberMe")
|
||||
}
|
||||
f.dropdownDescriptorSelector(title: _("Security Realm"), field: 'securityRealm', descriptors: h.filterDescriptors(app, SecurityRealm.all()))
|
||||
f.dropdownDescriptorSelector(title: _("Authorization"), field: 'authorizationStrategy', descriptors: h.filterDescriptors(app, AuthorizationStrategy.all()))
|
||||
f.section(title:_("Authentication")) {
|
||||
f.entry(help: '/descriptor/hudson.security.GlobalSecurityConfiguration/help/disableRememberMe') {
|
||||
f.checkbox(title:_("Disable remember me"), field: "disableRememberMe")
|
||||
}
|
||||
f.dropdownDescriptorSelector(title: _("Security Realm"), field: 'securityRealm', descriptors: h.filterDescriptors(app, SecurityRealm.all()))
|
||||
f.dropdownDescriptorSelector(title: _("Authorization"), field: 'authorizationStrategy', descriptors: h.filterDescriptors(app, AuthorizationStrategy.all()))
|
||||
}
|
||||
|
||||
f.section(title: _("Markup Formatter")) {
|
||||
f.dropdownDescriptorSelector(title:_("Markup Formatter"), descriptors: MarkupFormatterDescriptor.all(), field: 'markupFormatter')
|
||||
}
|
||||
f.section(title: _("Markup Formatter")) {
|
||||
f.dropdownDescriptorSelector(title:_("Markup Formatter"), descriptors: MarkupFormatterDescriptor.all(), field: 'markupFormatter')
|
||||
}
|
||||
|
||||
f.section(title: _("Agents")) {
|
||||
f.entry(title: _("TCP port for inbound agents"), field: "slaveAgentPort") {
|
||||
if (my.slaveAgentPortEnforced) {
|
||||
if (my.slaveAgentPort == -1) {
|
||||
text(_("slaveAgentPortEnforcedDisabled"))
|
||||
} else if (my.slaveAgentPort == 0) {
|
||||
text(_("slaveAgentPortEnforcedRandom"))
|
||||
} else {
|
||||
text(_("slaveAgentPortEnforced", my.slaveAgentPort))
|
||||
}
|
||||
f.section(title: _("Agents")) {
|
||||
f.entry(title: _("TCP port for inbound agents"), field: "slaveAgentPort") {
|
||||
if (my.slaveAgentPortEnforced) {
|
||||
if (my.slaveAgentPort == -1) {
|
||||
text(_("slaveAgentPortEnforcedDisabled"))
|
||||
} else if (my.slaveAgentPort == 0) {
|
||||
text(_("slaveAgentPortEnforcedRandom"))
|
||||
} else {
|
||||
f.serverTcpPort()
|
||||
text(_("slaveAgentPortEnforced", my.slaveAgentPort))
|
||||
}
|
||||
} else {
|
||||
f.serverTcpPort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Functions.getSortedDescriptorsForGlobalConfigByDescriptor(my.FILTER).each { Descriptor descriptor ->
|
||||
set("descriptor", descriptor)
|
||||
set("instance", descriptor)
|
||||
f.rowSet(name:descriptor.jsonSafeClassName) {
|
||||
st.include(from:descriptor, page:descriptor.globalConfigPage)
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _("Save"))
|
||||
f.apply()
|
||||
}
|
||||
Functions.getSortedDescriptorsForGlobalConfigByDescriptor(my.FILTER).each { Descriptor descriptor ->
|
||||
set("descriptor", descriptor)
|
||||
set("instance", descriptor)
|
||||
f.rowSet(name:descriptor.jsonSafeClassName) {
|
||||
st.include(from:descriptor, page:descriptor.globalConfigPage)
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
st.adjunct(includes: "lib.form.confirm")
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _("Save"))
|
||||
f.apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
st.adjunct(includes: "lib.form.confirm")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
|
||||
<l:layout permission="${app.ADMINISTER}" title="${%Users}" type="one-column">
|
||||
<l:main-panel>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
|
||||
<j:set var="header">
|
||||
<l:view>
|
||||
<l:app-bar title="${%Users}" subtitle="${it.allUsers.size()}">
|
||||
<l:isAdmin>
|
||||
<a href="addUser" class="jenkins-button jenkins-button--primary">
|
||||
@@ -35,53 +35,55 @@ THE SOFTWARE.
|
||||
</l:isAdmin>
|
||||
</l:app-bar>
|
||||
|
||||
<p class="jenkins-description">${%blurb}</p>
|
||||
<p class="jenkins-page-description">${%blurb}</p>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<table class="jenkins-table sortable" id="people">
|
||||
<thead>
|
||||
<l:settings-subpage header="${header}" permission="${app.ADMINISTER}">
|
||||
<table class="jenkins-table sortable" id="people">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
<th initialSortDir="down">${%User ID}</th>
|
||||
<th>${%Name}</th>
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="user" items="${it.allUsers}">
|
||||
<tr>
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
<th initialSortDir="down">${%User ID}</th>
|
||||
<th>${%Name}</th>
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
|
||||
<td class="jenkins-table__cell--tight jenkins-table__icon">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<l:icon src="${h.getUserAvatar(user, '64x64')}" class="jenkins-avatar" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="${user.url}/" class="jenkins-table__link model-link inside">${user.id}</a>
|
||||
</td>
|
||||
<td>
|
||||
${user.fullName}
|
||||
</td>
|
||||
<td>
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<a href="${user.url}/" class="jenkins-button jenkins-button--tertiary">
|
||||
<l:icon src="symbol-settings" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<j:if test="${user.canDelete()}">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<l:confirmationLink href="${user.url}/doDelete" class="jenkins-button jenkins-button--tertiary jenkins-!-destructive-color"
|
||||
post="true" message="${%delete.user(user.displayName)}">
|
||||
<l:icon src="symbol-trash" />
|
||||
</l:confirmationLink>
|
||||
</div>
|
||||
</j:if>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="user" items="${it.allUsers}">
|
||||
<tr>
|
||||
<td class="jenkins-table__cell--tight jenkins-table__icon">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<l:icon src="${h.getUserAvatar(user, '64x64')}" class="jenkins-avatar" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="${user.url}/" class="jenkins-table__link model-link inside">${user.id}</a>
|
||||
</td>
|
||||
<td>
|
||||
${user.fullName}
|
||||
</td>
|
||||
<td>
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<a href="${user.url}/" class="jenkins-button jenkins-button--tertiary">
|
||||
<l:icon src="symbol-settings" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<j:if test="${user.canDelete()}">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<l:confirmationLink href="${user.url}/doDelete" class="jenkins-button jenkins-button--tertiary jenkins-!-destructive-color"
|
||||
post="true" message="${%delete.user(user.displayName)}">
|
||||
<l:icon src="symbol-trash" />
|
||||
</l:confirmationLink>
|
||||
</div>
|
||||
</j:if>
|
||||
</td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -27,124 +27,126 @@ THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form">
|
||||
<l:layout title="${it.displayName}" type="one-column">
|
||||
<l:isAdmin>
|
||||
<l:header>
|
||||
<script src="${resURL}/jsbundles/pages/cloud-set.js" type="text/javascript" />
|
||||
<link rel="stylesheet" href="${resURL}/jsbundles/pages/cloud-set.css" type="text/css" />
|
||||
</l:header>
|
||||
</l:isAdmin>
|
||||
<l:main-panel>
|
||||
<j:set var="readOnlyMode" value="${!app.hasPermission(app.ADMINISTER)}"/>
|
||||
<j:choose>
|
||||
<j:when test="${it.cloudAvailable and it.hasClouds()}">
|
||||
<l:app-bar title="${%Clouds}">
|
||||
<l:isAdmin>
|
||||
<a class="jenkins-button jenkins-button--primary" href="new">
|
||||
<l:icon src="symbol-add"/>
|
||||
${%newCloud}
|
||||
</a>
|
||||
</l:isAdmin>
|
||||
</l:app-bar>
|
||||
<p class="description">${%description}</p>
|
||||
<f:form method="post" name="config" action="reorder">
|
||||
<table id="clouds" class="jenkins-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th tooltip="${%Drag and drop cells to reorder}">${%Order}</th>
|
||||
<th>${%Name}</th>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="with-drag-drop">
|
||||
<j:forEach var="cloud" items="${it.clouds}">
|
||||
<tr class="repeated-chunk" id="cloud_${cloud.name}">
|
||||
<td data="${cloud.icon}" class="jenkins-table__cell--tight jenkins-table__icon">
|
||||
<div class="jenkins-table__cell__button-wrapper dd-handle">
|
||||
<l:icon src="${cloud.iconClassName}" tooltip="${cloud.iconAltText}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="name" value="${cloud.name}" />
|
||||
<a href="${it.getCloudUrl(request2,app,cloud)}" class="jenkins-table__link model-link inside">${cloud.name}</a>
|
||||
</td>
|
||||
<td class="jenkins-table__cell--tight">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<a href="${it.getCloudUrl(request2,app,cloud)}configure" class="jenkins-button">
|
||||
<l:icon src="symbol-settings"/>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
<l:isAdmin>
|
||||
<f:bottomButtonBar>
|
||||
<f:submit id="saveButton" value="${%Save}" clazz="jenkins-hidden" />
|
||||
</f:bottomButtonBar>
|
||||
</l:isAdmin>
|
||||
</f:form>
|
||||
<st:adjunct includes="lib.form.confirm" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<div class="empty-state-block">
|
||||
<l:app-bar title="${%Clouds}"/>
|
||||
<j:set var="previousIt" value="${it}" />
|
||||
<j:new var="it" className="jenkins.agents.CloudsLink" />
|
||||
|
||||
<section class="empty-state-section">
|
||||
<j:choose>
|
||||
<j:when test="${it.cloudAvailable}">
|
||||
<j:choose>
|
||||
<j:when test="${h.hasPermission(app.ADMINISTER)}">
|
||||
<p>${%noCloudAvailableCTA}</p>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%noCloudAvailable}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%noCloudPlugin}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
<ul class="empty-state-section-list">
|
||||
<l:isAdmin>
|
||||
<j:if test="${it.cloudAvailable}">
|
||||
<li class="content-block">
|
||||
<a href="new"
|
||||
class="content-block__link">
|
||||
<span>${%newCloud}</span>
|
||||
<span class="trailing-icon">
|
||||
<l:icon src="symbol-add"/>
|
||||
</span>
|
||||
<l:settings-subpage header="${null}">
|
||||
<j:set var="it" value="${previousIt}" />
|
||||
|
||||
<l:isAdmin>
|
||||
<script src="${resURL}/jsbundles/pages/cloud-set.js" type="text/javascript" />
|
||||
<link rel="stylesheet" href="${resURL}/jsbundles/pages/cloud-set.css" type="text/css" />
|
||||
</l:isAdmin>
|
||||
|
||||
<j:set var="readOnlyMode" value="${!app.hasPermission(app.ADMINISTER)}"/>
|
||||
<j:choose>
|
||||
<j:when test="${it.cloudAvailable and it.hasClouds()}">
|
||||
<l:app-bar title="${%Clouds}">
|
||||
<l:isAdmin>
|
||||
<a class="jenkins-button jenkins-button--primary" href="new">
|
||||
<l:icon src="symbol-add"/>
|
||||
${%newCloud}
|
||||
</a>
|
||||
</l:isAdmin>
|
||||
</l:app-bar>
|
||||
<p class="description">${%description}</p>
|
||||
<f:form method="post" name="config" action="reorder">
|
||||
<table id="clouds" class="jenkins-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th tooltip="${%Drag and drop cells to reorder}">${%Order}</th>
|
||||
<th>${%Name}</th>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="with-drag-drop">
|
||||
<j:forEach var="cloud" items="${it.clouds}">
|
||||
<tr class="repeated-chunk" id="cloud_${cloud.name}">
|
||||
<td data="${cloud.icon}" class="jenkins-table__cell--tight jenkins-table__icon">
|
||||
<div class="jenkins-table__cell__button-wrapper dd-handle">
|
||||
<l:icon src="${cloud.iconClassName}" tooltip="${cloud.iconAltText}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="name" value="${cloud.name}" />
|
||||
<a href="${it.getCloudUrl(request2,app,cloud)}" class="jenkins-table__link model-link inside">${cloud.name}</a>
|
||||
</td>
|
||||
<td class="jenkins-table__cell--tight">
|
||||
<div class="jenkins-table__cell__button-wrapper">
|
||||
<a href="${it.getCloudUrl(request2,app,cloud)}configure" class="jenkins-button">
|
||||
<l:icon src="symbol-settings"/>
|
||||
</a>
|
||||
</li>
|
||||
</j:if>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
<l:isAdmin>
|
||||
<f:bottomButtonBar>
|
||||
<f:submit id="saveButton" value="${%Save}" clazz="jenkins-hidden" />
|
||||
</f:bottomButtonBar>
|
||||
</l:isAdmin>
|
||||
</f:form>
|
||||
<st:adjunct includes="lib.form.confirm" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<div class="empty-state-block">
|
||||
<l:app-bar title="${%Clouds}"/>
|
||||
|
||||
<section class="empty-state-section">
|
||||
<j:choose>
|
||||
<j:when test="${it.cloudAvailable}">
|
||||
<j:choose>
|
||||
<j:when test="${h.hasPermission(app.ADMINISTER)}">
|
||||
<p>${%noCloudAvailableCTA}</p>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%noCloudAvailable}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<p>${%noCloudPlugin}</p>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
<ul class="empty-state-section-list">
|
||||
<l:isAdmin>
|
||||
<j:if test="${it.cloudAvailable}">
|
||||
<li class="content-block">
|
||||
<a href="${rootURL}/manage/pluginManager/available?filter=${it.cloudUpdateCenterCategoryLabel}"
|
||||
<a href="new"
|
||||
class="content-block__link">
|
||||
<span>${%installCloudPlugin}</span>
|
||||
<span>${%newCloud}</span>
|
||||
<span class="trailing-icon">
|
||||
<l:icon src="symbol-plugins" />
|
||||
<l:icon src="symbol-add"/>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</l:isAdmin>
|
||||
</j:if>
|
||||
<li class="content-block">
|
||||
<a href="https://www.jenkins.io/redirect/distributed-builds"
|
||||
class="content-block__link"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
<span>${%learnMoreDistributedBuilds}</span>
|
||||
<a href="${rootURL}/manage/pluginManager/available?filter=${it.cloudUpdateCenterCategoryLabel}"
|
||||
class="content-block__link">
|
||||
<span>${%installCloudPlugin}</span>
|
||||
<span class="trailing-icon">
|
||||
<l:icon src="symbol-help-circle" />
|
||||
<l:icon src="symbol-plugins" />
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:isAdmin>
|
||||
<li class="content-block">
|
||||
<a href="https://www.jenkins.io/redirect/distributed-builds"
|
||||
class="content-block__link"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
<span>${%learnMoreDistributedBuilds}</span>
|
||||
<span class="trailing-icon">
|
||||
<l:icon src="symbol-help-circle" />
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
+43
-34
@@ -24,13 +24,12 @@ THE SOFTWARE.
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form">
|
||||
<l:layout title="${%Appearance}" permissions="${app.MANAGE_AND_SYSTEM_READ}" type="one-column">
|
||||
<j:set var="readOnlyMode" value="${!app.hasPermission(app.MANAGE)}"/>
|
||||
<j:set var="pluginsUrl" value="${rootURL}/manage/pluginManager/available?filter=UI Themes" />
|
||||
<j:set var="hasPlugins" value="${it.hasPlugins()}" />
|
||||
<j:set var="header">
|
||||
<l:view>
|
||||
<j:set var="pluginsUrl" value="${rootURL}/manage/pluginManager/available?filter=UI Themes" />
|
||||
<j:set var="hasPlugins" value="${it.hasPlugins()}" />
|
||||
|
||||
<l:main-panel>
|
||||
<l:app-bar title="${%Appearance}">
|
||||
<l:app-bar title="${it.displayName}">
|
||||
<j:if test="${hasPlugins}">
|
||||
<a href="${pluginsUrl}" class="jenkins-button">
|
||||
<l:icon src="symbol-plugins" />
|
||||
@@ -39,36 +38,46 @@ THE SOFTWARE.
|
||||
</j:if>
|
||||
</l:app-bar>
|
||||
|
||||
<j:choose>
|
||||
<j:when test="${!hasPlugins}">
|
||||
<l:notice title="${%noPlugins}" icon="symbol-brush-outline">
|
||||
<a href="${pluginsUrl}">
|
||||
${%findPlugins}
|
||||
</a>
|
||||
</l:notice>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:skeleton />
|
||||
<div class="jenkins-page-description">
|
||||
${it.description}
|
||||
</div>
|
||||
</l:view>
|
||||
</j:set>
|
||||
|
||||
<f:form method="post" name="config" action="configure">
|
||||
<j:set var="instance" value="${it}" />
|
||||
<j:set var="descriptor" value="${instance.descriptor}" />
|
||||
<l:settings-subpage header="${header}" permissions="${app.MANAGE_AND_SYSTEM_READ}">
|
||||
<j:set var="readOnlyMode" value="${!app.hasPermission(app.MANAGE)}"/>
|
||||
<j:set var="pluginsUrl" value="${rootURL}/manage/pluginManager/available?filter=UI Themes" />
|
||||
<j:set var="hasPlugins" value="${it.hasPlugins()}" />
|
||||
|
||||
<j:forEach var="descriptor" items="${h.getSortedDescriptorsForGlobalConfigByDescriptor(it.FILTER)}">
|
||||
<j:set var="instance" value="${descriptor}" />
|
||||
<f:rowSet name="${descriptor.jsonSafeClassName}">
|
||||
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" />
|
||||
</f:rowSet>
|
||||
</j:forEach>
|
||||
<j:choose>
|
||||
<j:when test="${!hasPlugins}">
|
||||
<l:notice title="${%noPlugins}" icon="symbol-brush-outline">
|
||||
<a href="${pluginsUrl}">
|
||||
${%findPlugins}
|
||||
</a>
|
||||
</l:notice>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:skeleton />
|
||||
|
||||
<f:saveApplyBar/>
|
||||
</f:form>
|
||||
<f:form method="post" name="config" action="configure">
|
||||
<j:set var="instance" value="${it}" />
|
||||
<j:set var="descriptor" value="${instance.descriptor}" />
|
||||
|
||||
<l:hasAdministerOrManage>
|
||||
<st:adjunct includes="lib.form.confirm" />
|
||||
</l:hasAdministerOrManage>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
<j:forEach var="descriptor" items="${h.getSortedDescriptorsForGlobalConfigByDescriptor(it.FILTER)}">
|
||||
<j:set var="instance" value="${descriptor}" />
|
||||
<f:rowSet name="${descriptor.jsonSafeClassName}">
|
||||
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" />
|
||||
</f:rowSet>
|
||||
</j:forEach>
|
||||
|
||||
<f:saveApplyBar/>
|
||||
</f:form>
|
||||
|
||||
<l:hasAdministerOrManage>
|
||||
<st:adjunct includes="lib.form.confirm" />
|
||||
</l:hasAdministerOrManage>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -6,35 +6,25 @@ def f = namespace(lib.FormTagLib)
|
||||
def l = namespace(lib.LayoutTagLib)
|
||||
def st = namespace("jelly:stapler")
|
||||
|
||||
l.layout(permission: app.MANAGE, title: my.displayName, type: "one-column") {
|
||||
l.main_panel {
|
||||
h1 {
|
||||
text(Messages.ShutdownLink_DisplayName_prepare())
|
||||
l.'settings-subpage'(permission: app.MANAGE) {
|
||||
f.form(method: "post", name: "prepareShutdown", action: "prepare") {
|
||||
f.entry(title: Messages.ShutdownLink_ShutDownReason_title()) {
|
||||
f.textbox(name: "parameter.shutdownReason", value: app.quietDownReason ?: null)
|
||||
}
|
||||
|
||||
p {
|
||||
text(my.description)
|
||||
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _(app.isQuietingDown()
|
||||
? Messages.ShutdownLink_ShutDownReason_update()
|
||||
: Messages.ShutdownLink_DisplayName_prepare()),
|
||||
clazz: "jenkins-!-destructive-color")
|
||||
}
|
||||
}
|
||||
|
||||
f.form(method: "post", name: "prepareShutdown", action: "prepare") {
|
||||
f.entry(title: Messages.ShutdownLink_ShutDownReason_title()) {
|
||||
f.textbox(name: "parameter.shutdownReason", value: app.quietDownReason ?: null)
|
||||
}
|
||||
|
||||
|
||||
if (app.isQuietingDown()) {
|
||||
f.form(method: "post", name: "cancelShutdown", action: "cancel") {
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _(app.isQuietingDown()
|
||||
? Messages.ShutdownLink_ShutDownReason_update()
|
||||
: Messages.ShutdownLink_DisplayName_prepare()),
|
||||
clazz: "jenkins-!-destructive-color")
|
||||
}
|
||||
}
|
||||
|
||||
if (app.isQuietingDown()) {
|
||||
f.form(method: "post", name: "cancelShutdown", action: "cancel") {
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _(Messages.ShutdownLink_DisplayName_cancel()))
|
||||
}
|
||||
f.submit(value: _(Messages.ShutdownLink_DisplayName_cancel()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,30 +26,40 @@ THE SOFTWARE.
|
||||
Config page
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%System}" type="one-column">
|
||||
<st:include page="sidepanel.jelly" />
|
||||
<f:breadcrumb-config-outline title="${%System}" />
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form">
|
||||
<j:new className="jenkins.management.ConfigureLink" var="it" />
|
||||
|
||||
<l:main-panel>
|
||||
<l:settings-subpage permissions="${app.MANAGE_AND_SYSTEM_READ}" includeBreadcrumb="true">
|
||||
<j:set var="managementLink" value="${it}" />
|
||||
<j:set var="it" value="${app}" />
|
||||
<j:set var="readOnlyMode" value="${!h.hasPermission(app.MANAGE)}"/>
|
||||
<l:app-bar title="${%System}" />
|
||||
|
||||
<l:skeleton />
|
||||
<l:userExperimentalFlag var="newManageJenkins" flagClassName="jenkins.model.experimentalflags.NewManageJenkinsUserExperimentalFlag" />
|
||||
<j:if test="${newManageJenkins}">
|
||||
<section class="manage-messages">
|
||||
<j:forEach var="am" items="${app.activeAdministrativeMonitors}">
|
||||
<st:include page="message.jelly" it="${am}" />
|
||||
</j:forEach>
|
||||
<st:include page="downgrade.jelly" it="${app}" />
|
||||
</section>
|
||||
</j:if>
|
||||
|
||||
<f:form method="post" name="config" action="configSubmit" class="jenkins-form">
|
||||
<j:set var="instance" value="${it}" />
|
||||
<j:set var="descriptor" value="${instance.descriptor}" />
|
||||
|
||||
<f:entry title="${%Home directory}" description="${%By default, Jenkins stores all of its data in this directory on the file system}" help="/help/system-config/homeDirectory.html">
|
||||
<div class="jenkins-quote jenkins-quote--monospace">
|
||||
${it.rootDir}
|
||||
<l:copyButton tooltip="${%Copy home directory}" text="${it.rootDir}" iconOnly="true" />
|
||||
</div>
|
||||
</f:entry>
|
||||
<f:entry title="${%System Message}" description="${%This message will be displayed at the top of the Jenkins main page. This can be useful for posting notifications to your users}">
|
||||
<f:textarea name="system_message" value="${it.systemMessage}" disabled="${readOnlyMode?'true':null}" readonly="${readOnlyMode?'true':null}"
|
||||
codemirror-mode="${app.markupFormatter.codeMirrorMode}" codemirror-config="${app.markupFormatter.codeMirrorConfig}" previewEndpoint="/markupFormatter/previewDescription"/>
|
||||
</f:entry>
|
||||
<f:section title="General">
|
||||
<f:entry title="${%Home directory}" description="${%By default, Jenkins stores all of its data in this directory on the file system}" help="/help/system-config/homeDirectory.html">
|
||||
<div class="jenkins-quote jenkins-quote--monospace">
|
||||
${it.rootDir}
|
||||
<l:copyButton tooltip="${%Copy home directory}" text="${it.rootDir}" iconOnly="true" />
|
||||
</div>
|
||||
</f:entry>
|
||||
<f:entry title="${%System Message}" description="${%This message will be displayed at the top of the Jenkins main page. This can be useful for posting notifications to your users}">
|
||||
<f:textarea name="system_message" value="${it.systemMessage}" disabled="${readOnlyMode?'true':null}" readonly="${readOnlyMode?'true':null}"
|
||||
codemirror-mode="${app.markupFormatter.codeMirrorMode}" codemirror-config="${app.markupFormatter.codeMirrorConfig}" previewEndpoint="/markupFormatter/previewDescription"/>
|
||||
</f:entry>
|
||||
</f:section>
|
||||
|
||||
<!-- TODO remove after January 2026 LTS line -->
|
||||
<div class="jenkins-alert jenkins-alert-info">
|
||||
@@ -69,6 +79,5 @@ THE SOFTWARE.
|
||||
<j:set var="readOnlyMode" value="${!h.hasPermission(app.MANAGE)}"/>
|
||||
<f:saveApplyBar/>
|
||||
</f:form>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -24,11 +24,12 @@ THE SOFTWARE.
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<l:layout title="${it.displayName} ${%Load Statistics}" type="one-column">
|
||||
<l:breadcrumb title="${%Load Statistics}"/>
|
||||
<l:main-panel>
|
||||
<j:set var="prefix" value="overallLoad" />
|
||||
<st:include page="main.jelly" from="${it.overallLoad}" />
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
<j:new className="jenkins.management.StatisticsLink" var="it" />
|
||||
|
||||
<l:settings-subpage includeBreadcrumb="true">
|
||||
<j:set var="it" value="${app}" />
|
||||
|
||||
<j:set var="prefix" value="overallLoad" />
|
||||
<st:include page="main.jelly" from="${it.overallLoad}" />
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -27,85 +27,79 @@ THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<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">
|
||||
<l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%System Information}" type="one-column">
|
||||
<l:header>
|
||||
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
|
||||
<script src="${resURL}/jsbundles/pages/manage-jenkins/system-information.js" type="text/javascript" defer="true" />
|
||||
</l:header>
|
||||
|
||||
<l:breadcrumb title="${%System Information}"/>
|
||||
<l:main-panel>
|
||||
<l:app-bar title="${%System Information}" />
|
||||
<j:new className="jenkins.management.SystemInfoLink" var="it" />
|
||||
|
||||
<l:hasPermission permission="${app.SYSTEM_READ}">
|
||||
<l:tabPane title="${%System Properties}">
|
||||
<t:propertyTable items="${h.systemProperties}" sensitive="true" />
|
||||
</l:tabPane>
|
||||
</l:hasPermission>
|
||||
<l:settings-subpage permissions="${app.MANAGE_AND_SYSTEM_READ}" includeBreadcrumb="true">
|
||||
<script src="${resURL}/jsbundles/section-to-tabs.js" type="text/javascript" defer="true" />
|
||||
<script src="${resURL}/jsbundles/pages/manage-jenkins/system-information.js" type="text/javascript" defer="true" />
|
||||
|
||||
<l:hasPermission permission="${app.SYSTEM_READ}">
|
||||
<l:tabPane title="${%Environment Variables}">
|
||||
<t:propertyTable items="${h.envVars}" sensitive="true" />
|
||||
</l:tabPane>
|
||||
</l:hasPermission>
|
||||
<l:hasPermission permission="${app.SYSTEM_READ}">
|
||||
<l:tabPane title="${%System Properties}">
|
||||
<t:propertyTable items="${h.systemProperties}" sensitive="true" />
|
||||
</l:tabPane>
|
||||
</l:hasPermission>
|
||||
|
||||
<l:tabPane title="${%Plugins}">
|
||||
<l:hasPermission permission="${app.SYSTEM_READ}">
|
||||
<l:tabPane title="${%Environment Variables}">
|
||||
<t:propertyTable items="${h.envVars}" sensitive="true" />
|
||||
</l:tabPane>
|
||||
</l:hasPermission>
|
||||
|
||||
<j:choose>
|
||||
<j:when test="${empty(app.pluginManager.plugins) and empty(app.pluginManager.failedPlugins)}">
|
||||
<l:notice icon="symbol-plugins" title="${%No plugins installed}" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<l:tabPane title="${%Plugins}">
|
||||
<j:choose>
|
||||
<j:when test="${empty(app.pluginManager.plugins) and empty(app.pluginManager.failedPlugins)}">
|
||||
<l:notice icon="symbol-plugins" title="${%No plugins installed}" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<table class="jenkins-table sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th initialSortDir="down">${%Name}</th>
|
||||
<th>${%Version}</th>
|
||||
<th>${%Enabled}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="p" items="${app.pluginManager.plugins}">
|
||||
<j:set var="state" value="${p.enabled?'true':'false'}"/>
|
||||
<tr>
|
||||
<th initialSortDir="down">${%Name}</th>
|
||||
<th>${%Version}</th>
|
||||
<th>${%Enabled}</th>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="${p.url}" target="_blank" rel="noopener noreferrer">
|
||||
<st:out value="${p.displayName}"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<st:out value="${p.version}"/>
|
||||
</td>
|
||||
<td>
|
||||
<st:out value="${state}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<j:forEach var="p" items="${app.pluginManager.plugins}">
|
||||
<j:set var="state" value="${p.enabled?'true':'false'}"/>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="jenkins-table__link" href="${p.url}" target="_blank" rel="noopener noreferrer">
|
||||
<st:out value="${p.displayName}"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<st:out value="${p.version}"/>
|
||||
</td>
|
||||
<td>
|
||||
<st:out value="${state}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:tabPane>
|
||||
|
||||
<l:tabPane title="${%Memory Usage}">
|
||||
<f:entry title="${%Timespan}" class="jenkins-form-item--small">
|
||||
<div class="jenkins-select">
|
||||
<select id="timespan-select" class="jenkins-select__input">
|
||||
<option value="sec10">${%Short}</option>
|
||||
<option value="min" selected="true">${%Medium}</option>
|
||||
<option value="hour">${%Long}</option>
|
||||
</select>
|
||||
</div>
|
||||
</f:entry>
|
||||
|
||||
<div id="graph-host" />
|
||||
</l:tabPane>
|
||||
|
||||
<l:isAdmin>
|
||||
<l:tabPane title="${%Thread Dumps}">
|
||||
<p>${%threadDump_blurb('threadDump')}</p>
|
||||
</l:tabPane>
|
||||
|
||||
<l:tabPane title="${%Memory Usage}">
|
||||
<f:entry title="${%Timespan}" class="jenkins-form-item--small">
|
||||
<div class="jenkins-select">
|
||||
<select id="timespan-select" class="jenkins-select__input">
|
||||
<option value="sec10">${%Short}</option>
|
||||
<option value="min" selected="true">${%Medium}</option>
|
||||
<option value="hour">${%Long}</option>
|
||||
</select>
|
||||
</div>
|
||||
</f:entry>
|
||||
|
||||
<div id="graph-host" />
|
||||
</l:tabPane>
|
||||
|
||||
<l:isAdmin>
|
||||
<l:tabPane title="${%Thread Dumps}">
|
||||
<p>${%threadDump_blurb('threadDump')}</p>
|
||||
</l:tabPane>
|
||||
</l:isAdmin>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</l:isAdmin>
|
||||
</l:settings-subpage>
|
||||
</j:jelly>
|
||||
@@ -7,32 +7,29 @@ def f=namespace(lib.FormTagLib)
|
||||
def l=namespace(lib.LayoutTagLib)
|
||||
def st=namespace("jelly:stapler")
|
||||
|
||||
l.layout(permission:app.SYSTEM_READ, title:my.displayName, type:"one-column") {
|
||||
l.'settings-subpage'(permission: app.SYSTEM_READ) {
|
||||
set("readOnlyMode", !app.hasPermission(app.ADMINISTER))
|
||||
l.main_panel {
|
||||
l.app_bar(title: my.displayName)
|
||||
|
||||
l.skeleton()
|
||||
l.skeleton()
|
||||
|
||||
f.form(method:"post",name:"config",action:"configure", class: "jenkins-form") {
|
||||
Functions.getSortedDescriptorsForGlobalConfigByDescriptor(my.FILTER).each { Descriptor descriptor ->
|
||||
set("descriptor",descriptor)
|
||||
set("instance",descriptor)
|
||||
f.rowSet(name:descriptor.jsonSafeClassName) {
|
||||
st.include(from:descriptor, page:descriptor.globalConfigPage)
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _("Save"))
|
||||
f.apply(value: _("Apply"))
|
||||
}
|
||||
f.form(method:"post",name:"config",action:"configure", class: "jenkins-form") {
|
||||
Functions.getSortedDescriptorsForGlobalConfigByDescriptor(my.FILTER).each { Descriptor descriptor ->
|
||||
set("descriptor",descriptor)
|
||||
set("instance",descriptor)
|
||||
f.rowSet(name:descriptor.jsonSafeClassName) {
|
||||
st.include(from:descriptor, page:descriptor.globalConfigPage)
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
st.adjunct(includes: "lib.form.confirm")
|
||||
f.bottomButtonBar {
|
||||
f.submit(value: _("Save"))
|
||||
f.apply(value: _("Apply"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
st.adjunct(includes: "lib.form.confirm")
|
||||
}
|
||||
}
|
||||
@@ -40,23 +40,21 @@ THE SOFTWARE.
|
||||
Generates a row containing the page title and an optional set of controls
|
||||
</st:documentation>
|
||||
|
||||
<j:if test="${mode=='main-panel' or mode=='side-panel'}">
|
||||
<div class="jenkins-app-bar ${attrs.icon != null ? 'jenkins-app-bar--with-icon' :''}">
|
||||
<div class="jenkins-app-bar__content">
|
||||
<j:if test="${attrs.icon != null}">
|
||||
<l:icon src="${attrs.icon}" class="jenkins-avatar" />
|
||||
</j:if>
|
||||
<div class="jenkins-app-bar ${attrs.icon != null ? 'jenkins-app-bar--with-icon' :''}">
|
||||
<div class="jenkins-app-bar__content">
|
||||
<j:if test="${attrs.icon != null}">
|
||||
<l:icon src="${attrs.icon}" class="jenkins-avatar" />
|
||||
</j:if>
|
||||
|
||||
<x:element name="${headingLevel ?: 'h1'}">
|
||||
${title}
|
||||
<j:if test="${attrs.subtitle != null}">
|
||||
<span class="jenkins-app-bar__subtitle">${attrs.subtitle}</span>
|
||||
</j:if>
|
||||
</x:element>
|
||||
</div>
|
||||
<div class="jenkins-app-bar__controls">
|
||||
<d:invokeBody/>
|
||||
</div>
|
||||
<x:element name="${headingLevel ?: 'h1'}">
|
||||
${title}
|
||||
<j:if test="${attrs.subtitle != null}">
|
||||
<span class="jenkins-app-bar__subtitle">${attrs.subtitle}</span>
|
||||
</j:if>
|
||||
</x:element>
|
||||
</div>
|
||||
</j:if>
|
||||
<div class="jenkins-app-bar__controls">
|
||||
<d:invokeBody/>
|
||||
</div>
|
||||
</div>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2025 Jan Faracik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
|
||||
<st:documentation>
|
||||
A lazy-loaded element.
|
||||
</st:documentation>
|
||||
|
||||
<j:if test="${deferMode == 'children'}">
|
||||
<d:invokeBody />
|
||||
</j:if>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2025 Jan Faracik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
|
||||
<st:documentation>
|
||||
A lazy-loaded element.
|
||||
</st:documentation>
|
||||
|
||||
<j:set var="deferMode" value="placeholder" scope="parent" />
|
||||
<div class="jenkins-!-display-contents">
|
||||
<d:invokeBody />
|
||||
</div>
|
||||
|
||||
<j:set var="deferMode" value="children" scope="parent" />
|
||||
<l:renderOnDemand clazz="defer-element" tag="div" capture="it,deferMode,previousIt">
|
||||
<l:ajax>
|
||||
<div>
|
||||
<d:invokeBody />
|
||||
</div>
|
||||
</l:ajax>
|
||||
</l:renderOnDemand>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2025 Jan Faracik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
|
||||
<st:documentation>
|
||||
A lazy-loaded element.
|
||||
</st:documentation>
|
||||
|
||||
<j:if test="${deferMode == 'placeholder'}">
|
||||
<d:invokeBody />
|
||||
</j:if>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,144 @@
|
||||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2025, Jan Faracik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:d="jelly:define" xmlns:st="jelly:stapler" xmlns:f="/lib/form">
|
||||
<st:documentation>
|
||||
A reusable container for subpages relating under Manage Jenkins.
|
||||
<st:attribute name="permission">
|
||||
If given, this page is only made available to users who have the specified permission.
|
||||
(The permission will be checked against the "it" object.)
|
||||
</st:attribute>
|
||||
<st:attribute name="permissions">
|
||||
If given, this page is made available to users who have one or more of the specified permissions.
|
||||
(The permissions will be checked against the "it" object.)
|
||||
</st:attribute>
|
||||
<st:attribute name="placeholder">
|
||||
Optional placeholder for loading this page, falls back to a generic skeleton if not set.
|
||||
</st:attribute>
|
||||
<st:attribute name="header">
|
||||
Optional header for the page, falls back to a default header if not set.
|
||||
</st:attribute>
|
||||
<st:attribute name="includeBreadcrumb">
|
||||
<![CDATA[
|
||||
Optional breadcrumb, will add a breadcrumb for the current <code>it</code>
|
||||
]]>
|
||||
</st:attribute>
|
||||
</st:documentation>
|
||||
|
||||
<j:new className="hudson.model.ManageJenkinsAction" var="manageJenkinsAction" />
|
||||
<l:userExperimentalFlag var="newManageJenkins" flagClassName="jenkins.model.experimentalflags.NewManageJenkinsUserExperimentalFlag" />
|
||||
|
||||
<l:layout title="${it.displayName} - ${manageJenkinsAction.displayName}"
|
||||
permission="${attrs.permission}"
|
||||
permissions="${attrs.permissions}"
|
||||
type="${newManageJenkins ? 'two-column' : 'one-column'}">
|
||||
|
||||
<j:if test="${attrs.includeBreadcrumb}">
|
||||
<!-- Hacky - this will be improved in subsequent PRs -->
|
||||
<j:choose>
|
||||
<j:when test="${it.class.name eq 'jenkins.management.ConfigureLink'}">
|
||||
<f:breadcrumb-config-outline title="${it.displayName}" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:breadcrumb title="${it.displayName}" />
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:if>
|
||||
|
||||
<j:choose>
|
||||
<j:when test="${newManageJenkins}">
|
||||
<l:side-panel>
|
||||
<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">
|
||||
${category.key.label}
|
||||
</summary>
|
||||
<j:forEach var="item" items="${category.value}">
|
||||
<l:task title="${item.displayName}"
|
||||
href="${rootURL}/manage/${item.urlName}"
|
||||
icon="${item.iconFileName}"
|
||||
badge="${item.badge}"
|
||||
requiresConfirmation="${item.requiresConfirmation}"
|
||||
post="${item.requiresPOST}"
|
||||
confirmationMessage="${%sure}" />
|
||||
</j:forEach>
|
||||
</j:forEach>
|
||||
</l:tasks>
|
||||
</l:side-panel>
|
||||
<l:main-panel>
|
||||
<div class="app-settings-container">
|
||||
<div class="app-settings-container__inner">
|
||||
<j:if test="${!attrs.containsKey('header')}">
|
||||
<l:app-bar title="${it.displayName}" />
|
||||
<div class="jenkins-page-description">
|
||||
${it.description}
|
||||
</div>
|
||||
</j:if>
|
||||
|
||||
<j:out value="${attrs.header}" />
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</l:main-panel>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:main-panel>
|
||||
<j:if test="${!attrs.containsKey('header')}">
|
||||
<l:app-bar title="${it.displayName}" />
|
||||
<div class="jenkins-page-description">
|
||||
${it.description}
|
||||
</div>
|
||||
</j:if>
|
||||
|
||||
<j:out value="${attrs.header}" />
|
||||
|
||||
<d:invokeBody />
|
||||
</l:main-panel>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:layout>
|
||||
</j:jelly>
|
||||
@@ -43,6 +43,30 @@ THE SOFTWARE.
|
||||
<div />
|
||||
</div>
|
||||
</j:case>
|
||||
<j:case value="form">
|
||||
<div class="jenkins-form-skeleton-2">
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
<span />
|
||||
<div />
|
||||
</div>
|
||||
</j:case>
|
||||
<j:default>
|
||||
<div class="jenkins-form-skeleton">
|
||||
<span />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.jenkins-side-panel-skeleton,
|
||||
.jenkins-form-skeleton {
|
||||
.jenkins-form-skeleton,
|
||||
.jenkins-form-skeleton-2 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -6,9 +6,11 @@ import Tooltips from "@/components/tooltips";
|
||||
import StopButtonLink from "@/components/stop-button-link";
|
||||
import ConfirmationLink from "@/components/confirmation-link";
|
||||
import Dialogs from "@/components/dialogs";
|
||||
import Defer from "@/components/defer";
|
||||
|
||||
Dropdowns.init();
|
||||
CommandPalette.init();
|
||||
Defer.init();
|
||||
Notifications.init();
|
||||
SearchBar.init();
|
||||
Tooltips.init();
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import behaviorShim from "@/util/behavior-shim";
|
||||
|
||||
function init() {
|
||||
behaviorShim.specify(".defer-element", "-defer-", 1000, (element) => {
|
||||
const parent = element.parentElement;
|
||||
const placeholder = element.previousElementSibling;
|
||||
|
||||
renderOnDemand(element, () => {
|
||||
placeholder.remove();
|
||||
|
||||
// Emit DOMContentLoaded in case it's tracked in a script
|
||||
const evt = new Event("DOMContentLoaded", { bubbles: true });
|
||||
parent.dispatchEvent(evt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default { init };
|
||||
@@ -1,4 +1,5 @@
|
||||
import { toId } from "@/util/dom";
|
||||
import BehaviorShim from "@/util/behavior-shim";
|
||||
|
||||
/*
|
||||
* Generates a jump list for the active breadcrumb to jump to
|
||||
@@ -8,13 +9,15 @@ function init() {
|
||||
const inpageNavigationBreadcrumb = document.querySelector("#inpage-nav div");
|
||||
|
||||
if (inpageNavigationBreadcrumb) {
|
||||
inpageNavigationBreadcrumb.items = Array.from(
|
||||
document.querySelectorAll(
|
||||
"form > div > .jenkins-section > .jenkins-section__title",
|
||||
),
|
||||
).map((section) => {
|
||||
section.id = toId(section.textContent);
|
||||
return { label: section.textContent, url: "#" + section.id };
|
||||
BehaviorShim.specify("form", "inpage-nav", 999, (element) => {
|
||||
inpageNavigationBreadcrumb.items = Array.from(
|
||||
element.querySelectorAll(
|
||||
"& > div > .jenkins-section > .jenkins-section__title",
|
||||
),
|
||||
).map((section) => {
|
||||
section.id = toId(section.textContent);
|
||||
return { label: section.textContent, url: "#" + section.id };
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
const searchBarInput = document.querySelector("#settings-search-bar");
|
||||
|
||||
searchBarInput.suggestions = function () {
|
||||
return Array.from(document.querySelectorAll(".jenkins-section__item"))
|
||||
return Array.from(
|
||||
document.querySelectorAll(
|
||||
".jenkins-section__item, #tasks .task-link-wrapper",
|
||||
),
|
||||
)
|
||||
.map((item) => ({
|
||||
url: item.querySelector("a").href,
|
||||
icon: item.querySelector(
|
||||
".jenkins-section__item__icon svg, .jenkins-section__item__icon img",
|
||||
".jenkins-section__item__icon svg, .jenkins-section__item__icon img, .task-icon-link svg, .task-icon-link img",
|
||||
).outerHTML,
|
||||
label: item.querySelector("dt").textContent,
|
||||
label: (
|
||||
item.querySelector("dt") ||
|
||||
item.querySelector(".task-link-text") ||
|
||||
item.querySelector(".task-link")
|
||||
).textContent,
|
||||
}))
|
||||
.filter((item) => !item.url.endsWith("#"));
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
// Converts a page's section headings into clickable tabs, see 'About Jenkins' page for example
|
||||
const tabPanes = document.querySelectorAll(".jenkins-tab-pane");
|
||||
const content = document.querySelector("#main-panel");
|
||||
|
||||
// Hide tab panes
|
||||
tabPanes.forEach((tabPane) => {
|
||||
@@ -47,4 +46,4 @@ tabPanes.forEach((tabPane, index) => {
|
||||
tabBar.append(tab);
|
||||
});
|
||||
|
||||
content.insertBefore(tabBar, tabPanes[0]);
|
||||
tabPanes[0].parentElement.insertBefore(tabBar, tabPanes[0]);
|
||||
@@ -94,10 +94,6 @@ $background-outset: 0.5rem;
|
||||
font-weight: var(--font-bold-weight);
|
||||
cursor: default;
|
||||
|
||||
svg * {
|
||||
stroke-width: 35px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: var(--item-background--active) !important;
|
||||
}
|
||||
@@ -114,3 +110,14 @@ $background-outset: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jenkins-side-nav__heading {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-color-secondary);
|
||||
font-weight: var(--font-bold-weight);
|
||||
margin: 1rem 0 0.375rem;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,14 @@
|
||||
}
|
||||
|
||||
.jenkins-page-description {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-color-secondary);
|
||||
margin: 0 0 var(--section-padding) 0;
|
||||
line-height: 1.66;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.jenkins-app-bar + .jenkins-page-description {
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.jenkins-fieldset {
|
||||
|
||||
@@ -127,13 +127,8 @@
|
||||
}
|
||||
|
||||
.app-about-version {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-bold-weight);
|
||||
color: var(--text-color-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.app-about-paragraph {
|
||||
font-size: 1rem;
|
||||
margin-bottom: var(--section-padding);
|
||||
font-weight: var(--font-bold-weight);
|
||||
}
|
||||
@@ -138,7 +138,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
body:has(.app-build-bar) {
|
||||
body:has(.app-build-bar),
|
||||
body:has(.app-settings-container) {
|
||||
--top: color-mix(in sRGB, var(--text-color-secondary) 4%, var(--background));
|
||||
--bottom: color-mix(
|
||||
in sRGB,
|
||||
@@ -150,6 +151,34 @@ body:has(.app-build-bar) {
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.app-settings-container {
|
||||
--background: var(--card-background);
|
||||
--top: color-mix(
|
||||
in sRGB,
|
||||
var(--text-color-secondary) 2%,
|
||||
var(--card-background)
|
||||
);
|
||||
|
||||
position: relative;
|
||||
background-color: var(--card-background);
|
||||
background-image: linear-gradient(var(--top), var(--card-background));
|
||||
background-size: 100% 90vh;
|
||||
background-repeat: no-repeat;
|
||||
border: var(--jenkins-border);
|
||||
border-radius: var(--form-input-border-radius);
|
||||
z-index: 0;
|
||||
min-height: calc(100vh - var(--header-height) - var(--section-padding));
|
||||
|
||||
&__inner {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
overflow-x: clip;
|
||||
padding: var(--section-padding);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.app-build-bar {
|
||||
position: relative;
|
||||
display: grid;
|
||||
|
||||
Reference in new issue
Block a user