Merge branch 'master' into new-buttons-1-dashboard

This commit is contained in:
Jan Faracik 2022-06-19 20:01:02 +01:00
commit c4075772f0
44 changed files with 484 additions and 242 deletions

27
.gitpod.yml Normal file
View File

@ -0,0 +1,27 @@
tasks:
- init: |
mvn -am -pl war,bom -P quick-build clean install
command: |
mvn -pl war jetty:run -Dhost=0.0.0.0
name: Run
- command: gp await-port 8080 && gp url 8080 && gp preview $(gp url 8080)/jenkins/
name: Preview
github:
prebuilds:
pullRequestsFromForks: true
addBadge: true
jetbrains:
intellij:
plugins:
- Stapler plugin for IntelliJ IDEA
prebuilds:
version: stable
vscode:
extensions:
- vscjava.vscode-java-pack
image:
file: .gitpod/Dockerfile

4
.gitpod/Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM gitpod/workspace-full
RUN brew install gh && \
bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && sdk install maven 3.8.4 && sdk default maven 3.8.4"

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1 +1 @@
-Xmx800m
-Xmx1100m

View File

@ -31,7 +31,7 @@ You can read a description of the [building and debugging process here].
If you want simply to build the `jenkins.war` file as fast as possible without tests, run:
```sh
mvn -am -pl war,bom -DskipTests -Dspotbugs.skip -Dspotless.check.skip clean install
mvn -am -pl war,bom -Pquick-build clean install
```
The WAR file will be created in `war/target/jenkins.war`.
@ -62,6 +62,16 @@ On another terminal, move to the war folder and start a [webpack](https://webpac
cd war; yarn start
```
### Gitpod
You can open this project as a [Gitpod workspace](https://www.gitpod.io/) which comes pre-configured with all the tools you will need.
You can use IntelliJ IDEA (preferred) or VS Code (alternate) in the browser.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/jenkinsci/jenkins)
If you prefer using IntelliJ IDEA, you can setup Gitpod integration with JetBrains Gateway using the instructions on [gitpod.io](https://www.gitpod.io/docs/ides-and-editors/intellij),
which will open the workspace in IntelliJ IDEA using JetBrains Gateway.
## Testing changes
Jenkins core includes unit and functional tests as a part of the repository.
@ -220,4 +230,4 @@ just submit a pull request.
[Jenkins Pipeline]: https://www.jenkins.io/doc/book/pipeline/
[Jenkinsfile]: ./Jenkinsfile
[download Maven here]: https://maven.apache.org/download.cgi
[GitHub pull request]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
[GitHub pull request]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests

11
Jenkinsfile vendored
View File

@ -61,9 +61,13 @@ for (i = 0; i < buildTypes.size(); i++) {
'clean',
'install',
]
infra.runMaven(mavenOptions, jdk)
if (isUnix()) {
sh 'git add . && git diff --exit-code HEAD'
try {
infra.runMaven(mavenOptions, jdk)
if (isUnix()) {
sh 'git add . && git diff --exit-code HEAD'
}
} finally {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/target/surefire-reports/*.dumpstream'
}
}
}
@ -71,7 +75,6 @@ for (i = 0; i < buildTypes.size(); i++) {
// Once we've built, archive the artifacts and the test results.
stage("${buildType} Publishing") {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/target/surefire-reports/*.dumpstream'
if (!fileExists('core/target/surefire-reports/TEST-jenkins.Junit4TestsRanTest.xml')) {
error 'JUnit 4 tests are no longer being run for the core package'
}

View File

@ -187,6 +187,7 @@ import org.xml.sax.helpers.DefaultHandler;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable, StaplerProxy {
/** Custom plugin manager system property or context param. */
public static final String CUSTOM_PLUGIN_MANAGER = PluginManager.class.getName() + ".className";

View File

@ -27,6 +27,7 @@ package hudson.cli.declarative;
import static java.util.logging.Level.SEVERE;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.AbortException;
import hudson.Extension;
import hudson.ExtensionComponent;
@ -71,6 +72,12 @@ import org.springframework.security.core.context.SecurityContextHolder;
* @author Kohsuke Kawaguchi
*/
@Extension
@SuppressFBWarnings(
value = {
"THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION",
"THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"
},
justification = "TODO needs triage")
public class CLIRegisterer extends ExtensionFinder {
@Override
public ExtensionComponentSet refresh() throws ExtensionRefreshException {

View File

@ -110,6 +110,7 @@ import org.xml.sax.SAXException;
// Item doesn't necessarily have to be Actionable, but
// Java doesn't let multiple inheritance.
@ExportedBean
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "TODO needs triage")
public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled, DescriptorByNameOwner, StaplerProxy {
private static final Logger LOGGER = Logger.getLogger(AbstractItem.class.getName());

View File

@ -32,6 +32,7 @@ import static java.util.logging.Level.WARNING;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.FilePath;
import hudson.Functions;
import hudson.Util;
@ -89,6 +90,12 @@ import org.springframework.security.core.Authentication;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
@SuppressFBWarnings(
value = {
"THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION",
"THROWS_METHOD_THROWS_CLAUSE_THROWABLE"
},
justification = "TODO needs triage")
public class Executor extends Thread implements ModelObject {
protected final @NonNull Computer owner;
private final Queue queue;

View File

@ -24,6 +24,7 @@
package hudson.model;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.listeners.ItemListener;
@ -61,6 +62,7 @@ import org.xml.sax.SAXException;
* @author Kohsuke Kawaguchi
* @see ViewGroupMixIn
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "TODO needs triage")
public abstract class ItemGroupMixIn {
/**
* {@link ItemGroup} for which we are working.

View File

@ -171,6 +171,7 @@ import org.springframework.security.core.Authentication;
* @see QueueTaskDispatcher
*/
@ExportedBean
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class Queue extends ResourceController implements Saveable {
/**

View File

@ -39,6 +39,12 @@ import jenkins.security.NotReallyRoleSensitiveCallable;
* Controls mutual exclusion of {@link ResourceList}.
* @author Kohsuke Kawaguchi
*/
@SuppressFBWarnings(
value = {
"THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION",
"THROWS_METHOD_THROWS_CLAUSE_THROWABLE"
},
justification = "TODO needs triage")
public class ResourceController {
/**
* {@link ResourceList}s that are used by activities that are in progress.

View File

@ -104,6 +104,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
* @since 1.333
*/
@ExportedBean
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class UpdateSite {
/**
* What's the time stamp of data file?

View File

@ -24,6 +24,7 @@
package hudson.triggers;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.AperiodicWork;
import hudson.model.AsyncAperiodicWork;
import hudson.model.AsyncPeriodicWork;
@ -47,6 +48,7 @@ import jenkins.util.Timer;
* @author Kohsuke Kawaguchi
* @since 1.124
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public abstract class SafeTimerTask extends TimerTask {
/**

View File

@ -230,14 +230,12 @@ public abstract class FormFieldValidator {
ok();
} else {
response.setContentType("text/html;charset=UTF-8");
// 1x16 spacer needed for IE since it doesn't support min-height
if (APPLY_CONTENT_SECURITY_POLICY_HEADERS) {
for (String header : new String[]{"Content-Security-Policy", "X-WebKit-CSP", "X-Content-Security-Policy"}) {
response.setHeader(header, "sandbox; default-src 'none';");
}
}
response.getWriter().print("<div class=" + cssClass + "><img src='" +
request.getContextPath() + Jenkins.RESOURCE_PATH + "/images/none.gif' height=16 width=1>" +
response.getWriter().print("<div class=" + cssClass + ">" +
message + "</div>");
}
}

View File

@ -31,7 +31,6 @@ import java.io.IOException;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
@ -95,7 +94,7 @@ public abstract class FormFillFailure extends IOException implements HttpRespons
}
return _errorWithMarkup(Util.escape(message) +
" <a href='#' class='showDetails'>"
" </div><div><a href='#' class='showDetails'>"
+ Messages.FormValidation_Error_Details()
+ "</a><pre style='display:none'>"
+ Util.escape(Functions.printThrowable(e)) +
@ -137,9 +136,7 @@ public abstract class FormFillFailure extends IOException implements HttpRespons
if (req == null) { // being called from some other context
return message;
}
// 1x16 spacer needed for IE since it doesn't support min-height
return "<div class=" + getKind().name().toLowerCase(Locale.ENGLISH) + "><img src='" +
req.getContextPath() + Jenkins.RESOURCE_PATH + "/images/none.gif' height=16 width=1>" +
return "<div class=" + getKind().name().toLowerCase(Locale.ENGLISH) + ">" +
message + "</div>";
}

View File

@ -198,7 +198,7 @@ public abstract class FormValidation extends IOException implements HttpResponse
if (e == null) return _errorWithMarkup(Util.escape(message), kind);
return _errorWithMarkup(Util.escape(message) +
" <a href='#' class='showDetails'>"
" </div><div><a href='#' class='showDetails'>"
+ Messages.FormValidation_Error_Details()
+ "</a><pre style='display:none'>"
+ Util.escape(Functions.printThrowable(e)) +
@ -272,9 +272,7 @@ public abstract class FormValidation extends IOException implements HttpResponse
if (req == null) { // being called from some other context
return message;
}
// 1x16 spacer needed for IE since it doesn't support min-height
return "<div class=" + kind.name().toLowerCase(Locale.ENGLISH) + "><img src='" +
req.getContextPath() + Jenkins.RESOURCE_PATH + "/images/none.gif' height=16 width=1>" +
return "<div class=\"" + kind.name().toLowerCase(Locale.ENGLISH) + "\">" +
message + "</div>";
}

View File

@ -1,5 +1,6 @@
package hudson.util;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -10,6 +11,7 @@ import java.lang.reflect.Proxy;
*
* @author Kohsuke Kawaguchi
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "TODO needs triage")
public abstract class InterceptingProxy {
/**
* Intercepts every method call.

View File

@ -44,6 +44,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.security.InputManipulationException;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Saveable;
import hudson.security.ACL;
@ -77,6 +78,7 @@ import org.acegisecurity.Authentication;
* </ul>
*
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_RUNTIMEEXCEPTION", justification = "TODO needs triage")
@SuppressWarnings({"rawtypes", "unchecked"})
public class RobustReflectionConverter implements Converter {

View File

@ -337,6 +337,7 @@ import org.xml.sax.InputSource;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLevelItemGroup, StaplerProxy, StaplerFallback,
ModifiableViewGroup, AccessControlled, DescriptorByNameOwner,
ModelObjectWithContextMenu, ModelObjectWithChildren, OnMaster {

View File

@ -26,6 +26,7 @@ package jenkins.model;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.BulkChange;
import hudson.Util;
import hudson.XmlFile;
@ -61,6 +62,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
* @since 1.607
*/
@Restricted(NoExternalUse.class) // for now, we may make it public later
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class Nodes implements Saveable {
/**

View File

@ -28,6 +28,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.ExtensionList;
import hudson.ExtensionListListener;
import hudson.ExtensionPoint;
@ -48,6 +49,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
* @see Actionable#getAllActions
* @since 1.548
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public abstract class TransientActionFactory<T> implements ExtensionPoint {
/**

View File

@ -24,6 +24,7 @@
package jenkins.security;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.security.ACL;
import hudson.security.ACLContext;
import java.util.concurrent.Callable;
@ -36,6 +37,7 @@ import org.springframework.security.core.Authentication;
* @see SecurityContextExecutorService
* @since 2.51
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public final class ImpersonatingExecutorService extends InterceptingExecutorService {
private final Authentication authentication;

View File

@ -24,6 +24,7 @@
package jenkins.security;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.security.ACL;
import hudson.security.ACLContext;
import java.util.concurrent.Callable;
@ -35,6 +36,7 @@ import org.springframework.security.core.Authentication;
* Variant of {@link ImpersonatingExecutorService} for scheduled services.
* @since 2.51
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public final class ImpersonatingScheduledExecutorService extends InterceptingScheduledExecutorService {
private final Authentication authentication;

View File

@ -27,6 +27,7 @@ package jenkins.security;
import static org.springframework.security.core.context.SecurityContextHolder.getContext;
import static org.springframework.security.core.context.SecurityContextHolder.setContext;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import jenkins.util.InterceptingExecutorService;
@ -43,6 +44,7 @@ import org.springframework.security.core.context.SecurityContext;
* @author Kohsuke Kawaguchi
* @since 1.561
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class SecurityContextExecutorService extends InterceptingExecutorService {
public SecurityContextExecutorService(ExecutorService service) {

View File

@ -44,6 +44,7 @@ import jenkins.security.ImpersonatingExecutorService;
* @author Kohsuke Kawaguchi
* @see AtmostOneThreadExecutor
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class AtmostOneTaskExecutor<V> {
private static final Logger LOGGER = Logger.getLogger(AtmostOneTaskExecutor.class.getName());

View File

@ -1,5 +1,6 @@
package jenkins.util;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@ -11,6 +12,7 @@ import java.util.concurrent.ExecutorService;
*
* @author Kohsuke Kawaguchi
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", justification = "TODO needs triage")
public class ContextResettingExecutorService extends InterceptingExecutorService {
public ContextResettingExecutorService(ExecutorService base) {
super(base);

View File

@ -25,6 +25,7 @@
package jenkins.util;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.AbstractItem;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
@ -73,6 +74,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
* {@code ui-samples-plugin} demonstrates all this.
* @since 1.484
*/
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "TODO needs triage")
public abstract class ProgressiveRendering {
private static final Logger LOG = Logger.getLogger(ProgressiveRendering.class.getName());

View File

@ -24,6 +24,7 @@
package org.acegisecurity.providers.dao;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.acegisecurity.AcegiSecurityException;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
@ -35,6 +36,7 @@ import org.acegisecurity.userdetails.UserDetails;
* @deprecated use {@link org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider}
*/
@Deprecated
@SuppressFBWarnings(value = "THROWS_METHOD_THROWS_RUNTIMEEXCEPTION", justification = "TODO needs triage")
public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider {
private final org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider delegate =

View File

@ -84,13 +84,12 @@ THE SOFTWARE.
<j:out value="${escapeEntryTitleAndDescription ? h.escape(attrs.description) : attrs.description}"/>
</f:description>
</j:if>
<!-- Needs to have a .tr class otherwise custom uses of findFollowing
findFollowingTR(listBox, "validation-error-area") will break -->
<div class="setting-main">
<d:invokeBody />
</div>
<!-- used to display the form validation error -->
<div class="validation-error-area tr"><div colspan="2"></div><div></div><div></div></div>
<div class="validation-error-area">
<!-- Used to display the form validation status -->
</div>
<j:if test="${attrs.help!=null}">
<f:helpArea />
</j:if>
@ -109,8 +108,9 @@ THE SOFTWARE.
</div>
</j:otherwise>
</j:choose>
<!-- used to display the form validation error -->
<div class="validation-error-area tr"><div colspan="2"></div><div></div><div></div></div>
<div class="validation-error-area">
<!-- Used to display the form validation status -->
</div>
<f:helpArea />
<j:if test="${!empty(attrs.description)}">
<f:description>

View File

@ -33,12 +33,13 @@ THE SOFTWARE.
and the presence of the value.
<st:attribute name="field" use="required" />
<st:attribute name="title" use="required" />
<st:attribute name="help" />
</st:documentation>
<!--
Without @checked, optionalBlock will try to coerce an object to a boolean, which fails,
so override @checked manually.
-->
<f:optionalBlock field="${field}" title="${title}" checked="${instance[field]!=null}">
<f:optionalBlock field="${field}" title="${title}" checked="${instance[field]!=null}" help="${help}">
<j:set var="descriptor" value="${app.getDescriptorOrDie(descriptor.getPropertyTypeOrDie(instance,field).clazz)}" />
<j:set var="instance" value="${instance[field]}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />

View File

@ -11,12 +11,12 @@ function updateListBox(listBox,url,config) {
// form entry using tables-to-divs markup.
function getStatusElement() {
function getStatusForTabularForms() {
return findFollowingTR(listBox, "validation-error-area").firstElementChild.nextSibling;
return listBox.parentNode.querySelector(".validation-error-area");
}
function getStatusForDivBasedForms() {
var settingMain = listBox.closest('.setting-main')
if (!settingMain) {
console.warn("Couldn't find the expected parent element (.setting-main) for element", listBox)
console.warn("Couldn't find the expected validation element (.validation-error-area) for element", listBox.parentNode)
return;
}

View File

@ -51,16 +51,18 @@ THE SOFTWARE.
</st:attribute>
</st:documentation>
<f:entry>
<div style="float:right">
<div class="jenkins-validate-button__container">
<div style="display:none;">
<l:spinner text="${attrs.progress}" />
</div>
<input type="button" value="${title}" class="yui-button validate-button ${attrs.clazz} Behaviour-validateButton"
data-validate-button-descriptor-url="${descriptor.descriptorFullUrl}"
data-validate-button-method="${method}"
data-validate-button-with="${with}" />
<div class="jenkins-validate-button__container__status">
<!-- Used to display the form validation status -->
</div>
</div>
<div style="display:none;">
<l:spinner text="${attrs.progress}" />
</div>
<div><!-- this is where the error message goes --></div>
<st:adjunct includes="lib.form.validateButton.adjunct"/>
</f:entry>
</j:jelly>

View File

@ -52,7 +52,7 @@ THE SOFTWARE.
</j:when>
<j:otherwise>
<a href="${attrs.href ?: rootURL+'/'+r.url}" class="model-link">
<l:icon alt="${r.iconColor.description}" class="${r.buildStatusIconClassName} icon-sm" style="margin-left: 0; position: relative; top: 0.2rem;"/><span class="jenkins-icon-adjacent">${jobName_}#<!-- -->${number}</span></a>
<l:icon alt="${r.iconColor.description}" class="${r.buildStatusIconClassName} icon-sm" style="margin-left: 0; position: relative; top: -0.1rem;"/><span class="jenkins-icon-adjacent">${jobName_}#<!-- -->${number}</span></a>
</j:otherwise>
</j:choose>
</j:otherwise>

14
pom.xml
View File

@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci</groupId>
<artifactId>jenkins</artifactId>
<version>1.74</version>
<version>1.76</version>
<relativePath />
</parent>
@ -71,7 +71,7 @@ THE SOFTWARE.
</issueManagement>
<properties>
<revision>2.351</revision>
<revision>2.355</revision>
<changelist>-SNAPSHOT</changelist>
<!-- *.html files are in UTF-8, and *.properties are in iso-8859-1, so this configuration is actually incorrect,
@ -101,12 +101,8 @@ THE SOFTWARE.
<access-modifier.version>1.27</access-modifier.version>
<bridge-method-injector.version>1.23</bridge-method-injector.version>
<junit.jupiter.version>5.8.2</junit.jupiter.version>
<mockito.version>4.6.0</mockito.version>
<spotless.version>2.22.5</spotless.version>
<!-- TODO 3.x versions expose "GC overhead limit exceeded" errors in this test suite -->
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-surefire-report-plugin.version>2.22.2</maven-surefire-report-plugin.version>
<mockito.version>4.6.1</mockito.version>
<spotless.version>2.22.7</spotless.version>
</properties>
<dependencyManagement>
@ -475,7 +471,7 @@ THE SOFTWARE.
<arguments>-P release,sign</arguments>
<!-- work around for a bug in javadoc plugin that causes the release to fail. see MRELEASE-271 -->
<preparationGoals>clean install</preparationGoals>
<goals>-DskipTests -Danimal.sniffer.skip=false javadoc:javadoc deploy</goals>
<goals>-DskipTests -Danimal.sniffer.skip=false -Dspotbugs.skip -Dmaven.checkstyle.skip -Dspotless.check.skip generate-resources javadoc:javadoc deploy</goals>
<pushChanges>false</pushChanges>
<localCheckout>true</localCheckout>
<tagNameFormat>jenkins-@{project.version}</tagNameFormat>

View File

@ -573,6 +573,194 @@
<Class name="jenkins.security.CryptoConfidentialKey"/>
</Or>
</And>
<And>
<Bug pattern="THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"/>
<Or>
<Class name="hudson.ClassicPluginStrategy"/>
<Class name="hudson.cli.AbstractBuildRangeCommand"/>
<Class name="hudson.cli.AddJobToViewCommand"/>
<Class name="hudson.cli.BuildCommand"/>
<Class name="hudson.cli.CancelQuietDownCommand"/>
<Class name="hudson.cli.ClearQueueCommand"/>
<Class name="hudson.cli.CLI"/>
<Class name="hudson.cli.CLICommand"/>
<Class name="hudson.cli.ConnectNodeCommand"/>
<Class name="hudson.cli.ConsoleCommand"/>
<Class name="hudson.cli.CopyJobCommand"/>
<Class name="hudson.cli.CreateJobCommand"/>
<Class name="hudson.cli.CreateNodeCommand"/>
<Class name="hudson.cli.CreateViewCommand"/>
<Class name="hudson.cli.declarative.MethodBinder"/>
<Class name="hudson.cli.DeleteJobCommand"/>
<Class name="hudson.cli.DeleteNodeCommand"/>
<Class name="hudson.cli.DeleteViewCommand"/>
<Class name="hudson.cli.DisablePluginCommand"/>
<Class name="hudson.cli.DisconnectNodeCommand"/>
<Class name="hudson.cli.EnablePluginCommand"/>
<Class name="hudson.cli.GetJobCommand"/>
<Class name="hudson.cli.GetViewCommand"/>
<Class name="hudson.cli.GroovyCommand"/>
<Class name="hudson.cli.HelpCommand"/>
<Class name="hudson.cli.InstallPluginCommand"/>
<Class name="hudson.cli.ListJobsCommand"/>
<Class name="hudson.cli.OfflineNodeCommand"/>
<Class name="hudson.cli.OnlineNodeCommand"/>
<Class name="hudson.cli.QuietDownCommand"/>
<Class name="hudson.cli.ReloadConfigurationCommand"/>
<Class name="hudson.cli.ReloadJobCommand"/>
<Class name="hudson.cli.RemoveJobFromViewCommand"/>
<Class name="hudson.cli.RunRangeCommand"/>
<Class name="hudson.cli.SetBuildDescriptionCommand"/>
<Class name="hudson.cli.SetBuildDisplayNameCommand"/>
<Class name="hudson.cli.UpdateJobCommand"/>
<Class name="hudson.cli.UpdateViewCommand"/>
<Class name="hudson.cli.WaitNodeOfflineCommand"/>
<Class name="hudson.cli.WaitNodeOnlineCommand"/>
<Class name="hudson.Functions"/>
<Class name="hudson.logging.LogRecorderManager"/>
<Class name="hudson.Main"/>
<Class name="hudson.model.AbstractBuild$AbstractBuildExecution"/>
<Class name="hudson.model.Actionable"/>
<Class name="hudson.model.AperiodicWork"/>
<Class name="hudson.model.Build$BuildExecution"/>
<Class name="hudson.model.ComputerSet"/>
<Class name="hudson.model.FileParameterValue$FileItemImpl"/>
<Class name="hudson.model.Job"/>
<Class name="hudson.model.Label"/>
<Class name="hudson.model.listeners.SCMListener"/>
<Class name="hudson.model.Run$RunExecution"/>
<Class name="hudson.model.TaskThread"/>
<Class name="hudson.model.User"/>
<Class name="hudson.model.View"/>
<Class name="hudson.model.View$AsynchPeople"/>
<Class name="hudson.Plugin"/>
<Class name="hudson.PluginWrapper"/>
<Class name="hudson.security.ACL"/>
<Class name="hudson.slaves.SlaveComputer"/>
<Class name="hudson.tools.ToolInstallation"/>
<Class name="hudson.util.Retrier"/>
<Class name="jenkins.agents.WebSocketAgents$Session"/>
<Class name="jenkins.ClassLoaderReflectionToolkit"/>
<Class name="jenkins.cli.StopBuildsCommand"/>
<Class name="jenkins.management.AsynchronousAdministrativeMonitor"/>
<Class name="jenkins.model.ModelObjectWithChildren"/>
<Class name="jenkins.model.ModelObjectWithContextMenu"/>
<Class name="jenkins.model.RunIdMigrator"/>
<Class name="jenkins.security.RekeySecretAdminMonitor"/>
<Class name="jenkins.security.ResourceDomainRootAction"/>
<Class name="jenkins.security.UserDetailsCache$Retriever"/>
<Class name="jenkins.slaves.restarter.JnlpSlaveRestarterInstaller$Install"/>
<Class name="jenkins.slaves.restarter.SlaveRestarter"/>
<Class name="jenkins.slaves.restarter.UnixSlaveRestarter"/>
<Class name="jenkins.slaves.restarter.WinswSlaveRestarter"/>
<Class name="jenkins.slaves.StandardOutputSwapper$ChannelSwapper"/>
<Class name="jenkins.util.ProgressiveRendering"/>
<Class name="jenkins.websocket.WebSockets"/>
<Class name="jenkins.websocket.WebSocketSession"/>
<Class name="jenkins.widgets.RunListProgressiveRendering"/>
</Or>
</And>
<And>
<Bug pattern="THROWS_METHOD_THROWS_CLAUSE_THROWABLE"/>
<Or>
<Class name="hudson.EnvVars$GetEnvVars"/>
<Class name="hudson.FilePath"/>
<Class name="hudson.FilePath$GetHomeDirectory"/>
<Class name="hudson.FilePath$IsUnix"/>
<Class name="hudson.Launcher$RemoteChannelLaunchCallable"/>
<Class name="hudson.Launcher$RemoteLaunchCallable"/>
<Class name="hudson.Launcher$RemoteLauncher$KillTask"/>
<Class name="hudson.logging.LogRecorder$SetLevel"/>
<Class name="hudson.model.Computer$DumpExportTableTask"/>
<Class name="hudson.model.Computer$GetFallbackName"/>
<Class name="hudson.model.Computer$ListPossibleNames"/>
<Class name="hudson.model.DirectoryBrowserSupport$BuildChildPaths"/>
<Class name="hudson.model.DirectoryBrowserSupport$IsAbsolute"/>
<Class name="hudson.model.Items"/>
<Class name="hudson.model.Queue"/>
<Class name="hudson.model.Queue$LockedHRCallable"/>
<Class name="hudson.model.Run$AddArtifacts"/>
<Class name="hudson.model.Slave$GetClockDifference1"/>
<Class name="hudson.model.Slave$GetClockDifference2"/>
<Class name="hudson.node_monitors.ArchitectureMonitor$GetArchTask"/>
<Class name="hudson.node_monitors.ResponseTimeMonitor$Step1"/>
<Class name="hudson.node_monitors.ResponseTimeMonitor$Step2"/>
<Class name="hudson.node_monitors.SwapSpaceMonitor$MonitorTask"/>
<Class name="hudson.os.SU"/>
<Class name="hudson.slaves.ChannelPinger$SetUpRemotePing"/>
<Class name="hudson.slaves.ConnectionActivityMonitor$PingCommand"/>
<Class name="hudson.slaves.SlaveComputer$AbsolutePath"/>
<Class name="hudson.slaves.SlaveComputer$CommunicationProtocol"/>
<Class name="hudson.slaves.SlaveComputer$DetectDefaultCharset"/>
<Class name="hudson.slaves.SlaveComputer$DetectOS"/>
<Class name="hudson.slaves.SlaveComputer$ListFullEnvironment"/>
<Class name="hudson.slaves.SlaveComputer$LoadingCount"/>
<Class name="hudson.slaves.SlaveComputer$LoadingPrefetchCacheCount"/>
<Class name="hudson.slaves.SlaveComputer$LoadingTime"/>
<Class name="hudson.slaves.SlaveComputer$SlaveInitializer"/>
<Class name="hudson.slaves.SlaveComputer$SlaveLogFetcher"/>
<Class name="hudson.slaves.SlaveComputer$SlaveVersion"/>
<Class name="hudson.tasks.Maven$MavenInstallation$GetExecutable"/>
<Class name="hudson.tasks.Maven$MavenInstallation$GetMavenVersion"/>
<Class name="hudson.tasks.Shell$DescriptorImpl$Shellinterpreter"/>
<Class name="hudson.util.AtomicFileWriter"/>
<Class name="hudson.util.InvocationInterceptor"/>
<Class name="hudson.util.io.ParserConfigurator$GetParserConfigurators"/>
<Class name="hudson.util.jna.InitializationErrorInvocationHandler"/>
<Class name="hudson.util.jna.RegistryKey"/>
<Class name="hudson.util.ProcessTree$DoVetoersExist"/>
<Class name="hudson.util.ProcessTree$ListAll"/>
<Class name="hudson.util.ProcessTree$OSProcess$CheckVetoes"/>
<Class name="hudson.util.RemotingDiagnostics$GetHeapDump"/>
<Class name="hudson.util.RemotingDiagnostics$GetSystemProperties"/>
<Class name="hudson.util.RemotingDiagnostics$GetThreadDump"/>
<Class name="hudson.util.RemotingDiagnostics$Script"/>
<Class name="jenkins.model.Jenkins$ClockDifferenceCallable"/>
<Class name="jenkins.slaves.restarter.JnlpSlaveRestarterInstaller$FindEffectiveRestarters"/>
<Class name="jenkins.slaves.StandardOutputSwapper$ChannelSwapper"/>
<Class name="jenkins.util.SystemProperties$AgentCopier$CopySystemProperties"/>
<Class name="jenkins.util.VirtualFile$CollectFiles"/>
</Or>
</And>
<And>
<Bug pattern="THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"/>
<Or>
<Class name="hudson.cli.CLICommand"/>
<Class name="hudson.cli.GroovyshCommand"/>
<Class name="hudson.cli.ListChangesCommand"/>
<Class name="hudson.cli.ReloadConfigurationCommand"/>
<Class name="hudson.ExtensionFinder$GuiceFinder$SezpozModule"/>
<Class name="hudson.lifecycle.Lifecycle"/>
<Class name="hudson.model.queue.Executables"/>
<Class name="hudson.model.UpdateCenter"/>
<Class name="hudson.model.UpdateCenter$InstallationJob"/>
<Class name="hudson.model.User"/>
<Class name="hudson.search.ParsedQuickSilver$MethodGetter"/>
<Class name="hudson.security.AbstractPasswordBasedSecurityRealm"/>
<Class name="hudson.security.HudsonPrivateSecurityRealm"/>
<Class name="hudson.security.SecurityRealm"/>
<Class name="hudson.slaves.SlaveComputer"/>
<Class name="hudson.util.ChunkedInputStream"/>
<Class name="hudson.util.ConsistentHash"/>
<Class name="hudson.util.DescribableList$ConverterImpl"/>
<Class name="hudson.util.DoubleLaunchChecker"/>
<Class name="hudson.util.PersistedList"/>
<Class name="hudson.util.PersistedList$ConverterImpl"/>
<Class name="jenkins.ClassLoaderReflectionToolkit"/>
<Class name="jenkins.install.InstallState$InitialSecuritySetup"/>
<Class name="jenkins.install.InstallState$InitialSetupCompleted"/>
<Class name="jenkins.install.SetupWizard"/>
<Class name="jenkins.model.Jenkins"/>
<Class name="jenkins.model.lazy.LazyBuildMixIn"/>
<Class name="jenkins.model.Nodes"/>
<Class name="jenkins.security.AcegiSecurityExceptionFilter"/>
<Class name="jenkins.security.ImpersonatingUserDetailsService"/>
<Class name="jenkins.security.ImpersonatingUserDetailsService2"/>
<Class name="org.acegisecurity.AuthenticationManager"/>
<Class name="org.acegisecurity.userdetails.UserDetailsService"/>
<Class name="org.acegisecurity.util.FieldUtils"/>
</Or>
</And>
<And>
<Bug pattern="UNENCRYPTED_SERVER_SOCKET"/>
<Class name="hudson.slaves.Channels"/>

View File

@ -85,7 +85,7 @@ THE SOFTWARE.
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>1752.v86627a_c48d91</version>
<version>1753.v45c760e2400f</version>
<scope>test</scope>
<exclusions>
<exclusion>
@ -145,13 +145,13 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloudbees-folder</artifactId>
<version>6.722.v8165b_a_cf25e9</version>
<version>6.729.v2b_9d1a_74d673</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1.63</version>
<version>1119.va_a_5e9068da_d7</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -613,17 +613,16 @@ public class AbstractProjectTest {
* </div>
*/
DomNodeList<DomNode> domNodes = htmlPage.getDocumentElement().querySelectorAll("*");
assertThat(domNodes, hasSize(5));
assertThat(domNodes, hasSize(4));
assertEquals("head", domNodes.get(0).getNodeName());
assertEquals("body", domNodes.get(1).getNodeName());
assertEquals("div", domNodes.get(2).getNodeName());
assertEquals("img", domNodes.get(3).getNodeName());
assertEquals("a", domNodes.get(4).getNodeName());
assertEquals("a", domNodes.get(3).getNodeName());
// only: "><img src=x onerror=alert(123)>
// the first double quote was escaped during creation (with the backslash)
String unquotedLabel = Label.parseExpression(label).getName();
HtmlAnchor anchor = (HtmlAnchor) domNodes.get(4);
HtmlAnchor anchor = (HtmlAnchor) domNodes.get(3);
assertThat(anchor.getHrefAttribute(), containsString(Util.rawEncode(unquotedLabel)));
assertThat(responseContent, containsString("ok"));

View File

@ -177,7 +177,7 @@ public class NumberTest {
input.reset(); // Remove the value that already in the <input>
input.type(value); // Type value to <input>
input.fireEvent(Event.TYPE_CHANGE); // The error message is triggered by change event
return input.getParentNode().getNextSibling().getChildNodes().get(1).getChildNodes().get(0).getTextContent();
return input.getParentNode().getNextSibling().getTextContent();
}

View File

@ -542,64 +542,6 @@ div.behavior-loading {
padding: 0;
}
/* ======================== error/warning message (mainly in the form.) Use them on block elements ======================== */
.error {
color: #c00;
font-weight: bold;
padding-left: 20px;
min-height: 16px;
line-height: 16px;
background-image: url("../../images/svgs/error.svg");
background-position: left top;
background-repeat: no-repeat;
background-size: 16px 16px;
}
.error-inline {
color: #c00;
font-weight: bold;
}
.warning {
color: #c4a000;
font-weight: bold;
padding-left: 20px;
min-height: 16px;
line-height: 16px;
background-image: url("../../images/svgs/warning.svg");
background-position: left top;
background-repeat: no-repeat;
background-size: 16px 16px;
}
.warning-inline {
color: #c4a000;
font-weight: bold;
}
.info {
position: relative;
color: var(--text-color);
font-weight: bold;
min-height: 16px;
padding-left: 30px;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 20px;
background: currentColor;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'%3E%3Ctitle%3EArrow Forward%3C/title%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='32' d='M268 112l144 144-144 144M392 256H100'/%3E%3C/svg%3E");
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
}
}
.icon16x16 {
width: 16px;
height: 16px;

View File

@ -0,0 +1,84 @@
.validation-error-area {
transition: var(--standard-transition);
opacity: 0;
height: 0;
overflow: hidden;
}
.validation-error-area--visible {
margin-top: 0.75rem;
opacity: 1;
& > * {
animation: animate-validation-error-area var(--standard-transition);
}
}
@keyframes animate-validation-error-area {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.error,
.warning,
.info {
position: relative;
padding-left: calc(22px + 0.4rem);
font-weight: 500;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 22px;
height: 22px;
background-color: currentColor;
mask-position: top center;
mask-repeat: no-repeat;
mask-size: contain;
}
}
.ok {
color: var(--text-color-secondary);
}
.error {
color: var(--red);
&::before {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'%3E%3Ctitle%3Eionicons-v5-a%3C/title%3E%3Cpath d='M256,48C141.31,48,48,141.31,48,256s93.31,208,208,208,208-93.31,208-208S370.69,48,256,48Zm0,319.91a20,20,0,1,1,20-20A20,20,0,0,1,256,367.91Zm21.72-201.15-5.74,122a16,16,0,0,1-32,0l-5.74-121.94v-.05a21.74,21.74,0,1,1,43.44,0Z'/%3E%3C/svg%3E");
}
}
.error-inline {
color: var(--red);
font-weight: 500;
}
.warning {
color: var(--orange);
&::before {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'%3E%3Ctitle%3Eionicons-v5-r%3C/title%3E%3Cpath d='M449.07,399.08,278.64,82.58c-12.08-22.44-44.26-22.44-56.35,0L51.87,399.08A32,32,0,0,0,80,446.25H420.89A32,32,0,0,0,449.07,399.08Zm-198.6-1.83a20,20,0,1,1,20-20A20,20,0,0,1,250.47,397.25ZM272.19,196.1l-5.74,122a16,16,0,0,1-32,0l-5.74-121.95v0a21.73,21.73,0,0,1,21.5-22.69h.21a21.74,21.74,0,0,1,21.73,22.7Z'/%3E%3C/svg%3E");
}
}
.warning-inline {
color: var(--orange);
font-weight: 500;
}
.info {
color: var(--text-color);
&::before {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'%3E%3Ctitle%3EArrow Forward%3C/title%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='32' d='M268 112l144 144-144 144M392 256H100'/%3E%3C/svg%3E");
}
}

View File

@ -32,6 +32,7 @@ html {
@import './form/search';
@import './form/select';
@import './form/toggle-switch';
@import './form/validation';
@import './modules/app-bar';
@import './modules/badges';

View File

@ -232,7 +232,7 @@ var FormChecker = {
this.sendRequest(next.url, {
method : next.method,
onComplete : function(x) {
applyErrorMessage(next.target, x);
updateValidationArea(next.target, x.responseText);
FormChecker.inProgress--;
FormChecker.schedule();
layoutUpdateCallback.call();
@ -500,16 +500,53 @@ var tooltip;
// Behavior rules
//========================================================
// using tag names in CSS selector makes the processing faster
/**
* Updates the validation area for a form element
* @param {HTMLElement} validationArea The validation area for a given form element
* @param {string} content The content to update the validation area with
*/
function updateValidationArea(validationArea, content) {
validationArea.classList.add("validation-error-area--visible");
if (content === "<div/>") {
validationArea.classList.remove("validation-error-area--visible");
validationArea.style.height = "0px";
validationArea.innerHTML = content;
} else {
// Only change content if different, causes an unnecessary animation otherwise
if (validationArea.innerHTML !== content) {
validationArea.innerHTML = content;
validationArea.style.height = validationArea.children[0].offsetHeight + "px";
// Only include the notice in the validation-error-area, move all other elements out
if (validationArea.children.length > 1) {
Array.from(validationArea.children).slice(1).forEach((element) => {
validationArea.after(element);
})
}
Behaviour.applySubtree(validationArea);
// For errors with additional details, apply the subtree to the expandable details pane
if (validationArea.nextElementSibling) {
Behaviour.applySubtree(validationArea.nextElementSibling);
}
}
}
}
function registerValidator(e) {
// Retrieve the validation error area
var tr = findFollowingTR(e, "validation-error-area");
var tr = e.closest(".jenkins-form-item").querySelector(".validation-error-area");
if (!tr) {
console.warn("Couldn't find the expected parent element (.setting-main) for element", e)
console.warn("Couldn't find the expected validation element (.validation-error-area) for element",
e.closest(".jenkins-form-item"))
return;
}
// find the validation-error-area
e.targetElement = tr.firstElementChild.nextSibling;
e.targetElement = tr;
e.targetUrl = function() {
var url = this.getAttribute("checkUrl");
@ -545,19 +582,13 @@ function registerValidator(e) {
}
var checker = function() {
var target = this.targetElement;
const validationArea = this.targetElement;
FormChecker.sendRequest(this.targetUrl(), {
method : method,
onComplete : function(x) {
if (x.status == 200) {
// All FormValidation responses are 200
target.innerHTML = x.responseText;
} else {
// Content is taken from FormValidation#_errorWithMarkup
// TODO Add i18n support
target.innerHTML = "<div class='error'>An internal error occurred during form field validation (HTTP " + x.status + "). Please reload the page and if the problem persists, ask the administrator for help.</div>";
}
Behaviour.applySubtree(target);
onComplete: function({status, responseText}) {
// TODO Add i18n support
const errorMessage = `<div class="error">An internal error occurred during form field validation (HTTP ${status}). Please reload the page and if the problem persists, ask the administrator for help.</div>`;
updateValidationArea(validationArea, status === 200 ? responseText : errorMessage);
}
});
}
@ -584,22 +615,25 @@ function registerValidator(e) {
}
function registerRegexpValidator(e,regexp,message) {
var tr = findFollowingTR(e, "validation-error-area");
var tr = e.closest(".jenkins-form-item").querySelector( ".validation-error-area");
if (!tr) {
console.warn("Couldn't find the expected parent element (.setting-main) for element", e)
console.warn("Couldn't find the expected parent element (.setting-main) for element",
e.closest(".jenkins-form-item"))
return;
}
// find the validation-error-area
e.targetElement = tr.firstElementChild.nextSibling;
e.targetElement = tr;
var checkMessage = e.getAttribute('checkMessage');
if (checkMessage) message = checkMessage;
var oldOnchange = e.onchange;
e.onchange = function() {
var set = oldOnchange != null ? oldOnchange.call(this) : false;
if (this.value.match(regexp)) {
if (!set) this.targetElement.innerHTML = "<div/>";
if (!set) {
updateValidationArea(this.targetElement, `<div/>`)
}
} else {
this.targetElement.innerHTML = "<div class=error>" + message + "</div>";
updateValidationArea(this.targetElement, `<div class="error">${message}</div>`);
set = true;
}
return set;
@ -613,13 +647,14 @@ function registerRegexpValidator(e,regexp,message) {
* @param e Input element
*/
function registerMinMaxValidator(e) {
var tr = findFollowingTR(e, "validation-error-area");
var tr = e.closest(".jenkins-form-item").querySelector( ".validation-error-area");
if (!tr) {
console.warn("Couldn't find the expected parent element (.setting-main) for element", e)
console.warn("Couldn't find the expected parent element (.setting-main) for element",
e.closest(".jenkins-form-item"))
return;
}
// find the validation-error-area
e.targetElement = tr.firstElementChild.nextSibling;
e.targetElement = tr;
var checkMessage = e.getAttribute('checkMessage');
if (checkMessage) message = checkMessage;
var oldOnchange = e.onchange;
@ -638,29 +673,35 @@ function registerMinMaxValidator(e) {
if (min <= max) { // Add the validator if min <= max
if (parseInt(min) > parseInt(this.value) || parseInt(this.value) > parseInt(max)) { // The value is out of range
this.targetElement.innerHTML = "<div class=error>This value should be between " + min + " and " + max + "</div>";
updateValidationArea(this.targetElement, `<div class="error">This value should be between ${min} and ${max}</div>`);
set = true;
} else {
if (!set) this.targetElement.innerHTML = "<div/>"; // The value is valid
if (!set) {
updateValidationArea(this.targetElement, `<div/>`)
}
}
}
} else if ((min !== null && isInteger(min)) && (max === null || !isInteger(max))) { // There is only 'min' available
if (parseInt(min) > parseInt(this.value)) {
this.targetElement.innerHTML = "<div class=error>This value should be larger than " + min + "</div>";
updateValidationArea(this.targetElement, `<div class="error">This value should be larger than ${min}</div>`);
set = true;
} else {
if (!set) this.targetElement.innerHTML = "<div/>";
if (!set) {
updateValidationArea(this.targetElement, `<div/>`)
}
}
} else if ((min === null || !isInteger(min)) && (max !== null && isInteger(max))) { // There is only 'max' available
if (parseInt(max) < parseInt(this.value)) {
this.targetElement.innerHTML = "<div class=error>This value should be less than " + max + "</div>";
updateValidationArea(this.targetElement, `<div class="error">This value should be less than ${max}</div>`);
set = true;
} else {
if (!set) this.targetElement.innerHTML = "<div/>";
if (!set) {
updateValidationArea(this.targetElement, `<div/>`)
}
}
}
}
@ -2324,15 +2365,16 @@ function validateButton(checkUrl,paramList,button) {
}
});
var spinner = $(button).up("DIV").next();
var target = spinner.next();
var spinner = button.up("DIV").children[0];
var target = spinner.next().next();
spinner.style.display="block";
new Ajax.Request(checkUrl, {
parameters: parameters,
onComplete: function(rsp) {
spinner.style.display="none";
applyErrorMessage(target, rsp);
target.innerHTML = `<div class="validation-error-area" />`;
updateValidationArea(target.children[0], rsp.responseText);
layoutUpdateCallback.call();
var s = rsp.getResponseHeader("script");
try {
@ -2344,26 +2386,6 @@ function validateButton(checkUrl,paramList,button) {
});
}
function applyErrorMessage(elt, rsp) {
if (rsp.status == 200) {
elt.innerHTML = rsp.responseText;
} else {
var id = 'valerr' + (iota++);
elt.innerHTML = '<a href="" onclick="document.getElementById(\'' + id
+ '\').style.display=\'block\';return false">ERROR</a><div id="'
+ id + '" style="display:none">' + rsp.responseText + '</div>';
var error = document.getElementById('error-description'); // cf. oops.jelly
if (error) {
var div = document.getElementById(id);
while (div.firstElementChild) {
div.removeChild(div.firstElementChild);
}
div.appendChild(error);
}
}
Behaviour.applySubtree(elt);
}
// create a combobox.
// @param idOrField
// ID of the <input type=text> element that becomes a combobox, or the field itself.