diff --git a/.gitignore b/.gitignore index 60ba89065c90d09f0cc1ea8935b0bf08b691b0e9..5c8b1b7ac8b0be6f49db372e82de07e26bdac765 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ out .project build +# VSCode workspace file +*.code-workspace + # vim *~ *.swp @@ -49,4 +52,5 @@ jenkins_*.changes *.zip push-build.sh war/node_modules/ +war/yarn-error.log .java-version diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2d496cc2b211139d6f9b2eee27cb0be380ec8f5 --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,7 @@ + + + io.jenkins.tools.incrementals + git-changelist-maven-extension + 1.0-beta-4 + + diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000000000000000000000000000000000000..e54fdacfe62468e4e85ebe51890384926933b476 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-Pmight-produce-incrementals diff --git a/BUILDING.TXT b/BUILDING.TXT deleted file mode 100644 index bde5cadf353210259628baa34b1813f0acddc5b1..0000000000000000000000000000000000000000 --- a/BUILDING.TXT +++ /dev/null @@ -1,11 +0,0 @@ -If you want simply to have the jenkins.war as fast as possible (without test -execution), run: - - mvn clean install -pl war -am -DskipTests - -The WAR file will be in war/target/jenkins.war (you can play with it) - -For more information on building Jenkins, visit -https://wiki.jenkins-ci.org/display/JENKINS/Building+Jenkins - -Have Fun !! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 01de1fea2cc3acf1ea8e561dcb74edb6cdd64aba..ab12568a0a3b58e3b1789651e0d0f0d3343556df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ This page provides information about contributing code to the Jenkins core codeb - In Jenkins project we usually use [OpenJDK](http://openjdk.java.net/), but you can use other JDKs as well. - Java 9 is **not supported** in Jenkins. - * Maven 3.3.9 or above. You can download it [here](https://maven.apache.org/download.cgi) + * Maven 3.5.3 or above. You can download it [here](https://maven.apache.org/download.cgi) * Any IDE which supports importing Maven projects 4. Setup your development environment as described in [Preparing for Plugin Development](https://jenkins.io/doc/developer/tutorial/prepare/) @@ -30,7 +30,7 @@ Building and debugging process is described [here](https://jenkins.io/doc/develo If you want simply to have the `jenkins.war` file as fast as possible without tests, run: - mvn clean package -pl war -am -DskipTests + mvn clean package -pl war -am -DskipTests -Dfindbugs.skip The WAR file will be created in `war/target/jenkins.war`. After that you can start Jenkins using Java CLI ([guide](https://wiki.jenkins.io/display/JENKINS/Starting+and+Accessing+Jenkins)). @@ -46,6 +46,12 @@ Functional tests (`test` module) take a while even on server-grade machines. Most of the tests will be launched by the continuous integration instance, so there is no strict need to run full test suites before proposing a pull request. +There are 3 profiles for tests: + +* `light-test` - only unit tests, no functional tests +* `smoke-test` - run unit tests + a number of functional tests +* `all-tests` - Runs all tests, with re-run (default) + In addition to the included tests, you can also find extra integration and UI tests in the [Acceptance Test Harness (ATH)](https://github.com/jenkinsci/acceptance-test-harness) repository. If you propose complex UI changes, you should create new ATH tests for them. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d4541bdfcbaa7904cfc9264f6a823d80d1b55992 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# This is a Dockerfile definition for Experimental Docker builds. +# DockerHub: https://hub.docker.com/r/jenkins/jenkins-experimental/ +# If you are looking for official images, see https://github.com/jenkinsci/docker +FROM maven:3.5.4-jdk-8 as builder + +COPY .mvn/ /jenkins/src/.mvn/ +COPY cli/ /jenkins/src/cli/ +COPY core/ /jenkins/src/core/ +COPY src/ /jenkins/src/src/ +COPY test/ /jenkins/src/test/ +COPY war/ /jenkins/src/war/ +COPY *.xml /jenkins/src/ +COPY LICENSE.txt /jenkins/src/LICENSE.txt +COPY licenseCompleter.groovy /jenkins/src/licenseCompleter.groovy +COPY show-pom-version.rb /jenkins/src/show-pom-version.rb + +WORKDIR /jenkins/src/ +RUN mvn clean install --batch-mode -Plight-test + +# The image is based on the previous weekly, new changes in jenkinci/docker are not applied +FROM jenkins/jenkins:latest + +LABEL Description="This is an experimental image for the master branch of the Jenkins core" Vendor="Jenkins Project" + +COPY --from=builder /jenkins/src/war/target/jenkins.war /usr/share/jenkins/jenkins.war +ENTRYPOINT ["tini", "--", "/usr/local/bin/jenkins.sh"] diff --git a/Jenkinsfile b/Jenkinsfile index 5d2a10435f3932cdcc36d3b64390aeb3fba33031..a3660f59f0070fb5efebeb913ad420d2e603aca3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,15 +13,18 @@ def runTests = true def failFast = false -properties([buildDiscarder(logRotator(numToKeepStr: '50', artifactNumToKeepStr: '20'))]) +properties([buildDiscarder(logRotator(numToKeepStr: '50', artifactNumToKeepStr: '20')), durabilityHint('PERFORMANCE_OPTIMIZED')]) // see https://github.com/jenkins-infra/documentation/blob/master/ci.adoc for information on what node types are available def buildTypes = ['Linux', 'Windows'] +def jdks = [8, 11] def builds = [:] for(i = 0; i < buildTypes.size(); i++) { +for(j = 0; j < jdks.size(); j++) { def buildType = buildTypes[i] - builds[buildType] = { + def jdk = jdks[j] + builds["${buildType}-jdk${jdk}"] = { node(buildType.toLowerCase()) { timestamps { // First stage is actually checking out the source. Since we're using Multibranch @@ -30,16 +33,20 @@ for(i = 0; i < buildTypes.size(); i++) { checkout scm } + def changelistF = "${pwd tmp: true}/changelist" + def m2repo = "${pwd tmp: true}/m2repo" + // Now run the actual build. stage("${buildType} Build / Test") { timeout(time: 180, unit: 'MINUTES') { // See below for what this method does - we're passing an arbitrary environment // variable to it so that JAVA_OPTS and MAVEN_OPTS are set correctly. withMavenEnv(["JAVA_OPTS=-Xmx1536m -Xms512m", - "MAVEN_OPTS=-Xmx1536m -Xms512m"]) { + "MAVEN_OPTS=-Xmx1536m -Xms512m"], jdk) { // Actually run Maven! // -Dmaven.repo.local=… tells Maven to create a subdir in the temporary directory for the local Maven repository - def mvnCmd = "mvn -Pdebug -U javadoc:javadoc clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -Dmaven.repo.local=${pwd tmp: true}/m2repo -s settings-azure.xml -e" + def mvnCmd = "mvn -Pdebug -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -Dmaven.repo.local=$m2repo -s settings-azure.xml -e" + if(isUnix()) { sh mvnCmd sh 'test `git status --short | tee /dev/stderr | wc --bytes` -eq 0' @@ -52,33 +59,62 @@ for(i = 0; i < buildTypes.size(); i++) { // Once we've built, archive the artifacts and the test results. stage("${buildType} Publishing") { - def files = findFiles(glob: '**/target/*.jar, **/target/*.war, **/target/*.hpi') - renameFiles(files, buildType.toLowerCase()) - - archiveArtifacts artifacts: '**/target/*.jar, **/target/*.war, **/target/*.hpi', - fingerprint: true if (runTests) { junit healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml' } + if (buildType == 'Linux') { + def changelist = readFile(changelistF) + dir(m2repo) { + archiveArtifacts artifacts: "**/*$changelist/*$changelist*", + excludes: '**/*.lastUpdated,**/jenkins-test/', + allowEmptyArchive: true, // in case we forgot to reincrementalify + fingerprint: true + } + } } } } } +}} + +// TODO: ATH flow now supports Java 8 only, it needs to be reworked (INFRA-1690) +builds.ath = { + node("docker&&highmem") { + // Just to be safe + deleteDir() + def fileUri + def metadataPath + dir("sources") { + checkout scm + withMavenEnv(["JAVA_OPTS=-Xmx1536m -Xms512m", + "MAVEN_OPTS=-Xmx1536m -Xms512m"], 8) { + sh "mvn --batch-mode --show-version -DskipTests -am -pl war package -Dmaven.repo.local=${pwd tmp: true}/m2repo -s settings-azure.xml" + } + dir("war/target") { + fileUri = "file://" + pwd() + "/jenkins.war" + } + metadataPath = pwd() + "/essentials.yml" + } + dir("ath") { + runATH jenkins: fileUri, metadataFile: metadataPath + } + } } builds.failFast = failFast parallel builds +infra.maybePublishIncrementals() // This method sets up the Maven and JDK tools, puts them in the environment along // with whatever other arbitrary environment variables we passed in, and runs the // body we passed in within that environment. -void withMavenEnv(List envVars = [], def body) { +void withMavenEnv(List envVars = [], def javaVersion, def body) { // The names here are currently hardcoded for my test environment. This needs // to be made more flexible. // Using the "tool" Workflow call automatically installs those tools on the // node. String mvntool = tool name: "mvn", type: 'hudson.tasks.Maven$MavenInstallation' - String jdktool = tool name: "jdk8", type: 'hudson.model.JDK' + String jdktool = tool name: "jdk${javaVersion}", type: 'hudson.model.JDK' // Set JAVA_HOME, MAVEN_HOME and special PATH variables for the tools we're // using. @@ -92,15 +128,3 @@ void withMavenEnv(List envVars = [], def body) { body.call() } } - -void renameFiles(def files, String prefix) { - for(i = 0; i < files.length; i++) { - def newPath = files[i].path.replace(files[i].name, "${prefix}-${files[i].name}") - def rename = "${files[i].path} ${newPath}" - if(isUnix()) { - sh "mv ${rename}" - } else { - bat "move ${rename}" - } - } -} diff --git a/cli/pom.xml b/cli/pom.xml index 5ccf113f0c4c1b1a3e9dfb4192d4680ecdf1d348..670e416a26dd5c5f5bc6943ad046e9f8c4c41c00 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -4,8 +4,8 @@ org.jenkins-ci.main - pom - 2.104-SNAPSHOT + jenkins-parent + ${revision}${changelist} cli @@ -13,6 +13,11 @@ Jenkins cli Command line interface for Jenkins + + + Medium + + org.powermock @@ -21,13 +26,17 @@ org.powermock - powermock-api-mockito + powermock-api-mockito2 test org.kohsuke access-modifier-annotation + + org.jenkins-ci + annotation-indexer + commons-codec commons-codec @@ -54,7 +63,7 @@ org.apache.sshd sshd-core - 1.6.0 + 1.7.0 true @@ -67,6 +76,15 @@ trilead-ssh2 build214-jenkins-1 + + com.google.code.findbugs + annotations + provided + + + commons-lang + commons-lang + @@ -89,7 +107,7 @@ hudson.cli.CLI - ${build.version} + ${project.version} diff --git a/cli/src/filter/resources/jenkins/cli/jenkins-cli-version.properties b/cli/src/filter/resources/jenkins/cli/jenkins-cli-version.properties index 39b91895a20a85254399d1f4f0206c6af45ff094..defbd48204e4784090fe0e17e28fe5bc395abf47 100644 --- a/cli/src/filter/resources/jenkins/cli/jenkins-cli-version.properties +++ b/cli/src/filter/resources/jenkins/cli/jenkins-cli-version.properties @@ -1 +1 @@ -version=${build.version} +version=${project.version} diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index cbd3acd04458b6fe1416089e1660784f54bf6686..0ffef53f9305a6e9d260826e092e805fe9b8465e 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -77,6 +77,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import static java.util.logging.Level.*; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; /** * CLI entry point to Jenkins. @@ -452,6 +453,10 @@ public class CLI implements AutoCloseable { String user = null; String auth = null; + + String userIdEnv = System.getenv("JENKINS_USER_ID"); + String tokenEnv = System.getenv("JENKINS_API_TOKEN"); + boolean strictHostKey = false; while(!args.isEmpty()) { @@ -563,6 +568,17 @@ public class CLI implements AutoCloseable { return -1; } + if (auth == null) { + // -auth option not set + if (StringUtils.isNotBlank(userIdEnv) && StringUtils.isNotBlank(tokenEnv)) { + auth = StringUtils.defaultString(userIdEnv).concat(":").concat(StringUtils.defaultString(tokenEnv)); + } else if (StringUtils.isNotBlank(userIdEnv) || StringUtils.isNotBlank(tokenEnv)) { + printUsage(Messages.CLI_BadAuth()); + return -1; + } // Otherwise, none credentials were set + + } + if (!url.endsWith("/")) { url += '/'; } @@ -689,13 +705,13 @@ public class CLI implements AutoCloseable { connection.sendLocale(Locale.getDefault().toString()); connection.sendStart(); connection.begin(); - final OutputStream stdin = connection.streamStdin(); new Thread("input reader") { @Override public void run() { try { + final OutputStream stdin = connection.streamStdin(); int c; - while ((c = System.in.read()) != -1) { // TODO use InputStream.available + while (!connection.complete && (c = System.in.read()) != -1) { stdin.write(c); } connection.sendEndStdin(); @@ -809,9 +825,14 @@ public class CLI implements AutoCloseable { return authenticate(Collections.singleton(key)); } + /** For access from {@code HelpCommand}. */ + static String usage() { + return Messages.CLI_Usage(); + } + private static void printUsage(String msg) { if(msg!=null) System.out.println(msg); - System.err.println(Messages.CLI_Usage()); + System.err.println(usage()); } static final Logger LOGGER = Logger.getLogger(CLI.class.getName()); diff --git a/cli/src/main/java/hudson/cli/Connection.java b/cli/src/main/java/hudson/cli/Connection.java index 017051a64a646a76b4ecfe81966381e3874adbca..eacc567a7e5d822afc9e4d1b20afc5473cbb0b3b 100644 --- a/cli/src/main/java/hudson/cli/Connection.java +++ b/cli/src/main/java/hudson/cli/Connection.java @@ -55,6 +55,7 @@ import java.security.Signature; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; +import org.jenkinsci.remoting.util.AnonymousClassWarnings; /** * Used by Jenkins core only in deprecated Remoting-based CLI. @@ -102,7 +103,7 @@ public class Connection { * Sends a serializable object. */ public void writeObject(Object o) throws IOException { - ObjectOutputStream oos = new ObjectOutputStream(out); + ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(out); oos.writeObject(o); // don't close oss, which will close the underlying stream // no need to flush either, given the way oos is implemented diff --git a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java index d7753a750498aff78325fc04326eb63b7e45209d..9f87dd4a7b38a174229ccfc02b04779b51002951 100644 --- a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java +++ b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java @@ -71,7 +71,7 @@ public class PrivateKeyProvider { /** * Read keys from default keyFiles * - * .ssh/id_rsa, .ssh/id_dsa and .ssh/identity. + * {@code .ssh/id_rsa}, {@code .ssh/id_dsa} and {@code .ssh/identity}. * * @return true if some key was read successfully. */ diff --git a/cli/src/main/java/hudson/cli/SSHCLI.java b/cli/src/main/java/hudson/cli/SSHCLI.java index bdd6c2beb50e5e696af3467e5bae7598f41eb5ef..39dc9210a3e1615bac888932c689d97ad4dd1b4d 100644 --- a/cli/src/main/java/hudson/cli/SSHCLI.java +++ b/cli/src/main/java/hudson/cli/SSHCLI.java @@ -51,6 +51,8 @@ import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.NoCloseOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + /** * Implements SSH connection mode of {@link CLI}. * In a separate class to avoid any class loading of {@code sshd-core} when not using {@code -ssh} mode. @@ -59,6 +61,7 @@ import org.apache.sshd.common.util.security.SecurityUtils; */ class SSHCLI { + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Due to whatever reason FindBugs reports it fot try-with-resources") static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider URL url = new URL(jenkinsUrl + "login"); diff --git a/cli/src/main/resources/hudson/cli/client/Messages.properties b/cli/src/main/resources/hudson/cli/client/Messages.properties index 921fe67a211da560ac75fc355179a117b3c00dc2..2e7fde44cb075a87813b4cced7eb48f2952c85cd 100644 --- a/cli/src/main/resources/hudson/cli/client/Messages.properties +++ b/cli/src/main/resources/hudson/cli/client/Messages.properties @@ -1,22 +1,25 @@ CLI.Usage=Jenkins CLI\n\ Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\ Options:\n\ - -s URL : the server URL (defaults to the JENKINS_URL env var)\n\ - -http : use a plain CLI protocol over HTTP(S) (the default; mutually exclusive with -ssh and -remoting)\n\ - -ssh : use SSH protocol (requires -user; SSH port must be open on server, and user must have registered a public key)\n\ - -remoting : use deprecated Remoting channel protocol (if enabled on server; for compatibility with legacy commands or command modes only)\n\ - -i KEY : SSH private key file used for authentication (for use with -ssh or -remoting)\n\ - -p HOST:PORT : HTTP proxy host and port for HTTPS proxy tunneling. See https://jenkins.io/redirect/cli-https-proxy-tunnel\n\ - -noCertificateCheck : bypass HTTPS certificate check entirely. Use with caution\n\ - -noKeyAuth : don't try to load the SSH authentication private key. Conflicts with -i\n\ - -user : specify user (for use with -ssh)\n\ - -strictHostKey : request strict host key checking (for use with -ssh)\n\ - -logger FINE : enable detailed logging from the client\n\ - -auth [ USER:SECRET | @FILE ] : specify username and either password or API token (or load from them both from a file);\n\ - for use with -http, or -remoting but only when the JNLP agent port is disabled\n\ + \ -s URL : the server URL (defaults to the JENKINS_URL env var)\n\ + \ -http : use a plain CLI protocol over HTTP(S) (the default; mutually exclusive with -ssh and -remoting)\n\ + \ -ssh : use SSH protocol (requires -user; SSH port must be open on server, and user must have registered a public key)\n\ + \ -remoting : use deprecated Remoting channel protocol (if enabled on server; for compatibility with legacy commands or command modes only)\n\ + \ -i KEY : SSH private key file used for authentication (for use with -ssh or -remoting)\n\ + \ -p HOST:PORT : HTTP proxy host and port for HTTPS proxy tunneling. See https://jenkins.io/redirect/cli-https-proxy-tunnel\n\ + \ -noCertificateCheck : bypass HTTPS certificate check entirely. Use with caution\n\ + \ -noKeyAuth : don't try to load the SSH authentication private key. Conflicts with -i\n\ + \ -user : specify user (for use with -ssh)\n\ + \ -strictHostKey : request strict host key checking (for use with -ssh)\n\ + \ -logger FINE : enable detailed logging from the client\n\ + \ -auth [ USER:SECRET | @FILE ] : specify username and either password or API token (or load from them both from a file);\n\ + \ for use with -http, or -remoting but only when the JNLP agent port is disabled.\n\ + \ Passing crendentials by a file is recommended.\n\ + \ See https://jenkins.io/redirect/cli-http-connection-mode for more info and options.\n\ \n\ - The available commands depend on the server. Run the 'help' command to\n\ + The available commands depend on the server. Run the 'help' command to \ see the list. CLI.NoURL=Neither -s nor the JENKINS_URL env var is specified. CLI.VersionMismatch=Version mismatch. This CLI cannot work with this Jenkins server. CLI.NoSuchFileExists=No such file exists: {0} +CLI.BadAuth=The JENKINS_USER_ID and JENKINS_API_TOKEN env vars should be both set or left empty. diff --git a/cli/src/main/resources/hudson/cli/client/Messages_es.properties b/cli/src/main/resources/hudson/cli/client/Messages_es.properties index f9979a5619831289dfb4952ffe328e4d6d6b9798..b8ff42b3c23b707bdd8b105f53a624e034ebcc7d 100644 --- a/cli/src/main/resources/hudson/cli/client/Messages_es.properties +++ b/cli/src/main/resources/hudson/cli/client/Messages_es.properties @@ -1,10 +1,24 @@ CLI.Usage=Jenkins CLI\n\ - Usar: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\ - Options:\n\ - \ -s URL : direccin web (por defecto se usa la variable JENKINS_URL)\n\ + Uso: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\ + Opciones:\n\ + -s URL : direcci\u00f3n web (por defecto se usa la variable JENKINS_URL)\n\ + -http : usa un protocolo plano sobre HTTP(S) (es el uso por defecto; excluyente con -ssh y -remoting)\n\ + -ssh : usa el protocolo SSH (requiere -user; el puerto SSH debe estar abierto en el servidor y el usuario debe tener registrada su clave publica)\n\ + -remoting : usa el protocolo deprecado de Remoting (siempre que est\u00e9 habilitado en el servidor; s\u00f3lo para compatibilidad con comandos heredados o legacy)\n\ + -i KEY : clave privada SSH usada para autenticaci\u00f3n (usado con -ssh o -remoting)\n\ + -p HOST:PORT : host y puerto para el uso de proxy HTTPS. Ver https://jenkins.io/redirect/cli-https-proxy-tunnel\n\ + -noCertificateCheck : elude por completo la verificaci\u00f3n del certificado HTTPS. Usar con precauci\u00f3n\n\ + -noKeyAuth : intenta no cargar la clave privada de autenticaci\u00f3n SSH. Presenta conflicto con -i\n\ + -user : especifica el usuario (se usa con -ssh)\n\ + -strictHostKey : solicita la comprobaci\u00f3n estricta de la clave del host (se usa con -ssh)\n\ + -logger FINE : habilita logs detallados en el cliente\n\ + -auth [ USER:SECRET | @FILE ] : especifica el usuario y contrase\u00f1a o API token (o los carga de un fichero);\n\ + se usa con -http o -remoting pero s\u00f3lo si el puerto del agente JNLP est\u00e1 deshabilitado.\n\ + No es necesario si las variables de entorno JENKINS_USER_ID y JENKINS_API_TOKEN se encuentran configuradas.\n\ \n\ - La lista completa de comandos disponibles depende del servidor. Ejecuta\n\ - el comando ''help'' para ver la lista. -CLI.NoURL=No se ha especificado el parmetro -s ni la variable JENKINS_URL -CLI.VersionMismatch=La versin no coincide. Esta consola "CLI" no se puede usar en este Jenkins + La lista de comandos disponibles depende del servidor. Ejecuta\n\ + el comando ''help'' para ver la lista completa. +CLI.NoURL=No se ha especificado el par\u00e1metro -s ni la variable JENKINS_URL +CLI.VersionMismatch=La versi\u00f3n no coincide. Esta consola "CLI" no se puede usar en este Jenkins CLI.NoSuchFileExists=El fichero {0} no existe. +CLI.BadAuth=Ambas variables de entorno JENKINS_USER_ID y JENKINS_API_TOKEN deber\u00edan estar configuradas o mantenerse vac\u00edas. diff --git a/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java b/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java index d5636f3bd74833ca4075971865647c9312d91aea..a29af91e6eb818245140e987dda1c323786a3fd8 100644 --- a/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java +++ b/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java @@ -113,15 +113,9 @@ public class PrivateKeyProviderTest { private Iterable withKeyPairs(final KeyPair... expected) { return Mockito.argThat(new ArgumentMatcher>() { - @Override public void describeTo(Description description) { - description.appendText(Arrays.asList(expected).toString()); - } - - @Override public boolean matches(Object argument) { - if (!(argument instanceof Iterable)) throw new IllegalArgumentException("Not an instance of Iterrable"); - @SuppressWarnings("unchecked") - final Iterable actual = (Iterable) argument; + @Override + public boolean matches(Iterable actual) { int i = 0; for (KeyPair akp: actual) { if (!eq(expected[i].getPublic(), akp.getPublic())) return false; diff --git a/core/pom.xml b/core/pom.xml index ce9d90f5abd7db0ec75e0a3291610c9d0e1b57c5..cbbf3f30616771fbd074ee27f7f709e815ef576c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -28,8 +28,8 @@ THE SOFTWARE. org.jenkins-ci.main - pom - 2.104-SNAPSHOT + jenkins-parent + ${revision}${changelist} jenkins-core @@ -39,11 +39,9 @@ THE SOFTWARE. true - 1.254 + 1.255 2.5.6.SEC03 - 2.4.11 - - true + 2.4.12 @@ -111,7 +109,7 @@ THE SOFTWARE. com.github.jnr jnr-posix - 3.0.41 + 3.0.45 org.kohsuke @@ -209,17 +207,16 @@ THE SOFTWARE. org.jenkins-ci annotation-indexer - 1.12 org.jenkins-ci bytecode-compatibility-transformer - 1.8 + 2.0-beta-2 org.jenkins-ci task-reactor - 1.4 + 1.5 org.jvnet.localizer @@ -246,6 +243,20 @@ THE SOFTWARE. + + + xpp3 + xpp3 + 1.1.4c + + + net.sf.kxml + kxml2 + 2.3.0 + jfree jfreechart @@ -268,7 +279,6 @@ THE SOFTWARE. commons-lang commons-lang - 2.6 commons-digester @@ -452,11 +462,6 @@ THE SOFTWARE. spring-aop ${spring.version} - - xpp3 - xpp3 - 1.1.4c - junit junit @@ -474,7 +479,7 @@ THE SOFTWARE. org.powermock - powermock-api-mockito + powermock-api-mockito2 test @@ -508,7 +513,7 @@ THE SOFTWARE. org.jvnet.winp winp - 1.25 + 1.27 org.jenkins-ci @@ -528,7 +533,7 @@ THE SOFTWARE. net.java.dev.jna jna - 4.2.1 + 4.5.2 org.kohsuke @@ -538,7 +543,7 @@ THE SOFTWARE. org.kohsuke libpam4j - 1.8 + 1.11 org.kohsuke @@ -553,7 +558,7 @@ THE SOFTWARE. net.java.sezpoz sezpoz - 1.12 + 1.13 org.kohsuke.jinterop @@ -563,8 +568,7 @@ THE SOFTWARE. org.kohsuke.metainf-services metainf-services - 1.4 - provided + 1.8 true @@ -591,7 +595,6 @@ THE SOFTWARE. com.google.code.findbugs annotations - 3.0.0 provided @@ -638,6 +641,7 @@ THE SOFTWARE. org.codehaus.mojo build-helper-maven-plugin + 1.7 add-source diff --git a/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties deleted file mode 100644 index 0ef79b530b974daaaed8ae9bc346ed86fb71a2bc..0000000000000000000000000000000000000000 --- a/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -NewVersionAvailable=Jenkins\u65B0\u7248\u672C ({0})\u53EF\u70B9\u51FB download (\u53D8\u66F4\u8BF4\u660E)\u4E0B\u8F7D\u3002 -Or\ Upgrade\ Automatically=\u6216 \u81EA\u52A8\u5347\u7EA7\u7248\u672C -UpgradeComplete=Jenkins {0} \u7248\u672C\u5347\u7EA7\u5DF2\u5B8C\u6210,\u7B49\u5F85\u91CD\u542F\u4E2D. -UpgradeCompleteRestartNotSupported=Jenkins {0} \u7248\u672C\u5347\u7EA7\u5DF2\u5B8C\u6210,\u7B49\u5F85\u91CD\u542F\u4E2D. diff --git a/core/src/filter/resources/hudson/model/hudson-version.properties b/core/src/filter/resources/hudson/model/hudson-version.properties index 9b9343d10d659357b7acccbcf34bc2aece573625..defbd48204e4784090fe0e17e28fe5bc395abf47 100644 --- a/core/src/filter/resources/hudson/model/hudson-version.properties +++ b/core/src/filter/resources/hudson/model/hudson-version.properties @@ -1 +1 @@ -version=${build.version} \ No newline at end of file +version=${project.version} diff --git a/core/src/filter/resources/jenkins/model/jenkins-version.properties b/core/src/filter/resources/jenkins/model/jenkins-version.properties index 9b9343d10d659357b7acccbcf34bc2aece573625..defbd48204e4784090fe0e17e28fe5bc395abf47 100644 --- a/core/src/filter/resources/jenkins/model/jenkins-version.properties +++ b/core/src/filter/resources/jenkins/model/jenkins-version.properties @@ -1 +1 @@ -version=${build.version} \ No newline at end of file +version=${project.version} diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index bc00b2a0c9a68ac9412d4d7934a4afc445a84b3d..5d173c3839022d162265f7b2a2779c436e30791a 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -25,10 +25,13 @@ package hudson; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; + +import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.InvalidPathException; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import jenkins.util.AntWithFindResourceClassLoader; import jenkins.util.SystemProperties; import com.google.common.collect.Lists; @@ -110,11 +113,20 @@ public class ClassicPluginStrategy implements PluginStrategy { @Override public String getShortName(File archive) throws IOException { Manifest manifest; + if (!archive.exists()) { + throw new FileNotFoundException("Failed to load " + archive + ". The file does not exist"); + } else if (!archive.isFile()) { + throw new FileNotFoundException("Failed to load " + archive + ". It is not a file"); + } + if (isLinked(archive)) { manifest = loadLinkedManifest(archive); } else { try (JarFile jf = new JarFile(archive, false)) { manifest = jf.getManifest(); + } catch (IOException ex) { + // Mention file name in the exception + throw new IOException("Failed to load " + archive, ex); } } return PluginWrapper.computeShortName(manifest, archive.getName()); @@ -767,19 +779,20 @@ public class ClassicPluginStrategy implements PluginStrategy { Class c = ClassLoaderReflectionToolkit._findLoadedClass(pw.classLoader, name); if (c!=null) return c; return ClassLoaderReflectionToolkit._findClass(pw.classLoader, name); - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException ignored) { //not found. try next } } } else { for (Dependency dep : dependencies) { PluginWrapper p = pluginManager.getPlugin(dep.shortName); - if(p!=null) + if(p!=null) { try { return p.classLoader.loadClass(name); - } catch (ClassNotFoundException _) { - // try next + } catch (ClassNotFoundException ignored) { + // OK, try next } + } } } @@ -787,6 +800,8 @@ public class ClassicPluginStrategy implements PluginStrategy { } @Override + @SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS", + justification = "Should not produce network overheads since the URL is local. JENKINS-53793 is a follow-up") protected Enumeration findResources(String name) throws IOException { HashSet result = new HashSet(); diff --git a/core/src/main/java/hudson/CopyOnWrite.java b/core/src/main/java/hudson/CopyOnWrite.java index efe21357c4405e122651a284c0cb4c30157fce62..891cbf04bb9ffef6d6f657d05eb4708d94fa0f5e 100644 --- a/core/src/main/java/hudson/CopyOnWrite.java +++ b/core/src/main/java/hudson/CopyOnWrite.java @@ -43,7 +43,7 @@ import java.lang.annotation.Target; * *

* The field marked with this annotation usually needs to be marked as - * volatile. + * {@code volatile}. * * @author Kohsuke Kawaguchi */ diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java index 1849ecbf66aebaf6ca6a4df5ce197b5b825c8b36..cd553f3aba01ea325c58912b990d88aab9cb5e66 100644 --- a/core/src/main/java/hudson/EnvVars.java +++ b/core/src/main/java/hudson/EnvVars.java @@ -44,6 +44,7 @@ import java.util.TreeSet; import java.util.UUID; import java.util.logging.Logger; import javax.annotation.Nonnull; +import javax.annotation.CheckForNull; /** * Environment variables. @@ -54,7 +55,7 @@ import javax.annotation.Nonnull; * but case insensitive way (that is, cmd.exe can get both FOO and foo as environment variables * when it's launched, and the "set" command will display it accordingly, but "echo %foo%" results in * echoing the value of "FOO", not "foo" — this is presumably caused by the behavior of the underlying - * Win32 API GetEnvironmentVariable acting in case insensitive way.) Windows users are also + * Win32 API {@code GetEnvironmentVariable} acting in case insensitive way.) Windows users are also * used to write environment variable case-insensitively (like %Path% vs %PATH%), and you can see many * documents on the web that claims Windows environment variables are case insensitive. * @@ -65,10 +66,10 @@ import javax.annotation.Nonnull; *

* In Jenkins, often we need to build up "environment variable overrides" * on master, then to execute the process on agents. This causes a problem - * when working with variables like PATH. So to make this work, - * we introduce a special convention PATH+FOO — all entries - * that starts with PATH+ are merged and prepended to the inherited - * PATH variable, on the process where a new process is executed. + * when working with variables like {@code PATH}. So to make this work, + * we introduce a special convention {@code PATH+FOO} — all entries + * that starts with {@code PATH+} are merged and prepended to the inherited + * {@code PATH} variable, on the process where a new process is executed. * * @author Kohsuke Kawaguchi */ @@ -84,7 +85,24 @@ public class EnvVars extends TreeMap { * So this property remembers that information. */ private Platform platform; + + /** + * Gets the platform for which these env vars targeted. + * @since TODO + * @return The platform. + */ + public @CheckForNull Platform getPlatform() { + return platform; + } + /** + * Sets the platform for which these env vars target. + * @since TODO + * @param platform the platform to set. + */ + public void setPlatform(@Nonnull Platform platform) { + this.platform = platform; + } public EnvVars() { super(CaseInsensitiveComparator.INSTANCE); } @@ -107,7 +125,7 @@ public class EnvVars extends TreeMap { } /** - * Builds an environment variables from an array of the form "key","value","key","value"... + * Builds an environment variables from an array of the form {@code "key","value","key","value"...} */ public EnvVars(String... keyValuePairs) { this(); @@ -121,7 +139,7 @@ public class EnvVars extends TreeMap { * Overrides the current entry by the given entry. * *

- * Handles PATH+XYZ notation. + * Handles {@code PATH+XYZ} notation. */ public void override(String key, String value) { if(value==null || value.length()==0) { @@ -425,7 +443,7 @@ public class EnvVars extends TreeMap { * *

* If you access this field from agents, then this is the environment - * variable of the agent agent. + * variable of the agent. */ public static final Map masterEnvVars = initMaster(); diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java index 6208f6cec8322a0145f2fc4436519000d07e0ce6..1ff0638c10d6b578927ea90757d5acb8bdbeff6b 100644 --- a/core/src/main/java/hudson/ExtensionFinder.java +++ b/core/src/main/java/hudson/ExtensionFinder.java @@ -23,6 +23,7 @@ */ package hudson; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -35,11 +36,13 @@ import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Scope; import com.google.inject.Scopes; +import com.google.inject.matcher.Matchers; import com.google.inject.name.Names; -import com.google.common.collect.ImmutableList; +import com.google.inject.spi.ProvisionListener; import hudson.init.InitMilestone; import hudson.model.Descriptor; import hudson.model.Hudson; +import hudson.util.ReflectionUtils; import jenkins.ExtensionComponentSet; import jenkins.ExtensionFilter; import jenkins.ExtensionRefreshException; @@ -49,20 +52,25 @@ import net.java.sezpoz.Index; import net.java.sezpoz.IndexItem; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.springframework.util.ClassUtils; +import javax.annotation.PostConstruct; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.logging.Logger; +import java.util.Set; import java.util.logging.Level; -import java.util.List; -import java.util.ArrayList; -import java.util.Collection; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.util.logging.Logger; /** * Discovers the implementations of an extension point. @@ -254,12 +262,9 @@ public abstract class ExtensionFinder implements ExtensionPoint { private Map,GuiceExtensionAnnotation> extensionAnnotations = Maps.newHashMap(); public GuiceFinder() { - for (ExtensionComponent ec : moduleFinder.find(GuiceExtensionAnnotation.class, Hudson.getInstance())) { - GuiceExtensionAnnotation gea = ec.getInstance(); - extensionAnnotations.put(gea.annotationType,gea); - } + refreshExtensionAnnotations(); - sezpozIndex = loadSezpozIndices(Jenkins.getInstance().getPluginManager().uberClassLoader); + SezpozModule extensions = new SezpozModule(loadSezpozIndices(Jenkins.getInstance().getPluginManager().uberClassLoader)); List modules = new ArrayList<>(); modules.add(new AbstractModule() { @@ -270,7 +275,7 @@ public abstract class ExtensionFinder implements ExtensionPoint { bind(PluginManager.class).toInstance(j.getPluginManager()); } }); - modules.add(new SezpozModule(sezpozIndex)); + modules.add(extensions); for (ExtensionComponent ec : moduleFinder.find(Module.class, Hudson.getInstance())) { modules.add(ec.getInstance()); @@ -278,6 +283,7 @@ public abstract class ExtensionFinder implements ExtensionPoint { try { container = Guice.createInjector(modules); + sezpozIndex = extensions.getLoadedIndex(); } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Failed to create Guice container from all the plugins",e); // failing to load all bindings are disastrous, so recover by creating minimum that works @@ -293,6 +299,13 @@ public abstract class ExtensionFinder implements ExtensionPoint { }); } + private void refreshExtensionAnnotations() { + for (ExtensionComponent ec : moduleFinder.find(GuiceExtensionAnnotation.class, Hudson.getInstance())) { + GuiceExtensionAnnotation gea = ec.getInstance(); + extensionAnnotations.put(gea.annotationType,gea); + } + } + private ImmutableList> loadSezpozIndices(ClassLoader classLoader) { List> indices = Lists.newArrayList(); for (GuiceExtensionAnnotation gea : extensionAnnotations.values()) { @@ -315,17 +328,17 @@ public abstract class ExtensionFinder implements ExtensionPoint { */ @Override public synchronized ExtensionComponentSet refresh() throws ExtensionRefreshException { + refreshExtensionAnnotations(); // figure out newly discovered sezpoz components List> delta = Lists.newArrayList(); for (Class annotationType : extensionAnnotations.keySet()) { delta.addAll(Sezpoz.listDelta(annotationType,sezpozIndex)); } - List> l = Lists.newArrayList(sezpozIndex); - l.addAll(delta); - sezpozIndex = l; + + SezpozModule deltaExtensions = new SezpozModule(delta); List modules = new ArrayList<>(); - modules.add(new SezpozModule(delta)); + modules.add(deltaExtensions); for (ExtensionComponent ec : moduleFinder.refresh().find(Module.class)) { modules.add(ec.getInstance()); } @@ -333,6 +346,9 @@ public abstract class ExtensionFinder implements ExtensionPoint { try { final Injector child = container.createChildInjector(modules); container = child; + List> l = Lists.newArrayList(sezpozIndex); + l.addAll(deltaExtensions.getLoadedIndex()); + sezpozIndex = l; return new ExtensionComponentSet() { @Override @@ -446,11 +462,13 @@ public abstract class ExtensionFinder implements ExtensionPoint { * Instead of using SezPoz to instantiate, we'll instantiate them by using Guice, * so that we can take advantage of dependency injection. */ - private class SezpozModule extends AbstractModule { + private class SezpozModule extends AbstractModule implements ProvisionListener { private final List> index; + private final List> loadedIndex; public SezpozModule(List> index) { this.index = index; + this.loadedIndex = new ArrayList<>(); } /** @@ -493,6 +511,9 @@ public abstract class ExtensionFinder implements ExtensionPoint { @SuppressWarnings({"unchecked", "ChainOfInstanceofChecks"}) @Override protected void configure() { + + bindListener(Matchers.any(), this); + for (final IndexItem item : index) { boolean optional = isOptional(item.annotation()); try { @@ -527,6 +548,7 @@ public abstract class ExtensionFinder implements ExtensionPoint { } }).in(scope); } + loadedIndex.add(item); } catch (Exception|LinkageError e) { // sometimes the instantiation fails in an indirect classloading failure, // which results in a LinkageError @@ -535,9 +557,68 @@ public abstract class ExtensionFinder implements ExtensionPoint { } } } + + public List> getLoadedIndex() { + return Collections.unmodifiableList(loadedIndex); + } + + @Override + public void onProvision(ProvisionInvocation provision) { + final T instance = provision.provision(); + if (instance == null) return; + List methods = new LinkedList<>(); + Class c = instance.getClass(); + + // find PostConstruct methods in class hierarchy, the one from parent class being first in list + // so that we invoke them before derived class one. This isn't specified in JSR-250 but implemented + // this way in Spring and what most developers would expect to happen. + + final Set interfaces = ClassUtils.getAllInterfacesAsSet(instance); + + while (c != Object.class) { + Arrays.stream(c.getDeclaredMethods()) + .map(m -> getMethodAndInterfaceDeclarations(m, interfaces)) + .flatMap(Collection::stream) + .filter(m -> m.getAnnotation(PostConstruct.class) != null) + .findFirst() + .ifPresent(method -> methods.add(0, method)); + c = c.getSuperclass(); + } + + for (Method postConstruct : methods) { + try { + postConstruct.setAccessible(true); + postConstruct.invoke(instance); + } catch (final Exception e) { + throw new RuntimeException(String.format("@PostConstruct %s", postConstruct), e); + } + } + } } } + /** + * Returns initial {@link Method} as well as all matching ones found in interfaces. + * This allows to introspect metadata for a method which is both declared in parent class and in implemented + * interface(s). interfaces typically is obtained by {@link ClassUtils#getAllInterfacesAsSet} + */ + Collection getMethodAndInterfaceDeclarations(Method method, Collection interfaces) { + final List methods = new ArrayList<>(); + methods.add(method); + + // we search for matching method by iteration and comparison vs getMethod to avoid repeated NoSuchMethodException + // being thrown, while interface typically only define a few set of methods to check. + interfaces.stream() + .map(Class::getMethods) + .flatMap(Arrays::stream) + .filter(m -> m.getName().equals(method.getName()) && Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) + .findFirst() + .ifPresent(methods::add); + + return methods; + } + + /** * The bootstrap implementation that looks for the {@link Extension} marker. * diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index 9a4c77d2f5f5db24c736c6f58433737505f9a4c6..566e3de404911db2fbc418bab933a4bdd8261c79 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -145,15 +145,29 @@ public class ExtensionList extends AbstractList implements OnMaster { * Looks for the extension instance of the given type (subclasses excluded), * or return null. */ - public @CheckForNull U get(Class type) { + public @CheckForNull U get(@Nonnull Class type) { for (T ext : this) if(ext.getClass()==type) return type.cast(ext); return null; } + /** + * Looks for the extension instance of the given type (subclasses excluded), + * or throws an IllegalStateException. + * + * Meant to simplify call inside @Extension annotated class to retrieve their own instance. + */ + public @Nonnull U getInstance(@Nonnull Class type) throws IllegalStateException { + for (T ext : this) + if(ext.getClass()==type) + return type.cast(ext); + + throw new IllegalStateException("The class " + type.getName() + " was not found, potentially not yet loaded"); + } + @Override - public Iterator iterator() { + public @Nonnull Iterator iterator() { // we need to intercept mutation, so for now don't allow Iterator.remove return new AdaptedIterator,T>(Iterators.readOnly(ensureLoaded().iterator())) { protected T adapt(ExtensionComponent item) { diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index b7b5b2d79d3a14090cf421c583d7265afbf2b360..9452064e8105e124c0e62328f3310c03765b4ff7 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -58,6 +58,9 @@ import hudson.util.IOUtils; import hudson.util.NamingThreadFactory; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; + +import static java.util.logging.Level.FINE; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileFilter; @@ -88,7 +91,6 @@ import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.Enumeration; @@ -139,6 +141,7 @@ import static hudson.Util.fixEmpty; import static hudson.Util.isSymlink; import java.util.Collections; +import org.apache.tools.ant.BuildException; /** * {@link File} like object with remoting support. @@ -225,9 +228,14 @@ public final class FilePath implements Serializable { * This is used to determine whether we are running on the master or the agent. */ private transient VirtualChannel channel; - - // since the platform of the agent might be different, can't use java.io.File - private final String remote; + + /** + * Represent the path to the file in the master or the agent + * Since the platform of the agent might be different, can't use java.io.File + * + * The field could not be final since it's modified in {@link #readResolve()} + */ + private /*final*/ String remote; /** * If this {@link FilePath} is deserialized to handle file access request from a remote computer, @@ -275,6 +283,11 @@ public final class FilePath implements Serializable { this.remote = normalize(resolvePathIfRelative(base, rel)); } + private Object readResolve() { + this.remote = normalize(this.remote); + return this; + } + private String resolvePathIfRelative(@Nonnull FilePath base, @Nonnull String rel) { if(isAbsolute(rel)) return rel; if(base.isUnix()) { @@ -302,7 +315,8 @@ public final class FilePath implements Serializable { * {@link File#getParent()} etc cannot handle ".." and "." in the path component very well, * so remove them. */ - private static String normalize(@Nonnull String path) { + @Restricted(NoExternalUse.class) + public static String normalize(@Nonnull String path) { StringBuilder buf = new StringBuilder(); // Check for prefix designating absolute path Matcher m = ABSOLUTE_PREFIX_PATTERN.matcher(path); @@ -466,7 +480,18 @@ public final class FilePath implements Serializable { */ public int archive(final ArchiverFactory factory, OutputStream os, final DirScanner scanner) throws IOException, InterruptedException { final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os; - return act(new SecureFileCallable() { + return act(new Archive(factory, out, scanner)); + } + private class Archive extends SecureFileCallable { + private final ArchiverFactory factory; + private final OutputStream out; + private final DirScanner scanner; + Archive(ArchiverFactory factory, OutputStream out, DirScanner scanner) { + this.factory = factory; + this.out = out; + this.scanner = scanner; + } + @Override public Integer invoke(File f, VirtualChannel channel) throws IOException { Archiver a = factory.create(out); try { @@ -478,7 +503,6 @@ public final class FilePath implements Serializable { } private static final long serialVersionUID = 1L; - }); } public int archive(final ArchiverFactory factory, OutputStream os, final FileFilter filter) throws IOException, InterruptedException { @@ -501,27 +525,32 @@ public final class FilePath implements Serializable { // TODO: post release, re-unite two branches by introducing FileStreamCallable that resolves InputStream if (this.channel!=target.channel) {// local -> remote or remote->local final RemoteInputStream in = new RemoteInputStream(read(), Flag.GREEDY); - target.act(new SecureFileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { - unzip(dir, in); - return null; - } - - private static final long serialVersionUID = 1L; - }); + target.act(new UnzipRemote(in)); } else {// local -> local or remote->remote - target.act(new SecureFileCallable() { - - public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { - assert !FilePath.this.isRemote(); // this.channel==target.channel above - unzip(dir, reading(new File(FilePath.this.getRemote()))); // shortcut to local file - return null; - } - - private static final long serialVersionUID = 1L; - }); + target.act(new UnzipLocal()); } } + private class UnzipRemote extends SecureFileCallable { + private final RemoteInputStream in; + UnzipRemote(RemoteInputStream in) { + this.in = in; + } + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { + unzip(dir, in); + return null; + } + private static final long serialVersionUID = 1L; + } + private class UnzipLocal extends SecureFileCallable { + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { + assert !FilePath.this.isRemote(); // this.channel==target.channel above + unzip(dir, reading(new File(FilePath.this.getRemote()))); // shortcut to local file + return null; + } + private static final long serialVersionUID = 1L; + } /** * When this {@link FilePath} represents a tar file, extracts that tar file. @@ -537,24 +566,37 @@ public final class FilePath implements Serializable { // TODO: post release, re-unite two branches by introducing FileStreamCallable that resolves InputStream if (this.channel!=target.channel) {// local -> remote or remote->local final RemoteInputStream in = new RemoteInputStream(read(), Flag.GREEDY); - target.act(new SecureFileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { - readFromTar(FilePath.this.getName(),dir,compression.extract(in)); - return null; - } - - private static final long serialVersionUID = 1L; - }); + target.act(new UntarRemote(compression, in)); } else {// local -> local or remote->remote - target.act(new SecureFileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { - readFromTar(FilePath.this.getName(),dir,compression.extract(FilePath.this.read())); - return null; - } - private static final long serialVersionUID = 1L; - }); + target.act(new UntarLocal(compression)); } } + private class UntarRemote extends SecureFileCallable { + private final TarCompression compression; + private final RemoteInputStream in; + UntarRemote(TarCompression compression, RemoteInputStream in) { + this.compression = compression; + this.in = in; + } + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { + readFromTar(FilePath.this.getName(), dir, compression.extract(in)); + return null; + } + private static final long serialVersionUID = 1L; + } + private class UntarLocal extends SecureFileCallable { + private final TarCompression compression; + UntarLocal(TarCompression compression) { + this.compression = compression; + } + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { + readFromTar(FilePath.this.getName(), dir, compression.extract(FilePath.this.read())); + return null; + } + private static final long serialVersionUID = 1L; + } /** * Reads the given InputStream as a zip file and extracts it into this directory. @@ -566,13 +608,19 @@ public final class FilePath implements Serializable { */ public void unzipFrom(InputStream _in) throws IOException, InterruptedException { final InputStream in = new RemoteInputStream(_in, Flag.GREEDY); - act(new SecureFileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException { - unzip(dir, in); - return null; - } - private static final long serialVersionUID = 1L; - }); + act(new UnzipFrom(in)); + } + private class UnzipFrom extends SecureFileCallable { + private final InputStream in; + UnzipFrom(InputStream in) { + this.in = in; + } + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException { + unzip(dir, in); + return null; + } + private static final long serialVersionUID = 1L; } private void unzip(File dir, InputStream in) throws IOException { @@ -597,6 +645,10 @@ public final class FilePath implements Serializable { while (entries.hasMoreElements()) { ZipEntry e = entries.nextElement(); File f = new File(dir, e.getName()); + if (!f.toPath().normalize().startsWith(dir.toPath())) { + throw new IOException( + "Zip " + zipFile.getPath() + " contains illegal file name that breaks out of the target directory: " + e.getName()); + } if (e.isDirectory()) { mkdirs(f); } else { @@ -627,12 +679,13 @@ public final class FilePath implements Serializable { * Absolutizes this {@link FilePath} and returns the new one. */ public FilePath absolutize() throws IOException, InterruptedException { - return new FilePath(channel,act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File f, VirtualChannel channel) throws IOException { - return f.getAbsolutePath(); - } - })); + return new FilePath(channel, act(new Absolutize())); + } + private static class Absolutize extends SecureFileCallable { + private static final long serialVersionUID = 1L; + public String invoke(File f, VirtualChannel channel) throws IOException { + return f.getAbsolutePath(); + } } /** @@ -645,14 +698,22 @@ public final class FilePath implements Serializable { * @since 1.456 */ public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException { - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - symlinking(f); - Util.createSymlink(f.getParentFile(),target,f.getName(),listener); - return null; - } - }); + act(new SymlinkTo(target, listener)); + } + private class SymlinkTo extends SecureFileCallable { + private final String target; + private final TaskListener listener; + SymlinkTo(String target, TaskListener listener) { + this.target = target; + this.listener = listener; + } + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + symlinking(f); + Util.createSymlink(f.getParentFile(), target, f.getName(), listener); + return null; + } } /** @@ -663,12 +724,14 @@ public final class FilePath implements Serializable { * @since 1.456 */ public String readLink() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return Util.resolveSymlink(reading(f)); - } - }); + return act(new ReadLink()); + } + private class ReadLink extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + return Util.resolveSymlink(reading(f)); + } } @Override @@ -732,17 +795,25 @@ public final class FilePath implements Serializable { public void untarFrom(InputStream _in, final TarCompression compression) throws IOException, InterruptedException { try { final InputStream in = new RemoteInputStream(_in, Flag.GREEDY); - act(new SecureFileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException { - readFromTar("input stream",dir, compression.extract(in)); - return null; - } - private static final long serialVersionUID = 1L; - }); + act(new UntarFrom(compression, in)); } finally { _in.close(); } } + private class UntarFrom extends SecureFileCallable { + private final TarCompression compression; + private final InputStream in; + UntarFrom(TarCompression compression, InputStream in) { + this.compression = compression; + this.in = in; + } + @Override + public Void invoke(File dir, VirtualChannel channel) throws IOException { + readFromTar("input stream",dir, compression.extract(in)); + return null; + } + private static final long serialVersionUID = 1L; + } /** * Given a tgz/zip file, extracts it to the given target directory, if necessary. @@ -892,9 +963,10 @@ public final class FilePath implements Serializable { } /** - * Reads the URL on the current VM, and writes all the data to this {@link FilePath} - * (this is different from resolving URL remotely.) - * + * Reads the URL on the current VM, and streams the data to this file using the Remoting channel. + *

This is different from resolving URL remotely. + * If you instead wished to open an HTTP(S) URL on the remote side, + * prefer {@code RobustHTTPClient.copyFromRemotely}. * @since 1.293 */ public void copyFrom(URL url) throws IOException, InterruptedException { @@ -998,11 +1070,6 @@ public final class FilePath implements Serializable { return channel.call(wrapper); } catch (TunneledInterruptedException e) { throw (InterruptedException)new InterruptedException(e.getMessage()).initCause(e); - } catch (AbortException e) { - throw e; // pass through so that the caller can catch it as AbortException - } catch (IOException e) { - // wrap it into a new IOException so that we get the caller's stack trace as well. - throw new IOException("remote file operation failed: " + remote + " at " + channel + ": " + e, e); } } else { // the file is on the local machine. @@ -1105,23 +1172,28 @@ public final class FilePath implements Serializable { * @since 1.522 */ public Callable asCallableWith(final FileCallable task) { - return new Callable() { - @Override - public V call() throws IOException { - try { - return act(task); - } catch (InterruptedException e) { - throw (IOException)new InterruptedIOException().initCause(e); - } + return new CallableWith<>(task); + } + private class CallableWith implements Callable { + private final FileCallable task; + CallableWith(FileCallable task) { + this.task = task; + } + @Override + public V call() throws IOException { + try { + return act(task); + } catch (InterruptedException e) { + throw (IOException)new InterruptedIOException().initCause(e); } + } - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - task.checkRoles(checker); - } + @Override + public void checkRoles(RoleChecker checker) throws SecurityException { + task.checkRoles(checker); + } - private static final long serialVersionUID = 1L; - }; + private static final long serialVersionUID = 1L; } /** @@ -1129,12 +1201,14 @@ public final class FilePath implements Serializable { * on which this file is available. */ public URI toURI() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public URI invoke(File f, VirtualChannel channel) { - return f.toURI(); - } - }); + return act(new ToURI()); + } + private static class ToURI extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public URI invoke(File f, VirtualChannel channel) { + return f.toURI(); + } } /** @@ -1167,45 +1241,52 @@ public final class FilePath implements Serializable { * Creates this directory. */ public void mkdirs() throws IOException, InterruptedException { - if(!act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - if(mkdirs(f) || f.exists()) - return true; // OK + if (!act(new Mkdirs())) { + throw new IOException("Failed to mkdirs: " + remote); + } + } + private class Mkdirs extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + if(mkdirs(f) || f.exists()) + return true; // OK - // following Ant task to avoid possible race condition. - Thread.sleep(10); + // following Ant task to avoid possible race condition. + Thread.sleep(10); - return mkdirs(f) || f.exists(); - } - })) - throw new IOException("Failed to mkdirs: "+remote); + return mkdirs(f) || f.exists(); + } } /** * Deletes this directory, including all its contents recursively. */ public void deleteRecursive() throws IOException, InterruptedException { - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - deleteRecursive(deleting(f)); - return null; - } - }); + act(new DeleteRecursive()); + } + private class DeleteRecursive extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + deleteRecursive(deleting(f)); + return null; + } } /** * Deletes all the contents of this directory, but not the directory itself */ public void deleteContents() throws IOException, InterruptedException { - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - deleteContentsRecursive(f); - return null; - } - }); + act(new DeleteContents()); + } + private class DeleteContents extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + deleteContentsRecursive(f); + return null; + } } private void deleteRecursive(File dir) throws IOException { @@ -1316,17 +1397,25 @@ public final class FilePath implements Serializable { */ public FilePath createTempFile(final String prefix, final String suffix) throws IOException, InterruptedException { try { - return new FilePath(this,act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File dir, VirtualChannel channel) throws IOException { - File f = writing(File.createTempFile(prefix, suffix, dir)); - return f.getName(); - } - })); + return new FilePath(this, act(new CreateTempFile(prefix, suffix))); } catch (IOException e) { throw new IOException("Failed to create a temp file on "+remote,e); } } + private class CreateTempFile extends SecureFileCallable { + private final String prefix; + private final String suffix; + CreateTempFile(String prefix, String suffix) { + this.prefix = prefix; + this.suffix = suffix; + } + private static final long serialVersionUID = 1L; + @Override + public String invoke(File dir, VirtualChannel channel) throws IOException { + File f = writing(File.createTempFile(prefix, suffix, dir)); + return f.getName(); + } + } /** * Creates a temporary file in this directory and set the contents to the @@ -1372,30 +1461,42 @@ public final class FilePath implements Serializable { */ public FilePath createTextTempFile(final String prefix, final String suffix, final String contents, final boolean inThisDirectory) throws IOException, InterruptedException { try { - return new FilePath(channel,act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File dir, VirtualChannel channel) throws IOException { - if(!inThisDirectory) - dir = new File(System.getProperty("java.io.tmpdir")); - else - mkdirs(dir); + return new FilePath(channel, act(new CreateTextTempFile(inThisDirectory, prefix, suffix, contents))); + } catch (IOException e) { + throw new IOException("Failed to create a temp file on "+remote,e); + } + } + private final class CreateTextTempFile extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private final boolean inThisDirectory; + private final String prefix; + private final String suffix; + private final String contents; + CreateTextTempFile(boolean inThisDirectory, String prefix, String suffix, String contents) { + this.inThisDirectory = inThisDirectory; + this.prefix = prefix; + this.suffix = suffix; + this.contents = contents; + } + @Override + public String invoke(File dir, VirtualChannel channel) throws IOException { + if(!inThisDirectory) + dir = new File(System.getProperty("java.io.tmpdir")); + else + mkdirs(dir); - File f; - try { - f = creating(File.createTempFile(prefix, suffix, dir)); - } catch (IOException e) { - throw new IOException("Failed to create a temporary directory in "+dir,e); - } + File f; + try { + f = creating(File.createTempFile(prefix, suffix, dir)); + } catch (IOException e) { + throw new IOException("Failed to create a temporary directory in "+dir,e); + } - try (Writer w = new FileWriter(writing(f))) { - w.write(contents); - } + try (Writer w = new FileWriter(writing(f))) { + w.write(contents); + } - return f.getAbsolutePath(); - } - })); - } catch (IOException e) { - throw new IOException("Failed to create a temp file on "+remote,e); + return f.getAbsolutePath(); } } @@ -1422,29 +1523,35 @@ public final class FilePath implements Serializable { s = new String[]{prefix, suffix}; } String name = StringUtils.join(s, "."); - return new FilePath(this,act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File dir, VirtualChannel channel) throws IOException { + return new FilePath(this, act(new CreateTempDir(name))); + } catch (IOException e) { + throw new IOException("Failed to create a temp directory on "+remote,e); + } + } + private class CreateTempDir extends SecureFileCallable { + private final String name; + CreateTempDir(String name) { + this.name = name; + } + private static final long serialVersionUID = 1L; + @Override + public String invoke(File dir, VirtualChannel channel) throws IOException { - Path tempPath; - final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); + Path tempPath; + final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); - if (isPosix) { - tempPath = Files.createTempDirectory(Util.fileToPath(dir), name, - PosixFilePermissions.asFileAttribute(EnumSet.allOf(PosixFilePermission.class))); - } else { - tempPath = Files.createTempDirectory(Util.fileToPath(dir), name, new FileAttribute[] {}); - } + if (isPosix) { + tempPath = Files.createTempDirectory(Util.fileToPath(dir), name, + PosixFilePermissions.asFileAttribute(EnumSet.allOf(PosixFilePermission.class))); + } else { + tempPath = Files.createTempDirectory(Util.fileToPath(dir), name, new FileAttribute[] {}); + } - if (tempPath.toFile() == null) { - throw new IOException("Failed to obtain file from path " + dir + " on " + remote); - } - return tempPath.toFile().getName(); + if (tempPath.toFile() == null) { + throw new IOException("Failed to obtain file from path " + dir + " on " + remote); } - })); - } catch (IOException e) { - throw new IOException("Failed to create a temp directory on "+remote,e); - } + return tempPath.toFile().getName(); + } } /** @@ -1453,26 +1560,30 @@ public final class FilePath implements Serializable { * @return true, for a modicum of compatibility */ public boolean delete() throws IOException, InterruptedException { - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteFile(deleting(f)); - return null; - } - }); + act(new Delete()); return true; } + private class Delete extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + Util.deleteFile(deleting(f)); + return null; + } + } /** * Checks if the file exists. */ public boolean exists() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).exists(); - } - }); + return act(new Exists()); + } + private class Exists extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Boolean invoke(File f, VirtualChannel channel) throws IOException { + return stating(f).exists(); + } } /** @@ -1483,12 +1594,14 @@ public final class FilePath implements Serializable { * @see #touch(long) */ public long lastModified() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Long invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).lastModified(); - } - }); + return act(new LastModified()); + } + private class LastModified extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Long invoke(File f, VirtualChannel channel) throws IOException { + return stating(f).lastModified(); + } } /** @@ -1497,8 +1610,15 @@ public final class FilePath implements Serializable { * @since 1.299 */ public void touch(final long timestamp) throws IOException, InterruptedException { - act(new SecureFileCallable() { + act(new Touch(timestamp)); + } + private class Touch extends SecureFileCallable { + private final long timestamp; + Touch(long timestamp) { + this.timestamp = timestamp; + } private static final long serialVersionUID = -5094638816500738429L; + @Override public Void invoke(File f, VirtualChannel channel) throws IOException { if(!f.exists()) { try { @@ -1511,12 +1631,22 @@ public final class FilePath implements Serializable { throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp); return null; } - }); } private void setLastModifiedIfPossible(final long timestamp) throws IOException, InterruptedException { - String message = act(new SecureFileCallable() { + String message = act(new SetLastModified(timestamp)); + + if (message!=null) { + LOGGER.warning(message); + } + } + private class SetLastModified extends SecureFileCallable { + private final long timestamp; + SetLastModified(long timestamp) { + this.timestamp = timestamp; + } private static final long serialVersionUID = -828220335793641630L; + @Override public String invoke(File f, VirtualChannel channel) throws IOException { if(!writing(f).setLastModified(timestamp)) { if (Functions.isWindows()) { @@ -1529,23 +1659,20 @@ public final class FilePath implements Serializable { } return null; } - }); - - if (message!=null) { - LOGGER.warning(message); - } } /** * Checks if the file is a directory. */ public boolean isDirectory() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).isDirectory(); - } - }); + return act(new IsDirectory()); + } + private final class IsDirectory extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Boolean invoke(File f, VirtualChannel channel) throws IOException { + return stating(f).isDirectory(); + } } /** @@ -1554,12 +1681,14 @@ public final class FilePath implements Serializable { * @since 1.129 */ public long length() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Long invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).length(); - } - }); + return act(new Length()); + } + private class Length extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Long invoke(File f, VirtualChannel channel) throws IOException { + return stating(f).length(); + } } /** @@ -1567,12 +1696,14 @@ public final class FilePath implements Serializable { * @since 1.542 */ public long getFreeDiskSpace() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - @Override public Long invoke(File f, VirtualChannel channel) throws IOException { - return f.getFreeSpace(); - } - }); + return act(new GetFreeDiskSpace()); + } + private static class GetFreeDiskSpace extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Long invoke(File f, VirtualChannel channel) throws IOException { + return f.getFreeSpace(); + } } /** @@ -1580,12 +1711,14 @@ public final class FilePath implements Serializable { * @since 1.542 */ public long getTotalDiskSpace() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - @Override public Long invoke(File f, VirtualChannel channel) throws IOException { - return f.getTotalSpace(); - } - }); + return act(new GetTotalDiskSpace()); + } + private static class GetTotalDiskSpace extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Long invoke(File f, VirtualChannel channel) throws IOException { + return f.getTotalSpace(); + } } /** @@ -1593,12 +1726,14 @@ public final class FilePath implements Serializable { * @since 1.542 */ public long getUsableDiskSpace() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - @Override public Long invoke(File f, VirtualChannel channel) throws IOException { - return f.getUsableSpace(); - } - }); + return act(new GetUsableDiskSpace()); + } + private static class GetUsableDiskSpace extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Long invoke(File f, VirtualChannel channel) throws IOException { + return f.getUsableSpace(); + } } /** @@ -1623,14 +1758,20 @@ public final class FilePath implements Serializable { */ public void chmod(final int mask) throws IOException, InterruptedException { if(!isUnix() || mask==-1) return; - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - _chmod(writing(f), mask); + act(new Chmod(mask)); + } + private class Chmod extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private final int mask; + Chmod(int mask) { + this.mask = mask; + } + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + _chmod(writing(f), mask); - return null; - } - }); + return null; + } } /** @@ -1660,12 +1801,14 @@ public final class FilePath implements Serializable { */ public int mode() throws IOException, InterruptedException, PosixException { if(!isUnix()) return -1; - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Integer invoke(File f, VirtualChannel channel) throws IOException { - return IOUtils.mode(stating(f)); - } - }); + return act(new Mode()); + } + private class Mode extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public Integer invoke(File f, VirtualChannel channel) throws IOException { + return IOUtils.mode(stating(f)); + } } /** @@ -1710,8 +1853,15 @@ public final class FilePath implements Serializable { if (filter != null && !(filter instanceof Serializable)) { throw new IllegalArgumentException("Non-serializable filter of " + filter.getClass()); } - return act(new SecureFileCallable>() { + return act(new ListFilter(filter), (filter != null ? filter : this).getClass().getClassLoader()); + } + private class ListFilter extends SecureFileCallable> { + private final FileFilter filter; + ListFilter(FileFilter filter) { + this.filter = filter; + } private static final long serialVersionUID = 1L; + @Override public List invoke(File f, VirtualChannel channel) throws IOException { File[] children = reading(f).listFiles(filter); if (children == null) { @@ -1724,7 +1874,6 @@ public final class FilePath implements Serializable { return r; } - }, (filter!=null?filter:this).getClass().getClassLoader()); } /** @@ -1768,8 +1917,19 @@ public final class FilePath implements Serializable { */ @Nonnull public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException { - return act(new SecureFileCallable() { + return act(new ListGlob(includes, excludes, defaultExcludes)); + } + private class ListGlob extends SecureFileCallable { + private final String includes; + private final String excludes; + private final boolean defaultExcludes; + ListGlob(String includes, String excludes, boolean defaultExcludes) { + this.includes = includes; + this.excludes = excludes; + this.defaultExcludes = defaultExcludes; + } private static final long serialVersionUID = 1L; + @Override public FilePath[] invoke(File f, VirtualChannel channel) throws IOException { String[] files = glob(reading(f), includes, excludes, defaultExcludes); @@ -1779,7 +1939,6 @@ public final class FilePath implements Serializable { return r; } - }); } /** @@ -1794,7 +1953,12 @@ public final class FilePath implements Serializable { throw new IOException("Expecting Ant GLOB pattern, but saw '"+includes+"'. See http://ant.apache.org/manual/Types/fileset.html for syntax"); FileSet fs = Util.createFileSet(dir,includes,excludes); fs.setDefaultexcludes(defaultExcludes); - DirectoryScanner ds = fs.getDirectoryScanner(new Project()); + DirectoryScanner ds; + try { + ds = fs.getDirectoryScanner(new Project()); + } catch (BuildException x) { + throw new IOException(x.getMessage()); + } String[] files = ds.getIncludedFiles(); return files; } @@ -1812,25 +1976,29 @@ public final class FilePath implements Serializable { } final Pipe p = Pipe.createRemoteToLocal(); - actAsync(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - - @Override - public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - try (InputStream fis = Files.newInputStream(reading(f).toPath()); - OutputStream out = p.getOut()) { - org.apache.commons.io.IOUtils.copy(fis, out); - } catch (InvalidPathException e) { - p.error(new IOException(e)); - } catch (Exception x) { - p.error(x); - } - return null; - } - }); + actAsync(new Read(p)); return p.getIn(); } + private class Read extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private final Pipe p; + Read(Pipe p) { + this.p = p; + } + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + try (InputStream fis = Files.newInputStream(reading(f).toPath()); + OutputStream out = p.getOut()) { + org.apache.commons.io.IOUtils.copy(fis, out); + } catch (InvalidPathException e) { + p.error(new IOException(e)); + } catch (Exception x) { + p.error(x); + } + return null; + } + } /** * Reads this file from the specific offset. @@ -1929,8 +2097,11 @@ public final class FilePath implements Serializable { } } - return act(new SecureFileCallable() { + return act(new WritePipe()); + } + private class WritePipe extends SecureFileCallable { private static final long serialVersionUID = 1L; + @Override public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { f = f.getAbsoluteFile(); mkdirs(f.getParentFile()); @@ -1941,7 +2112,6 @@ public final class FilePath implements Serializable { throw new IOException(e); } } - }); } /** @@ -1952,19 +2122,27 @@ public final class FilePath implements Serializable { * @since 1.105 */ public void write(final String content, final String encoding) throws IOException, InterruptedException { - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - mkdirs(f.getParentFile()); - try (OutputStream fos = Files.newOutputStream(writing(f).toPath()); - Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos)) { - w.write(content); - } catch (InvalidPathException e) { - throw new IOException(e); - } - return null; + act(new Write(encoding, content)); + } + private class Write extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private final String encoding; + private final String content; + Write(String encoding, String content) { + this.encoding = encoding; + this.content = content; + } + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + mkdirs(f.getParentFile()); + try (OutputStream fos = Files.newOutputStream(writing(f).toPath()); + Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos)) { + w.write(content); + } catch (InvalidPathException e) { + throw new IOException(e); } - }); + return null; + } } /** @@ -1972,12 +2150,14 @@ public final class FilePath implements Serializable { * @see Util#getDigestOf(File) */ public String digest() throws IOException, InterruptedException { - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public String invoke(File f, VirtualChannel channel) throws IOException { - return Util.getDigestOf(reading(f)); - } - }); + return act(new Digest()); + } + private class Digest extends SecureFileCallable { + private static final long serialVersionUID = 1L; + @Override + public String invoke(File f, VirtualChannel channel) throws IOException { + return Util.getDigestOf(reading(f)); + } } /** @@ -1988,13 +2168,19 @@ public final class FilePath implements Serializable { if(this.channel != target.channel) { throw new IOException("renameTo target must be on the same host"); } - act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - Files.move(fileToPath(reading(f)), fileToPath(creating(new File(target.remote))), LinkOption.NOFOLLOW_LINKS); - return null; - } - }); + act(new RenameTo(target)); + } + private class RenameTo extends SecureFileCallable { + private final FilePath target; + RenameTo(FilePath target) { + this.target = target; + } + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + Files.move(fileToPath(reading(f)), fileToPath(creating(new File(target.remote))), LinkOption.NOFOLLOW_LINKS); + return null; + } } /** @@ -2006,8 +2192,15 @@ public final class FilePath implements Serializable { if(this.channel != target.channel) { throw new IOException("pullUpTo target must be on the same host"); } - act(new SecureFileCallable() { + act(new MoveAllChildrenTo(target)); + } + private class MoveAllChildrenTo extends SecureFileCallable { + private final FilePath target; + MoveAllChildrenTo(FilePath target) { + this.target = target; + } private static final long serialVersionUID = 1L; + @Override public Void invoke(File f, VirtualChannel channel) throws IOException { // JENKINS-16846: if f.getName() is the same as one of the files/directories in f, // then the rename op will fail @@ -2016,7 +2209,7 @@ public final class FilePath implements Serializable { throw new IOException("Failed to rename "+f+" to "+tmp); File t = new File(target.getRemote()); - + for(File child : reading(tmp).listFiles()) { File target = new File(t, child.getName()); if(!stating(child).renameTo(creating(target))) @@ -2025,7 +2218,6 @@ public final class FilePath implements Serializable { deleting(tmp).delete(); return null; } - }); } /** @@ -2048,16 +2240,7 @@ public final class FilePath implements Serializable { public void copyToWithPermission(FilePath target) throws IOException, InterruptedException { // Use NIO copy with StandardCopyOption.COPY_ATTRIBUTES when copying on the same machine. if (this.channel == target.channel) { - act(new SecureFileCallable() { - public Void invoke(File f, VirtualChannel channel) throws IOException { - File targetFile = new File(target.remote); - File targetDir = targetFile.getParentFile(); - filterNonNull().mkdirs(targetDir); - Files.createDirectories(fileToPath(targetDir)); - Files.copy(fileToPath(reading(f)), fileToPath(writing(targetFile)), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); - return null; - } - }); + act(new CopyToWithPermission(target)); return; } @@ -2066,6 +2249,21 @@ public final class FilePath implements Serializable { target.chmod(mode()); target.setLastModifiedIfPossible(lastModified()); } + private class CopyToWithPermission extends SecureFileCallable { + private final FilePath target; + CopyToWithPermission(FilePath target) { + this.target = target; + } + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + File targetFile = new File(target.remote); + File targetDir = targetFile.getParentFile(); + filterNonNull().mkdirs(targetDir); + Files.createDirectories(fileToPath(targetDir)); + Files.copy(fileToPath(reading(f)), fileToPath(writing(targetFile)), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); + return null; + } + } /** * Sends the contents of this file into the given {@link OutputStream}. @@ -2073,24 +2271,30 @@ public final class FilePath implements Serializable { public void copyTo(OutputStream os) throws IOException, InterruptedException { final OutputStream out = new RemoteOutputStream(os); - act(new SecureFileCallable() { - private static final long serialVersionUID = 4088559042349254141L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - try (InputStream fis = Files.newInputStream(reading(f).toPath())) { - org.apache.commons.io.IOUtils.copy(fis, out); - return null; - } catch (InvalidPathException e) { - throw new IOException(e); - } finally { - out.close(); - } - } - }); + act(new CopyTo(out)); // make sure the writes fully got delivered to 'os' before we return. // this is needed because I/O operation is asynchronous syncIO(); } + private class CopyTo extends SecureFileCallable { + private static final long serialVersionUID = 4088559042349254141L; + private final OutputStream out; + CopyTo(OutputStream out) { + this.out = out; + } + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + try (InputStream fis = Files.newInputStream(reading(f).toPath())) { + org.apache.commons.io.IOUtils.copy(fis, out); + return null; + } catch (InvalidPathException e) { + throw new IOException(e); + } finally { + out.close(); + } + } + } /** * With fix to JENKINS-11251 (remoting 2.15), this is no longer necessary. @@ -2105,7 +2309,7 @@ public final class FilePath implements Serializable { // legacy agent.jar. Handle this gracefully try { LOGGER.log(Level.WARNING,"Looks like an old agent.jar. Please update "+ Which.jarFile(Channel.class)+" to the new version",e); - } catch (IOException _) { + } catch (IOException ignored) { // really ignore this time } } @@ -2188,64 +2392,14 @@ public final class FilePath implements Serializable { public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description) throws IOException, InterruptedException { if(this.channel==target.channel) { // local to local copy. - return act(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Integer invoke(File base, VirtualChannel channel) throws IOException { - if(!base.exists()) return 0; - assert target.channel==null; - final File dest = new File(target.remote); - final AtomicInteger count = new AtomicInteger(); - scanner.scan(base, reading(new FileVisitor() { - @Override - public void visit(File f, String relativePath) throws IOException { - if (f.isFile()) { - File target = new File(dest, relativePath); - mkdirsE(target.getParentFile()); - Util.copyFile(f, writing(target)); - count.incrementAndGet(); - } - } - - @Override - public boolean understandsSymlink() { - return true; - } - - @Override - public void visitSymlink(File link, String target, String relativePath) throws IOException { - try { - mkdirsE(new File(dest, relativePath).getParentFile()); - writing(new File(dest, target)); - Util.createSymlink(dest, target, relativePath, TaskListener.NULL); - } catch (InterruptedException x) { - throw new IOException(x); - } - count.incrementAndGet(); - } - })); - return count.get(); - } - }); + return act(new CopyRecursiveLocal(target, scanner)); } else if(this.channel==null) { // local -> remote copy final Pipe pipe = Pipe.createLocalToRemote(); - Future future = target.actAsync(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Void invoke(File f, VirtualChannel channel) throws IOException { - try (InputStream in = pipe.getIn()) { - readFromTar(remote + '/' + description, f,TarCompression.GZIP.extract(in)); - return null; - } - } - }); - Future future2 = actAsync(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - @Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return writeToTar(new File(remote), scanner, TarCompression.GZIP.compress(pipe.getOut())); - } - }); + Future future = target.actAsync(new ReadToTar(pipe, description)); + Future future2 = actAsync(new WriteToTar(scanner, pipe)); try { // JENKINS-9540 in case the reading side failed, report that error first future.get(); @@ -2262,14 +2416,7 @@ public final class FilePath implements Serializable { // remote -> local copy final Pipe pipe = Pipe.createRemoteToLocal(); - Future future = actAsync(new SecureFileCallable() { - private static final long serialVersionUID = 1L; - public Integer invoke(File f, VirtualChannel channel) throws IOException { - try (OutputStream out = pipe.getOut()) { - return writeToTar(f, scanner, TarCompression.GZIP.compress(out)); - } - } - }); + Future future = actAsync(new CopyRecursiveRemoteToLocal(pipe, scanner)); try { readFromTar(remote + '/' + description,new File(target.remote),TarCompression.GZIP.extract(pipe.getIn())); } catch (IOException e) {// BuildException or IOException @@ -2280,8 +2427,8 @@ public final class FilePath implements Serializable { // report both errors e.addSuppressed(x); throw e; - } catch (TimeoutException _) { - // remote is hanging + } catch (TimeoutException ignored) { + // remote is hanging, just throw the original exception throw e; } } @@ -2297,7 +2444,117 @@ public final class FilePath implements Serializable { } } } - + private class CopyRecursiveLocal extends SecureFileCallable { + private final FilePath target; + private final DirScanner scanner; + CopyRecursiveLocal(FilePath target, DirScanner scanner) { + this.target = target; + this.scanner = scanner; + } + private static final long serialVersionUID = 1L; + @Override + public Integer invoke(File base, VirtualChannel channel) throws IOException { + if (!base.exists()) { + return 0; + } + assert target.channel == null; + final File dest = new File(target.remote); + final AtomicInteger count = new AtomicInteger(); + scanner.scan(base, reading(new FileVisitor() { + private boolean exceptionEncountered; + private boolean logMessageShown; + @Override + public void visit(File f, String relativePath) throws IOException { + if (f.isFile()) { + File target = new File(dest, relativePath); + mkdirsE(target.getParentFile()); + Path targetPath = fileToPath(writing(target)); + exceptionEncountered = exceptionEncountered || !tryCopyWithAttributes(f, targetPath); + if (exceptionEncountered) { + Files.copy(fileToPath(f), targetPath, StandardCopyOption.REPLACE_EXISTING); + if (!logMessageShown) { + LOGGER.log(Level.INFO, + "JENKINS-52325: Jenkins failed to retain attributes when copying to {0}, so proceeding without attributes.", + dest.getAbsolutePath()); + logMessageShown = true; + } + } + count.incrementAndGet(); + } + } + private boolean tryCopyWithAttributes(File f, Path targetPath) { + try { + Files.copy(fileToPath(f), targetPath, + StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + LOGGER.log(Level.FINE, "Unable to copy: {0}", e.getMessage()); + return false; + } + return true; + } + @Override + public boolean understandsSymlink() { + return true; + } + @Override + public void visitSymlink(File link, String target, String relativePath) throws IOException { + try { + mkdirsE(new File(dest, relativePath).getParentFile()); + writing(new File(dest, target)); + Util.createSymlink(dest, target, relativePath, TaskListener.NULL); + } catch (InterruptedException x) { + throw new IOException(x); + } + count.incrementAndGet(); + } + })); + return count.get(); + } + } + private class ReadToTar extends SecureFileCallable { + private final Pipe pipe; + private final String description; + ReadToTar(Pipe pipe, String description) { + this.pipe = pipe; + this.description = description; + } + private static final long serialVersionUID = 1L; + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException { + try (InputStream in = pipe.getIn()) { + readFromTar(remote + '/' + description, f, TarCompression.GZIP.extract(in)); + return null; + } + } + } + private class WriteToTar extends SecureFileCallable { + private final DirScanner scanner; + private final Pipe pipe; + WriteToTar(DirScanner scanner, Pipe pipe) { + this.scanner = scanner; + this.pipe = pipe; + } + private static final long serialVersionUID = 1L; + @Override + public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + return writeToTar(new File(remote), scanner, TarCompression.GZIP.compress(pipe.getOut())); + } + } + private class CopyRecursiveRemoteToLocal extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private final Pipe pipe; + private final DirScanner scanner; + CopyRecursiveRemoteToLocal(Pipe pipe, DirScanner scanner) { + this.pipe = pipe; + this.scanner = scanner; + } + @Override + public Integer invoke(File f, VirtualChannel channel) throws IOException { + try (OutputStream out = pipe.getOut()) { + return writeToTar(f, scanner, TarCompression.GZIP.compress(out)); + } + } + } /** * Writes files in 'this' directory to a tar stream. @@ -2347,6 +2604,10 @@ public final class FilePath implements Serializable { TarArchiveEntry te; while ((te = t.getNextTarEntry()) != null) { File f = new File(baseDir, te.getName()); + if (!f.toPath().normalize().startsWith(baseDir.toPath())) { + throw new IOException( + "Tar " + name + " contains illegal file name that breaks out of the target directory: " + te.getName()); + } if (te.isDirectory()) { mkdirs(f); } else { @@ -2438,9 +2699,20 @@ public final class FilePath implements Serializable { * @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches * @since 1.484 */ - public String validateAntFileMask(final String fileMasks, final int bound, final boolean caseSensitive) throws IOException, InterruptedException { - return act(new MasterToSlaveFileCallable() { + public @CheckForNull String validateAntFileMask(final String fileMasks, final int bound, final boolean caseSensitive) throws IOException, InterruptedException { + return act(new ValidateAntFileMask(fileMasks, caseSensitive, bound)); + } + private class ValidateAntFileMask extends MasterToSlaveFileCallable { + private final String fileMasks; + private final boolean caseSensitive; + private final int bound; + ValidateAntFileMask(String fileMasks, boolean caseSensitive, int bound) { + this.fileMasks = fileMasks; + this.caseSensitive = caseSensitive; + this.bound = bound; + } private static final long serialVersionUID = 1; + @Override public String invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { if(fileMasks.startsWith("~")) return Messages.FilePath_TildaDoesntWork(); @@ -2586,7 +2858,6 @@ public final class FilePath implements Serializable { if(idx2==-1) return idx1; return Math.min(idx1,idx2); } - }); } private static final UrlFactory DEFAULT_URL_FACTORY = new UrlFactory(); @@ -2828,6 +3099,11 @@ public final class FilePath implements Serializable { return classLoader; } + @Override + public String toString() { + return callable.toString(); + } + private static final long serialVersionUID = 1L; } @@ -2852,11 +3128,13 @@ public final class FilePath implements Serializable { * (User's home directory in the Unix sense) of the given channel. */ public static FilePath getHomeDirectory(VirtualChannel ch) throws InterruptedException, IOException { - return ch.call(new MasterToSlaveCallable() { - public FilePath call() throws IOException { - return new FilePath(new File(System.getProperty("user.home"))); - } - }); + return ch.call(new GetHomeDirectory()); + } + private static class GetHomeDirectory extends MasterToSlaveCallable { + @Override + public FilePath call() throws IOException { + return new FilePath(new File(System.getProperty("user.home"))); + } } /** @@ -3002,5 +3280,60 @@ public final class FilePath implements Serializable { return IOUtils.mkdirs(dir); } + /** + * Check if the relative child is really a descendant after symlink resolution if any. + * + * TODO un-restrict it in a weekly after the patch + */ + @Restricted(NoExternalUse.class) + public boolean isDescendant(@Nonnull String potentialChildRelativePath) throws IOException, InterruptedException { + return act(new IsDescendant(potentialChildRelativePath)); + } + + private class IsDescendant extends SecureFileCallable { + private static final long serialVersionUID = 1L; + private String potentialChildRelativePath; + + private IsDescendant(@Nonnull String potentialChildRelativePath){ + this.potentialChildRelativePath = potentialChildRelativePath; + } + + @Override + public Boolean invoke(@Nonnull File parentFile, @Nonnull VirtualChannel channel) throws IOException, InterruptedException { + if (new File(potentialChildRelativePath).isAbsolute()) { + throw new IllegalArgumentException("Only a relative path is supported, the given path is absolute: " + potentialChildRelativePath); + } + + Path parent = parentFile.getAbsoluteFile().toPath().normalize(); + + String remainingPath = potentialChildRelativePath; + File currentFile = parentFile; + while (!remainingPath.isEmpty()) { + File directChild = this.getDirectChild(currentFile, remainingPath); + File childUsingFullPath = new File(currentFile, remainingPath); + remainingPath = childUsingFullPath.getAbsolutePath().substring(directChild.getAbsolutePath().length()); + + File childFileSymbolic = Util.resolveSymlinkToFile(directChild); + if (childFileSymbolic == null) { + currentFile = directChild; + } else { + currentFile = childFileSymbolic; + } + } + + //TODO could be refactored using Util#isDescendant(File, File) from 2.80+ + Path child = currentFile.getAbsoluteFile().toPath().normalize(); + return child.startsWith(parent); + } + + private @CheckForNull File getDirectChild(File parentFile, String childPath){ + File current = new File(parentFile, childPath); + while (current != null && !parentFile.equals(current.getParentFile())) { + current = current.getParentFile(); + } + return current; + } + } + private static final SoloFilePathFilter UNRESTRICTED = SoloFilePathFilter.wrap(FilePathFilter.UNRESTRICTED); } diff --git a/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java b/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java index 1e02e11f7f4dfdbedf2f00b853535725c1d43f86..9e162d666feb466d4487911fb4b2b2cdbd81a99a 100644 --- a/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java +++ b/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java @@ -50,12 +50,12 @@ public abstract class FileSystemProvisionerDescriptor extends Descriptortrue. + * perform the necessary deletion operation, and return {@code true}. * *

* If the workspace isn't the one created by this {@link FileSystemProvisioner}, or if the * workspace can be simply deleted by {@link FilePath#deleteRecursive()}, then simply - * return false to give other {@link FileSystemProvisionerDescriptor}s a chance to + * return {@code false} to give other {@link FileSystemProvisionerDescriptor}s a chance to * discard them. * * @param ws diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 8462b889946e8facd489ca242f940d754aa63afe..5e2ab71644b933ba7424afd14268e11a2e339b9b 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -47,6 +47,7 @@ import hudson.model.JobPropertyDescriptor; import hudson.model.ModelObject; import hudson.model.Node; import hudson.model.PageDecorator; +import jenkins.model.SimplePageDecorator; import hudson.model.PaneStatusProperties; import hudson.model.ParameterDefinition; import hudson.model.ParameterDefinition.ParameterDescriptor; @@ -133,8 +134,6 @@ import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu; -import org.acegisecurity.Authentication; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.apache.commons.jelly.JellyContext; import org.apache.commons.jelly.JellyTagException; import org.apache.commons.jelly.Script; @@ -397,11 +396,11 @@ public class Functions { * is chosen, this part remains intact. * *

- * The 524 is the path from {@link Job} to {@link Run}. + * The {@code 524} is the path from {@link Job} to {@link Run}. * *

- * The bbb portion is the path after that till the last - * {@link Run} subtype. The ccc portion is the part + * The {@code bbb} portion is the path after that till the last + * {@link Run} subtype. The {@code ccc} portion is the part * after that. */ public static final class RunUrl { @@ -619,7 +618,7 @@ public class Functions { private static final SimpleFormatter formatter = new SimpleFormatter(); /** - * Used by layout.jelly to control the auto refresh behavior. + * Used by {@code layout.jelly} to control the auto refresh behavior. * * @param noAutoRefresh * On certain pages, like a page with forms, will have annoying interference @@ -773,7 +772,7 @@ public class Functions { } /** - * This version is so that the 'checkPermission' on layout.jelly + * This version is so that the 'checkPermission' on {@code layout.jelly} * degrades gracefully if "it" is not an {@link AccessControlled} object. * Otherwise it will perform no check and that problem is hard to notice. */ @@ -1379,6 +1378,7 @@ public class Functions { } public static String jsStringEscape(String s) { + if (s == null) return null; StringBuilder buf = new StringBuilder(); for( int i=0; i - * This is primarily used in slave-agent.jnlp.jelly to specify the destination + * This is primarily used in {@code slave-agent.jnlp.jelly} to specify the destination * that the agents talk to. */ public String getServerName() { @@ -1746,7 +1746,7 @@ public class Functions { /** * If the given href link is matching the current page, return true. * - * Used in task.jelly to decide if the page should be highlighted. + * Used in {@code task.jelly} to decide if the page should be highlighted. */ public boolean hyperlinkMatchesCurrentPage(String href) throws UnsupportedEncodingException { String url = Stapler.getCurrentRequest().getRequestURL().toString(); @@ -1771,7 +1771,14 @@ public class Functions { if(Jenkins.getInstanceOrNull()==null) return Collections.emptyList(); return PageDecorator.all(); } - + /** + * Gets only one {@link SimplePageDecorator}. + * @since 2.128 + */ + public static SimplePageDecorator getSimplePageDecorator() { + return SimplePageDecorator.first(); + } + public static List> getCloudDescriptors() { return Cloud.all(); } @@ -1820,7 +1827,7 @@ public class Functions { * from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s. */ public static String generateConsoleAnnotationScriptAndStylesheet() { - String cp = Stapler.getCurrentRequest().getContextPath(); + String cp = Stapler.getCurrentRequest().getContextPath() + Jenkins.RESOURCE_PATH; StringBuilder buf = new StringBuilder(); for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) { String path = cp + "/extensionList/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName(); diff --git a/core/src/main/java/hudson/Indenter.java b/core/src/main/java/hudson/Indenter.java index c1f6971b6540a6b66c36f34e0378b605835bd290..aa4348b57a9bbc54b2a5b89fe272e4ed663562c5 100644 --- a/core/src/main/java/hudson/Indenter.java +++ b/core/src/main/java/hudson/Indenter.java @@ -26,7 +26,7 @@ package hudson; import hudson.model.Job; /** - * Used by projectView.jelly to indent modules. + * Used by {@code projectView.jelly} to indent modules. * * @author Kohsuke Kawaguchi */ diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 432a5afe249c7bee7e68c11d2a9ec0be490cb8b8..f0f18b4f81df12bb56440e68872bf0871fca1a55 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -26,6 +26,7 @@ package hudson; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Proc.LocalProc; import hudson.model.Computer; +import jenkins.util.MemoryReductionUtil; import hudson.util.QuotedStringTokenizer; import jenkins.model.Jenkins; import hudson.model.TaskListener; @@ -166,6 +167,8 @@ public abstract class Launcher { @CheckForNull protected OutputStream stdout = NULL_OUTPUT_STREAM, stderr; @CheckForNull + private TaskListener stdoutListener; + @CheckForNull protected InputStream stdin = NULL_INPUT_STREAM; @CheckForNull protected String[] envs = null; @@ -280,22 +283,25 @@ public abstract class Launcher { * Sets STDOUT destination. * * @param out Output stream. - * Use {@code null} to send STDOUT to /dev/null. + * Use {@code null} to send STDOUT to {@code /dev/null}. * @return {@code this} */ public ProcStarter stdout(@CheckForNull OutputStream out) { this.stdout = out; + stdoutListener = null; return this; } /** * Sends the stdout to the given {@link TaskListener}. * - * @param out Task listener + * @param out Task listener (must be safely remotable) * @return {@code this} */ public ProcStarter stdout(@Nonnull TaskListener out) { - return stdout(out.getLogger()); + stdout = out.getLogger(); + stdoutListener = out; + return this; } /** @@ -329,7 +335,7 @@ public abstract class Launcher { /** * Controls where the stdin of the process comes from. - * By default, /dev/null. + * By default, {@code /dev/null}. * * @return {@code this} */ @@ -391,7 +397,7 @@ public abstract class Launcher { */ @Nonnull public String[] envs() { - return envs != null ? envs.clone() : new String[0]; + return envs != null ? envs.clone() : MemoryReductionUtil.EMPTY_STRING_ARRAY; } /** @@ -490,6 +496,7 @@ public abstract class Launcher { @Nonnull public ProcStarter copy() { ProcStarter rhs = new ProcStarter().cmds(commands).pwd(pwd).masks(masks).stdin(stdin).stdout(stdout).stderr(stderr).envs(envs).quiet(quiet); + rhs.stdoutListener = stdoutListener; rhs.reverseStdin = this.reverseStdin; rhs.reverseStderr = this.reverseStderr; rhs.reverseStdout = this.reverseStdout; @@ -1041,7 +1048,7 @@ public abstract class Launcher { } public Proc launch(ProcStarter ps) throws IOException { - final OutputStream out = ps.stdout == null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stdout)); + final OutputStream out = ps.stdout == null || ps.stdoutListener != null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stdout)); final OutputStream err = ps.stderr==null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stderr)); final InputStream in = (ps.stdin==null || ps.stdin==NULL_INPUT_STREAM) ? null : new RemoteInputStream(ps.stdin,false); @@ -1049,7 +1056,7 @@ public abstract class Launcher { final String workDir = psPwd==null ? null : psPwd.getRemote(); try { - return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener))); + return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.stdoutListener))); } catch (InterruptedException e) { throw (IOException)new InterruptedIOException().initCause(e); } @@ -1265,6 +1272,7 @@ public abstract class Launcher { private final @CheckForNull OutputStream err; private final @CheckForNull String workDir; private final @Nonnull TaskListener listener; + private final @CheckForNull TaskListener stdoutListener; private final boolean reverseStdin, reverseStdout, reverseStderr; private final boolean quiet; @@ -1272,7 +1280,7 @@ public abstract class Launcher { @CheckForNull InputStream in, boolean reverseStdin, @CheckForNull OutputStream out, boolean reverseStdout, @CheckForNull OutputStream err, boolean reverseStderr, - boolean quiet, @CheckForNull String workDir, @Nonnull TaskListener listener) { + boolean quiet, @CheckForNull String workDir, @Nonnull TaskListener listener, @CheckForNull TaskListener stdoutListener) { this.cmd = new ArrayList<>(cmd); this.masks = masks; this.env = env; @@ -1281,6 +1289,7 @@ public abstract class Launcher { this.err = err; this.workDir = workDir; this.listener = listener; + this.stdoutListener = stdoutListener; this.reverseStdin = reverseStdin; this.reverseStdout = reverseStdout; this.reverseStderr = reverseStderr; @@ -1290,7 +1299,12 @@ public abstract class Launcher { public RemoteProcess call() throws IOException { final Channel channel = getOpenChannelOrFail(); Launcher.ProcStarter ps = new LocalLauncher(listener).launch(); - ps.cmds(cmd).masks(masks).envs(env).stdin(in).stdout(out).stderr(err).quiet(quiet); + ps.cmds(cmd).masks(masks).envs(env).stdin(in).stderr(err).quiet(quiet); + if (stdoutListener != null) { + ps.stdout(stdoutListener.getLogger()); + } else { + ps.stdout(out); + } if(workDir!=null) ps.pwd(workDir); if (reverseStdin) ps.writeStdin(); if (reverseStdout) ps.readStdout(); diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java index 896ae4dd53398525db56af00f19aeec7c40dde05..11cb516cee31be90a3f118b308f27179083f9d5a 100644 --- a/core/src/main/java/hudson/Main.java +++ b/core/src/main/java/hudson/Main.java @@ -144,7 +144,7 @@ public class Main { int ret; try (OutputStream os = Files.newOutputStream(tmpFile.toPath()); Writer w = new OutputStreamWriter(os,"UTF-8")) { - w.write(""); + w.write(""); w.write(""); w.flush(); diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java index 9539737878719eb636c01d89acb8a9b8fcdc2314..2f065102becc4c8a3a247a9869a6ebb8a0150844 100644 --- a/core/src/main/java/hudson/Plugin.java +++ b/core/src/main/java/hudson/Plugin.java @@ -35,6 +35,7 @@ import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.File; @@ -42,10 +43,10 @@ import net.sf.json.JSONObject; import com.thoughtworks.xstream.XStream; import hudson.init.Initializer; import hudson.init.Terminator; -import java.net.URI; -import java.net.URISyntaxException; +import java.net.URL; +import java.util.Locale; +import java.util.logging.Logger; import jenkins.model.GlobalConfiguration; -import org.kohsuke.stapler.HttpResponses; /** * Base class of Hudson plugin. @@ -61,26 +62,28 @@ import org.kohsuke.stapler.HttpResponses; * to plugin functionality. * *

- * A plugin is bound to URL space of Hudson as ${rootURL}/plugin/foo/, + * A plugin is bound to URL space of Hudson as {@code ${rootURL}/plugin/foo/}, * where "foo" is taken from your plugin name "foo.jpi". All your web resources * in src/main/webapp are visible from this URL, and you can also define Jelly * views against your Plugin class, and those are visible in this URL, too. * *

- * {@link Plugin} can have an optional config.jelly page. If present, + * {@link Plugin} can have an optional {@code config.jelly} page. If present, * it will become a part of the system configuration page (http://server/hudson/configure). * This is convenient for exposing/maintaining configuration that doesn't * fit any {@link Descriptor}s. * *

* Up until Hudson 1.150 or something, subclasses of {@link Plugin} required - * @plugin javadoc annotation, but that is no longer a requirement. + * {@code @plugin} javadoc annotation, but that is no longer a requirement. * * @author Kohsuke Kawaguchi * @since 1.42 */ public abstract class Plugin implements Saveable { + private static final Logger LOGGER = Logger.getLogger(Plugin.class.getName()); + /** * You do not need to create custom subtypes: *

    @@ -191,11 +194,11 @@ public abstract class Plugin implements Saveable { * Handles the submission for the system configuration. * *

    - * If this class defines config.jelly view, be sure to + * If this class defines {@code config.jelly} view, be sure to * override this method and persists the submitted values accordingly. * *

    - * The following is a sample config.jelly that you can start yours with: + * The following is a sample {@code config.jelly} that you can start yours with: *

    {@code 
          * <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">
          *   <f:section title="Locale">
    @@ -219,18 +222,22 @@ public abstract class Plugin implements Saveable {
         }
     
         /**
    -     * This method serves static resources in the plugin under <tt>hudson/plugin/SHORTNAME</tt>.
    +     * This method serves static resources in the plugin under {@code hudson/plugin/SHORTNAME}.
          */
         public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
             String path = req.getRestOfPath();
     
    -        if (path.startsWith("/META-INF/") || path.startsWith("/WEB-INF/")) {
    -            throw HttpResponses.notFound();
    +        String pathUC = path.toUpperCase(Locale.ENGLISH);
    +        if (path.isEmpty() || path.contains("..") || path.startsWith(".") || path.contains("%")
    +                || pathUC.contains("META-INF") || pathUC.contains("WEB-INF")
    +                // ClassicPluginStrategy#explode produce that file to know if a new explosion is required or not
    +                || pathUC.equals("/.TIMESTAMP2")
    +        ) {
    +            LOGGER.warning("rejecting possibly malicious " + req.getRequestURIWithQueryString());
    +            rsp.sendError(HttpServletResponse.SC_BAD_REQUEST);
    +            return;
             }
     
    -        if(path.length()==0)
    -            path = "/";
    -
             // Stapler routes requests like the "/static/.../foo/bar/zot" to be treated like "/foo/bar/zot"
             // and this is used to serve long expiration header, by using Jenkins.VERSION_HASH as "..."
             // to create unique URLs. Recognize that and set a long expiration header.
    @@ -240,11 +247,7 @@ public abstract class Plugin implements Saveable {
             long expires = staticLink ? TimeUnit.DAYS.toMillis(365) : -1;
     
             // use serveLocalizedFile to support automatic locale selection
    -        try {
    -            rsp.serveLocalizedFile(req, wrapper.baseResourceURL.toURI().resolve(new URI(null, '.' + path, null)).toURL(), expires);
    -        } catch (URISyntaxException x) {
    -            throw new IOException(x);
    -        }
    +        rsp.serveLocalizedFile(req, new URL(wrapper.baseResourceURL, '.' + path), expires);
         }
     
     //
    diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
    index a7971ca33c7701b1b11a8866227fe21f2566d72e..8e2c5fd400e7dc1b3b38f249dd26a4c08101414c 100644
    --- a/core/src/main/java/hudson/PluginManager.java
    +++ b/core/src/main/java/hudson/PluginManager.java
    @@ -25,8 +25,6 @@ package hudson;
     
     import edu.umd.cs.findbugs.annotations.NonNull;
     import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    -import hudson.security.ACLContext;
    -import jenkins.util.SystemProperties;
     import hudson.PluginWrapper.Dependency;
     import hudson.init.InitMilestone;
     import hudson.init.InitStrategy;
    @@ -36,22 +34,26 @@ import hudson.model.AbstractModelObject;
     import hudson.model.AdministrativeMonitor;
     import hudson.model.Api;
     import hudson.model.Descriptor;
    +import hudson.model.DownloadService;
     import hudson.model.Failure;
     import hudson.model.ItemGroupMixIn;
     import hudson.model.UpdateCenter;
    -import hudson.model.UpdateSite;
     import hudson.model.UpdateCenter.DownloadJob;
     import hudson.model.UpdateCenter.InstallationJob;
    +import hudson.model.UpdateSite;
     import hudson.security.ACL;
    +import hudson.security.ACLContext;
     import hudson.security.Permission;
     import hudson.security.PermissionScope;
     import hudson.util.CyclicGraphDetector;
     import hudson.util.CyclicGraphDetector.CycleDetectedException;
    +import hudson.util.FormValidation;
     import hudson.util.PersistedList;
     import hudson.util.Service;
     import hudson.util.VersionNumber;
     import hudson.util.XStream2;
     import jenkins.ClassLoaderReflectionToolkit;
    +import jenkins.ExtensionRefreshException;
     import jenkins.InitReactorRunner;
     import jenkins.MissingDependencyException;
     import jenkins.RestartRequiredException;
    @@ -59,12 +61,12 @@ import jenkins.YesNoMaybe;
     import jenkins.install.InstallState;
     import jenkins.install.InstallUtil;
     import jenkins.model.Jenkins;
    +import jenkins.security.CustomClassFilter;
    +import jenkins.util.SystemProperties;
     import jenkins.util.io.OnMaster;
     import jenkins.util.xml.RestrictiveEntityResolver;
    -
     import net.sf.json.JSONArray;
     import net.sf.json.JSONObject;
    -
     import org.acegisecurity.Authentication;
     import org.apache.commons.fileupload.FileItem;
     import org.apache.commons.fileupload.FileUploadException;
    @@ -82,17 +84,24 @@ import org.jvnet.hudson.reactor.Reactor;
     import org.jvnet.hudson.reactor.ReactorException;
     import org.jvnet.hudson.reactor.TaskBuilder;
     import org.jvnet.hudson.reactor.TaskGraphBuilder;
    +import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.DoNotUse;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.HttpRedirect;
     import org.kohsuke.stapler.HttpResponse;
     import org.kohsuke.stapler.HttpResponses;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerOverridable;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.export.Exported;
     import org.kohsuke.stapler.export.ExportedBean;
     import org.kohsuke.stapler.interceptor.RequirePOST;
    +import org.xml.sax.Attributes;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.SAXException;
    +import org.xml.sax.helpers.DefaultHandler;
     
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
    @@ -100,6 +109,7 @@ import javax.servlet.ServletContext;
     import javax.servlet.ServletException;
     import javax.xml.parsers.ParserConfigurationException;
     import javax.xml.parsers.SAXParserFactory;
    +import java.io.ByteArrayInputStream;
     import java.io.Closeable;
     import java.io.File;
     import java.io.FilenameFilter;
    @@ -107,10 +117,12 @@ import java.io.IOException;
     import java.io.InputStream;
     import java.lang.ref.WeakReference;
     import java.lang.reflect.Method;
    +import java.net.JarURLConnection;
     import java.net.MalformedURLException;
     import java.net.URISyntaxException;
     import java.net.URL;
     import java.net.URLClassLoader;
    +import java.net.URLConnection;
     import java.util.ArrayList;
     import java.util.Collection;
     import java.util.Collections;
    @@ -121,6 +133,7 @@ import java.util.Iterator;
     import java.util.LinkedHashSet;
     import java.util.List;
     import java.util.Map;
    +import java.util.ServiceLoader;
     import java.util.Set;
     import java.util.TreeMap;
     import java.util.UUID;
    @@ -128,31 +141,15 @@ import java.util.concurrent.ConcurrentHashMap;
     import java.util.concurrent.ConcurrentMap;
     import java.util.concurrent.CopyOnWriteArrayList;
     import java.util.concurrent.Future;
    +import java.util.function.Supplier;
    +import java.util.jar.JarEntry;
     import java.util.jar.JarFile;
     import java.util.jar.Manifest;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    -import org.xml.sax.Attributes;
    -import org.xml.sax.InputSource;
    -import org.xml.sax.SAXException;
    -import org.xml.sax.helpers.DefaultHandler;
     
     import static hudson.init.InitMilestone.*;
    -import hudson.model.DownloadService;
    -import hudson.util.FormValidation;
    -import java.io.ByteArrayInputStream;
    -import java.net.JarURLConnection;
    -import java.net.URLConnection;
    -import java.util.ServiceLoader;
    -import java.util.jar.JarEntry;
    -
    -import static java.util.logging.Level.FINE;
    -import static java.util.logging.Level.INFO;
    -import static java.util.logging.Level.SEVERE;
    -import static java.util.logging.Level.WARNING;
    -import jenkins.security.CustomClassFilter;
    -import org.kohsuke.accmod.Restricted;
    -import org.kohsuke.accmod.restrictions.NoExternalUse;
    +import static java.util.logging.Level.*;
     
     /**
      * Manages {@link PluginWrapper}s.
    @@ -176,7 +173,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
      * @author Kohsuke Kawaguchi
      */
     @ExportedBean
    -public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable {
    +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";
     
    @@ -920,6 +917,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
                 // Redo who depends on who.
                 resolveDependantPlugins();
     
    +            try {
    +                Jenkins.get().refreshExtensions();
    +            } catch (ExtensionRefreshException e) {
    +                throw new IOException("Failed to refresh extensions after installing " + sn + " plugin", e);
    +            }
                 LOGGER.info("Plugin " + p.getShortName()+":"+p.getVersion() + " dynamically installed");
             }
         }
    @@ -927,8 +929,15 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
         @Restricted(NoExternalUse.class)
         public synchronized void resolveDependantPlugins() {
             for (PluginWrapper plugin : plugins) {
    +            // Set of optional dependants plugins of plugin
    +            Set<String> optionalDependants = new HashSet<>();
                 Set<String> dependants = new HashSet<>();
                 for (PluginWrapper possibleDependant : plugins) {
    +                // No need to check if plugin is dependant of itself
    +                if(possibleDependant.getShortName().equals(plugin.getShortName())) {
    +                    continue;
    +                }
    +
                     // The plugin could have just been deleted. If so, it doesn't
                     // count as a dependant.
                     if (possibleDependant.isDeleted()) {
    @@ -938,10 +947,20 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
                     for (Dependency dependency : dependencies) {
                         if (dependency.shortName.equals(plugin.getShortName())) {
                             dependants.add(possibleDependant.getShortName());
    +
    +                        // If, in addition, the dependency is optional, add to the optionalDependants list
    +                        if (dependency.optional) {
    +                            optionalDependants.add(possibleDependant.getShortName());
    +                        }
    +
    +                        // already know possibleDependant depends on plugin, no need to continue with the rest of
    +                        // dependencies. We continue with the next possibleDependant
    +                        break;
                         }
                     }
                 }
                 plugin.setDependants(dependants);
    +            plugin.setOptionalDependants(optionalDependants);
             }
         }
     
    @@ -1208,7 +1227,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
     
         /**
          * Discover all the service provider implementations of the given class,
    -     * via <tt>META-INF/services</tt>.
    +     * via {@code META-INF/services}.
          * @deprecated Use {@link ServiceLoader} instead, or (more commonly) {@link ExtensionList}.
          */
         @Deprecated
    @@ -1485,7 +1504,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
                         }
                         updateCenter.persistInstallStatus();
                         if(!failures) {
    -                        try (ACLContext _ = ACL.as(currentAuth)) {
    +                        try (ACLContext acl = ACL.as(currentAuth)) {
                                 InstallUtil.proceedToNextStateFrom(InstallState.INITIAL_PLUGINS_INSTALLING);
                             }
                         }
    @@ -1816,6 +1835,44 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
             return requestedPlugins;
         }
     
    +    @Restricted(DoNotUse.class) // table.jelly
    +    public MetadataCache createCache() {
    +        return new MetadataCache();
    +    }
    +
    +    /**
    +     * Disable a list of plugins using a strategy for their dependants plugins.
    +     * @param strategy the strategy regarding how the dependant plugins are processed
    +     * @param plugins the list of plugins
    +     * @return the list of results for every plugin and their dependant plugins.
    +     * @throws IOException see {@link PluginWrapper#disable()}
    +     */
    +    public @NonNull List<PluginWrapper.PluginDisableResult> disablePlugins(@NonNull PluginWrapper.PluginDisableStrategy strategy, @NonNull List<String> plugins) throws IOException {
    +        // Where we store the results of each plugin disablement
    +        List<PluginWrapper.PluginDisableResult> results = new ArrayList<>(plugins.size());
    +
    +        // Disable all plugins passed
    +        for (String pluginName : plugins) {
    +            PluginWrapper plugin = this.getPlugin(pluginName);
    +
    +            if (plugin == null) {
    +                results.add(new PluginWrapper.PluginDisableResult(pluginName, PluginWrapper.PluginDisableStatus.NO_SUCH_PLUGIN, Messages.PluginWrapper_NoSuchPlugin(pluginName)));
    +            } else {
    +                results.add(plugin.disable(strategy));
    +            }
    +        }
    +
    +        return results;
    +    }
    +
    +    @Restricted(NoExternalUse.class) // table.jelly
    +    public static final class MetadataCache {
    +        private final Map<String, Object> data = new HashMap<>();
    +        public <T> T of(String key, Class<T> type, Supplier<T> func) {
    +            return type.cast(data.computeIfAbsent(key, _ignored -> func.get()));
    +        }
    +    }
    +
         /**
          * {@link ClassLoader} that can see all plugins.
          */
    @@ -2063,4 +2120,19 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
             }
     
         }
    +
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(PluginManager.class.getName() + ".skipPermissionCheck");
     }
    diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java
    index 75e358a81193d1b45212fe0f06b7af16a93e1111..16f0fcc63f48fa2e0eee5cd1f40adc792fccf7b7 100644
    --- a/core/src/main/java/hudson/PluginWrapper.java
    +++ b/core/src/main/java/hudson/PluginWrapper.java
    @@ -25,18 +25,21 @@
     package hudson;
     
     import com.google.common.collect.ImmutableSet;
    +import com.google.common.collect.Sets;
     import hudson.PluginManager.PluginInstanceStore;
     import hudson.model.AdministrativeMonitor;
     import hudson.model.Api;
     import hudson.model.ModelObject;
    -import java.nio.file.Files;
    -import java.nio.file.InvalidPathException;
    -import jenkins.YesNoMaybe;
    -import jenkins.model.Jenkins;
     import hudson.model.UpdateCenter;
     import hudson.model.UpdateSite;
     import hudson.util.VersionNumber;
    -import org.jvnet.localizer.ResourceBundleHolder;
    +import jenkins.YesNoMaybe;
    +import jenkins.model.Jenkins;
    +import org.apache.commons.lang.StringUtils;
    +import org.apache.commons.logging.LogFactory;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.DoNotUse;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.HttpResponse;
     import org.kohsuke.stapler.HttpResponses;
     import org.kohsuke.stapler.StaplerRequest;
    @@ -45,16 +48,15 @@ import org.kohsuke.stapler.export.Exported;
     import org.kohsuke.stapler.export.ExportedBean;
     import org.kohsuke.stapler.interceptor.RequirePOST;
     
    -import org.apache.commons.lang.StringUtils;
    -import org.apache.commons.logging.LogFactory;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     import java.io.Closeable;
     import java.io.File;
    -import java.io.FileOutputStream;
     import java.io.IOException;
     import java.io.OutputStream;
     import java.net.URL;
    +import java.nio.file.Files;
    +import java.nio.file.InvalidPathException;
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Collection;
    @@ -65,12 +67,20 @@ import java.util.HashSet;
     import java.util.Iterator;
     import java.util.List;
     import java.util.Map;
    +import java.util.Objects;
     import java.util.Set;
    +import java.util.function.Predicate;
     import java.util.jar.JarFile;
     import java.util.jar.Manifest;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    +import java.util.stream.Collectors;
     
    +import static hudson.PluginWrapper.PluginDisableStatus.ALREADY_DISABLED;
    +import static hudson.PluginWrapper.PluginDisableStatus.DISABLED;
    +import static hudson.PluginWrapper.PluginDisableStatus.ERROR_DISABLING;
    +import static hudson.PluginWrapper.PluginDisableStatus.NOT_DISABLED_DEPENDANTS;
    +import static hudson.PluginWrapper.PluginDisableStatus.NO_SUCH_PLUGIN;
     import static java.util.logging.Level.WARNING;
     import static org.apache.commons.io.FilenameUtils.getBaseName;
     
    @@ -79,7 +89,7 @@ import static org.apache.commons.io.FilenameUtils.getBaseName;
      * for Jenkins to control {@link Plugin}.
      *
      * <p>
    - * A plug-in is packaged into a jar file whose extension is <tt>".jpi"</tt> (or <tt>".hpi"</tt> for backward compatibility),
    + * A plug-in is packaged into a jar file whose extension is {@code ".jpi"} (or {@code ".hpi"} for backward compatibility),
      * A plugin needs to have a special manifest entry to identify what it is.
      *
      * <p>
    @@ -124,7 +134,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
         /**
          * Base URL for loading static resources from this plugin.
          * Null if disabled. The static resources are mapped under
    -     * <tt>CONTEXTPATH/plugin/SHORTNAME/</tt>.
    +     * {@code CONTEXTPATH/plugin/SHORTNAME/}.
          */
         public final URL baseResourceURL;
     
    @@ -149,7 +159,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
     
         /**
          * True if this plugin is activated for this session.
    -     * The snapshot of <tt>disableFile.exists()</tt> as of the start up.
    +     * The snapshot of {@code disableFile.exists()} as of the start up.
          */
         private final boolean active;
         
    @@ -159,10 +169,34 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
         private final List<Dependency> optionalDependencies;
     
         public List<String> getDependencyErrors() {
    -        return Collections.unmodifiableList(dependencyErrors);
    +        return Collections.unmodifiableList(new ArrayList<>(dependencyErrors.keySet()));
         }
     
    -    private final transient List<String> dependencyErrors = new ArrayList<>();
    +    @Restricted(NoExternalUse.class) // Jelly use
    +    public List<String> getOriginalDependencyErrors() {
    +        Predicate<Map.Entry<String, Boolean>> p = Map.Entry::getValue;
    +        return dependencyErrors.entrySet().stream().filter(p.negate()).map(Map.Entry::getKey).collect(Collectors.toList());
    +    }
    +
    +    @Restricted(NoExternalUse.class) // Jelly use
    +    public boolean hasOriginalDependencyErrors() {
    +        return !getOriginalDependencyErrors().isEmpty();
    +    }
    +
    +    @Restricted(NoExternalUse.class) // Jelly use
    +    public List<String> getDerivedDependencyErrors() {
    +        return dependencyErrors.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).collect(Collectors.toList());
    +    }
    +
    +    @Restricted(NoExternalUse.class) // Jelly use
    +    public boolean hasDerivedDependencyErrors() {
    +        return !getDerivedDependencyErrors().isEmpty();
    +    }
    +
    +    /**
    +     * A String error message, and a boolean indicating whether it's an original error (false) or downstream from an original one (true)
    +     */
    +    private final transient Map<String, Boolean> dependencyErrors = new HashMap<>(0);
     
         /**
          * Is this plugin bundled in jenkins.war?
    @@ -174,6 +208,11 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
          */
         private Set<String> dependants = Collections.emptySet();
     
    +    /**
    +     * List of plugins that depend optionally on this plugin.
    +     */
    +    private Set<String> optionalDependants = Collections.emptySet();
    +
         /**
          * The core can depend on a plugin if it is bundled. Sometimes it's the only thing that
          * depends on the plugin e.g. UI support library bundle plugin.
    @@ -188,6 +227,14 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
             this.dependants = dependants;
         }
     
    +    /**
    +     * Set the list of components that depend optionally on this plugin.
    +     * @param optionalDependants The list of components that depend optionally on this plugin.
    +     */
    +    public void setOptionalDependants(@Nonnull Set<String> optionalDependants) {
    +        this.optionalDependants = optionalDependants;
    +    }
    +
         /**
          * Get the list of components that depend on this plugin.
          * @return The list of components that depend on this plugin.
    @@ -200,6 +247,13 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
             }
         }
     
    +    /**
    +     * @return The list of components that depend optionally on this plugin.
    +     */
    +    public @Nonnull Set<String> getOptionalDependants() {
    +        return optionalDependants;
    +    }
    +
         /**
          * Does this plugin have anything that depends on it.
          * @return {@code true} if something (Jenkins core, or another plugin) depends on this
    @@ -208,7 +262,17 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
         public boolean hasDependants() {
             return (isBundled || !dependants.isEmpty());
         }
    -    
    +
    +    /**
    +     * Does this plugin have anything that depends optionally on it.
    +     * @return {@code true} if something (Jenkins core, or another plugin) depends optionally on this
    +     * plugin, otherwise {@code false}.
    +     */
    +    public boolean hasOptionalDependants() {
    +        return !optionalDependants.isEmpty();
    +    }
    +
    +
         /**
          * Does this plugin depend on any other plugins.
          * @return {@code true} if this plugin depends on other plugins, otherwise {@code false}.
    @@ -230,8 +294,8 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 int idx = s.indexOf(':');
                 if(idx==-1)
                     throw new IllegalArgumentException("Illegal dependency specifier "+s);
    -            this.shortName = s.substring(0,idx);
    -            String version = s.substring(idx+1);
    +            this.shortName = Util.intern(s.substring(0,idx));
    +            String version = Util.intern(s.substring(idx+1));
     
                 boolean isOptional = false;
                 String[] osgiProperties = version.split("[;]");
    @@ -274,7 +338,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
     			List<Dependency> dependencies, List<Dependency> optionalDependencies) {
             this.parent = parent;
     		this.manifest = manifest;
    -		this.shortName = computeShortName(manifest, archive.getName());
    +		this.shortName = Util.intern(computeShortName(manifest, archive.getName()));
     		this.baseResourceURL = baseResourceURL;
     		this.classLoader = classLoader;
     		this.disableFile = disableFile;
    @@ -493,9 +557,19 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
         }
     
         /**
    -     * Disables this plugin next time Jenkins runs.
    +     * Disables this plugin next time Jenkins runs. As it doesn't check anything, it's recommended to use the method
    +     * {@link #disable(PluginDisableStrategy)}
          */
    +    @Deprecated //see https://issues.jenkins-ci.org/browse/JENKINS-27177
         public void disable() throws IOException {
    +        disableWithoutCheck();
    +    }
    +
    +    /**
    +     * Disable a plugin wihout checking any dependency. Only add the disable file.
    +     * @throws IOException
    +     */
    +    private void disableWithoutCheck() throws IOException {
             // creates an empty file
             try (OutputStream os = Files.newOutputStream(disableFile.toPath())) {
                 os.close();
    @@ -504,6 +578,103 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
             }
         }
     
    +    /**
    +     * Disable this plugin using a strategy.
    +     * @param strategy strategy to use
    +     * @return an object representing the result of the disablement of this plugin and its dependants plugins.
    +     */
    +    public @Nonnull PluginDisableResult disable(@Nonnull PluginDisableStrategy strategy) {
    +        PluginDisableResult result = new PluginDisableResult(shortName);
    +
    +        if (!this.isEnabled()) {
    +            result.setMessage(Messages.PluginWrapper_Already_Disabled(shortName));
    +            result.setStatus(ALREADY_DISABLED);
    +            return result;
    +        }
    +
    +        // Act as a flag indicating if this plugin, finally, can be disabled. If there is a not-disabled-dependant
    +        // plugin, this one couldn't be disabled.
    +        String aDependantNotDisabled = null;
    +
    +        // List of dependants plugins to 'check'. 'Check' means disable for mandatory or all strategies, or review if
    +        // this dependant-mandatory plugin is enabled in order to return an error for the NONE strategy.
    +        Set<String> dependantsToCheck = dependantsToCheck(strategy);
    +
    +        // Review all the dependants and add to the plugin result what happened with its dependants
    +        for (String dependant : dependantsToCheck) {
    +            PluginWrapper dependantPlugin = parent.getPlugin(dependant);
    +
    +            // The dependant plugin doesn't exist, add an error to the report
    +            if (dependantPlugin == null) {
    +                PluginDisableResult dependantStatus = new PluginDisableResult(dependant, NO_SUCH_PLUGIN, Messages.PluginWrapper_NoSuchPlugin(dependant));
    +                result.addDependantDisableStatus(dependantStatus);
    +
    +            // If the strategy is none and there is some enabled dependant plugin, the plugin cannot be disabled. If
    +            // this dependant plugin is not enabled, continue searching for one enabled.
    +            } else if (strategy.equals(PluginDisableStrategy.NONE)) {
    +                if (dependantPlugin.isEnabled()) {
    +                    aDependantNotDisabled = dependant;
    +                    break; // in this case, we don't need to continue with the rest of its dependants
    +                }
    +
    +            // If the strategy is not none and this dependant plugin is not enabled, add it as already disabled
    +            } else if (!dependantPlugin.isEnabled()) {
    +                PluginDisableResult dependantStatus = new PluginDisableResult(dependant, ALREADY_DISABLED, Messages.PluginWrapper_Already_Disabled(dependant));
    +                result.addDependantDisableStatus(dependantStatus);
    +
    +            // If the strategy is not none and this dependant plugin is enabled, disable it
    +            } else {
    +                // As there is no cycles in the plugin dependencies, the recursion shouldn't be infinite. The
    +                // strategy used is the same for its dependants plugins
    +                PluginDisableResult dependantResult = dependantPlugin.disable(strategy);
    +                PluginDisableStatus dependantStatus = dependantResult.status;
    +
    +                // If something wrong happened, flag this dependant plugin to set the plugin later as not-disabled due
    +                // to its dependants plugins.
    +                if (ERROR_DISABLING.equals(dependantStatus) || NOT_DISABLED_DEPENDANTS.equals(dependantStatus)) {
    +                    aDependantNotDisabled = dependant;
    +                    break; // we found a dependant plugin enabled, stop looking for dependant plugins to disable.
    +                }
    +                result.addDependantDisableStatus(dependantResult);
    +            }
    +        }
    +
    +        // If there is no enabled-dependant plugin, disable this plugin and add it to the result
    +        if (aDependantNotDisabled == null) {
    +            try {
    +                this.disableWithoutCheck();
    +                result.setMessage(Messages.PluginWrapper_Plugin_Disabled(shortName));
    +                result.setStatus(DISABLED);
    +            } catch (IOException io) {
    +                result.setMessage(Messages.PluginWrapper_Error_Disabling(shortName, io.toString()));
    +                result.setStatus(ERROR_DISABLING);
    +            }
    +        // if there is yet some not disabled dependant plugin (only possible with none strategy), this plugin cannot
    +        // be disabled.
    +        } else {
    +            result.setMessage(Messages.PluginWrapper_Plugin_Has_Dependant(shortName, aDependantNotDisabled, strategy));
    +            result.setStatus(NOT_DISABLED_DEPENDANTS);
    +        }
    +
    +        return result;
    +    }
    +
    +    private Set<String> dependantsToCheck(PluginDisableStrategy strategy) {
    +        Set<String> dependantsToCheck;
    +        switch (strategy) {
    +            case ALL:
    +                // getDependants returns all the dependant plugins, mandatory or optional.
    +                dependantsToCheck = this.getDependants();
    +                break;
    +            default:
    +                // It includes MANDATORY, NONE:
    +                // with NONE, the process only fail if mandatory dependant plugins exists
    +                // As of getDependants has all the dependants, we get the difference between them and only the optionals
    +                dependantsToCheck = Sets.difference(this.getDependants(), this.getOptionalDependants());
    +        }
    +        return dependantsToCheck;
    +    }
    +
         /**
          * Returns true if this plugin is enabled for this session.
          */
    @@ -571,7 +742,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 } else {
                     VersionNumber actualVersion = Jenkins.getVersion();
                     if (actualVersion.isOlderThan(new VersionNumber(requiredCoreVersion))) {
    -                    dependencyErrors.add(Messages.PluginWrapper_obsoleteCore(Jenkins.getVersion().toString(), requiredCoreVersion));
    +                    versionDependencyError(Messages.PluginWrapper_obsoleteCore(Jenkins.getVersion().toString(), requiredCoreVersion), Jenkins.getVersion().toString(), requiredCoreVersion);
                     }
                 }
             }
    @@ -581,21 +752,21 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 if (dependency == null) {
                     PluginWrapper failedDependency = NOTICE.getPlugin(d.shortName);
                     if (failedDependency != null) {
    -                    dependencyErrors.add(Messages.PluginWrapper_failed_to_load_dependency(failedDependency.getLongName(), failedDependency.getVersion()));
    +                    dependencyErrors.put(Messages.PluginWrapper_failed_to_load_dependency(failedDependency.getLongName(), failedDependency.getVersion()), true);
                         break;
                     } else {
    -                    dependencyErrors.add(Messages.PluginWrapper_missing(d.shortName, d.version));
    +                    dependencyErrors.put(Messages.PluginWrapper_missing(d.shortName, d.version), false);
                     }
                 } else {
                     if (dependency.isActive()) {
                         if (isDependencyObsolete(d, dependency)) {
    -                        dependencyErrors.add(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version));
    +                        versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
                         }
                     } else {
                         if (isDependencyObsolete(d, dependency)) {
    -                        dependencyErrors.add(Messages.PluginWrapper_disabledAndObsolete(dependency.getLongName(), dependency.getVersion(), d.version));
    +                        versionDependencyError(Messages.PluginWrapper_disabledAndObsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
                         } else {
    -                        dependencyErrors.add(Messages.PluginWrapper_disabled(dependency.getLongName()));
    +                        dependencyErrors.put(Messages.PluginWrapper_disabled(dependency.getLongName()), false);
                         }
                     }
     
    @@ -606,7 +777,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 PluginWrapper dependency = parent.getPlugin(d.shortName);
                 if (dependency != null && dependency.isActive()) {
                     if (isDependencyObsolete(d, dependency)) {
    -                    dependencyErrors.add(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version));
    +                    versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
                     } else {
                         dependencies.add(d);
                     }
    @@ -616,7 +787,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 NOTICE.addPlugin(this);
                 StringBuilder messageBuilder = new StringBuilder();
                 messageBuilder.append(Messages.PluginWrapper_failed_to_load_plugin(getLongName(), getVersion())).append(System.lineSeparator());
    -            for (Iterator<String> iterator = dependencyErrors.iterator(); iterator.hasNext(); ) {
    +            for (Iterator<String> iterator = dependencyErrors.keySet().iterator(); iterator.hasNext(); ) {
                     String dependencyError = iterator.next();
                     messageBuilder.append(" - ").append(dependencyError);
                     if (iterator.hasNext()) {
    @@ -631,6 +802,26 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
             return ENABLE_PLUGIN_DEPENDENCIES_VERSION_CHECK && dependency.getVersionNumber().isOlderThan(new VersionNumber(d.version));
         }
     
    +    /**
    +     * Called when there appears to be a core or plugin version which is too old for a stated dependency.
    +     * Normally records an error in {@link #dependencyErrors}.
    +     * But if one or both versions {@link #isSnapshot}, just issue a warning (JENKINS-52665).
    +     */
    +    private void versionDependencyError(String message, String actual, String minimum) {
    +        if (isSnapshot(actual) || isSnapshot(minimum)) {
    +            LOGGER.log(WARNING, "Suppressing dependency error in {0} v{1}: {2}", new Object[] {getLongName(), getVersion(), message});
    +        } else {
    +            dependencyErrors.put(message, false);
    +        }
    +    }
    +
    +    /**
    +     * Similar to {@code org.apache.maven.artifact.ArtifactUtils.isSnapshot}.
    +     */
    +    static boolean isSnapshot(@Nonnull String version) {
    +        return version.contains("-SNAPSHOT") || version.matches(".+-[0-9]{8}.[0-9]{6}-[0-9]+");
    +    }
    +
         /**
          * If the plugin has {@link #getUpdateInfo() an update},
          * returns the {@link hudson.model.UpdateSite.Plugin} object.
    @@ -752,6 +943,11 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
                 return !plugins.isEmpty();
             }
     
    +        @Restricted(DoNotUse.class) // Jelly
    +        public boolean hasAnyDerivedDependencyErrors() {
    +            return plugins.values().stream().anyMatch(PluginWrapper::hasDerivedDependencyErrors);
    +        }
    +
             @Override
             public String getDisplayName() {
                 return Messages.PluginWrapper_PluginWrapperAdministrativeMonitor_DisplayName();
    @@ -780,6 +976,93 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
             }
         }
     
    +    /**
    +     * The result of the disablement of a plugin and its dependants plugins.
    +     */
    +    public static class PluginDisableResult {
    +        private String plugin;
    +        private PluginDisableStatus status;
    +        private String message;
    +        private Set<PluginDisableResult> dependantsDisableStatus = new HashSet<>();
    +
    +        public PluginDisableResult(String plugin) {
    +            this.plugin = plugin;
    +        }
    +
    +        public PluginDisableResult(String plugin, PluginDisableStatus status, String message) {
    +            this.plugin = plugin;
    +            this.status = status;
    +            this.message = message;
    +        }
    +
    +        public String getPlugin() {
    +            return plugin;
    +        }
    +
    +        public PluginDisableStatus getStatus() {
    +            return status;
    +        }
    +
    +        @Override
    +        public boolean equals(Object o) {
    +            if (this == o) return true;
    +            if (o == null || getClass() != o.getClass()) return false;
    +            PluginDisableResult that = (PluginDisableResult) o;
    +            return Objects.equals(plugin, that.plugin);
    +        }
    +
    +        @Override
    +        public int hashCode() {
    +            return Objects.hash(plugin);
    +        }
    +
    +        public void setStatus(PluginDisableStatus status) {
    +            this.status = status;
    +        }
    +
    +        public String getMessage() {
    +            return message;
    +        }
    +
    +        public void setMessage(String message) {
    +            this.message = message;
    +        }
    +
    +        public Set<PluginDisableResult> getDependantsDisableStatus() {
    +            return dependantsDisableStatus;
    +        }
    +
    +        public void addDependantDisableStatus(PluginDisableResult dependantDisableStatus) {
    +            dependantsDisableStatus.add(dependantDisableStatus);
    +        }
    +
    +    }
    +
    +    /**
    +     * An enum to hold the status of a disabling action against a plugin. To do it more reader-friendly.
    +     */
    +    public enum PluginDisableStatus {
    +        NO_SUCH_PLUGIN,
    +        DISABLED,
    +        ALREADY_DISABLED,
    +        NOT_DISABLED_DEPENDANTS,
    +        ERROR_DISABLING
    +    }
    +
    +    /**
    +     * The strategies defined for disabling a plugin.
    +     */
    +    public enum PluginDisableStrategy {
    +        NONE,
    +        MANDATORY,
    +        ALL;
    +
    +        @Override
    +        public String toString() {
    +            return this.name().toLowerCase();
    +        }
    +    }
    +
     //
     //
     // Action methods
    diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java
    index 3baea805d42d341ae5b37cc6e43469dde3e39070..3f2cfe6f0af3f1260ae21c3d9a4b6d027f0104ee 100644
    --- a/core/src/main/java/hudson/ProxyConfiguration.java
    +++ b/core/src/main/java/hudson/ProxyConfiguration.java
    @@ -110,6 +110,10 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
         
         private String testUrl;
     
    +    private transient Authenticator authenticator;
    +
    +    private transient boolean authCacheSeeded;
    +
         public ProxyConfiguration(String name, int port) {
             this(name,port,null,null);
         }
    @@ -129,7 +133,17 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
             this.userName = Util.fixEmptyAndTrim(userName);
             this.secretPassword = Secret.fromString(password);
             this.noProxyHost = Util.fixEmptyAndTrim(noProxyHost);
    -        this.testUrl =Util.fixEmptyAndTrim(testUrl);
    +        this.testUrl = Util.fixEmptyAndTrim(testUrl);
    +        authenticator = new Authenticator() {
    +            @Override
    +            public PasswordAuthentication getPasswordAuthentication() {
    +                String userName = getUserName();
    +                if (getRequestorType() == RequestorType.PROXY && userName != null) {
    +                    return new PasswordAuthentication(userName, getPassword().toCharArray());
    +                }
    +                return null;
    +            }
    +        };
         }
     
         public String getUserName() {
    @@ -204,7 +218,7 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
             SaveableListener.fireOnChange(this, config);
         }
     
    -    public Object readResolve() {
    +    private Object readResolve() {
             if (secretPassword == null)
                 // backward compatibility : get scrambled password and store it encrypted
                 secretPassword = Secret.fromString(Scrambler.descramble(password));
    @@ -234,17 +248,12 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
             if(p==null) {
                 con = url.openConnection();
             } else {
    -            con = url.openConnection(p.createProxy(url.getHost()));
    +            Proxy proxy = p.createProxy(url.getHost());
    +            con = url.openConnection(proxy);
                 if(p.getUserName()!=null) {
                     // Add an authenticator which provides the credentials for proxy authentication
    -                Authenticator.setDefault(new Authenticator() {
    -                    @Override
    -                    public PasswordAuthentication getPasswordAuthentication() {
    -                        if (getRequestorType()!=RequestorType.PROXY)    return null;
    -                        return new PasswordAuthentication(p.getUserName(),
    -                                p.getPassword().toCharArray());
    -                    }
    -                });
    +                Authenticator.setDefault(p.authenticator);
    +                p.jenkins48775workaround(proxy, url);
                 }
             }
             
    @@ -261,27 +270,48 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
     
         public static InputStream getInputStream(URL url) throws IOException {
             final ProxyConfiguration p = get();
    -        if (p == null) 
    +        if (p == null)
                 return new RetryableHttpStream(url);
     
    -        InputStream is = new RetryableHttpStream(url, p.createProxy(url.getHost()));
    +        Proxy proxy = p.createProxy(url.getHost());
    +        InputStream is = new RetryableHttpStream(url, proxy);
             if (p.getUserName() != null) {
                 // Add an authenticator which provides the credentials for proxy authentication
    -            Authenticator.setDefault(new Authenticator() {
    -
    -                @Override
    -                public PasswordAuthentication getPasswordAuthentication() {
    -                    if (getRequestorType() != RequestorType.PROXY) {
    -                        return null;
    -                    }
    -                    return new PasswordAuthentication(p.getUserName(), p.getPassword().toCharArray());
    -                }
    -            });
    +            Authenticator.setDefault(p.authenticator);
    +            p.jenkins48775workaround(proxy, url);
             }
     
             return is;
         }
     
    +    /**
    +     * If the first URL we try to access with a HTTP proxy is HTTPS then the authentication cache will not have been
    +     * pre-populated, so we try to access at least one HTTP URL before the very first HTTPS url.
    +     * @param proxy
    +     * @param url the actual URL being opened.
    +     */
    +    private void jenkins48775workaround(Proxy proxy, URL url) {
    +        if ("https".equals(url.getProtocol()) && !authCacheSeeded && proxy != Proxy.NO_PROXY) {
    +            HttpURLConnection preAuth = null;
    +            try {
    +                // We do not care if there is anything at this URL, all we care is that it is using the proxy
    +                preAuth = (HttpURLConnection) new URL("http", url.getHost(), -1, "/").openConnection(proxy);
    +                preAuth.setRequestMethod("HEAD");
    +                preAuth.connect();
    +            } catch (IOException e) {
    +                // ignore, this is just a probe we don't care at all
    +            } finally {
    +                if (preAuth != null) {
    +                    preAuth.disconnect();
    +                }
    +            }
    +            authCacheSeeded = true;
    +        } else if ("https".equals(url.getProtocol())){
    +            // if we access any http url using a proxy then the auth cache will have been seeded
    +            authCacheSeeded = authCacheSeeded || proxy != Proxy.NO_PROXY;
    +        }
    +    }
    +
         @CheckForNull
         private static ProxyConfiguration get() {
             if (JenkinsJVM.isJenkinsJVM()) {
    @@ -341,6 +371,8 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
                     @QueryParameter("userName") String userName, @QueryParameter("password") String password,
                     @QueryParameter("noProxyHost") String noProxyHost) {
     
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +
                 if (Util.fixEmptyAndTrim(testUrl) == null) {
                     return FormValidation.error(Messages.ProxyConfiguration_TestUrlRequired());
                 }
    diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java
    index 03455a77532745be3c8767af13c87e416c13d8b4..f0a9c037002dbbce2b2dabfaaf7ae07283b032e6 100644
    --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java
    +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java
    @@ -34,6 +34,7 @@ import javax.annotation.Nullable;
     import hudson.model.AperiodicWork;
     import jenkins.model.Jenkins;
     import jenkins.model.identity.InstanceIdentityProvider;
    +import jenkins.slaves.RemotingVersionInfo;
     import jenkins.util.SystemProperties;
     import hudson.slaves.OfflineCause;
     import java.io.DataOutputStream;
    @@ -300,6 +301,7 @@ public final class TcpSlaveAgentListener extends Thread {
                         o.write("Jenkins-Session: " + Jenkins.SESSION_HASH + "\r\n");
                         o.write("Client: " + s.getInetAddress().getHostAddress() + "\r\n");
                         o.write("Server: " + s.getLocalAddress().getHostAddress() + "\r\n");
    +                    o.write("Remoting-Minimum-Version: " + RemotingVersionInfo.getMinimumSupportedVersion() + "\r\n");
                         o.flush();
                         s.shutdownOutput();
                     } else {
    diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java
    index f2f242a4a16ff534114beb040f0dabb11caf9e85..8007200a6293ead38d91608592412fe13cae2d9c 100644
    --- a/core/src/main/java/hudson/Util.java
    +++ b/core/src/main/java/hudson/Util.java
    @@ -23,15 +23,17 @@
      */
     package hudson;
     
    -import java.nio.file.InvalidPathException;
    -import jenkins.util.SystemProperties;
    -
     import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    +
     import hudson.model.TaskListener;
    +import jenkins.util.MemoryReductionUtil;
     import hudson.util.QuotedStringTokenizer;
     import hudson.util.VariableResolver;
    +import jenkins.util.SystemProperties;
     
    +import org.apache.commons.codec.digest.DigestUtils;
     import org.apache.commons.io.IOUtils;
    +import org.apache.commons.io.output.NullOutputStream;
     import org.apache.commons.lang.time.FastDateFormat;
     import org.apache.tools.ant.BuildException;
     import org.apache.tools.ant.Project;
    @@ -41,9 +43,6 @@ import org.apache.tools.ant.types.FileSet;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
     
    -import javax.crypto.SecretKey;
    -import javax.crypto.spec.SecretKeySpec;
    -
     import java.io.*;
     import java.lang.reflect.Method;
     import java.lang.reflect.Modifier;
    @@ -56,9 +55,12 @@ import java.nio.CharBuffer;
     import java.nio.charset.CharacterCodingException;
     import java.nio.charset.Charset;
     import java.nio.charset.CharsetEncoder;
    +import java.nio.charset.StandardCharsets;
     import java.nio.file.FileAlreadyExistsException;
     import java.nio.file.FileSystemException;
    +import java.nio.file.FileSystems;
     import java.nio.file.Files;
    +import java.nio.file.InvalidPathException;
     import java.nio.file.LinkOption;
     import java.nio.file.NoSuchFileException;
     import java.nio.file.Path;
    @@ -67,25 +69,31 @@ import java.nio.file.attribute.PosixFilePermission;
     import java.nio.file.attribute.BasicFileAttributes;
     import java.nio.file.attribute.PosixFileAttributes;
     import java.nio.file.attribute.DosFileAttributes;
    +import java.nio.file.attribute.PosixFilePermissions;
    +import java.security.DigestInputStream;
     import java.security.MessageDigest;
     import java.security.NoSuchAlgorithmException;
     import java.text.NumberFormat;
     import java.text.ParseException;
    +import java.time.LocalDate;
    +import java.time.ZoneId;
    +import java.time.temporal.ChronoUnit;
     import java.util.*;
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
     import java.util.logging.Level;
    +import java.util.logging.LogRecord;
     import java.util.logging.Logger;
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     
    -import java.security.DigestInputStream;
    -
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     import javax.annotation.Nullable;
    +import javax.crypto.SecretKey;
    +import javax.crypto.spec.SecretKeySpec;
     
    -import org.apache.commons.codec.digest.DigestUtils;
    +import org.apache.commons.io.FileUtils;
     
     /**
      * Various utility methods that don't have more proper home.
    @@ -130,7 +138,7 @@ public class Util {
         private static final Pattern VARIABLE = Pattern.compile("\\$([A-Za-z0-9_]+|\\{[A-Za-z0-9_.]+\\}|\\$)");
     
         /**
    -     * Replaces the occurrence of '$key' by <tt>properties.get('key')</tt>.
    +     * Replaces the occurrence of '$key' by {@code properties.get('key')}.
          *
          * <p>
          * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
    @@ -142,7 +150,7 @@ public class Util {
         }
     
         /**
    -     * Replaces the occurrence of '$key' by <tt>resolver.get('key')</tt>.
    +     * Replaces the occurrence of '$key' by {@code resolver.get('key')}.
          *
          * <p>
          * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
    @@ -179,28 +187,54 @@ public class Util {
         }
     
         /**
    -     * Loads the contents of a file into a string.
    +     * Reads the entire contents of the text file at <code>logfile</code> into a
    +     * string using the {@link Charset#defaultCharset() default charset} for
    +     * decoding. If no such file exists, an empty string is returned.
    +     * @param logfile The text file to read in its entirety.
    +     * @return The entire text content of <code>logfile</code>.
    +     * @throws IOException If an error occurs while reading the file.
    +     * @deprecated call {@link #loadFile(java.io.File, java.nio.charset.Charset)}
    +     * instead to specify the charset to use for decoding (preferably
    +     * {@link java.nio.charset.StandardCharsets#UTF_8}).
          */
         @Nonnull
    +    @Deprecated
         public static String loadFile(@Nonnull File logfile) throws IOException {
             return loadFile(logfile, Charset.defaultCharset());
         }
     
    +    /**
    +     * Reads the entire contents of the text file at <code>logfile</code> into a
    +     * string using <code>charset</code> for decoding. If no such file exists,
    +     * an empty string is returned.
    +     * @param logfile The text file to read in its entirety.
    +     * @param charset The charset to use for decoding the bytes in <code>logfile</code>.
    +     * @return The entire text content of <code>logfile</code>.
    +     * @throws IOException If an error occurs while reading the file.
    +     */
         @Nonnull
         public static String loadFile(@Nonnull File logfile, @Nonnull Charset charset) throws IOException {
    -        if(!logfile.exists())
    +        // Note: Until charset handling is resolved (e.g. by implementing
    +        // https://issues.jenkins-ci.org/browse/JENKINS-48923 ), this method
    +        // must be able to handle character encoding errors. As reported at
    +        // https://issues.jenkins-ci.org/browse/JENKINS-49112 Run.getLog() calls
    +        // loadFile() to fully read the generated log file. This file might
    +        // contain unmappable and/or malformed byte sequences. We need to make
    +        // sure that in such cases, no CharacterCodingException is thrown.
    +        //
    +        // One approach that cannot be used is to call Files.newBufferedReader()
    +        // because there is a difference in how an InputStreamReader constructed
    +        // from a Charset and the reader returned by Files.newBufferedReader()
    +        // handle malformed and unmappable byte sequences for the specified
    +        // encoding; the latter is more picky and will throw an exception.
    +        // See: https://issues.jenkins-ci.org/browse/JENKINS-49060?focusedCommentId=325989&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325989
    +        try {
    +            return FileUtils.readFileToString(logfile, charset);
    +        } catch (FileNotFoundException e) {
                 return "";
    -
    -        StringBuilder str = new StringBuilder((int)logfile.length());
    -
    -        try (BufferedReader r = Files.newBufferedReader(fileToPath(logfile), charset)) {
    -            char[] buf = new char[1024];
    -            int len;
    -            while ((len = r.read(buf, 0, buf.length)) > 0)
    -                str.append(buf, 0, len);
    +        } catch (Exception e) {
    +            throw new IOException("Failed to fully read " + logfile, e);
             }
    -
    -        return str.toString();
         }
     
         /**
    @@ -298,7 +332,7 @@ public class Util {
             if (!Functions.isWindows()) {
                 try {
                     PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class);
    -                Set<PosixFilePermission> newPermissions = ((PosixFileAttributes)attrs).permissions();
    +                Set<PosixFilePermission> newPermissions = attrs.permissions();
                     newPermissions.add(PosixFilePermission.OWNER_WRITE);
                     Files.setPosixFilePermissions(path, newPermissions);
                     return;
    @@ -491,7 +525,7 @@ public class Util {
              *  calling readAttributes.
              */
             try {
    -            Path path = file.toPath();
    +            Path path = fileToPath(file);
                 BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
                 if (attrs.isSymbolicLink()) {
                     return true;
    @@ -504,8 +538,6 @@ public class Util {
                 } else {
                     return false;
                 }
    -        } catch (InvalidPathException e) {
    -            throw new IOException(e);
             } catch (NoSuchFileException e) {
                 return false;
             }
    @@ -545,25 +577,35 @@ public class Util {
          * @see InvalidPathException
          */
         public static boolean isDescendant(File forParent, File potentialChild) throws IOException {
    -        try {
    -            Path child = potentialChild.getAbsoluteFile().toPath().normalize();
    -            Path parent = forParent.getAbsoluteFile().toPath().normalize();
    -            return child.startsWith(parent);
    -        } catch (InvalidPathException e) {
    -            throw new IOException(e);
    -        }
    +        Path child = fileToPath(potentialChild.getAbsoluteFile()).normalize();
    +        Path parent = fileToPath(forParent.getAbsoluteFile()).normalize();
    +        return child.startsWith(parent);
         }
     
         /**
          * Creates a new temporary directory.
          */
         public static File createTempDir() throws IOException {
    -        File tmp = File.createTempFile("jenkins", "tmp");
    -        if(!tmp.delete())
    -            throw new IOException("Failed to delete "+tmp);
    -        if(!tmp.mkdirs())
    -            throw new IOException("Failed to create a new directory "+tmp);
    -        return tmp;
    +        // The previously used approach of creating a temporary file, deleting
    +        // it, and making a new directory having the same name in its place is
    +        // potentially  problematic:
    +        // https://stackoverflow.com/questions/617414/how-to-create-a-temporary-directory-folder-in-java
    +        // We can use the Java 7 Files.createTempDirectory() API, but note that
    +        // by default, the permissions of the created directory are 0700&(~umask)
    +        // whereas the old approach created a temporary directory with permissions
    +        // 0777&(~umask).
    +        // To avoid permissions problems like https://issues.jenkins-ci.org/browse/JENKINS-48407
    +        // we can pass POSIX file permissions as an attribute (see, for example,
    +        // https://github.com/jenkinsci/jenkins/pull/3161 )
    +        final Path tempPath;
    +        final String tempDirNamePrefix = "jenkins";
    +        if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
    +            tempPath = Files.createTempDirectory(tempDirNamePrefix,
    +                    PosixFilePermissions.asFileAttribute(EnumSet.allOf(PosixFilePermission.class)));
    +        } else {
    +            tempPath = Files.createTempDirectory(tempDirNamePrefix);
    +        }
    +        return tempPath.toFile();
         }
     
         private static final Pattern errorCodeParser = Pattern.compile(".*CreateProcess.*error=([0-9]+).*");
    @@ -598,7 +640,7 @@ public class Util {
                     try {
                         ResourceBundle rb = ResourceBundle.getBundle("/hudson/win32errors");
                         return rb.getString("error"+m.group(1));
    -                } catch (Exception _) {
    +                } catch (Exception ignored) {
                         // silently recover from resource related failures
                     }
                 }
    @@ -643,10 +685,7 @@ public class Util {
          */
         @Deprecated
         public static void copyStream(@Nonnull InputStream in,@Nonnull OutputStream out) throws IOException {
    -        byte[] buf = new byte[8192];
    -        int len;
    -        while((len=in.read(buf))>=0)
    -            out.write(buf,0,len);
    +        IOUtils.copy(in, out);
         }
     
         /**
    @@ -654,10 +693,7 @@ public class Util {
          */
         @Deprecated
         public static void copyStream(@Nonnull Reader in, @Nonnull Writer out) throws IOException {
    -        char[] buf = new char[8192];
    -        int len;
    -        while((len=in.read(buf))>0)
    -            out.write(buf,0,len);
    +        IOUtils.copy(in, out);
         }
     
         /**
    @@ -666,7 +702,7 @@ public class Util {
         @Deprecated
         public static void copyStreamAndClose(@Nonnull InputStream in, @Nonnull OutputStream out) throws IOException {
             try (InputStream _in = in; OutputStream _out = out) { // make sure both are closed, and use Throwable.addSuppressed
    -            copyStream(in,out);
    +            IOUtils.copy(_in, _out);
             }
         }
     
    @@ -676,7 +712,7 @@ public class Util {
         @Deprecated
         public static void copyStreamAndClose(@Nonnull Reader in, @Nonnull Writer out) throws IOException {
             try (Reader _in = in; Writer _out = out) {
    -            copyStream(in,out);
    +            IOUtils.copy(_in, _out);
             }
         }
     
    @@ -766,15 +802,15 @@ public class Util {
         public static String getDigestOf(@Nonnull InputStream source) throws IOException {
             try {
                 MessageDigest md5 = MessageDigest.getInstance("MD5");
    -
    -            byte[] buffer = new byte[1024];
    -            try (DigestInputStream in = new DigestInputStream(source, md5)) {
    -                while (in.read(buffer) >= 0)
    -                    ; // simply discard the input
    -            }
    +            DigestInputStream in = new DigestInputStream(source, md5);
    +            // Note: IOUtils.copy() buffers the input internally, so there is no
    +            // need to use a BufferedInputStream.
    +            IOUtils.copy(in, NullOutputStream.NULL_OUTPUT_STREAM);
                 return toHexString(md5.digest());
             } catch (NoSuchAlgorithmException e) {
                 throw new IOException("MD5 not installed",e);    // impossible
    +        } finally {
    +            source.close();
             }
             /* JENKINS-18178: confuses Maven 2 runner
             try {
    @@ -788,7 +824,7 @@ public class Util {
         @Nonnull
         public static String getDigestOf(@Nonnull String text) {
             try {
    -            return getDigestOf(new ByteArrayInputStream(text.getBytes("UTF-8")));
    +            return getDigestOf(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)));
             } catch (IOException e) {
                 throw new Error(e);
             }
    @@ -803,11 +839,8 @@ public class Util {
          */
         @Nonnull
         public static String getDigestOf(@Nonnull File file) throws IOException {
    -        try (InputStream is = Files.newInputStream(file.toPath())) {
    -            return getDigestOf(new BufferedInputStream(is));
    -        } catch (InvalidPathException e) {
    -            throw new IOException(e);
    -        }
    +        // Note: getDigestOf() closes the input stream.
    +        return getDigestOf(Files.newInputStream(fileToPath(file)));
         }
     
         /**
    @@ -820,14 +853,12 @@ public class Util {
                 // turn secretKey into 256 bit hash
                 MessageDigest digest = MessageDigest.getInstance("SHA-256");
                 digest.reset();
    -            digest.update(s.getBytes("UTF-8"));
    +            digest.update(s.getBytes(StandardCharsets.UTF_8));
     
                 // Due to the stupid US export restriction JDK only ships 128bit version.
                 return new SecretKeySpec(digest.digest(),0,128/8, "AES");
             } catch (NoSuchAlgorithmException e) {
                 throw new Error(e);
    -        } catch (UnsupportedEncodingException e) {
    -            throw new Error(e);
             }
         }
     
    @@ -849,6 +880,8 @@ public class Util {
     
         @Nonnull
         public static byte[] fromHexString(@Nonnull String data) {
    +        if (data.length() % 2 != 0)
    +            throw new IllegalArgumentException("data must have an even number of hexadecimal digits");
             byte[] r = new byte[data.length() / 2];
             for (int i = 0; i < data.length(); i += 2)
                 r[i / 2] = (byte) Integer.parseInt(data.substring(i, i + 2), 16);
    @@ -979,7 +1012,7 @@ public class Util {
                 StringBuilder out = new StringBuilder(s.length());
     
                 ByteArrayOutputStream buf = new ByteArrayOutputStream();
    -            OutputStreamWriter w = new OutputStreamWriter(buf,"UTF-8");
    +            OutputStreamWriter w = new OutputStreamWriter(buf, StandardCharsets.UTF_8);
     
                 for (int i = 0; i < s.length(); i++) {
                     int c = s.charAt(i);
    @@ -1042,7 +1075,7 @@ public class Util {
                     if (!escaped) {
                         out = new StringBuilder(i + (m - i) * 3);
                         out.append(s.substring(0, i));
    -                    enc = Charset.forName("UTF-8").newEncoder();
    +                    enc = StandardCharsets.UTF_8.newEncoder();
                         buf = CharBuffer.allocate(1);
                         escaped = true;
                     }
    @@ -1079,8 +1112,8 @@ public class Util {
         /**
          * Escapes HTML unsafe characters like &lt;, &amp; to the respective character entities.
          */
    -    @Nonnull
    -    public static String escape(@Nonnull String text) {
    +    @Nullable
    +    public static String escape(@CheckForNull String text) {
             if (text==null)     return null;
             StringBuilder buf = new StringBuilder(text.length()+64);
             for( int i=0; i<text.length(); i++ ) {
    @@ -1136,14 +1169,13 @@ public class Util {
         }
     
         /**
    -     * Creates an empty file.
    +     * Creates an empty file if nonexistent or truncates the existing file.
    +     * Note: The behavior of this method in the case where the file already
    +     * exists is unlike the POSIX <code>touch</code> utility which merely
    +     * updates the file's access and/or modification time.
          */
         public static void touch(@Nonnull File file) throws IOException {
    -        try {
    -            Files.newOutputStream(file.toPath()).close();
    -        } catch (InvalidPathException e) {
    -            throw new IOException(e);
    -        }
    +        Files.newOutputStream(fileToPath(file)).close();
         }
     
         /**
    @@ -1163,8 +1195,17 @@ public class Util {
          */
         @Nonnull
         public static String fixNull(@CheckForNull String s) {
    -        if(s==null)     return "";
    -        else            return s;
    +        return fixNull(s, "");
    +    }
    +
    +    /**
    +     * Convert {@code null} to a default value.
    +     * @param defaultValue Default value. It may be immutable or not, depending on the implementation.
    +     * @since TODO
    +     */
    +    @Nonnull
    +    public static <T> T fixNull(@CheckForNull T s, @Nonnull T defaultValue) {
    +        return s != null ? s : defaultValue;
         }
     
         /**
    @@ -1187,24 +1228,60 @@ public class Util {
             return fixEmpty(s.trim());
         }
     
    +    /**
    +     *
    +     * @param l list to check.
    +     * @param <T>
    +     *     Type of the list.
    +     * @return
    +     *     {@code l} if l is not {@code null}.
    +     *     An empty <b>immutable list</b> if l is {@code null}.
    +     */
         @Nonnull
         public static <T> List<T> fixNull(@CheckForNull List<T> l) {
    -        return l!=null ? l : Collections.<T>emptyList();
    +        return fixNull(l, Collections.<T>emptyList());
         }
     
    +    /**
    +     *
    +     * @param l set to check.
    +     * @param <T>
    +     *     Type of the set.
    +     * @return
    +     *     {@code l} if l is not {@code null}.
    +     *     An empty <b>immutable set</b> if l is {@code null}.
    +     */
         @Nonnull
         public static <T> Set<T> fixNull(@CheckForNull Set<T> l) {
    -        return l!=null ? l : Collections.<T>emptySet();
    +        return fixNull(l, Collections.<T>emptySet());
         }
     
    +    /**
    +     *
    +     * @param l collection to check.
    +     * @param <T>
    +     *     Type of the collection.
    +     * @return
    +     *     {@code l} if l is not {@code null}.
    +     *     An empty <b>immutable set</b> if l is {@code null}.
    +     */
         @Nonnull
         public static <T> Collection<T> fixNull(@CheckForNull Collection<T> l) {
    -        return l!=null ? l : Collections.<T>emptySet();
    +        return fixNull(l, Collections.<T>emptySet());
         }
     
    +    /**
    +     *
    +     * @param l iterable to check.
    +     * @param <T>
    +     *     Type of the iterable.
    +     * @return
    +     *     {@code l} if l is not {@code null}.
    +     *     An empty <b>immutable set</b> if l is {@code null}.
    +     */
         @Nonnull
         public static <T> Iterable<T> fixNull(@CheckForNull Iterable<T> l) {
    -        return l!=null ? l : Collections.<T>emptySet();
    +        return fixNull(l, Collections.<T>emptySet());
         }
     
         /**
    @@ -1310,8 +1387,8 @@ public class Util {
         public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath,
                 @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException {
             try {
    -            Path path = new File(baseDir, symlinkPath).toPath();
    -            Path target = Paths.get(targetPath, new String[0]);
    +            Path path = fileToPath(new File(baseDir, symlinkPath));
    +            Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY);
     
                 final int maxNumberOfTries = 4;
                 final int timeInMillis = 100;
    @@ -1325,7 +1402,7 @@ public class Util {
                             TimeUnit.MILLISECONDS.sleep(timeInMillis); //trying to defeat likely ongoing race condition
                             continue;
                         }
    -                    LOGGER.warning("symlink FileAlreadyExistsException thrown " + maxNumberOfTries + " times => cannot createSymbolicLink");
    +                    LOGGER.log(Level.WARNING, "symlink FileAlreadyExistsException thrown {0} times => cannot createSymbolicLink", maxNumberOfTries);
                         throw fileAlreadyExistsException;
                     }
                 }
    @@ -1333,7 +1410,7 @@ public class Util {
                 PrintStream log = listener.getLogger();
                 log.print("Symbolic links are not supported on this platform");
                 Functions.printStackTrace(e, log);
    -        } catch (InvalidPathException | IOException e) {
    +        } catch (IOException e) {
                 if (Functions.isWindows() && e instanceof FileSystemException) {
                     warnWindowsSymlink();
                     return;
    @@ -1388,9 +1465,9 @@ public class Util {
          *      The relative path is meant to be resolved from the location of the symlink.
          */
         @CheckForNull
    -    public static String resolveSymlink(@Nonnull File link) throws InterruptedException, IOException {
    +    public static String resolveSymlink(@Nonnull File link) throws IOException {
             try {
    -            Path path =  link.toPath();
    +            Path path = fileToPath(link);
                 return Files.readSymbolicLink(path).toString();
             } catch (UnsupportedOperationException | FileSystemException x) {
                 // no symlinks on this platform (windows?),
    @@ -1400,7 +1477,7 @@ public class Util {
             } catch (IOException x) {
                 throw x;
             } catch (Exception x) {
    -            throw (IOException) new IOException(x.toString()).initCause(x);
    +            throw new IOException(x);
             }
         }
     
    @@ -1421,7 +1498,7 @@ public class Util {
             try {
                 return new URI(null,url,null).toASCIIString();
             } catch (URISyntaxException e) {
    -            LOGGER.warning("Failed to encode "+url);    // could this ever happen?
    +            LOGGER.log(Level.WARNING, "Failed to encode {0}", url);    // could this ever happen?
                 return url;
             }
         }
    @@ -1579,7 +1656,10 @@ public class Util {
             try {
                 toClose.close();
             } catch(IOException ex) {
    -            logger.log(Level.WARNING, String.format("Failed to close %s of %s", closeableName, closeableOwner), ex);
    +            LogRecord record = new LogRecord(Level.WARNING, "Failed to close {0} of {1}");
    +            record.setParameters(new Object[] { closeableName, closeableOwner });
    +            record.setThrown(ex);
    +            logger.log(record);
             }
         }
     
    @@ -1627,7 +1707,28 @@ public class Util {
                 throw new IOException(e);
             }
         }
    -
    +    
    +    /**
    +     * Compute the number of calendar days elapsed since the given date.
    +     * As it's only the calendar days difference that matter, "11.00pm" to "2.00am the day after" returns 1,
    +     * even if there are only 3 hours between. As well as "10am" to "2pm" both on the same day, returns 0.
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static long daysBetween(@Nonnull Date a, @Nonnull Date b){
    +        LocalDate aLocal = a.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    +        LocalDate bLocal = b.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    +        return ChronoUnit.DAYS.between(aLocal, bLocal);
    +    }
    +    
    +    /**
    +     * @return positive number of days between the given date and now
    +     * @see #daysBetween(Date, Date)
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static long daysElapsedSince(@Nonnull Date date){
    +        return Math.max(0, daysBetween(date, new Date()));
    +    }
    +    
         public static final FastDateFormat XS_DATETIME_FORMATTER = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'",new SimpleTimeZone(0,"GMT"));
     
         // Note: RFC822 dates must not be localized!
    diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java
    index 243318e2296a44e91547cf16c2332f3c840c67dd..7f630dfb33dc23bf1021ae16f0a0cfd081a82f7a 100644
    --- a/core/src/main/java/hudson/XmlFile.java
    +++ b/core/src/main/java/hudson/XmlFile.java
    @@ -26,7 +26,7 @@ package hudson;
     import com.thoughtworks.xstream.XStream;
     import com.thoughtworks.xstream.converters.Converter;
     import com.thoughtworks.xstream.converters.UnmarshallingContext;
    -import com.thoughtworks.xstream.io.xml.Xpp3Driver;
    +import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
     import hudson.diagnosis.OldDataMonitor;
     import hudson.model.Descriptor;
     import hudson.util.AtomicFileWriter;
    @@ -39,7 +39,6 @@ import org.xml.sax.Locator;
     import org.xml.sax.SAXException;
     import org.xml.sax.ext.Locator2;
     import org.xml.sax.helpers.DefaultHandler;
    -
     import javax.xml.parsers.ParserConfigurationException;
     import javax.xml.parsers.SAXParserFactory;
     import java.io.BufferedInputStream;
    @@ -73,12 +72,12 @@ import org.apache.commons.io.IOUtils;
      * not have any data, the newly added field is left to the VM-default
      * value (if you let XStream create the object, such as
      * {@link #read()} &mdash; which is the majority), or to the value initialized by the
    - * constructor (if the object is created via <tt>new</tt> and then its
    + * constructor (if the object is created via {@code new} and then its
      * value filled by XStream, such as {@link #unmarshal(Object)}.)
      *
      * <p>
      * Removing a field requires that you actually leave the field with
    - * <tt>transient</tt> keyword. When you read the old XML, XStream
    + * {@code transient} keyword. When you read the old XML, XStream
      * will set the value to this field. But when the data is saved,
      * the field will no longer will be written back to XML.
      * (It might be possible to tweak XStream so that we can simply
    @@ -86,13 +85,13 @@ import org.apache.commons.io.IOUtils;
      *
      * <p>
      * Changing the data structure is usually a combination of the two
    - * above. You'd leave the old data store with <tt>transient</tt>,
    + * above. You'd leave the old data store with {@code transient},
      * and then add the new data. When you are reading the old XML,
      * only the old field will be set. When you are reading the new XML,
      * only the new field will be set. You'll then need to alter the code
      * so that it will be able to correctly handle both situations,
      * and that as soon as you see data in the old field, you'll have to convert
    - * that into the new data structure, so that the next <tt>save</tt> operation
    + * that into the new data structure, so that the next {@code save} operation
      * will write the new data (otherwise you'll end up losing the data, because
      * old fields will be never written back.)
      *
    @@ -155,7 +154,7 @@ public final class XmlFile {
          * Loads the contents of this file into an existing object.
          *
          * @return
    -     *      The unmarshalled object. Usually the same as <tt>o</tt>, but would be different
    +     *      The unmarshalled object. Usually the same as {@code o}, but would be different
          *      if the XML representation is completely new.
          */
         public Object unmarshal( Object o ) throws IOException {
    @@ -187,7 +186,7 @@ public final class XmlFile {
             mkdirs();
             AtomicFileWriter w = new AtomicFileWriter(file);
             try {
    -            w.write("<?xml version='1.0' encoding='UTF-8'?>\n");
    +            w.write("<?xml version='1.1' encoding='UTF-8'?>\n");
                 beingWritten.put(o, null);
                 writing.set(file);
                 try {
    @@ -352,13 +351,14 @@ public final class XmlFile {
         /**
          * {@link XStream} instance is supposed to be thread-safe.
          */
    -    private static final XStream DEFAULT_XSTREAM = new XStream2();
     
         private static final Logger LOGGER = Logger.getLogger(XmlFile.class.getName());
     
         private static final SAXParserFactory JAXP = SAXParserFactory.newInstance();
     
    -    private static final Xpp3Driver DEFAULT_DRIVER = new Xpp3Driver();
    +    private static final HierarchicalStreamDriver DEFAULT_DRIVER = XStream2.getDefaultDriver();
    +
    +    private static final XStream DEFAULT_XSTREAM = new XStream2(DEFAULT_DRIVER);
     
         static {
             JAXP.setNamespaceAware(true);
    diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
    index 5efbaf3a33edc915cb5d7f89f0ffe6408339c2f8..91d88118d1dcf99ca57a3bdc407c010e509b3d74 100644
    --- a/core/src/main/java/hudson/cli/CLICommand.java
    +++ b/core/src/main/java/hudson/cli/CLICommand.java
    @@ -84,7 +84,7 @@ import javax.annotation.Nonnull;
      * <h2>How does a CLI command work</h2>
      * <p>
      * The users starts {@linkplain CLI the "CLI agent"} on a remote system, by specifying arguments, like
    - * <tt>"java -jar jenkins-cli.jar command arg1 arg2 arg3"</tt>. The CLI agent creates
    + * {@code "java -jar jenkins-cli.jar command arg1 arg2 arg3"}. The CLI agent creates
      * a remoting channel with the server, and it sends the entire arguments to the server, along with
      * the remoted stdin/out/err.
      *
    @@ -183,7 +183,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
          * Gets the command name.
          *
          * <p>
    -     * For example, if the CLI is invoked as <tt>java -jar cli.jar foo arg1 arg2 arg4</tt>,
    +     * For example, if the CLI is invoked as {@code java -jar cli.jar foo arg1 arg2 arg4},
          * on the server side {@link CLICommand} that returns "foo" from {@link #getName()}
          * will be invoked.
          *
    @@ -260,6 +260,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
             // add options from the authenticator
             SecurityContext sc = null;
             Authentication old = null;
    +        Authentication auth = null;
             try {
                 sc = SecurityContextHolder.getContext();
                 old = sc.getAuthentication();
    @@ -268,33 +269,50 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
                 sc.setAuthentication(getTransportAuthentication());
                 new ClassParser().parse(authenticator,p);
     
    +            if (!(this instanceof LoginCommand || this instanceof LogoutCommand || this instanceof HelpCommand || this instanceof WhoAmICommand))
    +                Jenkins.getActiveInstance().checkPermission(Jenkins.READ);
                 p.parseArgument(args.toArray(new String[args.size()]));
    -            Authentication auth = authenticator.authenticate();
    +            auth = authenticator.authenticate();
                 if (auth==Jenkins.ANONYMOUS)
                     auth = loadStoredAuthentication();
                 sc.setAuthentication(auth); // run the CLI with the right credential
    -            if (!(this instanceof LoginCommand || this instanceof HelpCommand))
    +            if (!(this instanceof LoginCommand || this instanceof LogoutCommand || this instanceof HelpCommand || this instanceof WhoAmICommand))
                     Jenkins.getActiveInstance().checkPermission(Jenkins.READ);
    -            return run();
    +            LOGGER.log(Level.FINE, "Invoking CLI command {0}, with {1} arguments, as user {2}.",
    +                    new Object[] {getName(), args.size(), auth.getName()});
    +            int res = run();
    +            LOGGER.log(Level.FINE, "Executed CLI command {0}, with {1} arguments, as user {2}, return code {3}",
    +                    new Object[] {getName(), args.size(), auth.getName(), res});
    +            return res;
             } catch (CmdLineException e) {
    +            LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
    +                    getName(), args.size(), auth != null ? auth.getName() : "<unknown>"), e);
                 stderr.println("");
                 stderr.println("ERROR: " + e.getMessage());
                 printUsage(stderr, p);
                 return 2;
             } catch (IllegalStateException e) {
    +            LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
    +                    getName(), args.size(), auth != null ? auth.getName() : "<unknown>"), e);
                 stderr.println("");
                 stderr.println("ERROR: " + e.getMessage());
                 return 4;
             } catch (IllegalArgumentException e) {
    +            LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
    +                    getName(), args.size(), auth != null ? auth.getName() : "<unknown>"), e);
                 stderr.println("");
                 stderr.println("ERROR: " + e.getMessage());
                 return 3;
             } catch (AbortException e) {
    +            LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
    +                    getName(), args.size(), auth != null ? auth.getName() : "<unknown>"), e);
                 // signals an error without stack trace
                 stderr.println("");
                 stderr.println("ERROR: " + e.getMessage());
                 return 5;
             } catch (AccessDeniedException e) {
    +            LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
    +                    getName(), args.size(), auth != null ? auth.getName() : "<unknown>"), e);
                 stderr.println("");
                 stderr.println("ERROR: " + e.getMessage());
                 return 6;
    diff --git a/core/src/main/java/hudson/cli/CliProtocol.java b/core/src/main/java/hudson/cli/CliProtocol.java
    index 58b5c95a6438fb31266bf61f4c765241379957c9..30e18b9cb0efd5b92d24b294e1f843c6009994f8 100644
    --- a/core/src/main/java/hudson/cli/CliProtocol.java
    +++ b/core/src/main/java/hudson/cli/CliProtocol.java
    @@ -39,7 +39,7 @@ public class CliProtocol extends AgentProtocol {
          */
         @Override
         public boolean isOptIn() {
    -        return OPT_IN;
    +        return true;
         }
     
         @Override
    @@ -109,14 +109,4 @@ public class CliProtocol extends AgentProtocol {
                 channel.join();
             }
         }
    -
    -    /**
    -     * A/B test turning off this protocol by default.
    -     */
    -    private static final boolean OPT_IN;
    -
    -    static {
    -        byte hash = Util.fromHexString(Jenkins.getInstance().getLegacyInstanceId())[0];
    -        OPT_IN = (hash % 10) == 0;
    -    }
     }
    diff --git a/core/src/main/java/hudson/cli/CliProtocol2.java b/core/src/main/java/hudson/cli/CliProtocol2.java
    index 9fca4055fb10e67295186fc80c43b7fc89a27c2b..6e181714bb93b88640b06d728ce6cefa1ac2ab38 100644
    --- a/core/src/main/java/hudson/cli/CliProtocol2.java
    +++ b/core/src/main/java/hudson/cli/CliProtocol2.java
    @@ -35,7 +35,7 @@ public class CliProtocol2 extends CliProtocol {
          */
         @Override
         public boolean isOptIn() {
    -        return false;
    +        return true;
         }
     
         @Override
    diff --git a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
    index 48b96f46864f5ccf191eabe66658dec7d7024114..2cd45a77766f373035938f234f3ec47215114b84 100644
    --- a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
    +++ b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
    @@ -53,16 +53,7 @@ public class ClientAuthenticationCache implements Serializable {
         final Properties props = new Properties();
     
         public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException {
    -        store = (channel==null ? FilePath.localChannel :  channel).call(new MasterToSlaveCallable<FilePath, IOException>() {
    -            public FilePath call() throws IOException {
    -                File home = new File(System.getProperty("user.home"));
    -                File hudsonHome = new File(home, ".hudson");
    -                if (hudsonHome.exists()) {
    -                    return new FilePath(new File(hudsonHome, "cli-credentials"));
    -                }
    -                return new FilePath(new File(home, ".jenkins/cli-credentials"));
    -            }
    -        });
    +        store = (channel==null ? FilePath.localChannel :  channel).call(new CredentialsFilePathMasterToSlaveCallable());
             if (store.exists()) {
                 try (InputStream istream = store.read()) {
                     props.load(istream);
    @@ -151,4 +142,15 @@ public class ClientAuthenticationCache implements Serializable {
             // try to protect this file from other users, if we can.
             store.chmod(0600);
         }
    +
    +    private static class CredentialsFilePathMasterToSlaveCallable extends MasterToSlaveCallable<FilePath, IOException> {
    +        public FilePath call() throws IOException {
    +            File home = new File(System.getProperty("user.home"));
    +            File hudsonHome = new File(home, ".hudson");
    +            if (hudsonHome.exists()) {
    +                return new FilePath(new File(hudsonHome, "cli-credentials"));
    +            }
    +            return new FilePath(new File(home, ".jenkins/cli-credentials"));
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/cli/ConsoleCommand.java b/core/src/main/java/hudson/cli/ConsoleCommand.java
    index f98fdc95a29594a98fb4da2f0d81bb3aeac36215..8965af0fe6957553ebae2bc57c7fd2aa1d8d4a66 100644
    --- a/core/src/main/java/hudson/cli/ConsoleCommand.java
    +++ b/core/src/main/java/hudson/cli/ConsoleCommand.java
    @@ -41,7 +41,7 @@ public class ConsoleCommand extends CLICommand {
         public int n = -1;
     
         protected int run() throws Exception {
    -        job.checkPermission(Item.BUILD);
    +        job.checkPermission(Item.READ);
     
             Run<?,?> run;
     
    diff --git a/core/src/main/java/hudson/cli/DeleteBuildsCommand.java b/core/src/main/java/hudson/cli/DeleteBuildsCommand.java
    index db86c433ab538a8317058766afdc4f4d39dd93b5..bd1768786cd4c5bfd6e9df37547eda439b6a8632 100644
    --- a/core/src/main/java/hudson/cli/DeleteBuildsCommand.java
    +++ b/core/src/main/java/hudson/cli/DeleteBuildsCommand.java
    @@ -31,14 +31,14 @@ import java.io.PrintStream;
     import java.util.HashSet;
     import java.util.List;
     import org.kohsuke.accmod.Restricted;
    -import org.kohsuke.accmod.restrictions.DoNotUse;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     /**
      * Deletes builds records in a bulk.
      *
      * @author Kohsuke Kawaguchi
      */
    -@Restricted(DoNotUse.class) // command implementation only
    +@Restricted(NoExternalUse.class) // command implementation only
     @Extension
     public class DeleteBuildsCommand extends RunRangeCommand {
         @Override
    diff --git a/core/src/main/java/hudson/cli/DisablePluginCommand.java b/core/src/main/java/hudson/cli/DisablePluginCommand.java
    new file mode 100644
    index 0000000000000000000000000000000000000000..36d4b6bd8ca4b0ec0e60cfe1d66bd49bbc3afbeb
    --- /dev/null
    +++ b/core/src/main/java/hudson/cli/DisablePluginCommand.java
    @@ -0,0 +1,236 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2018 CloudBees, Inc.
    + *
    + * 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 hudson.cli;
    +
    +import hudson.Extension;
    +import hudson.PluginWrapper;
    +import hudson.lifecycle.RestartNotSupportedException;
    +import jenkins.model.Jenkins;
    +import org.kohsuke.args4j.Argument;
    +import org.kohsuke.args4j.Option;
    +
    +import java.io.PrintStream;
    +import java.util.List;
    +
    +/**
    + * Disable one or more installed plugins.
    + * @since TODO
    + */
    +@Extension
    +public class DisablePluginCommand extends CLICommand {
    +
    +    @Argument(metaVar = "plugin1 plugin2 plugin3", required = true, usage = "Plugins to be disabled.")
    +    private List<String> pluginNames;
    +
    +    @Option(name = "-restart", aliases = "-r", usage = "Restart Jenkins after disabling plugins.")
    +    private boolean restart;
    +
    +    @Option(name = "-strategy", aliases = "-s", metaVar = "strategy", usage = "How to process the dependant plugins. \n" +
    +            "- none: if a mandatory dependant plugin exists and it is enabled, the plugin cannot be disabled (default value).\n" +
    +            "- mandatory: all mandatory dependant plugins are also disabled, optional dependant plugins remain enabled.\n" +
    +            "- all: all dependant plugins are also disabled, no matter if its dependency is optional or mandatory.")
    +    private String strategy = PluginWrapper.PluginDisableStrategy.NONE.toString();
    +
    +    @Option(name = "-quiet", aliases = "-q", usage = "Be quiet, print only the error messages")
    +    private boolean quiet;
    +
    +    private static final int INDENT_SPACE = 3;
    +
    +    @Override
    +    public String getShortDescription() {
    +        return Messages.DisablePluginCommand_ShortDescription();
    +    }
    +
    +    // package-private access to be able to use it in the tests
    +    static final int RETURN_CODE_NOT_DISABLED_DEPENDANTS = 16;
    +    static final int RETURN_CODE_NO_SUCH_PLUGIN = 17;
    +
    +    @Override
    +    protected void printUsageSummary(PrintStream stderr) {
    +        super.printUsageSummary(stderr);
    +        stderr.println(Messages.DisablePluginCommand_PrintUsageSummary());
    +    }
    +
    +    @Override
    +    protected int run() throws Exception {
    +        Jenkins jenkins = Jenkins.get();
    +        jenkins.checkPermission(Jenkins.ADMINISTER);
    +
    +        // get the strategy as an enum
    +        PluginWrapper.PluginDisableStrategy strategyToUse;
    +        try {
    +            strategyToUse = PluginWrapper.PluginDisableStrategy.valueOf(strategy.toUpperCase());
    +        } catch (IllegalArgumentException iae) {
    +            throw new IllegalArgumentException(hudson.cli.Messages.DisablePluginCommand_NoSuchStrategy(strategy, String.format("%s, %s, %s", PluginWrapper.PluginDisableStrategy.NONE, PluginWrapper.PluginDisableStrategy.MANDATORY, PluginWrapper.PluginDisableStrategy.ALL)));
    +        }
    +
    +        // disable...
    +        List<PluginWrapper.PluginDisableResult> results = jenkins.pluginManager.disablePlugins(strategyToUse, pluginNames);
    +
    +        // print results ...
    +        printResults(results);
    +
    +        // restart if it was required and it's necessary (at least one plugin was disabled in this execution)
    +        restartIfNecessary(results);
    +
    +        // return the appropriate error code
    +        return getResultCode(results);
    +    }
    +
    +    /**
    +     * Print the result of all the process
    +     * @param results the list of results for the disablement of each plugin
    +     */
    +    private void printResults(List<PluginWrapper.PluginDisableResult> results) {
    +        for (PluginWrapper.PluginDisableResult oneResult : results) {
    +            printResult(oneResult, 0);
    +        }
    +    }
    +
    +    /**
    +     * Print indented the arguments with the format passed beginning with the indent passed.
    +     * @param indent number of spaces at the beginning.
    +     * @param format format as in {@link String#format(String, Object...)}
    +     * @param arguments arguments to print as in {@link String#format(String, Object...)}
    +     */
    +    private void printIndented(int indent, String format, String... arguments) {
    +        if (indent == 0) {
    +            stdout.format(format + "%n", (Object[]) arguments);
    +        } else {
    +            String[] newArgs = new String[arguments.length + 1];
    +            newArgs[0] = " ";
    +            System.arraycopy(arguments, 0, newArgs, 1, arguments.length);
    +
    +            String f = "%" + indent + "s" + format + "%n";
    +            stdout.format(f, newArgs);
    +        }
    +    }
    +
    +    /**
    +     * Print the result of a plugin disablement with the indent passed.
    +     * @param oneResult the result of the disablement of a plugin.
    +     * @param indent the initial indent.
    +     */
    +    private void printResult(PluginWrapper.PluginDisableResult oneResult, int indent) {
    +        PluginWrapper.PluginDisableStatus status = oneResult.getStatus();
    +        if (quiet && (PluginWrapper.PluginDisableStatus.DISABLED.equals(status) || PluginWrapper.PluginDisableStatus.ALREADY_DISABLED.equals(status))) {
    +            return;
    +        }
    +
    +        printIndented(indent, Messages.DisablePluginCommand_StatusMessage(oneResult.getPlugin(), oneResult.getStatus(), oneResult.getMessage()));
    +        if (oneResult.getDependantsDisableStatus().size() > 0) {
    +            indent += INDENT_SPACE;
    +            for (PluginWrapper.PluginDisableResult oneDependantResult : oneResult.getDependantsDisableStatus()) {
    +                printResult(oneDependantResult, indent);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Restart if at least one plugin was disabled in this process.
    +     * @param results the list of results after the disablement of the plugins.
    +     */
    +    private void restartIfNecessary(List<PluginWrapper.PluginDisableResult> results) throws RestartNotSupportedException {
    +        if (restart) {
    +            for (PluginWrapper.PluginDisableResult oneResult : results) {
    +                if (restartIfNecessary(oneResult)) {
    +                    break;
    +                }
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Restart if this particular result of the disablement of a plugin and its dependant plugins (depending on the
    +     * strategy used) has a plugin disablexd.
    +     * @param oneResult the result of a plugin (and its dependants).
    +     * @return true if it end up in restarting jenkins.
    +     */
    +    private boolean restartIfNecessary(PluginWrapper.PluginDisableResult oneResult) throws RestartNotSupportedException {
    +        PluginWrapper.PluginDisableStatus status = oneResult.getStatus();
    +        if (PluginWrapper.PluginDisableStatus.DISABLED.equals(status)) {
    +            Jenkins.get().safeRestart();
    +            return true;
    +        }
    +
    +        if (oneResult.getDependantsDisableStatus().size() > 0) {
    +            for (PluginWrapper.PluginDisableResult oneDependantResult : oneResult.getDependantsDisableStatus()) {
    +                if (restartIfNecessary(oneDependantResult)) {
    +                    return true;
    +                }
    +            }
    +        }
    +
    +        return false;
    +    }
    +
    +
    +    /**
    +     * Calculate the result code of the full process based in what went on during the process
    +     * @param results he list of results for the disablement of each plugin
    +     * @return the status code. 0 if all plugins disabled. {@link #RETURN_CODE_NOT_DISABLED_DEPENDANTS} if some
    +     * dependant plugin is not disabled (with strategy NONE), {@link #RETURN_CODE_NO_SUCH_PLUGIN} if some passed
    +     * plugin doesn't exist. Whatever happens first.
    +     */
    +    private int getResultCode(List<PluginWrapper.PluginDisableResult> results) {
    +        int result = 0;
    +        for (PluginWrapper.PluginDisableResult oneResult : results) {
    +            result = getResultCode(oneResult);
    +            if (result != 0) {
    +                break;
    +            }
    +        }
    +
    +        return result;
    +    }
    +
    +    /**
    +     * Calculate the result code of the disablement of one plugin based in what went on during the process of this one
    +     * and its dependant plugins.
    +     * @param result the result of the disablement of this plugin
    +     * @return the status code
    +     */
    +    private int getResultCode(PluginWrapper.PluginDisableResult result) {
    +        int returnCode = 0;
    +        switch (result.getStatus()){
    +            case NOT_DISABLED_DEPENDANTS:
    +                returnCode = RETURN_CODE_NOT_DISABLED_DEPENDANTS;
    +                break;
    +            case NO_SUCH_PLUGIN:
    +                returnCode = RETURN_CODE_NO_SUCH_PLUGIN;
    +        }
    +
    +        if (returnCode == 0) {
    +            for (PluginWrapper.PluginDisableResult oneDependantResult : result.getDependantsDisableStatus()) {
    +                returnCode = getResultCode(oneDependantResult);
    +                if (returnCode != 0) {
    +                    break;
    +                }
    +            }
    +        }
    +
    +        return returnCode;
    +    }
    +}
    diff --git a/core/src/main/java/hudson/cli/EnablePluginCommand.java b/core/src/main/java/hudson/cli/EnablePluginCommand.java
    new file mode 100644
    index 0000000000000000000000000000000000000000..fcee649c03d8d1d4ad605a656d835296a2e16b04
    --- /dev/null
    +++ b/core/src/main/java/hudson/cli/EnablePluginCommand.java
    @@ -0,0 +1,102 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2018 CloudBees, Inc.
    + *
    + * 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 hudson.cli;
    +
    +import hudson.Extension;
    +import hudson.PluginManager;
    +import hudson.PluginWrapper;
    +import jenkins.model.Jenkins;
    +import org.kohsuke.args4j.Argument;
    +import org.kohsuke.args4j.Option;
    +
    +import java.io.IOException;
    +import java.util.List;
    +
    +/**
    + * Enables one or more installed plugins. The listed plugins must already be installed along with its dependencies.
    + * Any listed plugin with disabled dependencies will have its dependencies enabled transitively. Note that enabling an
    + * already enabled plugin does nothing.
    + *
    + * @since 2.136
    + */
    +@Extension
    +public class EnablePluginCommand extends CLICommand {
    +
    +    @Argument(required = true, usage = "Enables the plugins with the given short names and their dependencies.")
    +    private List<String> pluginNames;
    +
    +    @Option(name = "-restart", usage = "Restart Jenkins after enabling plugins.")
    +    private boolean restart;
    +
    +    @Override
    +    public String getShortDescription() {
    +        return Messages.EnablePluginCommand_ShortDescription();
    +    }
    +
    +    @Override
    +    protected int run() throws Exception {
    +        Jenkins jenkins = Jenkins.get();
    +        jenkins.checkPermission(Jenkins.ADMINISTER);
    +        PluginManager manager = jenkins.getPluginManager();
    +        boolean enabledAnyPlugins = false;
    +        for (String pluginName : pluginNames) {
    +            enabledAnyPlugins |= enablePlugin(manager, pluginName);
    +        }
    +        if (restart && enabledAnyPlugins) {
    +            jenkins.safeRestart();
    +        }
    +        return 0;
    +    }
    +
    +    private boolean enablePlugin(PluginManager manager, String shortName) throws IOException {
    +        PluginWrapper plugin = manager.getPlugin(shortName);
    +        if (plugin == null) {
    +            throw new IllegalArgumentException(Messages.EnablePluginCommand_NoSuchPlugin(shortName));
    +        }
    +        if (plugin.isEnabled()) {
    +            return false;
    +        }
    +        stdout.println(String.format("Enabling plugin `%s' (%s)", plugin.getShortName(), plugin.getVersion()));
    +        enableDependencies(manager, plugin);
    +        plugin.enable();
    +        stdout.println(String.format("Plugin `%s' was enabled.", plugin.getShortName()));
    +        return true;
    +    }
    +
    +    private void enableDependencies(PluginManager manager, PluginWrapper plugin) throws IOException {
    +        for (PluginWrapper.Dependency dep : plugin.getDependencies()) {
    +            PluginWrapper dependency = manager.getPlugin(dep.shortName);
    +            if (dependency == null) {
    +                throw new IllegalArgumentException(Messages.EnablePluginCommand_MissingDependencies(plugin.getShortName(), dep));
    +            }
    +            if (!dependency.isEnabled()) {
    +                enableDependencies(manager, dependency);
    +                stdout.println(String.format("Enabling plugin dependency `%s' (%s) for `%s'", dependency.getShortName(), dependency.getVersion(), plugin.getShortName()));
    +                dependency.enable();
    +            }
    +        }
    +    }
    +
    +}
    diff --git a/core/src/main/java/hudson/cli/HelpCommand.java b/core/src/main/java/hudson/cli/HelpCommand.java
    index 60fcc0970be1f6faa8badffcdfa0b1dde132a42c..7b73c0b31dfef2993e39d773cfc47ad959cc3b06 100644
    --- a/core/src/main/java/hudson/cli/HelpCommand.java
    +++ b/core/src/main/java/hudson/cli/HelpCommand.java
    @@ -53,7 +53,7 @@ public class HelpCommand extends CLICommand {
         protected int run() throws Exception {
             if (!Jenkins.getActiveInstance().hasPermission(Jenkins.READ)) {
                 throw new AccessDeniedException("You must authenticate to access this Jenkins.\n"
    -                    + hudson.cli.client.Messages.CLI_Usage());
    +                    + CLI.usage());
             }
     
             if (command != null)
    diff --git a/core/src/main/java/hudson/cli/ListChangesCommand.java b/core/src/main/java/hudson/cli/ListChangesCommand.java
    index 8da20000734b65a56ab7eb9b6c326a85e443cf2e..1b398bddad9582c0bf01e94721eae9766213e21c 100644
    --- a/core/src/main/java/hudson/cli/ListChangesCommand.java
    +++ b/core/src/main/java/hudson/cli/ListChangesCommand.java
    @@ -15,14 +15,14 @@ import java.io.IOException;
     import java.io.PrintWriter;
     import java.util.List;
     import org.kohsuke.accmod.Restricted;
    -import org.kohsuke.accmod.restrictions.DoNotUse;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     /**
      * Retrieves a change list for the specified builds.
      *
      * @author Kohsuke Kawaguchi
      */
    -@Restricted(DoNotUse.class) // command implementation only
    +@Restricted(NoExternalUse.class) // command implementation only
     @Extension
     public class ListChangesCommand extends RunRangeCommand {
         @Override
    diff --git a/core/src/main/java/hudson/cli/ListPluginsCommand.java b/core/src/main/java/hudson/cli/ListPluginsCommand.java
    index 99c926cdaf1ef8c648ff1a339b6fdf61314497f2..faa35dfa678897d72a0ed5306d419dadfaf0e296 100644
    --- a/core/src/main/java/hudson/cli/ListPluginsCommand.java
    +++ b/core/src/main/java/hudson/cli/ListPluginsCommand.java
    @@ -47,7 +47,9 @@ public class ListPluginsCommand extends CLICommand {
         public String name;
     
         protected int run() {
    -        Jenkins h = Jenkins.getActiveInstance();
    +        Jenkins h = Jenkins.getInstance();
    +        h.checkPermission(Jenkins.ADMINISTER);
    +        
             PluginManager pluginManager = h.getPluginManager();
     
             if (this.name != null) {
    diff --git a/core/src/main/java/hudson/cli/declarative/CLIMethod.java b/core/src/main/java/hudson/cli/declarative/CLIMethod.java
    index d4e6c96ed4c2bf762a415b15297d9e764a7038ce..6366e4409ed667f116f64c06af8f37e9fd38d230 100644
    --- a/core/src/main/java/hudson/cli/declarative/CLIMethod.java
    +++ b/core/src/main/java/hudson/cli/declarative/CLIMethod.java
    @@ -38,8 +38,8 @@ import java.lang.annotation.Target;
      * Annotates methods on model objects to expose them as CLI commands.
      *
      * <p>
    - * You need to have <tt>Messages.properties</tt> in the same package with the
    - * <tt>CLI.<i>command-name</i>.shortDescription</tt> key to describe the command.
    + * You need to have {@code Messages.properties} in the same package with the
    + * {@code CLI.<i>command-name</i>.shortDescription} key to describe the command.
      * This is used for the same purpose as {@link CLICommand#getShortDescription()}.
      *
      * <p>
    diff --git a/core/src/main/java/hudson/cli/declarative/CLIResolver.java b/core/src/main/java/hudson/cli/declarative/CLIResolver.java
    index 2b31b47f2790f61d9c4b93ec142a0e6611b53967..5c4b93444e8e27f5ece3869135317d5b97a3616c 100644
    --- a/core/src/main/java/hudson/cli/declarative/CLIResolver.java
    +++ b/core/src/main/java/hudson/cli/declarative/CLIResolver.java
    @@ -40,12 +40,12 @@ import java.lang.annotation.Target;
      * <p>
      * Hudson uses the return type of the resolver method
      * to pick the resolver method to use, of all the resolver methods it discovers. That is,
    - * if Hudson is looking to find an instance of type <tt>T</tt> for the current command, it first
    - * looks for the resolver method whose return type is <tt>T</tt>, then it checks for the base type of <tt>T</tt>,
    + * if Hudson is looking to find an instance of type {@code T} for the current command, it first
    + * looks for the resolver method whose return type is {@code T}, then it checks for the base type of {@code T},
      * and so on.
      *
      * <p>
    - * If the chosen resolver method is an instance method on type <tt>S</tt>, the "parent resolver" is then
    + * If the chosen resolver method is an instance method on type {@code S}, the "parent resolver" is then
      * located to resolve an instance of type 'S'. This process repeats until a static resolver method is discovered
      * (since most of Hudson's model objects are anchored to the root {@link jenkins.model.Jenkins} object, normally that would become
      * the top-most resolver method.)
    diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
    index 6cd1d86eba57141695824a9ec52c22a8cad074f7..0648f057a04b28ab7aff06b283e6159b36a93054 100644
    --- a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
    +++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
    @@ -62,7 +62,7 @@ public abstract class GenericItemOptionHandler<T extends Item> extends OptionHan
             T s = j.getItemByFullName(src, type());
             if (s == null) {
                 final Authentication who = Jenkins.getAuthentication();
    -            try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +            try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                     Item actual = j.getItemByFullName(src);
                     if (actual == null) {
                         LOGGER.log(Level.FINE, "really no item exists named {0}", src);
    diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
    index 729d6f571b2ed1cca2fd7707c14993577458764e..6107855812c3de7b8b16c35dd1fd8f6b1c836b2f 100644
    --- a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
    +++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
    @@ -48,7 +48,7 @@ import javax.annotation.CheckForNull;
      * For example:
      * <dl>
      *   <dt>my_view_name</dt><dd>refers to a top level view with given name.</dd>
    - *   <dt>nested/inner</dt><dd>refers to a view named <tt>inner</tt> inside of a top level view group named <tt>nested</tt>.</dd>
    + *   <dt>nested/inner</dt><dd>refers to a view named {@code inner} inside of a top level view group named {@code nested}.</dd>
      * </dl>
      *
      * <p>
    @@ -103,12 +103,13 @@ public class ViewOptionHandler extends OptionHandler<View> {
                 String viewName = tok.nextToken();
     
                 view = group.getView(viewName);
    -            if (view == null)
    +            if (view == null) {
    +                group.checkPermission(View.READ);
                     throw new IllegalArgumentException(String.format(
                             "No view named %s inside view %s",
                             viewName, group.getDisplayName()
                     ));
    -
    +            }
                 view.checkPermission(View.READ);
                 if (view instanceof ViewGroup) {
                     group = (ViewGroup) view;
    diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java
    index 897661329cc1b6e4b0b2dad80c08f3a8ac08920c..6acbdeff6e78e90b0ccbd0529e9bcd19e2b8bbec 100644
    --- a/core/src/main/java/hudson/console/AnnotatedLargeText.java
    +++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java
    @@ -52,6 +52,7 @@ import com.jcraft.jzlib.GZIPInputStream;
     import com.jcraft.jzlib.GZIPOutputStream;
     
     import static java.lang.Math.abs;
    +import org.jenkinsci.remoting.util.AnonymousClassWarnings;
     
     /**
      * Extension to {@link LargeText} that handles annotations by {@link ConsoleAnnotator}.
    @@ -112,7 +113,7 @@ public class AnnotatedLargeText<T> extends LargeText {
             rsp.setContentType(isHtml() ? "text/html;charset=UTF-8" : "text/plain;charset=UTF-8");
         }
     
    -    private ConsoleAnnotator createAnnotator(StaplerRequest req) throws IOException {
    +    private ConsoleAnnotator<T> createAnnotator(StaplerRequest req) throws IOException {
             try {
                 String base64 = req!=null ? req.getHeader("X-ConsoleAnnotator") : null;
                 if (base64!=null) {
    @@ -134,7 +135,7 @@ public class AnnotatedLargeText<T> extends LargeText {
                 throw new IOException(e);
             }
             // start from scratch
    -        return ConsoleAnnotator.initial(context==null ? null : context.getClass());
    +        return ConsoleAnnotator.initial(context);
         }
     
         @Override
    @@ -163,13 +164,13 @@ public class AnnotatedLargeText<T> extends LargeText {
         }
     
         public long writeHtmlTo(long start, Writer w) throws IOException {
    -        ConsoleAnnotationOutputStream caw = new ConsoleAnnotationOutputStream(
    +        ConsoleAnnotationOutputStream<T> caw = new ConsoleAnnotationOutputStream<>(
                     w, createAnnotator(Stapler.getCurrentRequest()), context, charset);
             long r = super.writeLogTo(start,caw);
     
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             Cipher sym = PASSING_ANNOTATOR.encrypt();
    -        ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(new CipherOutputStream(baos,sym)));
    +        ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(new GZIPOutputStream(new CipherOutputStream(baos,sym)));
             oos.writeLong(System.currentTimeMillis()); // send timestamp to prevent a replay attack
             oos.writeObject(caw.getConsoleAnnotator());
             oos.close();
    diff --git a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
    index 919e3af0117fb48e9a9788e85dc2ce07d0fbcf36..f3a22c875cbdc9041424a7ab71070c9e5d8c5472 100644
    --- a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
    +++ b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
    @@ -72,7 +72,7 @@ public class ConsoleAnnotationOutputStream<T> extends LineTransformationOutputSt
             this.lineOut = new WriterOutputStream(line,charset);
         }
     
    -    public ConsoleAnnotator getConsoleAnnotator() {
    +    public ConsoleAnnotator<T> getConsoleAnnotator() {
             return ann;
         }
     
    @@ -80,6 +80,8 @@ public class ConsoleAnnotationOutputStream<T> extends LineTransformationOutputSt
          * Called after we read the whole line of plain text, which is stored in {@link #buf}.
          * This method performs annotations and send the result to {@link #out}.
          */
    +    @SuppressWarnings({"unchecked", "rawtypes"}) // appears to be unsound
    +    @Override
         protected void eol(byte[] in, int sz) throws IOException {
             line.reset();
             final StringBuffer strBuf = line.getStringBuffer();
    @@ -109,9 +111,10 @@ public class ConsoleAnnotationOutputStream<T> extends LineTransformationOutputSt
                         final ConsoleNote a = ConsoleNote.readFrom(new DataInputStream(b));
                         if (a!=null) {
                             if (annotators==null)
    -                            annotators = new ArrayList<ConsoleAnnotator<T>>();
    +                            annotators = new ArrayList<>();
                             annotators.add(new ConsoleAnnotator<T>() {
    -                            public ConsoleAnnotator annotate(T context, MarkupText text) {
    +                            @Override
    +                            public ConsoleAnnotator<T> annotate(T context, MarkupText text) {
                                     return a.annotate(context,text,charPos);
                                 }
                             });
    diff --git a/core/src/main/java/hudson/console/ConsoleAnnotator.java b/core/src/main/java/hudson/console/ConsoleAnnotator.java
    index c19ac974b686064cd0adb7767065c7d04a6a3af4..9701eb302f04d54476154473e0b8bc85aa5fe346 100644
    --- a/core/src/main/java/hudson/console/ConsoleAnnotator.java
    +++ b/core/src/main/java/hudson/console/ConsoleAnnotator.java
    @@ -82,50 +82,54 @@ public abstract class ConsoleAnnotator<T> implements Serializable {
          *      To indicate that you are not interested in the following lines, return {@code null}.
          */
         @CheckForNull
    -    public abstract ConsoleAnnotator annotate(@Nonnull T context, @Nonnull MarkupText text );
    +    public abstract ConsoleAnnotator<T> annotate(@Nonnull T context, @Nonnull MarkupText text );
     
         /**
          * Cast operation that restricts T.
          */
    +    @SuppressWarnings("unchecked")
         public static <T> ConsoleAnnotator<T> cast(ConsoleAnnotator<? super T> a) {
             return (ConsoleAnnotator)a;
         }
     
    -    /**
    -     * Bundles all the given {@link ConsoleAnnotator} into a single annotator.
    -     */
    -    public static <T> ConsoleAnnotator<T> combine(Collection<? extends ConsoleAnnotator<? super T>> all) {
    -        switch (all.size()) {
    -        case 0:     return null;    // none
    -        case 1:     return  cast(all.iterator().next()); // just one
    -        }
    -
    -        class Aggregator extends ConsoleAnnotator<T> {
    -            List<ConsoleAnnotator<T>> list;
    +    @SuppressWarnings({"unchecked", "rawtypes"}) // unclear to jglick what is going on here
    +    private static final class ConsoleAnnotatorAggregator<T> extends ConsoleAnnotator<T> {
    +        List<ConsoleAnnotator<T>> list;
     
    -            Aggregator(Collection list) {
    -                this.list = new ArrayList<ConsoleAnnotator<T>>(list);
    -            }
    +        ConsoleAnnotatorAggregator(Collection list) {
    +            this.list = new ArrayList<>(list);
    +        }
     
    -            public ConsoleAnnotator annotate(T context, MarkupText text) {
    -                ListIterator<ConsoleAnnotator<T>> itr = list.listIterator();
    -                while (itr.hasNext()) {
    -                    ConsoleAnnotator a =  itr.next();
    -                    ConsoleAnnotator b = a.annotate(context,text);
    -                    if (a!=b) {
    -                        if (b==null)    itr.remove();
    -                        else            itr.set(b);
    -                    }
    +        @Override
    +        public ConsoleAnnotator annotate(T context, MarkupText text) {
    +            ListIterator<ConsoleAnnotator<T>> itr = list.listIterator();
    +            while (itr.hasNext()) {
    +                ConsoleAnnotator a =  itr.next();
    +                ConsoleAnnotator b = a.annotate(context,text);
    +                if (a!=b) {
    +                    if (b==null)    itr.remove();
    +                    else            itr.set(b);
                     }
    +            }
     
    -                switch (list.size()) {
    +            switch (list.size()) {
                     case 0:     return null;    // no more annotator left
                     case 1:     return list.get(0); // no point in aggregating
                     default:    return this;
    -                }
                 }
             }
    -        return new Aggregator(all);
    +    }
    +
    +    /**
    +     * Bundles all the given {@link ConsoleAnnotator} into a single annotator.
    +     */
    +    public static <T> ConsoleAnnotator<T> combine(Collection<? extends ConsoleAnnotator<? super T>> all) {
    +        switch (all.size()) {
    +        case 0:     return null;    // none
    +        case 1:     return  cast(all.iterator().next()); // just one
    +        }
    +
    +        return new ConsoleAnnotatorAggregator<>(all);
         }
     
         /**
    @@ -139,8 +143,9 @@ public abstract class ConsoleAnnotator<T> implements Serializable {
         /**
          * List all the console annotators that can work for the specified context type.
          */
    +    @SuppressWarnings({"unchecked", "rawtypes"}) // reflective
         public static <T> List<ConsoleAnnotator<T>> _for(T context) {
    -        List<ConsoleAnnotator<T>> r  = new ArrayList<ConsoleAnnotator<T>>();
    +        List<ConsoleAnnotator<T>> r  = new ArrayList<>();
             for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) {
                 if (f.type().isInstance(context)) {
                     ConsoleAnnotator ca = f.newInstance(context);
    diff --git a/core/src/main/java/hudson/console/ConsoleAnnotatorFactory.java b/core/src/main/java/hudson/console/ConsoleAnnotatorFactory.java
    index f71b25e53edccf2497f08da4ef06a3779b243c2c..409ee59c67f4c531996b33b20e9a6f33bb6b14f0 100644
    --- a/core/src/main/java/hudson/console/ConsoleAnnotatorFactory.java
    +++ b/core/src/main/java/hudson/console/ConsoleAnnotatorFactory.java
    @@ -58,7 +58,7 @@ import java.net.URL;
      *
      * <h2>Behaviour, JavaScript, and CSS</h2>
      * <p>
    - * {@link ConsoleNote} can have associated <tt>script.js</tt> and <tt>style.css</tt> (put them
    + * {@link ConsoleNote} can have associated {@code script.js} and {@code style.css} (put them
      * in the same resource directory that you normally put Jelly scripts), which will be loaded into
      * the HTML page whenever the console notes are used. This allows you to use minimal markup in
      * code generation, and do the styling in CSS and perform the rest of the interesting work as a CSS behaviour/JavaScript.
    @@ -80,13 +80,13 @@ public abstract class ConsoleAnnotatorFactory<T> implements ExtensionPoint {
          * @return
          *      null if this factory is not going to participate in the annotation of this console.
          */
    -    public abstract ConsoleAnnotator newInstance(T context);
    +    public abstract ConsoleAnnotator<T> newInstance(T context);
     
         /**
          * For which context type does this annotator work?
          */
    -    public Class type() {
    -        Type type = Types.getBaseClass(getClass(), ConsoleAnnotator.class);
    +    public Class<?> type() {
    +        Type type = Types.getBaseClass(getClass(), ConsoleAnnotatorFactory.class);
             if (type instanceof ParameterizedType)
                 return Types.erasure(Types.getTypeArgument(type,0));
             else
    @@ -105,7 +105,7 @@ public abstract class ConsoleAnnotatorFactory<T> implements ExtensionPoint {
         }
     
         private URL getResource(String fileName) {
    -        Class c = getClass();
    +        Class<?> c = getClass();
             return c.getClassLoader().getResource(c.getName().replace('.','/').replace('$','/')+ fileName);
         }
     
    @@ -125,6 +125,7 @@ public abstract class ConsoleAnnotatorFactory<T> implements ExtensionPoint {
         /**
          * All the registered instances.
          */
    +    @SuppressWarnings("rawtypes")
         public static ExtensionList<ConsoleAnnotatorFactory> all() {
             return ExtensionList.lookup(ConsoleAnnotatorFactory.class);
         }
    diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java
    index c0b281c6ca9bce6cfda181c35b98aca9ae8dbe45..a572a966b614a2b8f10b451ecd8ae696eab6cbc2 100644
    --- a/core/src/main/java/hudson/console/ConsoleNote.java
    +++ b/core/src/main/java/hudson/console/ConsoleNote.java
    @@ -54,6 +54,7 @@ import com.jcraft.jzlib.GZIPOutputStream;
     import hudson.remoting.ClassFilter;
     import jenkins.security.HMACConfidentialKey;
     import jenkins.util.SystemProperties;
    +import org.jenkinsci.remoting.util.AnonymousClassWarnings;
     
     /**
      * Data that hangs off from a console output.
    @@ -108,7 +109,7 @@ import jenkins.util.SystemProperties;
      *
      * <h2>Behaviour, JavaScript, and CSS</h2>
      * <p>
    - * {@link ConsoleNote} can have associated <tt>script.js</tt> and <tt>style.css</tt> (put them
    + * {@link ConsoleNote} can have associated {@code script.js} and {@code style.css} (put them
      * in the same resource directory that you normally put Jelly scripts), which will be loaded into
      * the HTML page whenever the console notes are used. This allows you to use minimal markup in
      * code generation, and do the styling in CSS and perform the rest of the interesting work as a CSS behaviour/JavaScript.
    @@ -180,7 +181,7 @@ public abstract class ConsoleNote<T> implements Serializable, Describable<Consol
     
         private ByteArrayOutputStream encodeToBytes() throws IOException {
             ByteArrayOutputStream buf = new ByteArrayOutputStream();
    -        try (ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(buf))) {
    +        try (ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(new GZIPOutputStream(buf))) {
                 oos.writeObject(this);
             }
     
    diff --git a/core/src/main/java/hudson/console/HyperlinkNote.java b/core/src/main/java/hudson/console/HyperlinkNote.java
    index 79fbbffc70ec1284d530da6b7eeec284e4b195d7..3f8ccfda1d89ab00a50e809cafb4b443c31d8e76 100644
    --- a/core/src/main/java/hudson/console/HyperlinkNote.java
    +++ b/core/src/main/java/hudson/console/HyperlinkNote.java
    @@ -27,10 +27,13 @@ import hudson.Extension;
     import hudson.MarkupText;
     import jenkins.model.Jenkins;
     import org.jenkinsci.Symbol;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.Stapler;
     import org.kohsuke.stapler.StaplerRequest;
     
     import java.io.IOException;
    +import java.util.function.BiFunction;
     import java.util.logging.Level;
     import java.util.logging.Logger;
     
    @@ -75,8 +78,20 @@ public class HyperlinkNote extends ConsoleNote {
         }
     
         public static String encodeTo(String url, String text) {
    +        return encodeTo(url, text, HyperlinkNote::new);
    +    }
    +
    +    @Restricted(NoExternalUse.class)
    +    static String encodeTo(String url, String text, BiFunction<String, Integer, ConsoleNote> constructor) {
    +        // If text contains newlines, then its stored length will not match its length when being
    +        // displayed, since the display length will only include text up to the first newline,
    +        // which will cause an IndexOutOfBoundsException in MarkupText#rangeCheck when
    +        // ConsoleAnnotationOutputStream converts the note into markup. That stream treats '\n' as
    +        // the sole end-of-line marker on all platforms, so we ignore '\r' because it will not
    +        // break the conversion.
    +        text = text.replace('\n', ' ');
             try {
    -            return new HyperlinkNote(url,text.length()).encode()+text;
    +            return constructor.apply(url,text.length()).encode()+text;
             } catch (IOException e) {
                 // impossible, but don't make this a fatal problem
                 LOGGER.log(Level.WARNING, "Failed to serialize "+HyperlinkNote.class,e);
    @@ -92,4 +107,5 @@ public class HyperlinkNote extends ConsoleNote {
         }
     
         private static final Logger LOGGER = Logger.getLogger(HyperlinkNote.class.getName());
    +    private static final long serialVersionUID = 3908468829358026949L;
     }
    diff --git a/core/src/main/java/hudson/console/ModelHyperlinkNote.java b/core/src/main/java/hudson/console/ModelHyperlinkNote.java
    index 784934708d62dc95389f0877fd571601d865e61c..127911cc76fa848ac5dee822634739d815960c65 100644
    --- a/core/src/main/java/hudson/console/ModelHyperlinkNote.java
    +++ b/core/src/main/java/hudson/console/ModelHyperlinkNote.java
    @@ -5,8 +5,6 @@ import hudson.model.*;
     import jenkins.model.Jenkins;
     import org.jenkinsci.Symbol;
     
    -import java.io.IOException;
    -import java.util.logging.Level;
     import java.util.logging.Logger;
     import javax.annotation.Nonnull;
     
    @@ -57,13 +55,7 @@ public class ModelHyperlinkNote extends HyperlinkNote {
         }
     
         public static String encodeTo(String url, String text) {
    -        try {
    -            return new ModelHyperlinkNote(url,text.length()).encode()+text;
    -        } catch (IOException e) {
    -            // impossible, but don't make this a fatal problem
    -            LOGGER.log(Level.WARNING, "Failed to serialize "+ModelHyperlinkNote.class,e);
    -            return text;
    -        }
    +        return HyperlinkNote.encodeTo(url, text, ModelHyperlinkNote::new);
         }
     
         @Extension @Symbol("hyperlinkToModels")
    diff --git a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
    index 3d842e1d79c5b9bf9535339a4952abd5876c3ee2..56eaf7d941a0f281dd45a3f219204fce04719353 100644
    --- a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
    +++ b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
    @@ -31,7 +31,7 @@ import org.jenkinsci.Symbol;
     import java.util.logging.Logger;
     
     /**
    - * Periodically checks the disk usage of <tt>JENKINS_HOME</tt>,
    + * Periodically checks the disk usage of {@code JENKINS_HOME},
      * and activate {@link HudsonHomeDiskUsageMonitor} if necessary.
      *
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
    index 337f739dcb827d0d718d83208ff47cc909bf6b00..177141f746fa872774cc63aa1adea6d8fd52cd1b 100644
    --- a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
    +++ b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
    @@ -38,7 +38,7 @@ import java.io.IOException;
     import java.util.List;
     
     /**
    - * Monitors the disk usage of <tt>JENKINS_HOME</tt>, and if it's almost filled up, warn the user.
    + * Monitors the disk usage of {@code JENKINS_HOME}, and if it's almost filled up, warn the user.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
    index 38a9215c22aebd50389331a6ac5a6b6685f6b5fe..5e3563f3a4461fb57b60dd0f9a1b7e83bd9487d9 100644
    --- a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
    +++ b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
    @@ -92,7 +92,7 @@ public class ReverseProxySetupMonitor extends AdministrativeMonitor {
                 // of course the irony is that this redirect won't work
                 return HttpResponses.redirectViaContextPath("/manage");
             } else {
    -            return new HttpRedirect("https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+says+my+reverse+proxy+setup+is+broken");
    +            return new HttpRedirect("https://jenkins.io/redirect/troubleshooting/broken-reverse-proxy");
             }
         }
     
    diff --git a/core/src/main/java/hudson/init/Initializer.java b/core/src/main/java/hudson/init/Initializer.java
    index d41f45da52edba6cc1637d7c33f0bfb4eee50c29..d24a412ae8a30e5d6dacd61f56924e6646aeabd2 100644
    --- a/core/src/main/java/hudson/init/Initializer.java
    +++ b/core/src/main/java/hudson/init/Initializer.java
    @@ -92,7 +92,7 @@ public @interface Initializer {
         String[] attains() default {};
     
         /**
    -     * Key in <tt>Messages.properties</tt> that represents what this task is about. Used for rendering the progress.
    +     * Key in {@code Messages.properties} that represents what this task is about. Used for rendering the progress.
          * Defaults to "${short class name}.${method Name}".
          */
         String displayName() default "";
    diff --git a/core/src/main/java/hudson/init/Terminator.java b/core/src/main/java/hudson/init/Terminator.java
    index 3e1155babdcfee7954b507a33d93d81fd4a24be9..24bc781fb10a1d5ef8f2a037a8c5b2ef9eb51202 100644
    --- a/core/src/main/java/hudson/init/Terminator.java
    +++ b/core/src/main/java/hudson/init/Terminator.java
    @@ -52,7 +52,7 @@ public @interface Terminator {
         String[] attains() default {};
     
         /**
    -     * Key in <tt>Messages.properties</tt> that represents what this task is about. Used for rendering the progress.
    +     * Key in {@code Messages.properties} that represents what this task is about. Used for rendering the progress.
          * Defaults to "${short class name}.${method Name}".
          */
         String displayName() default "";
    diff --git a/core/src/main/java/hudson/init/package-info.java b/core/src/main/java/hudson/init/package-info.java
    index 9833d2090ec5adc3f1444d1a8828a2a9f1f519c9..5f66063ca3b2ef245b9491cdf4488513d8421b9d 100644
    --- a/core/src/main/java/hudson/init/package-info.java
    +++ b/core/src/main/java/hudson/init/package-info.java
    @@ -33,7 +33,7 @@
      *
      * <p>
      * Such micro-scopic dependencies are organized into a bigger directed acyclic graph, which is then executed
    - * via <tt>Session</tt>. During execution of the reactor, additional tasks can be discovered and added to
    + * via {@code Session}. During execution of the reactor, additional tasks can be discovered and added to
      * the DAG. We use this additional indirection to:
      *
      * <ol>
    diff --git a/core/src/main/java/hudson/lifecycle/Lifecycle.java b/core/src/main/java/hudson/lifecycle/Lifecycle.java
    index 34915da0de1beab1dbd7c085506d8bb51aab2644..4c52cdd87877e8e91f588831cdfbb244dba9c2d4 100644
    --- a/core/src/main/java/hudson/lifecycle/Lifecycle.java
    +++ b/core/src/main/java/hudson/lifecycle/Lifecycle.java
    @@ -112,7 +112,7 @@ public abstract class Lifecycle implements ExtensionPoint {
         }
     
         /**
    -     * If the location of <tt>jenkins.war</tt> is known in this life cycle,
    +     * If the location of {@code jenkins.war} is known in this life cycle,
          * return it location. Otherwise return null to indicate that it is unknown.
          *
          * <p>
    @@ -131,7 +131,7 @@ public abstract class Lifecycle implements ExtensionPoint {
          *
          * <p>
          * On some system, most notably Windows, a file being in use cannot be changed,
    -     * so rewriting <tt>jenkins.war</tt> requires some special trick. Override this method
    +     * so rewriting {@code jenkins.war} requires some special trick. Override this method
          * to do so.
          */
         public void rewriteHudsonWar(File by) throws IOException {
    diff --git a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java
    index 14bbb60584bca38a207ca23e8d52c5c8f4a72874..b218d5964077bb45df6909a644e0892276561b18 100644
    --- a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java
    +++ b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java
    @@ -112,6 +112,8 @@ public class WindowsInstallerLink extends ManagementLink {
          */
         @RequirePOST
         public void doDoInstall(StaplerRequest req, StaplerResponse rsp, @QueryParameter("dir") String _dir) throws IOException, ServletException {
    +        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +
             if(installationDir!=null) {
                 // installation already complete
                 sendError("Installation is already complete",req,rsp);
    @@ -121,8 +123,6 @@ public class WindowsInstallerLink extends ManagementLink {
                 sendError(".NET Framework 2.0 or later is required for this feature",req,rsp);
                 return;
             }
    -        
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
     
             File dir = new File(_dir).getAbsoluteFile();
             dir.mkdirs();
    @@ -174,6 +174,8 @@ public class WindowsInstallerLink extends ManagementLink {
     
         @RequirePOST
         public void doRestart(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
    +        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +
             if(installationDir==null) {
                 // if the user reloads the page after Hudson has restarted,
                 // it comes back here. In such a case, don't let this restart Hudson.
    @@ -181,7 +183,6 @@ public class WindowsInstallerLink extends ManagementLink {
                 rsp.sendRedirect(req.getContextPath()+"/");
                 return;
             }
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
     
             rsp.forward(this,"_restart",req);
             final File oldRoot = Jenkins.getInstance().getRootDir();
    diff --git a/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java b/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java
    index 42519eb21b0b2dc090df02b2331cc928b1e0af61..ad3940b4a4feab58375654f6a0f0bb870c18ad1b 100644
    --- a/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java
    +++ b/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java
    @@ -54,7 +54,7 @@ public class WindowsServiceLifecycle extends Lifecycle {
         }
     
         /**
    -     * If <tt>jenkins.exe</tt> is old compared to our copy,
    +     * If {@code jenkins.exe} is old compared to our copy,
          * schedule an overwrite (except that since it's currently running,
          * we can only do it when Jenkins restarts next time.)
          */
    diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java
    index 7313778df04399340fc09ac394bee6a6195bb4e7..51549a3bd34d8453945b08d344ed40b2f930239b 100644
    --- a/core/src/main/java/hudson/logging/LogRecorder.java
    +++ b/core/src/main/java/hudson/logging/LogRecorder.java
    @@ -23,6 +23,7 @@
      */
     package hudson.logging;
     
    +import com.google.common.annotations.VisibleForTesting;
     import com.thoughtworks.xstream.XStream;
     import hudson.BulkChange;
     import hudson.Extension;
    @@ -31,6 +32,7 @@ import hudson.Util;
     import hudson.XmlFile;
     import hudson.model.*;
     import hudson.util.HttpResponses;
    +import jenkins.util.MemoryReductionUtil;
     import jenkins.model.Jenkins;
     import hudson.model.listeners.SaveableListener;
     import hudson.remoting.Channel;
    @@ -41,6 +43,7 @@ import hudson.util.RingBufferLogHandler;
     import hudson.util.XStream2;
     import jenkins.security.MasterToSlaveCallable;
     import net.sf.json.JSONObject;
    +import org.apache.commons.lang.StringUtils;
     import org.kohsuke.stapler.*;
     import org.kohsuke.stapler.interceptor.RequirePOST;
     
    @@ -85,16 +88,60 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
             return ts;
         }
     
    +    @Restricted(NoExternalUse.class)
    +    @VisibleForTesting
    +    public static Set<String> getAutoCompletionCandidates(List<String> loggerNamesList) {
    +        Set<String> loggerNames = new HashSet<>(loggerNamesList);
    +
    +        // now look for package prefixes that make sense to offer for autocompletion:
    +        // Only prefixes that match multiple loggers will be shown.
    +        // Example: 'org' will show 'org', because there's org.apache, org.jenkinsci, etc.
    +        // 'io' might only show 'io.jenkins.plugins' rather than 'io' if all loggers starting with 'io' start with 'io.jenkins.plugins'.
    +        HashMap<String, Integer> seenPrefixes = new HashMap<>();
    +        SortedSet<String> relevantPrefixes = new TreeSet<>();
    +        for (String loggerName : loggerNames) {
    +            String[] loggerNameParts = loggerName.split("[.]");
    +
    +            String longerPrefix = null;
    +            for (int i = loggerNameParts.length; i > 0; i--) {
    +                String loggerNamePrefix = StringUtils.join(Arrays.copyOf(loggerNameParts, i), ".");
    +                seenPrefixes.put(loggerNamePrefix, seenPrefixes.getOrDefault(loggerNamePrefix, 0) + 1);
    +                if (longerPrefix == null) {
    +                    relevantPrefixes.add(loggerNamePrefix); // actual logger name
    +                    longerPrefix = loggerNamePrefix;
    +                    continue;
    +                }
    +
    +                if (seenPrefixes.get(loggerNamePrefix) > seenPrefixes.get(longerPrefix)) {
    +                    relevantPrefixes.add(loggerNamePrefix);
    +                }
    +                longerPrefix = loggerNamePrefix;
    +            }
    +        }
    +        return relevantPrefixes;
    +    }
    +
         @Restricted(NoExternalUse.class)
         public AutoCompletionCandidates doAutoCompleteLoggerName(@QueryParameter String value) {
    -        AutoCompletionCandidates candidates = new AutoCompletionCandidates();
    -        Enumeration<String> loggerNames = LogManager.getLogManager().getLoggerNames();
    -        while (loggerNames.hasMoreElements()) {
    -            String loggerName = loggerNames.nextElement();
    -            if (loggerName.toLowerCase(Locale.ENGLISH).contains(value.toLowerCase(Locale.ENGLISH))) {
    -                candidates.add(loggerName);
    +        if (value == null) {
    +            return new AutoCompletionCandidates();
    +        }
    +
    +        // get names of all actual loggers known to Jenkins
    +        Set<String> candidateNames = new LinkedHashSet<>(getAutoCompletionCandidates(Collections.list(LogManager.getLogManager().getLoggerNames())));
    +
    +        for (String part : value.split("[ ]+")) {
    +            HashSet<String> partCandidates = new HashSet<>();
    +            String lowercaseValue = part.toLowerCase(Locale.ENGLISH);
    +            for (String loggerName : candidateNames) {
    +                if (loggerName.toLowerCase(Locale.ENGLISH).contains(lowercaseValue)) {
    +                    partCandidates.add(loggerName);
    +                }
                 }
    +            candidateNames.retainAll(partCandidates);
             }
    +        AutoCompletionCandidates candidates = new AutoCompletionCandidates();
    +        candidates.add(candidateNames.toArray(MemoryReductionUtil.EMPTY_STRING_ARRAY));
             return candidates;
         }
     
    diff --git a/core/src/main/java/hudson/logging/LogRecorderManager.java b/core/src/main/java/hudson/logging/LogRecorderManager.java
    index bac8c8b6859a644119397b7b4fc8b2004d53a4d9..765253341bc13b4d9fb501320ddf32bb7db82e20 100644
    --- a/core/src/main/java/hudson/logging/LogRecorderManager.java
    +++ b/core/src/main/java/hudson/logging/LogRecorderManager.java
    @@ -25,6 +25,7 @@ package hudson.logging;
     
     import hudson.FeedAdapter;
     import hudson.Functions;
    +import hudson.PluginManager;
     import hudson.init.Initializer;
     import static hudson.init.InitMilestone.PLUGINS_PREPARED;
     import hudson.model.AbstractModelObject;
    @@ -35,7 +36,10 @@ import jenkins.model.JenkinsLocationConfiguration;
     import jenkins.model.ModelObjectWithChildren;
     import jenkins.model.ModelObjectWithContextMenu.ContextMenu;
     import org.apache.commons.io.filefilter.WildcardFileFilter;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.HttpResponse;
    @@ -61,7 +65,7 @@ import java.util.logging.Logger;
      *
      * @author Kohsuke Kawaguchi
      */
    -public class LogRecorderManager extends AbstractModelObject implements ModelObjectWithChildren {
    +public class LogRecorderManager extends AbstractModelObject implements ModelObjectWithChildren, StaplerProxy {
         /**
          * {@link LogRecorder}s keyed by their {@linkplain LogRecorder#name name}.
          */
    @@ -198,4 +202,19 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje
         public static void init(Jenkins h) throws IOException {
             h.getLog().load();
         }
    +
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(LogRecorderManager.class.getName() + ".skipPermissionCheck");
     }
    diff --git a/core/src/main/java/hudson/markup/MarkupFormatter.java b/core/src/main/java/hudson/markup/MarkupFormatter.java
    index d722fb6aa248854296df770cdfb58f0af6e9e425..be8004136ab2646a9ea2c7627643ebd778404548 100644
    --- a/core/src/main/java/hudson/markup/MarkupFormatter.java
    +++ b/core/src/main/java/hudson/markup/MarkupFormatter.java
    @@ -51,7 +51,7 @@ import org.kohsuke.stapler.QueryParameter;
      *   
      * <h2>Views</h2>
      * <p>
    - * This extension point must have a valid <tt>config.jelly</tt> that feeds the constructor.
    + * This extension point must have a valid {@code config.jelly} that feeds the constructor.
      *
      * TODO: allow {@link MarkupFormatter} to control the UI that the user uses to edit.
      *
    diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java
    index 5e66617e27087c13ad453a3b3de0087ed78a2921..2268f3dd9967c69df673fede3e6bf159c59a28dd 100644
    --- a/core/src/main/java/hudson/model/AbstractBuild.java
    +++ b/core/src/main/java/hudson/model/AbstractBuild.java
    @@ -297,7 +297,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
         /**
          * Returns the root directory of the checked-out module.
          * <p>
    -     * This is usually where <tt>pom.xml</tt>, <tt>build.xml</tt>
    +     * This is usually where {@code pom.xml}, {@code build.xml}
          * and so on exists.
          */
         public final FilePath getModuleRoot() {
    @@ -803,20 +803,6 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
             }
         }
     
    -    /**
    -     * get the fingerprints associated with this build
    -     *
    -     * @return never null
    -     */
    -    @Exported(name = "fingerprint", inline = true, visibility = -1)
    -    public Collection<Fingerprint> getBuildFingerprints() {
    -        FingerprintAction fingerprintAction = getAction(FingerprintAction.class);
    -        if (fingerprintAction != null) {
    -            return fingerprintAction.getFingerprints().values();
    -        }
    -        return Collections.<Fingerprint>emptyList();
    -    }
    -
     	/*
          * No need to lock the entire AbstractBuild on change set calculation
          */
    diff --git a/core/src/main/java/hudson/model/AbstractCIBase.java b/core/src/main/java/hudson/model/AbstractCIBase.java
    index 1b79051178cfb7a9a4ab5acb562a9c6f6b4eb254..d82afd6488d03d5e52f48abc3347332106d7f3b8 100644
    --- a/core/src/main/java/hudson/model/AbstractCIBase.java
    +++ b/core/src/main/java/hudson/model/AbstractCIBase.java
    @@ -227,8 +227,13 @@ public abstract class AbstractCIBase extends Node implements ItemGroup<TopLevelI
                 killComputer(c);
             }
             getQueue().scheduleMaintenance();
    -        for (ComputerListener cl : ComputerListener.all())
    -            cl.onConfigurationChange();
    +        for (ComputerListener cl : ComputerListener.all()) {
    +            try {
    +                cl.onConfigurationChange();
    +            } catch (Throwable t) {
    +                LOGGER.log(Level.WARNING, null, t);
    +            }
    +        }
         }
     
     }
    diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java
    index e06a3f1988c0b0de87d857c545c7662438641ae5..1374429381f550e0e8bddfa991431c794eb4d56b 100644
    --- a/core/src/main/java/hudson/model/AbstractItem.java
    +++ b/core/src/main/java/hudson/model/AbstractItem.java
    @@ -36,12 +36,14 @@ import hudson.model.listeners.ItemListener;
     import hudson.model.listeners.SaveableListener;
     import hudson.model.queue.Tasks;
     import hudson.model.queue.WorkUnit;
    +import hudson.security.ACLContext;
     import hudson.security.AccessControlled;
     import hudson.security.Permission;
     import hudson.security.ACL;
     import hudson.util.AlternativeUiTextProvider;
     import hudson.util.AlternativeUiTextProvider.Message;
     import hudson.util.AtomicFileWriter;
    +import hudson.util.FormValidation;
     import hudson.util.IOUtils;
     import hudson.util.Secret;
     import java.util.Iterator;
    @@ -56,6 +58,8 @@ import jenkins.util.xml.XMLUtils;
     
     import org.apache.tools.ant.taskdefs.Copy;
     import org.apache.tools.ant.types.FileSet;
    +import org.kohsuke.stapler.HttpResponses;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.WebMethod;
     import org.kohsuke.stapler.export.Exported;
     import org.kohsuke.stapler.export.ExportedBean;
    @@ -72,12 +76,16 @@ import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     import javax.annotation.Nonnull;
     
    +import org.acegisecurity.AccessDeniedException;
    +import org.kohsuke.stapler.HttpResponse;
    +import org.kohsuke.stapler.HttpResponses;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.Stapler;
     import org.kohsuke.stapler.HttpDeletable;
     import org.kohsuke.args4j.Argument;
     import org.kohsuke.args4j.CmdLineException;
    +import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.interceptor.RequirePOST;
     import org.xml.sax.SAXException;
     
    @@ -90,6 +98,8 @@ import javax.xml.transform.stream.StreamSource;
     import static hudson.model.queue.Executables.getParentOf;
     import hudson.model.queue.SubTask;
     import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
    +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
    +
     import org.apache.commons.io.FileUtils;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
    @@ -103,7 +113,7 @@ import org.kohsuke.stapler.Ancestor;
     // Item doesn't necessarily have to be Actionable, but
     // Java doesn't let multiple inheritance.
     @ExportedBean
    -public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled, DescriptorByNameOwner {
    +public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled, DescriptorByNameOwner, StaplerProxy {
     
         private static final Logger LOGGER = Logger.getLogger(AbstractItem.class.getName());
     
    @@ -228,6 +238,114 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
             this.name = name;
         }
     
    +    /**
    +     * Controls whether the default rename action is available for this item.
    +     *
    +     * @return whether {@link #name} can be modified by a user
    +     * @see #checkRename
    +     * @see #renameTo
    +     * @since 2.110
    +     */
    +    public boolean isNameEditable() {
    +        return false;
    +    }
    +
    +    /**
    +     * Renames this item
    +     */
    +    @RequirePOST
    +    @Restricted(NoExternalUse.class)
    +    public HttpResponse doConfirmRename(@QueryParameter String newName) throws IOException {
    +        newName = newName == null ? null : newName.trim();
    +        FormValidation validationError = doCheckNewName(newName);
    +        if (validationError.kind != FormValidation.Kind.OK) {
    +            throw new Failure(validationError.getMessage());
    +        }
    +
    +        renameTo(newName);
    +        // send to the new job page
    +        // note we can't use getUrl() because that would pick up old name in the
    +        // Ancestor.getUrl()
    +        return HttpResponses.redirectTo("../" + newName);
    +    }
    +
    +    /**
    +     * Called by {@link #doConfirmRename} and {@code rename.jelly} to validate renames.
    +     * @return {@link FormValidation#ok} if this item can be renamed as specified, otherwise
    +     * {@link FormValidation#error} with a message explaining the problem.
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public @Nonnull FormValidation doCheckNewName(@QueryParameter String newName) {
    +        // TODO: Create an Item.RENAME permission to use here, see JENKINS-18649.
    +        if (!hasPermission(Item.CONFIGURE)) {
    +            if (parent instanceof AccessControlled) {
    +                ((AccessControlled)parent).checkPermission(Item.CREATE);
    +            }
    +            checkPermission(Item.DELETE);
    +        }
    +
    +        newName = newName == null ? null : newName.trim();
    +        try {
    +            Jenkins.checkGoodName(newName);
    +            assert newName != null; // Would have thrown Failure
    +            if (newName.equals(name)) {
    +                return FormValidation.warning(Messages.AbstractItem_NewNameUnchanged());
    +            }
    +            Jenkins.get().getProjectNamingStrategy().checkName(newName);
    +            checkIfNameIsUsed(newName);
    +            checkRename(newName);
    +        } catch (Failure e) {
    +            return FormValidation.error(e.getMessage());
    +        }
    +        return FormValidation.ok();
    +    }
    +
    +    /**
    +     * Check new name for job
    +     * @param newName - New name for job.
    +     */
    +    private void checkIfNameIsUsed(@Nonnull String newName) throws Failure {
    +        try {
    +            Item item = getParent().getItem(newName);
    +            if (item != null) {
    +                throw new Failure(Messages.AbstractItem_NewNameInUse(newName));
    +            }
    +            try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
    +                item = getParent().getItem(newName);
    +                if (item != null) {
    +                    if (LOGGER.isLoggable(Level.FINE)) {
    +                        LOGGER.log(Level.FINE, "Unable to rename the job {0}: name {1} is already in use. " +
    +                                "User {2} has no {3} permission for existing job with the same name",
    +                                new Object[] {this.getFullName(), newName, ctx.getPreviousContext().getAuthentication().getName(), Item.DISCOVER.name} );
    +                    }
    +                    // Don't explicitly mention that there is another item with the same name.
    +                    throw new Failure(Messages.Jenkins_NotAllowedName(newName));
    +                }
    +            }
    +        } catch(AccessDeniedException ex) {
    +            if (LOGGER.isLoggable(Level.FINE)) {
    +                LOGGER.log(Level.FINE, "Unable to rename the job {0}: name {1} is already in use. " +
    +                        "User {2} has {3} permission, but no {4} for existing job with the same name",
    +                        new Object[] {this.getFullName(), newName, User.current(), Item.DISCOVER.name, Item.READ.name} );
    +            }
    +            throw new Failure(Messages.AbstractItem_NewNameInUse(newName));
    +        }
    +    }
    +
    +    /**
    +     * Allows subclasses to block renames for domain-specific reasons. Generic validation of the new name
    +     * (e.g., null checking, checking for illegal characters, and checking that the name is not in use)
    +     * always happens prior to calling this method.
    +     *
    +     * @param newName the new name for the item
    +     * @throws Failure if the rename should be blocked
    +     * @since 2.110
    +     * @see Job#checkRename
    +     */
    +    protected void checkRename(@Nonnull String newName) throws Failure {
    +
    +    }
    +
         /**
          * Renames this item.
          * Not all the Items need to support this operation, but if you decide to do so,
    @@ -675,7 +793,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
         }
     
         /**
    -     * Accepts <tt>config.xml</tt> submission, as well as serve it.
    +     * Accepts {@code config.xml} submission, as well as serve it.
          */
         @WebMethod(name = "config.xml")
         public void doConfigDotXml(StaplerRequest req, StaplerResponse rsp)
    @@ -819,6 +937,24 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
             return super.toString() + '[' + (parent != null ? getFullName() : "?/" + name) + ']';
         }
     
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            if (!getACL().hasPermission(Item.DISCOVER)) {
    +                return null;
    +            }
    +            getACL().checkPermission(Item.READ);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(AbstractItem.class.getName() + ".skipPermissionCheck");
    +
         /**
          * Used for CLI binding.
          */
    diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
    index cbade0ebac492f76833ae1dce0c7117eb7bbe736..3b60e8618519d41c5dc5b6a23a357377296d1d6a 100644
    --- a/core/src/main/java/hudson/model/AbstractProject.java
    +++ b/core/src/main/java/hudson/model/AbstractProject.java
    @@ -573,7 +573,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
         /**
          * Returns the root directory of the checked-out module.
          * <p>
    -     * This is usually where <tt>pom.xml</tt>, <tt>build.xml</tt>
    +     * This is usually where {@code pom.xml}, {@code build.xml}
          * and so on exists.
          *
          * @deprecated as of 1.319
    @@ -642,7 +642,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
         }
     
         /**
    -     * Used in <tt>sidepanel.jelly</tt> to decide whether to display
    +     * Used in {@code sidepanel.jelly} to decide whether to display
          * the config/delete/build links.
          */
         public boolean isConfigurable() {
    @@ -751,7 +751,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
          * Returns the live list of all {@link Publisher}s configured for this project.
          *
          * <p>
    -     * This method couldn't be called <tt>getPublishers()</tt> because existing methods
    +     * This method couldn't be called {@code getPublishers()} because existing methods
          * in sub-classes return different inconsistent types.
          */
         public abstract DescribableList<Publisher,Descriptor<Publisher>> getPublishersList();
    @@ -1198,7 +1198,12 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
                 return true;    // no SCM
     
             FilePath workspace = build.getWorkspace();
    -        workspace.mkdirs();
    +        if(workspace!=null){
    +            workspace.mkdirs();
    +        } else {
    +            throw new AbortException("Cannot checkout SCM, workspace is not defined");
    +        }
    +
     
             boolean r = scm.checkout(build, launcher, workspace, listener, changelogFile);
             if (r) {
    diff --git a/core/src/main/java/hudson/model/Action.java b/core/src/main/java/hudson/model/Action.java
    index ac7be33a6091c7f9c14d3a02bab31ce7f88c0b45..72d2f84e065afe93aaefceb0f93b530f960c95f3 100644
    --- a/core/src/main/java/hudson/model/Action.java
    +++ b/core/src/main/java/hudson/model/Action.java
    @@ -38,12 +38,12 @@ import javax.annotation.CheckForNull;
      *
      * <p>
      * Some actions use the latter without the former (for example, to add a link to an external website),
    - * while others do the former without the latter (for example, to just draw some graphs in <tt>floatingBox.jelly</tt>),
    + * while others do the former without the latter (for example, to just draw some graphs in {@code floatingBox.jelly}),
      * and still some others do both.
      *
      * <h2>Views</h2>
      * <p>
    - * If an action has a view named <tt>floatingBox.jelly</tt>,
    + * If an action has a view named {@code floatingBox.jelly},
      * it will be displayed as a floating box on the top page of
      * the target {@link ModelObject}. (For example, this is how
      * the JUnit test result trend shows up in the project top page.
    @@ -82,7 +82,7 @@ public interface Action extends ModelObject {
          *
          * @return
          *      If just a file name (like "abc.gif") is returned, it will be
    -     *      interpreted as a file name inside <tt>/images/24x24</tt>.
    +     *      interpreted as a file name inside {@code /images/24x24}.
          *      This is useful for using one of the stock images.
          *      <p>
          *      If an absolute file name that starts from '/' is returned (like
    @@ -91,7 +91,7 @@ public interface Action extends ModelObject {
          *      image files from a plugin.
          *      <p>
          *      Finally, return null to hide it from the task list. This is normally not very useful,
    -     *      but this can be used for actions that only contribute <tt>floatBox.jelly</tt>
    +     *      but this can be used for actions that only contribute {@code floatBox.jelly}
          *      and no task list item. The other case where this is useful is
          *      to avoid showing links that require a privilege when the user is anonymous.
          * @see Functions#isAnonymous()
    diff --git a/core/src/main/java/hudson/model/AdministrativeMonitor.java b/core/src/main/java/hudson/model/AdministrativeMonitor.java
    index 537f6d092ce63dd90cac459c1efb2da031ca2f71..16b3fbb69774c938aaf531c3d3cc53df353d5ab3 100644
    --- a/core/src/main/java/hudson/model/AdministrativeMonitor.java
    +++ b/core/src/main/java/hudson/model/AdministrativeMonitor.java
    @@ -65,10 +65,10 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
      * <dl>
      * <dt>message.jelly</dt>
      * <dd>
    - * If {@link #isActivated()} returns true, Jenkins will use the <tt>message.jelly</tt>
    + * If {@link #isActivated()} returns true, Jenkins will use the {@code message.jelly}
      * view of this object to render the warning text. This happens in the
    - * <tt>http://SERVER/jenkins/manage</tt> page. This view should typically render
    - * a DIV box with class='error' or class='warning' with a human-readable text
    + * {@code http://SERVER/jenkins/manage} page. This view should typically render
    + * a DIV box with class='alert alert-error' or class='alert alert-warning' with a human-readable text
      * inside it. It often also contains a link to a page that provides more details
      * about the problem.
      * </dd>
    diff --git a/core/src/main/java/hudson/model/AperiodicWork.java b/core/src/main/java/hudson/model/AperiodicWork.java
    index fbd4f2c4f7a3da987d57e4f61168d453647ff667..3c5385c07791a0c547edd8611c3038014763f137 100644
    --- a/core/src/main/java/hudson/model/AperiodicWork.java
    +++ b/core/src/main/java/hudson/model/AperiodicWork.java
    @@ -24,12 +24,15 @@
     package hudson.model;
     
     import hudson.ExtensionList;
    +import hudson.ExtensionListListener;
     import hudson.ExtensionPoint;
     import hudson.init.Initializer;
     import hudson.triggers.SafeTimerTask;
     import jenkins.util.Timer;
     
    +import java.util.HashSet;
     import java.util.Random;
    +import java.util.Set;
     import java.util.concurrent.TimeUnit;
     import java.util.logging.Logger;
     
    @@ -92,11 +95,17 @@ public abstract class AperiodicWork extends SafeTimerTask implements ExtensionPo
         @Initializer(after= JOB_LOADED)
         public static void init() {
             // start all AperidocWorks
    +        ExtensionList<AperiodicWork> extensionList = all();
    +        extensionList.addListener(new AperiodicWorkExtensionListListener(extensionList));
             for (AperiodicWork p : AperiodicWork.all()) {
    -            Timer.get().schedule(p, p.getInitialDelay(), TimeUnit.MILLISECONDS);
    +            scheduleAperiodWork(p);
             }
         }
     
    +    private static void scheduleAperiodWork(AperiodicWork ap) {
    +        Timer.get().schedule(ap, ap.getInitialDelay(), TimeUnit.MILLISECONDS);
    +    }
    +
         protected abstract void doAperiodicRun();
         
         /**
    @@ -107,4 +116,31 @@ public abstract class AperiodicWork extends SafeTimerTask implements ExtensionPo
         }
     
         private static final Random RANDOM = new Random();
    +
    +    /**
    +     * ExtensionListener that will kick off any new AperiodWork extensions from plugins that are dynamically
    +     * loaded.
    +     */
    +    private static class AperiodicWorkExtensionListListener extends ExtensionListListener {
    +
    +        private final Set<AperiodicWork> registered = new HashSet<>();
    +
    +        AperiodicWorkExtensionListListener(ExtensionList<AperiodicWork> initiallyRegistered) {
    +            for (AperiodicWork p : initiallyRegistered) {
    +                registered.add(p);
    +            }
    +        }
    +
    +        @Override
    +        public void onChange() {
    +            synchronized (registered) {
    +                for (AperiodicWork p : AperiodicWork.all()) {
    +                    if (!registered.contains(p)) {
    +                        scheduleAperiodWork(p);
    +                        registered.add(p);
    +                    }
    +                }
    +            }
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/model/Api.java b/core/src/main/java/hudson/model/Api.java
    index eef109f92f41e6087842a2a4b3e4964a9406cd2d..024d66869d4591afe4d28a31c5294e00b2fa8032 100644
    --- a/core/src/main/java/hudson/model/Api.java
    +++ b/core/src/main/java/hudson/model/Api.java
    @@ -60,7 +60,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
      * Used to expose remote access API for ".../api/"
      *
      * <p>
    - * If the parent object has a <tt>_api.jelly</tt> view, it will be included
    + * If the parent object has a {@code _api.jelly} view, it will be included
      * in the api index page.
      *
      * @author Kohsuke Kawaguchi
    @@ -135,7 +135,19 @@ public class Api extends AbstractModelObject {
                     XPath comp = dom.createXPath(xpath);
                     comp.setFunctionContext(functionContext);
                     List list = comp.selectNodes(dom);
    +
                     if (wrapper!=null) {
    +                    // check if the wrapper is a valid entity name
    +                    // First position:  letter or underscore
    +                    // Other positions: \w (letter, number, underscore), dash or dot
    +                    String validNameRE = "^[a-zA-Z_][\\w-\\.]*$";
    +
    +                    if(!wrapper.matches(validNameRE)) {
    +                        rsp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    +                        rsp.getWriter().print(Messages.Api_WrapperParamInvalid());
    +                        return;
    +                    }
    +
                         Element root = DocumentFactory.getInstance().createElement(wrapper);
                         for (Object o : list) {
                             if (o instanceof String) {
    diff --git a/core/src/main/java/hudson/model/AsyncAperiodicWork.java b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
    index 1d295fa7161db0abcdaffbb957773954b2ec7307..6e720c1419da95663cc16512711c35d6e9ee0486 100644
    --- a/core/src/main/java/hudson/model/AsyncAperiodicWork.java
    +++ b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
    @@ -200,7 +200,7 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
          * Determines the log file that records the result of this task.
          */
         protected File getLogFile() {
    -        return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
    +        return new File(getLogsRoot(), "/tasks/" + name + ".log");
         }
     
         /**
    diff --git a/core/src/main/java/hudson/model/AsyncPeriodicWork.java b/core/src/main/java/hudson/model/AsyncPeriodicWork.java
    index f3ffcc64976a4fa0cbe9892f8c19a3d21a364c1b..324d1853eedc66e2b7425204d1d8b8c0f6630e3d 100644
    --- a/core/src/main/java/hudson/model/AsyncPeriodicWork.java
    +++ b/core/src/main/java/hudson/model/AsyncPeriodicWork.java
    @@ -183,7 +183,7 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
          * Determines the log file that records the result of this task.
          */
         protected File getLogFile() {
    -        return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
    +        return new File(getLogsRoot(), "/tasks/" + name + ".log");
         }
         
         /**
    diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java
    index 0373b552bb007f560a566f63573db9ce683296b2..29ce16f4ca59e486771629b9ac0f82b9064d5ab7 100644
    --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java
    +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java
    @@ -25,6 +25,7 @@
     package hudson.model;
     
     import hudson.search.Search;
    +import hudson.search.UserSearchProperty;
     import jenkins.model.Jenkins;
     import org.kohsuke.stapler.HttpResponse;
     import org.kohsuke.stapler.StaplerRequest;
    @@ -37,6 +38,7 @@ import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.List;
     import javax.annotation.CheckForNull;
    +import org.apache.commons.lang.StringUtils;
     
     /**
      * Data representation of the auto-completion candidates.
    @@ -117,22 +119,28 @@ public class AutoCompletionCandidates implements HttpResponse {
     
                 @Override
                 public void onItem(Item i) {
    -                String n = contextualNameOf(i);
    -                if ((n.startsWith(value) || value.startsWith(n))
    +                String itemName = contextualNameOf(i);
    +                
    +                //Check user's setting on whether to do case sensitive comparison, configured in user -> configure
    +                //This is the same setting that is used by the global search field, should be consistent throughout
    +                //the whole application.
    +                boolean caseInsensitive = UserSearchProperty.isCaseInsensitive();
    +
    +                if ((startsWithImpl(itemName, value, caseInsensitive) || startsWithImpl(value, itemName, caseInsensitive))
                         // 'foobar' is a valid candidate if the current value is 'foo'.
                         // Also, we need to visit 'foo' if the current value is 'foo/bar'
    -                 && (value.length()>n.length() || !n.substring(value.length()).contains("/"))
    +                 && (value.length()> itemName.length() || !itemName.substring(value.length()).contains("/"))
                         // but 'foobar/zot' isn't if the current value is 'foo'
                         // we'll first show 'foobar' and then wait for the user to type '/' to show the rest
                      && i.hasPermission(Item.READ)
                         // and read permission required
                     ) {
    -                    if (type.isInstance(i) && n.startsWith(value))
    -                        candidates.add(n);
    +                    if (type.isInstance(i) && startsWithImpl(itemName, value, caseInsensitive))
    +                        candidates.add(itemName);
     
                         // recurse
                         String oldPrefix = prefix;
    -                    prefix = n;
    +                    prefix = itemName;
                         super.onItem(i);
                         prefix = oldPrefix;
                     }
    @@ -161,4 +169,8 @@ public class AutoCompletionCandidates implements HttpResponse {
     
             return candidates;
         }
    +
    +    private static boolean startsWithImpl(String str, String prefix, boolean ignoreCase) {
    +        return ignoreCase ? StringUtils.startsWithIgnoreCase(str, prefix) : str.startsWith(prefix);
    +    }
     }
    diff --git a/core/src/main/java/hudson/model/BuildBadgeAction.java b/core/src/main/java/hudson/model/BuildBadgeAction.java
    index fe35eddd6052cea7c00489eea24e502c4a78378d..3a673f95583521e29e904342ca2a09c148e55cd2 100644
    --- a/core/src/main/java/hudson/model/BuildBadgeAction.java
    +++ b/core/src/main/java/hudson/model/BuildBadgeAction.java
    @@ -32,7 +32,7 @@ package hudson.model;
      * with {@link Run}. 
      *
      * <p>
    - * Actions with this marker should have a view <tt>badge.jelly</tt>,
    + * Actions with this marker should have a view {@code badge.jelly},
      * which will be called to render the badges. The expected visual appearance
      * of a badge is a 16x16 icon.
      *
    diff --git a/core/src/main/java/hudson/model/BuildTimelineWidget.java b/core/src/main/java/hudson/model/BuildTimelineWidget.java
    index 31810b5f5e939e8409eb12422b150b3bffb2d91b..717052e5bf9e17d746b63ed15100478c97c35c5c 100644
    --- a/core/src/main/java/hudson/model/BuildTimelineWidget.java
    +++ b/core/src/main/java/hudson/model/BuildTimelineWidget.java
    @@ -23,6 +23,7 @@
      */
     package hudson.model;
     
    +import hudson.Util;
     import hudson.util.RunList;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
    @@ -64,7 +65,9 @@ public class BuildTimelineWidget {
                 Event e = new Event();
                 e.start = new Date(r.getStartTimeInMillis());
                 e.end   = new Date(r.getStartTimeInMillis()+r.getDuration());
    -            e.title = r.getFullDisplayName();
    +            // due to SimileAjax.HTML.deEntify (in simile-ajax-bundle.js), "&lt;" are transformed back to "<", but not the "&#60";
    +            // to protect against XSS
    +            e.title = Util.escape(r.getFullDisplayName()).replace("&lt;", "&#60;");
                 // what to put in the description?
                 // e.description = "Longish description of event "+r.getFullDisplayName();
                 // e.durationEvent = true;
    diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java
    index bdac357b6b45231e238277ebec36420cd2fb02ed..c0286bc55a8556d88df25c7db3e968938c2ce292 100644
    --- a/core/src/main/java/hudson/model/Cause.java
    +++ b/core/src/main/java/hudson/model/Cause.java
    @@ -453,9 +453,14 @@ public abstract class Cause {
     
             @Override
             public void print(TaskListener listener) {
    -            listener.getLogger().println(Messages.Cause_UserIdCause_ShortDescription(
    -                    // TODO JENKINS-48467 - better to use ModelHyperlinkNote.encodeTo(User), or User.getUrl, since it handles URL escaping
    -                    ModelHyperlinkNote.encodeTo("/user/"+getUserIdOrUnknown(), getUserName())));
    +            User user = getUserId() == null ? null : User.getById(getUserId(), false);
    +            if (user != null) {
    +                listener.getLogger().println(Messages.Cause_UserIdCause_ShortDescription(
    +                        ModelHyperlinkNote.encodeTo(user)));
    +            } else {
    +                listener.getLogger().println(Messages.Cause_UserIdCause_ShortDescription(
    +                        "unknown or anonymous"));
    +            }
             }
     
             @Override
    diff --git a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
    index f175bd001bd0fe08228eb0266fd764c3e4702f32..9ea14153624d8aa3d1d13b2728dec4ccd274a2d6 100644
    --- a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
    +++ b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
    @@ -2,6 +2,9 @@ package hudson.model;
     
     import hudson.util.FormValidation;
     import org.jenkinsci.Symbol;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
    +import org.kohsuke.stapler.DataBoundSetter;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.DataBoundConstructor;
    @@ -10,6 +13,8 @@ import org.apache.commons.lang.StringUtils;
     import net.sf.json.JSONObject;
     import hudson.Extension;
     
    +import javax.annotation.Nonnull;
    +import javax.annotation.Nullable;
     import java.util.ArrayList;
     import java.util.List;
     import java.util.Arrays;
    @@ -24,7 +29,7 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition {
         public static final String CHOICES_DELIMETER = CHOICES_DELIMITER;
     
     
    -    private final List<String> choices;
    +    private /* quasi-final */ List<String> choices;
         private final String defaultValue;
     
         public static boolean areValidChoices(String choices) {
    @@ -32,10 +37,9 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition {
             return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMITER).length > 0;
         }
     
    -    @DataBoundConstructor
         public ChoiceParameterDefinition(String name, String choices, String description) {
             super(name, description);
    -        this.choices = Arrays.asList(choices.split(CHOICES_DELIMITER));
    +        setChoicesText(choices);
             defaultValue = null;
         }
     
    @@ -51,6 +55,60 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition {
             this.defaultValue = defaultValue;
         }
     
    +    /**
    +     * Databound constructor for reflective instantiation.
    +     *
    +     * @param name parameter name
    +     * @param description parameter description
    +     *
    +     * @since 2.112
    +     */
    +    @DataBoundConstructor
    +    @Restricted(NoExternalUse.class) // there are specific constructors with String and List arguments for 'choices'
    +    public ChoiceParameterDefinition(String name, String description) {
    +        super(name, description);
    +        this.choices = new ArrayList<>();
    +        this.defaultValue = null;
    +    }
    +
    +    /**
    +     * Set the list of choices. Legal arguments are String (in which case the arguments gets split into lines) and Collection
    +     * which sets the list of legal parameters to the String representations of the argument's non-null entries.
    +     *
    +     * See JENKINS-26143 for background.
    +     *
    +     * This retains the compatibility with the legacy String 'choices' parameter, while supporting the list type as generated
    +     * by the snippet generator.
    +     *
    +     * @param choices String or Collection representing this parameter definition's possible values.
    +     *
    +     * @since 2.112
    +     *
    +     */
    +    @DataBoundSetter
    +    @Restricted(NoExternalUse.class) // this is terrible enough without being used anywhere
    +    public void setChoices(Object choices) {
    +        if (choices instanceof String) {
    +            setChoicesText((String) choices);
    +            return;
    +        }
    +        if (choices instanceof List) {
    +            ArrayList<String> newChoices = new ArrayList<>();
    +            for (Object o : (List) choices) {
    +                if (o != null) {
    +                    newChoices.add(o.toString());
    +                }
    +            }
    +            this.choices = newChoices;
    +            return;
    +        }
    +        throw new IllegalArgumentException("expected String or List, but got " + choices.getClass().getName());
    +    }
    +
    +    private void setChoicesText(String choices) {
    +        this.choices = Arrays.asList(choices.split(CHOICES_DELIMITER));
    +    }
    +
         @Override
         public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) {
             if (defaultValue instanceof StringParameterValue) {
    @@ -104,6 +162,17 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition {
                 return "/help/parameter/choice.html";
             }
     
    +        @Override
    +        /*
    +         * We need this for JENKINS-26143 -- reflective creation cannot handle setChoices(Object). See that method for context.
    +         */
    +        public ParameterDefinition newInstance(@Nullable StaplerRequest req, @Nonnull JSONObject formData) throws FormException {
    +            String name = formData.getString("name");
    +            String desc = formData.getString("description");
    +            String choiceText = formData.getString("choices");
    +            return new ChoiceParameterDefinition(name, choiceText, desc);
    +        }
    +
             /**
              * Checks if parameterized build choices are valid.
              */
    diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java
    index 43fa2d31d707140d8cfdd5d3ecfcbaae99e84b3c..641d82fc27ea758fd2087abd86a19205adff31f7 100644
    --- a/core/src/main/java/hudson/model/Computer.java
    +++ b/core/src/main/java/hudson/model/Computer.java
    @@ -30,7 +30,6 @@ import hudson.EnvVars;
     import hudson.Extension;
     import hudson.Launcher.ProcStarter;
     import hudson.slaves.Cloud;
    -import jenkins.util.SystemProperties;
     import hudson.Util;
     import hudson.cli.declarative.CLIResolver;
     import hudson.console.AnnotatedLargeText;
    @@ -65,8 +64,11 @@ import hudson.util.Futures;
     import hudson.util.NamingThreadFactory;
     import jenkins.model.Jenkins;
     import jenkins.util.ContextResettingExecutorService;
    +import jenkins.util.SystemProperties;
     import jenkins.security.MasterToSlaveCallable;
    +import jenkins.security.ImpersonatingExecutorService;
     
    +import org.apache.commons.lang.StringUtils;
     import org.jenkins.ui.icon.Icon;
     import org.jenkins.ui.icon.IconSet;
     import org.kohsuke.accmod.Restricted;
    @@ -323,6 +325,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
          * Used to URL-bind {@link AnnotatedLargeText}.
          */
         public AnnotatedLargeText<Computer> getLogText() {
    +        checkPermission(CONNECT);
             return new AnnotatedLargeText<Computer>(getLogFile(), Charset.defaultCharset(), false, this);
         }
     
    @@ -903,6 +906,9 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
         }
     
         private void addNewExecutorIfNecessary() {
    +        if (Jenkins.getInstanceOrNull() == null) {
    +            return;
    +        }
             Set<Integer> availableNumbers  = new HashSet<Integer>();
             for (int i = 0; i < numExecutors; i++)
                 availableNumbers.add(i);
    @@ -1351,9 +1357,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
         }
     
         public static final ExecutorService threadPoolForRemoting = new ContextResettingExecutorService(
    +        new ImpersonatingExecutorService(
                 Executors.newCachedThreadPool(
    -                    new ExceptionCatchingThreadFactory(
    -                            new NamingThreadFactory(new DaemonThreadFactory(), "Computer.threadPoolForRemoting"))));
    +                new ExceptionCatchingThreadFactory(
    +                    new NamingThreadFactory(
    +                        new DaemonThreadFactory(), "Computer.threadPoolForRemoting"))), ACL.SYSTEM));
     
     //
     //
    @@ -1472,6 +1480,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
                 throw new FormException(Messages.ComputerSet_SlaveAlreadyExists(proposedName), "name");
             }
     
    +        String nExecutors = req.getSubmittedForm().getString("numExecutors");
    +        if (StringUtils.isBlank(nExecutors) || Integer.parseInt(nExecutors)<=0) {
    +            throw new FormException(Messages.Slave_InvalidConfig_Executors(nodeName), "numExecutors");
    +        }
    +
             Node result = node.reconfigure(req, req.getSubmittedForm());
             Jenkins.getInstance().getNodesObject().replaceNode(this.getNode(), result);
     
    @@ -1480,7 +1493,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
         }
     
         /**
    -     * Accepts <tt>config.xml</tt> submission, as well as serve it.
    +     * Accepts {@code config.xml} submission, as well as serve it.
          */
         @WebMethod(name = "config.xml")
         public void doConfigDotXml(StaplerRequest req, StaplerResponse rsp)
    diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java
    index fa3511a5f68a18b9b63f4f5377c9ea8c0536bfb1..b7fc8097e8950523b3bd10cced0d698a5aea7dd4 100644
    --- a/core/src/main/java/hudson/model/Descriptor.java
    +++ b/core/src/main/java/hudson/model/Descriptor.java
    @@ -39,6 +39,7 @@ import hudson.views.ListViewColumn;
     import jenkins.model.GlobalConfiguration;
     import jenkins.model.GlobalConfigurationCategory;
     import jenkins.model.Jenkins;
    +import jenkins.security.RedactSecretJsonInErrorMessageSanitizer;
     import jenkins.util.io.OnMaster;
     import net.sf.json.JSONArray;
     import net.sf.json.JSONObject;
    @@ -51,6 +52,8 @@ import org.apache.commons.io.IOUtils;
     
     import static hudson.util.QuotedStringTokenizer.*;
     import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
    +
    +import javax.annotation.PostConstruct;
     import javax.servlet.ServletException;
     import javax.servlet.RequestDispatcher;
     import java.io.File;
    @@ -117,6 +120,8 @@ import javax.annotation.Nullable;
      * {@link Descriptor} can persist data just by storing them in fields.
      * However, it is the responsibility of the derived type to properly
      * invoke {@link #save()} and {@link #load()}.
    + * {@link #load()} is automatically invoked as a JSR-250 lifecycle method if derived class
    + * do implement {@link PersistentDescriptor}.
      *
      * <h2>Reflection Enhancement</h2>
      * {@link Descriptor} defines addition to the standard Java reflection
    @@ -133,7 +138,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
          */
         public transient final Class<? extends T> clazz;
     
    -    private transient final Map<String,CheckMethod> checkMethods = new ConcurrentHashMap<String,CheckMethod>();
    +    private transient final Map<String,CheckMethod> checkMethods = new ConcurrentHashMap<String,CheckMethod>(2);
     
         /**
          * Lazily computed list of properties on {@link #clazz} and on the descriptor itself.
    @@ -229,7 +234,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
          *
          * @see #getHelpFile(String) 
          */
    -    private transient final Map<String,HelpRedirect> helpRedirect = new HashMap<String,HelpRedirect>();
    +    private transient final Map<String,HelpRedirect> helpRedirect = new HashMap<String,HelpRedirect>(2);
     
         private static class HelpRedirect {
             private final Class<? extends Describable> owner;
    @@ -534,7 +539,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
          * Creates a configured instance from the submitted form.
          *
          * <p>
    -     * Hudson only invokes this method when the user wants an instance of <tt>T</tt>.
    +     * Hudson only invokes this method when the user wants an instance of {@code T}.
          * So there's no need to check that in the implementation.
          *
          * <p>
    @@ -597,7 +602,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
             } catch (NoSuchMethodException e) {
                 throw new AssertionError(e); // impossible
             } catch (InstantiationException | IllegalAccessException | RuntimeException e) {
    -            throw new Error("Failed to instantiate "+clazz+" from "+formData,e);
    +            throw new Error("Failed to instantiate "+clazz+" from "+RedactSecretJsonInErrorMessageSanitizer.INSTANCE.sanitize(formData),e);
             }
         }
     
    @@ -710,9 +715,9 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
          *
          * <p>
          * This value is relative to the context root of Hudson, so normally
    -     * the values are something like <tt>"/plugin/emma/help.html"</tt> to
    -     * refer to static resource files in a plugin, or <tt>"/publisher/EmmaPublisher/abc"</tt>
    -     * to refer to Jelly script <tt>abc.jelly</tt> or a method <tt>EmmaPublisher.doAbc()</tt>.
    +     * the values are something like {@code "/plugin/emma/help.html"} to
    +     * refer to static resource files in a plugin, or {@code "/publisher/EmmaPublisher/abc"}
    +     * to refer to Jelly script {@code abc.jelly} or a method {@code EmmaPublisher.doAbc()}.
          *
          * @return
          *      null to indicate that there's no help.
    @@ -821,7 +826,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
          *
          * @since 2.0, used to be in {@link GlobalConfiguration} before that.
          */
    -    public GlobalConfigurationCategory getCategory() {
    +    public @Nonnull GlobalConfigurationCategory getCategory() {
             return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class);
         }
     
    @@ -911,7 +916,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
         }
     
         /**
    -     * Serves <tt>help.html</tt> from the resource of {@link #clazz}.
    +     * Serves {@code help.html} from the resource of {@link #clazz}.
          */
         public void doHelp(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
             String path = req.getRestOfPath();
    diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
    index 5426c7842e115bc9e947c1c243b849412ae6ff6a..e89f28c8a5785c38230cf04b03fb1c0df701566d 100644
    --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
    +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
    @@ -29,11 +29,15 @@ import java.io.IOException;
     import java.io.InputStream;
     import java.io.OutputStream;
     import java.io.Serializable;
    +import java.net.URL;
     import java.text.Collator;
     import java.util.ArrayList;
     import java.util.Arrays;
    +import java.util.Calendar;
    +import java.util.Collection;
     import java.util.Collections;
     import java.util.Comparator;
    +import java.util.GregorianCalendar;
     import java.util.List;
     import java.util.Locale;
     import java.util.StringTokenizer;
    @@ -51,6 +55,7 @@ import org.apache.tools.zip.ZipOutputStream;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.HttpResponse;
    +import org.kohsuke.stapler.HttpResponses;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     
    @@ -213,7 +218,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
             String rest = _rest.toString();
     
             // this is the base file/directory
    -        VirtualFile baseFile = root.child(base);
    +        VirtualFile baseFile = base.isEmpty() ? root : root.child(base);
     
             if(baseFile.isDirectory()) {
                 if(zip) {
    @@ -295,6 +300,14 @@ public final class DirectoryBrowserSupport implements HttpResponse {
                 return;
             }
     
    +        URL external = baseFile.toExternalURL();
    +        if (external != null) {
    +            // or this URL could be emitted directly from dir.jelly
    +            // though we would prefer to delay toExternalURL calls unless and until needed
    +            rsp.sendRedirect2(external.toExternalForm());
    +            return;
    +        }
    +
             long lastModified = baseFile.lastModified();
             long length = baseFile.length();
     
    @@ -338,7 +351,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
             int current=1;
             while(tokens.hasMoreTokens()) {
                 String token = tokens.nextToken();
    -            r.add(new Path(createBackRef(total-current+restSize),token,true,0, true));
    +            r.add(new Path(createBackRef(total-current+restSize),token,true,0, true,0));
                 current++;
             }
             return r;
    @@ -355,7 +368,8 @@ public final class DirectoryBrowserSupport implements HttpResponse {
         private static void zip(OutputStream outputStream, VirtualFile dir, String glob) throws IOException {
             try (ZipOutputStream zos = new ZipOutputStream(outputStream)) {
                 zos.setEncoding(System.getProperty("file.encoding")); // TODO JENKINS-20663 make this overridable via query parameter
    -            for (String n : dir.list(glob.length() == 0 ? "**" : glob)) {
    +            // TODO consider using run(Callable) here
    +            for (String n : dir.list(glob.isEmpty() ? "**" : glob, null, /* TODO what is the user expectation? */true)) {
                     String relativePath;
                     if (glob.length() == 0) {
                         // JENKINS-19947: traditional behavior is to prepend the directory name
    @@ -404,12 +418,26 @@ public final class DirectoryBrowserSupport implements HttpResponse {
              */
             private final boolean isReadable;
     
    +       /**
    +        * For a file, the last modified timestamp.
    +        */
    +        private final long lastModified;
    +
    +        /**
    +         * @deprecated Use {@link #Path(String, String, boolean, long, boolean, long)}
    +         */
    +        @Deprecated
             public Path(String href, String title, boolean isFolder, long size, boolean isReadable) {
    +            this(href, title, isFolder, size, isReadable, 0L);
    +        }
    +
    +        public Path(String href, String title, boolean isFolder, long size, boolean isReadable, long lastModified) {
                 this.href = href;
                 this.title = title;
                 this.isFolder = isFolder;
                 this.size = size;
                 this.isReadable = isReadable;
    +            this.lastModified = lastModified;
             }
     
             public boolean isFolder() {
    @@ -446,6 +474,29 @@ public final class DirectoryBrowserSupport implements HttpResponse {
                 return size;
             }
     
    +        /**
    +         *
    +         * @return A long value representing the time the file was last modified, measured in milliseconds since
    +         * the epoch (00:00:00 GMT, January 1, 1970), or 0L if is not possible to obtain the times.
    +         * @since 2.127
    +         */
    +        public long getLastModified() {
    +            return lastModified;
    +        }
    +
    +        /**
    +         *
    +         * @return A Calendar representing the time the file was last modified, it lastModified is 0L
    +         * it will return 00:00:00 GMT, January 1, 1970.
    +         * @since 2.127
    +         */
    +        @Restricted(NoExternalUse.class)
    +        public Calendar getLastModifiedAsCalendar() {
    +            final Calendar cal = new GregorianCalendar();
    +            cal.setTimeInMillis(lastModified);
    +            return cal;
    +        }
    +
             private static final long serialVersionUID = 1L;
         }
     
    @@ -499,7 +550,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
                     Arrays.sort(files,new FileComparator(locale));
         
                     for( VirtualFile f : files ) {
    -                    Path p = new Path(Util.rawEncode(f.getName()), f.getName(), f.isDirectory(), f.length(), f.canRead());
    +                    Path p = new Path(Util.rawEncode(f.getName()), f.getName(), f.isDirectory(), f.length(), f.canRead(), f.lastModified());
                         if(!f.isDirectory()) {
                             r.add(Collections.singletonList(p));
                         } else {
    @@ -520,7 +571,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
                                     break;
                                 f = sub.get(0);
                                 relPath += '/'+Util.rawEncode(f.getName());
    -                            l.add(new Path(relPath,f.getName(),true,0, f.canRead()));
    +                            l.add(new Path(relPath,f.getName(),true, f.length(), f.canRead(), f.lastModified()));
                             }
                             r.add(l);
                         }
    @@ -535,10 +586,10 @@ public final class DirectoryBrowserSupport implements HttpResponse {
          * @param baseRef String like "../../../" that cancels the 'rest' portion. Can be "./"
          */
         private static List<List<Path>> patternScan(VirtualFile baseDir, String pattern, String baseRef) throws IOException {
    -            String[] files = baseDir.list(pattern);
    +            Collection<String> files = baseDir.list(pattern, null, /* TODO what is the user expectation? */true);
     
    -            if (files.length > 0) {
    -                List<List<Path>> r = new ArrayList<List<Path>>(files.length);
    +            if (!files.isEmpty()) {
    +                List<List<Path>> r = new ArrayList<List<Path>>(files.size());
                     for (String match : files) {
                         List<Path> file = buildPathList(baseDir, baseDir.child(match), baseRef);
                         r.add(file);
    @@ -574,7 +625,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
                     href.append("/");
                 }
     
    -            Path path = new Path(href.toString(), filePath.getName(), filePath.isDirectory(), filePath.length(), filePath.canRead());
    +            Path path = new Path(href.toString(), filePath.getName(), filePath.isDirectory(), filePath.length(), filePath.canRead(), filePath.lastModified());
                 pathList.add(path);
             }
     
    diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java
    index e20f9a1eeec69b4d3fd45c9b1a5c519cae2f92ee..5f90ad1bfcf37b9db0046b5be6f48f453052fa92 100644
    --- a/core/src/main/java/hudson/model/Executor.java
    +++ b/core/src/main/java/hudson/model/Executor.java
    @@ -144,7 +144,7 @@ public class Executor extends Thread implements ModelObject {
         public Executor(@Nonnull Computer owner, int n) {
             super("Executor #"+n+" for "+owner.getDisplayName());
             this.owner = owner;
    -        this.queue = Jenkins.getInstance().getQueue();
    +        this.queue = Jenkins.get().getQueue();
             this.number = n;
         }
     
    @@ -431,11 +431,12 @@ public class Executor extends Thread implements ModelObject {
                 } catch (AsynchronousExecution x) {
                     lock.writeLock().lock();
                     try {
    -                    x.setExecutor(this);
    +                    x.setExecutorWithoutCompleting(this);
                         this.asynchronousExecution = x;
                     } finally {
                         lock.writeLock().unlock();
                     }
    +                x.maybeComplete();
                 } catch (Throwable e) {
                     problems = e;
                 } finally {
    @@ -459,7 +460,6 @@ public class Executor extends Thread implements ModelObject {
                 if (asynchronousExecution == null) {
                     finish2();
                 }
    -            executableEstimatedDuration = DEFAULT_ESTIMATED_DURATION;
             }
         }
     
    @@ -490,6 +490,7 @@ public class Executor extends Thread implements ModelObject {
             if (this instanceof OneOffExecutor) {
                 owner.remove((OneOffExecutor) this);
             }
    +        executableEstimatedDuration = DEFAULT_ESTIMATED_DURATION;
             queue.scheduleMaintenance();
         }
     
    @@ -577,7 +578,7 @@ public class Executor extends Thread implements ModelObject {
         }
     
         /**
    -     * Same as {@link #getName()}.
    +     * Human readable name of the Jenkins executor. For the Java thread name use {@link #getName()}.
          */
         public String getDisplayName() {
             return "Executor #"+getNumber();
    diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java
    index 85bfb043874998fc2e743a8019a4a062e186e599..f911927285ae55af04f93d234a7d2e709de54a6f 100644
    --- a/core/src/main/java/hudson/model/FileParameterValue.java
    +++ b/core/src/main/java/hudson/model/FileParameterValue.java
    @@ -36,6 +36,7 @@ import java.io.OutputStream;
     import java.io.UnsupportedEncodingException;
     import java.nio.file.Files;
     import java.nio.file.InvalidPathException;
    +import java.nio.file.Path;
     import javax.servlet.ServletException;
     
     import org.apache.commons.fileupload.FileItem;
    @@ -45,6 +46,8 @@ import org.apache.commons.fileupload.util.FileItemHeadersImpl;
     import org.apache.commons.io.FilenameUtils;
     import org.apache.commons.io.IOUtils;
     import org.apache.commons.lang.StringUtils;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
    @@ -61,6 +64,16 @@ import org.kohsuke.stapler.StaplerResponse;
      * @author Kohsuke Kawaguchi
      */
     public class FileParameterValue extends ParameterValue {
    +    private static final String FOLDER_NAME = "fileParameters";
    +
    +    /**
    +     * Escape hatch for SECURITY-1074, fileParameter used to escape their expected folder.
    +     * It's not recommended to enable for security reasons. That option is only present for backward compatibility.
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE = 
    +            Boolean.getBoolean(FileParameterValue.class.getName() + ".allowFolderTraversalOutsideWorkspace");
    +
         private transient final FileItem file;
     
         /**
    @@ -70,6 +83,9 @@ public class FileParameterValue extends ParameterValue {
     
         /**
          * Overrides the location in the build to place this file. Initially set to {@link #getName()}
    +     * The location could be directly the filename or also a hierarchical path. 
    +     * The intermediate folders will be created automatically.
    +     * Take care that no escape from the current directory is allowed and will result in the failure of the build.
          */
         private String location;
     
    @@ -142,7 +158,16 @@ public class FileParameterValue extends ParameterValue {
                 public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
                 	if (!StringUtils.isEmpty(location) && !StringUtils.isEmpty(file.getName())) {
                 	    listener.getLogger().println("Copying file to "+location);
    -                    FilePath locationFilePath = build.getWorkspace().child(location);
    +                    FilePath ws = build.getWorkspace();
    +                    if (ws == null) {
    +                        throw new IllegalStateException("The workspace should be created when setUp method is called");
    +                    }
    +                    if (!ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE && !ws.isDescendant(location)) {
    +                        listener.error("Rejecting file path escaping base directory with relative path: " + location);
    +                        // force the build to fail
    +                        return null;
    +                    }
    +                    FilePath locationFilePath = ws.child(location);
                         locationFilePath.getParent().mkdirs();
                 	    locationFilePath.copyFrom(file);
                         locationFilePath.copyTo(new FilePath(getLocationUnderBuild(build)));
    @@ -204,6 +229,18 @@ public class FileParameterValue extends ParameterValue {
             if (("/" + originalFileName).equals(request.getRestOfPath())) {
                 AbstractBuild build = (AbstractBuild)request.findAncestor(AbstractBuild.class).getObject();
                 File fileParameter = getLocationUnderBuild(build);
    +
    +            if (!ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE) {
    +                File fileParameterFolder = getFileParameterFolderUnderBuild(build);
    +
    +                //TODO can be replaced by Util#isDescendant in 2.80+
    +                Path child = fileParameter.getAbsoluteFile().toPath().normalize();
    +                Path parent = fileParameterFolder.getAbsoluteFile().toPath().normalize();
    +                if (!child.startsWith(parent)) {
    +                    throw new IllegalStateException("The fileParameter tried to escape the expected folder: " + location);
    +                }
    +            }
    +
                 if (fileParameter.isFile()) {
                     try (InputStream data = Files.newInputStream(fileParameter.toPath())) {
                         long lastModified = fileParameter.lastModified();
    @@ -227,7 +264,11 @@ public class FileParameterValue extends ParameterValue {
          * @return the location to store the file parameter
          */
         private File getLocationUnderBuild(AbstractBuild build) {
    -        return new File(build.getRootDir(), "fileParameters/" + location);
    +        return new File(getFileParameterFolderUnderBuild(build), location);
    +    }
    +
    +    private File getFileParameterFolderUnderBuild(AbstractBuild<?, ?> build){
    +        return new File(build.getRootDir(), FOLDER_NAME);
         }
     
         /**
    diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java
    index b2fcc6c5d6ca5729d623325a5abb381193fff72b..e94790857676e28cca1c6def7f4b0d7f1ad30ca3 100644
    --- a/core/src/main/java/hudson/model/Fingerprint.java
    +++ b/core/src/main/java/hudson/model/Fingerprint.java
    @@ -822,24 +822,22 @@ public class Fingerprint implements ModelObject, Saveable {
         public static final class ProjectRenameListener extends ItemListener {
             @Override
             public void onLocationChanged(final Item item, final String oldName, final String newName) {
    -            try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +            try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                     locationChanged(item, oldName, newName);
                 }
             }
             private void locationChanged(Item item, String oldName, String newName) {
    -            if (item instanceof AbstractProject) {
    -                AbstractProject p = Jenkins.getInstance().getItemByFullName(newName, AbstractProject.class);
    +            if (item instanceof Job) {
    +                Job p = Jenkins.getInstance().getItemByFullName(newName, Job.class);
                     if (p != null) {
    -                    RunList builds = p.getBuilds();
    -                    for (Object build : builds) {
    -                        if (build instanceof AbstractBuild) {
    -                            Collection<Fingerprint> fingerprints = ((AbstractBuild)build).getBuildFingerprints();
    -                            for (Fingerprint f : fingerprints) {
    -                                try {
    -                                    f.rename(oldName, newName);
    -                                } catch (IOException e) {
    -                                    logger.log(Level.WARNING, "Failed to update fingerprint record " + f.getFileName() + " when " + oldName + " was renamed to " + newName, e);
    -                                }
    +                    RunList<? extends Run> builds = p.getBuilds();
    +                    for (Run build : builds) {
    +                        Collection<Fingerprint> fingerprints = build.getBuildFingerprints();
    +                        for (Fingerprint f : fingerprints) {
    +                            try {
    +                                f.rename(oldName, newName);
    +                            } catch (IOException e) {
    +                                logger.log(Level.WARNING, "Failed to update fingerprint record " + f.getFileName() + " when " + oldName + " was renamed to " + newName, e);
                                 }
                             }
                         }
    @@ -1256,7 +1254,7 @@ public class Fingerprint implements ModelObject, Saveable {
                 AtomicFileWriter afw = new AtomicFileWriter(file);
                 try {
                     PrintWriter w = new PrintWriter(afw);
    -                w.println("<?xml version='1.0' encoding='UTF-8'?>");
    +                w.println("<?xml version='1.1' encoding='UTF-8'?>");
                     w.println("<fingerprint>");
                     w.print("  <timestamp>");
                     w.print(DATE_CONVERTER.toString(timestamp));
    @@ -1366,7 +1364,12 @@ public class Fingerprint implements ModelObject, Saveable {
                 start = System.currentTimeMillis();
     
             try {
    -            Fingerprint f = (Fingerprint) configFile.read();
    +            Object loaded = configFile.read();
    +            if (!(loaded instanceof Fingerprint)) {
    +                throw new IOException("Unexpected Fingerprint type. Expected " + Fingerprint.class + " or subclass but got "
    +                        + (loaded != null ? loaded.getClass() : "null"));
    +            }
    +            Fingerprint f = (Fingerprint) loaded;
                 if(logger.isLoggable(Level.FINE))
                     logger.fine("Loading fingerprint "+file+" took "+(System.currentTimeMillis()-start)+"ms");
                 if (f.facets==null)
    @@ -1435,7 +1438,7 @@ public class Fingerprint implements ModelObject, Saveable {
             // Probably it failed due to the missing Item.DISCOVER
             // We try to retrieve the job using SYSTEM user and to check permissions manually.
             final Authentication userAuth = Jenkins.getAuthentication();
    -        try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +        try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                 final Item itemBySystemUser = jenkins.getItemByFullName(fullName);
                 if (itemBySystemUser == null) {
                     return false;
    diff --git a/core/src/main/java/hudson/model/FingerprintCleanupThread.java b/core/src/main/java/hudson/model/FingerprintCleanupThread.java
    index 59187a3a400630e63968c886541652f8c439e7b4..292248ed528336439dc96c1cef7306b9881cd6ff 100644
    --- a/core/src/main/java/hudson/model/FingerprintCleanupThread.java
    +++ b/core/src/main/java/hudson/model/FingerprintCleanupThread.java
    @@ -28,9 +28,10 @@ import hudson.ExtensionList;
     import hudson.Functions;
     import jenkins.model.Jenkins;
     import org.jenkinsci.Symbol;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     import java.io.File;
    -import java.io.FileFilter;
     import java.io.IOException;
     import java.util.regex.Pattern;
     
    @@ -45,7 +46,11 @@ import java.util.regex.Pattern;
      * @author Kohsuke Kawaguchi
      */
     @Extension @Symbol("fingerprintCleanup")
    -public final class FingerprintCleanupThread extends AsyncPeriodicWork {
    +@Restricted(NoExternalUse.class)
    +public class FingerprintCleanupThread extends AsyncPeriodicWork {
    +
    +    static final String FINGERPRINTS_DIR_NAME = "fingerprints";
    +    private static final Pattern FINGERPRINT_FILE_PATTERN = Pattern.compile("[0-9a-f]{28}\\.xml");
     
         public FingerprintCleanupThread() {
             super("Fingerprint cleanup");
    @@ -66,13 +71,13 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
         public void execute(TaskListener listener) {
             int numFiles = 0;
     
    -        File root = new File(Jenkins.getInstance().getRootDir(),"fingerprints");
    -        File[] files1 = root.listFiles(LENGTH2DIR_FILTER);
    +        File root = new File(getRootDir(), FINGERPRINTS_DIR_NAME);
    +        File[] files1 = root.listFiles(f -> f.isDirectory() && f.getName().length()==2);
             if(files1!=null) {
                 for (File file1 : files1) {
    -                File[] files2 = file1.listFiles(LENGTH2DIR_FILTER);
    +                File[] files2 = file1.listFiles(f -> f.isDirectory() && f.getName().length()==2);
                     for(File file2 : files2) {
    -                    File[] files3 = file2.listFiles(FINGERPRINTFILE_FILTER);
    +                    File[] files3 = file2.listFiles(f -> f.isFile() && FINGERPRINT_FILE_PATTERN.matcher(f.getName()).matches());
                         for(File file3 : files3) {
                             if(check(file3, listener))
                                 numFiles++;
    @@ -101,7 +106,7 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
          */
         private boolean check(File fingerprintFile, TaskListener listener) {
             try {
    -            Fingerprint fp = Fingerprint.load(fingerprintFile);
    +            Fingerprint fp = loadFingerprint(fingerprintFile);
                 if (fp == null || !fp.isAlive()) {
                     listener.getLogger().println("deleting obsolete " + fingerprintFile);
                     fingerprintFile.delete();
    @@ -109,8 +114,7 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
                 } else {
                     // get the fingerprint in the official map so have the changes visible to Jenkins
                     // otherwise the mutation made in FingerprintMap can override our trimming.
    -                listener.getLogger().println("possibly trimming " + fingerprintFile);
    -                fp = Jenkins.getInstance()._getFingerprint(fp.getHashString());
    +                fp = getFingerprint(fp);
                     return fp.trim();
                 }
             } catch (IOException e) {
    @@ -119,17 +123,16 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
             }
         }
     
    -    private static final FileFilter LENGTH2DIR_FILTER = new FileFilter() {
    -        public boolean accept(File f) {
    -            return f.isDirectory() && f.getName().length()==2;
    -        }
    -    };
    +    protected Fingerprint loadFingerprint(File fingerprintFile) throws IOException {
    +        return Fingerprint.load(fingerprintFile);
    +    }
     
    -    private static final FileFilter FINGERPRINTFILE_FILTER = new FileFilter() {
    -        private final Pattern PATTERN = Pattern.compile("[0-9a-f]{28}\\.xml");
    +    protected Fingerprint getFingerprint(Fingerprint fp) throws IOException {
    +        return Jenkins.get()._getFingerprint(fp.getHashString());
    +    }
    +
    +    protected File getRootDir() {
    +        return Jenkins.get().getRootDir();
    +    }
     
    -        public boolean accept(File f) {
    -            return f.isFile() && PATTERN.matcher(f.getName()).matches();
    -        }
    -    };
     }
    diff --git a/core/src/main/java/hudson/model/Item.java b/core/src/main/java/hudson/model/Item.java
    index 8187d608bd8923851cf945f0c6db09971b7836d1..be8a5495e16d886a8d543f4caf9f0951955bb05e 100644
    --- a/core/src/main/java/hudson/model/Item.java
    +++ b/core/src/main/java/hudson/model/Item.java
    @@ -266,5 +266,5 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont
         Permission BUILD = new Permission(PERMISSIONS, "Build", Messages._AbstractProject_BuildPermission_Description(),  Permission.UPDATE, PermissionScope.ITEM);
         Permission WORKSPACE = new Permission(PERMISSIONS, "Workspace", Messages._AbstractProject_WorkspacePermission_Description(), Permission.READ, PermissionScope.ITEM);
         Permission WIPEOUT = new Permission(PERMISSIONS, "WipeOut", Messages._AbstractProject_WipeOutPermission_Description(), null, Functions.isWipeOutPermissionEnabled(), new PermissionScope[]{PermissionScope.ITEM});
    -    Permission CANCEL = new Permission(PERMISSIONS, "Cancel", Messages._AbstractProject_CancelPermission_Description(), BUILD, PermissionScope.ITEM);
    +    Permission CANCEL = new Permission(PERMISSIONS, "Cancel", Messages._AbstractProject_CancelPermission_Description(), Permission.UPDATE, PermissionScope.ITEM);
     }
    diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java
    index 13f72c3f079219432ed98748324b9ed3d6aa7d44..195d4d8c0ce95322284d279d8b6176286cd24958 100644
    --- a/core/src/main/java/hudson/model/ItemGroupMixIn.java
    +++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java
    @@ -45,6 +45,8 @@ import java.io.File;
     import java.io.FileFilter;
     import java.io.IOException;
     import java.io.InputStream;
    +import java.nio.file.Files;
    +import java.nio.file.StandardCopyOption;
     import java.util.Map;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    @@ -239,7 +241,8 @@ public abstract class ItemGroupMixIn {
             T result = (T)createProject(src.getDescriptor(),name,false);
     
             // copy config
    -        Util.copyFile(srcConfigFile.getFile(), Items.getConfigFile(result).getFile());
    +        Files.copy(Util.fileToPath(srcConfigFile.getFile()), Util.fileToPath(Items.getConfigFile(result).getFile()),
    +                StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
     
             // reload from the new config
             final File rootDir = result.getRootDir();
    diff --git a/core/src/main/java/hudson/model/Items.java b/core/src/main/java/hudson/model/Items.java
    index 927abc72e0bbe5e658d85c3eb14214b90fcbfbf6..d0a26238a3d3f71a650c631ab5ca2caec3a901b3 100644
    --- a/core/src/main/java/hudson/model/Items.java
    +++ b/core/src/main/java/hudson/model/Items.java
    @@ -35,6 +35,7 @@ import hudson.security.AccessControlled;
     import hudson.triggers.Trigger;
     import hudson.util.DescriptorList;
     import hudson.util.EditDistance;
    +import jenkins.util.MemoryReductionUtil;
     import hudson.util.XStream2;
     import java.io.File;
     import java.io.IOException;
    @@ -313,8 +314,8 @@ public class Items {
     
         // Had difficulty adapting the version in Functions to use no live items, so rewrote it:
         static String getRelativeNameFrom(String itemFullName, String groupFullName) {
    -        String[] itemFullNameA = itemFullName.isEmpty() ? new String[0] : itemFullName.split("/");
    -        String[] groupFullNameA = groupFullName.isEmpty() ? new String[0] : groupFullName.split("/");
    +        String[] itemFullNameA = itemFullName.isEmpty() ? MemoryReductionUtil.EMPTY_STRING_ARRAY : itemFullName.split("/");
    +        String[] groupFullNameA = groupFullName.isEmpty() ? MemoryReductionUtil.EMPTY_STRING_ARRAY : groupFullName.split("/");
             for (int i = 0; ; i++) {
                 if (i == itemFullNameA.length) {
                     if (i == groupFullNameA.length) {
    diff --git a/core/src/main/java/hudson/model/JDK.java b/core/src/main/java/hudson/model/JDK.java
    index b04376eb74aead7cde5dc2bac587aa49bf3a30b2..abcb1a9f74d039af0fce62acac8e52b89da2effe 100644
    --- a/core/src/main/java/hudson/model/JDK.java
    +++ b/core/src/main/java/hudson/model/JDK.java
    @@ -32,16 +32,20 @@ import hudson.EnvVars;
     import hudson.slaves.NodeSpecific;
     import hudson.tools.ToolInstallation;
     import hudson.tools.ToolDescriptor;
    +import hudson.tools.ToolInstaller;
     import hudson.tools.ToolProperty;
    -import hudson.tools.JDKInstaller;
     import hudson.util.XStream2;
     
     import java.io.File;
     import java.io.IOException;
    +import java.lang.reflect.Constructor;
    +import java.lang.reflect.InvocationTargetException;
     import java.util.Map;
     import java.util.List;
     import java.util.Arrays;
     import java.util.Collections;
    +import java.util.logging.Level;
    +import java.util.logging.Logger;
     
     import jenkins.model.Jenkins;
     import org.jenkinsci.Symbol;
    @@ -183,8 +187,18 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
             }
     
             @Override
    -        public List<JDKInstaller> getDefaultInstallers() {
    -            return Collections.singletonList(new JDKInstaller(null,false));
    +        public List<? extends ToolInstaller> getDefaultInstallers() {
    +            try {
    +                Class<? extends ToolInstaller> jdkInstallerClass = Jenkins.getInstance().getPluginManager()
    +                        .uberClassLoader.loadClass("hudson.tools.JDKInstaller").asSubclass(ToolInstaller.class);
    +                Constructor<? extends ToolInstaller> constructor = jdkInstallerClass.getConstructor(String.class, boolean.class);
    +                return Collections.singletonList(constructor.newInstance(null, false));
    +            } catch (ClassNotFoundException e) {
    +                return Collections.emptyList();
    +            } catch (Exception e) {
    +                LOGGER.log(Level.WARNING, "Unable to get default installer", e);
    +                return Collections.emptyList();
    +            }
             }
     
             /**
    @@ -211,4 +225,6 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
                 return ((JDK)obj).javaHome;
             }
         }
    +
    +    private static final Logger LOGGER = Logger.getLogger(JDK.class.getName());
     }
    diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java
    index a5e477ede90d2f24591296cb24a1f852244ed64d..4b149034bd825bf448e8b6aad5416a8afd748b12 100644
    --- a/core/src/main/java/hudson/model/Job.java
    +++ b/core/src/main/java/hudson/model/Job.java
    @@ -56,7 +56,6 @@ import hudson.util.DescribableList;
     import hudson.util.FormApply;
     import hudson.util.Graph;
     import hudson.util.ProcessTree;
    -import hudson.util.QuotedStringTokenizer;
     import hudson.util.RunList;
     import hudson.util.ShiftedCategoryAxis;
     import hudson.util.StackedAreaRenderer2;
    @@ -68,7 +67,6 @@ import java.awt.Color;
     import java.awt.Paint;
     import java.io.File;
     import java.io.IOException;
    -import java.net.URLEncoder;
     import java.nio.file.Files;
     import java.util.ArrayList;
     import java.util.Calendar;
    @@ -122,9 +120,6 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
     import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
    -import org.acegisecurity.AccessDeniedException;
    -import org.acegisecurity.context.SecurityContext;
    -import org.acegisecurity.context.SecurityContextHolder;
     
     /**
      * A job is an runnable entity under the monitoring of Hudson.
    @@ -324,6 +319,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
         /**
          * Returns whether the name of this job can be changed by user.
          */
    +    @Override
         public boolean isNameEditable() {
             return true;
         }
    @@ -815,7 +811,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
         }
     
         /**
    -     * Gets the youngest build #m that satisfies <tt>n&lt;=m</tt>.
    +     * Gets the youngest build #m that satisfies {@code n&lt;=m}.
          * 
          * This is useful when you'd like to fetch a build but the exact build might
          * be already gone (deleted, rotated, etc.)
    @@ -830,7 +826,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
         }
     
         /**
    -     * Gets the latest build #m that satisfies <tt>m&lt;=n</tt>.
    +     * Gets the latest build #m that satisfies {@code m&lt;=n}.
          * 
          * This is useful when you'd like to fetch a build but the exact build might
          * be already gone (deleted, rotated, etc.)
    @@ -1356,39 +1352,17 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
                 }
                 ItemListener.fireOnUpdated(this);
     
    -            String newName = req.getParameter("name");
                 final ProjectNamingStrategy namingStrategy = Jenkins.getInstance().getProjectNamingStrategy();
    -            if (validRename(name, newName)) {
    -                newName = newName.trim();
    -                // check this error early to avoid HTTP response splitting.
    -                Jenkins.checkGoodName(newName);
    -                namingStrategy.checkName(newName);
    -                if (FormApply.isApply(req)) {
    -                    FormApply.applyResponse("notificationBar.show(" + QuotedStringTokenizer.quote(Messages.Job_you_must_use_the_save_button_if_you_wish()) + ",notificationBar.WARNING)").generateResponse(req, rsp, null);
    -                } else {
    -                    rsp.sendRedirect("rename?newName=" + URLEncoder.encode(newName, "UTF-8"));
    -                }
    -            } else {
                     if(namingStrategy.isForceExistingJobs()){
                         namingStrategy.checkName(name);
                     }
                     FormApply.success(".").generateResponse(req, rsp, null);
    -            }
             } catch (JSONException e) {
                 LOGGER.log(Level.WARNING, "failed to parse " + json, e);
                 sendError(e, req, rsp);
             }
         }
     
    -    private boolean validRename(String oldName, String newName) {
    -        if (newName == null) {
    -            return false;
    -        }
    -        boolean noChange = oldName.equals(newName);
    -        boolean spaceAdded = oldName.equals(newName.trim());
    -        return !noChange && !spaceAdded;
    -    }
    -
         /**
          * Derived class can override this to perform additional config submission
          * work.
    @@ -1586,32 +1560,25 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
     
         /**
          * Renames this job.
    +     * @deprecated Exists for backwards compatibility, use {@link #doConfirmRename} instead.
          */
    +    @Deprecated
         @RequirePOST
         public/* not synchronized. see renameTo() */void doDoRename(
                 StaplerRequest req, StaplerResponse rsp) throws IOException,
                 ServletException {
    -
    -        if (!hasPermission(CONFIGURE)) {
    -            // rename is essentially delete followed by a create
    -            checkPermission(CREATE);
    -            checkPermission(DELETE);
    -        }
    -
             String newName = req.getParameter("newName");
    -        Jenkins.checkGoodName(newName);
    +        doConfirmRename(newName).generateResponse(req, rsp, null);
    +    }
     
    +    /**
    +     * {@inheritDoc}
    +     */
    +    @Override
    +    protected void checkRename(String newName) throws Failure {
             if (isBuilding()) {
    -            // redirect to page explaining that we can't rename now
    -            rsp.sendRedirect("rename?newName=" + URLEncoder.encode(newName, "UTF-8"));
    -            return;
    +            throw new Failure(Messages.Job_NoRenameWhileBuilding());
             }
    -
    -        renameTo(newName);
    -        // send to the new job page
    -        // note we can't use getUrl() because that would pick up old name in the
    -        // Ancestor.getUrl()
    -        rsp.sendRedirect2("../" + newName);
         }
     
         public void doRssAll(StaplerRequest req, StaplerResponse rsp)
    @@ -1645,58 +1612,4 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
         }
     
         private final static HexStringConfidentialKey SERVER_COOKIE = new HexStringConfidentialKey(Job.class,"serverCookie",16);
    -    
    -    /**
    -     * Check new name for job
    -     * @param newName - New name for job
    -     * @return {@code true} - if newName occupied and user has permissions for this job
    -     *         {@code false} - if newName occupied and user hasn't permissions for this job
    -     *         {@code null} - if newName didn't occupied
    -     * 
    -     * @throws Failure if the given name is not good
    -     */
    -    @CheckForNull
    -    @Restricted(NoExternalUse.class)
    -    public Boolean checkIfNameIsUsed(@Nonnull String newName) throws Failure{
    -        
    -        Item item = null;
    -        Jenkins.checkGoodName(newName);
    -        
    -        try {
    -            item = getParent().getItem(newName);
    -        } catch(AccessDeniedException ex) {  
    -            if (LOGGER.isLoggable(Level.FINE)) {
    -                LOGGER.log(Level.FINE, "Unable to rename the job {0}: name {1} is already in use. " +
    -                        "User {2} has {3} permission, but no {4} for existing job with the same name", 
    -                        new Object[] {this.getFullName(), newName, User.current().getFullName(), Item.DISCOVER.name, Item.READ.name} );
    -            }
    -            return true;
    -        }
    -        
    -        if (item != null) {
    -            // User has Read permissions for existing job with the same name
    -            return true;
    -        } else {
    -            SecurityContext initialContext = null;
    -            try {
    -                initialContext = hudson.security.ACL.impersonate(ACL.SYSTEM);
    -                item = getParent().getItem(newName);
    -
    -                if (item != null) {
    -                    if (LOGGER.isLoggable(Level.FINE)) {
    -                        LOGGER.log(Level.FINE, "Unable to rename the job {0}: name {1} is already in use. " +
    -                                "User {2} has no {3} permission for existing job with the same name", 
    -                                new Object[] {this.getFullName(), newName, initialContext.getAuthentication().getName(), Item.DISCOVER.name} );
    -                    }
    -                    return false;
    -                }
    -
    -            } finally {
    -                if (initialContext != null) {
    -                    SecurityContextHolder.setContext(initialContext);
    -                }
    -            }
    -        }
    -        return null;
    -    }
     }
    diff --git a/core/src/main/java/hudson/model/JobProperty.java b/core/src/main/java/hudson/model/JobProperty.java
    index 2c982286e41a57b62344d93d10f7e8c3b07ad46a..a16c685ec73a000018e0d48d51f6e132abe017c7 100644
    --- a/core/src/main/java/hudson/model/JobProperty.java
    +++ b/core/src/main/java/hudson/model/JobProperty.java
    @@ -53,9 +53,9 @@ import javax.annotation.Nonnull;
      * configuration screen, and they are persisted with the job object.
      *
      * <p>
    - * Configuration screen should be defined in <tt>config.jelly</tt>.
    + * Configuration screen should be defined in {@code config.jelly}.
      * Within this page, the {@link JobProperty} instance is available
    - * as <tt>instance</tt> variable (while <tt>it</tt> refers to {@link Job}.
    + * as {@code instance} variable (while {@code it} refers to {@link Job}.
      *
      * <p>
      * Starting 1.150, {@link JobProperty} implements {@link BuildStep},
    diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java
    index 0c2605776593e3ccd5daa8d6cee068c51118fc11..606068104e3f3ecf925343c70973908d0183eb53 100644
    --- a/core/src/main/java/hudson/model/Label.java
    +++ b/core/src/main/java/hudson/model/Label.java
    @@ -49,6 +49,8 @@ import jenkins.model.Jenkins;
     import jenkins.model.ModelObjectWithChildren;
     import org.acegisecurity.context.SecurityContext;
     import org.acegisecurity.context.SecurityContextHolder;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.DoNotUse;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.export.Exported;
    @@ -58,6 +60,7 @@ import java.io.StringReader;
     import java.util.ArrayList;
     import java.util.Collection;
     import java.util.Collections;
    +import java.util.Comparator;
     import java.util.HashSet;
     import java.util.List;
     import java.util.Set;
    @@ -196,6 +199,16 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
             return nodes.size() == 1 && nodes.iterator().next().getSelfLabel() == this;
         }
     
    +    private static class NodeSorter implements Comparator<Node> {
    +        @Override
    +        public int compare(Node o1, Node o2) {
    +            if (o1 == o2) {
    +                return 0;
    +            }
    +            return o1 instanceof Jenkins ? -1 : (o2 instanceof Jenkins ? 1 : o1.getNodeName().compareTo(o2.getNodeName()));
    +        }
    +    }
    +
         /**
          * Gets all {@link Node}s that belong to this label.
          */
    @@ -204,7 +217,7 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
             Set<Node> nodes = this.nodes;
             if(nodes!=null) return nodes;
     
    -        Set<Node> r = new HashSet<Node>();
    +        Set<Node> r = new HashSet<>();
             Jenkins h = Jenkins.getInstance();
             if(this.matches(h))
                 r.add(h);
    @@ -215,6 +228,13 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
             return this.nodes = Collections.unmodifiableSet(r);
         }
     
    +    @Restricted(DoNotUse.class) // Jelly
    +    public Set<Node> getSortedNodes() {
    +        Set<Node> r = new TreeSet<>(new NodeSorter());
    +        r.addAll(getNodes());
    +        return r;
    +    }
    +
         /**
          * Gets all {@link Cloud}s that can launch for this label.
          */
    diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java
    index 002646e10d0bec0e225361dbfb07a6b069436471..9d84eb32cebb49ea99f06ffb7f57fc1e8b688d41 100644
    --- a/core/src/main/java/hudson/model/ListView.java
    +++ b/core/src/main/java/hudson/model/ListView.java
    @@ -518,7 +518,7 @@ public class ListView extends View implements DirectlyModifiableView {
         public static final class Listener extends ItemListener {
             @Override
             public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) {
    -            try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +            try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                     locationChanged(oldFullName, newFullName);
                 }
             }
    @@ -563,7 +563,7 @@ public class ListView extends View implements DirectlyModifiableView {
     
             @Override
             public void onDeleted(final Item item) {
    -            try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +            try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                     deleted(item);
                 }
             }
    diff --git a/core/src/main/java/hudson/model/LoadBalancer.java b/core/src/main/java/hudson/model/LoadBalancer.java
    index a0403e1b974f957c45e882fc9aa190783be99c68..ce69e89779309af042019fdd4365734f977e8d7e 100644
    --- a/core/src/main/java/hudson/model/LoadBalancer.java
    +++ b/core/src/main/java/hudson/model/LoadBalancer.java
    @@ -112,7 +112,7 @@ public abstract class LoadBalancer implements ExtensionPoint {
             private boolean assignGreedily(Mapping m, Task task, List<ConsistentHash<ExecutorChunk>> hashes, int i) {
                 if (i==hashes.size())   return true;    // fully assigned
     
    -            String key = task.getFullDisplayName() + (i>0 ? String.valueOf(i) : "");
    +            String key = task.getAffinityKey() + (i>0 ? String.valueOf(i) : "");
     
                 for (ExecutorChunk ec : hashes.get(i).list(key)) {
                     // let's attempt this assignment
    diff --git a/core/src/main/java/hudson/model/ManagementLink.java b/core/src/main/java/hudson/model/ManagementLink.java
    index 041fda35d05ef0b18b8405a21701236517205813..00d6871097cc66730ad6a50caa3bb3d3bf15b67a 100644
    --- a/core/src/main/java/hudson/model/ManagementLink.java
    +++ b/core/src/main/java/hudson/model/ManagementLink.java
    @@ -37,7 +37,7 @@ import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     
     /**
    - * Extension point to add icon to <tt>http://server/hudson/manage</tt> page.
    + * Extension point to add icon to {@code http://server/hudson/manage} page.
      *
      * <p>
      * This is a place for exposing features that are only meant for system admins
    diff --git a/core/src/main/java/hudson/model/ModelObject.java b/core/src/main/java/hudson/model/ModelObject.java
    index 5467b2a7a77e6e02ec2b073148d670ce55a92f3f..166a2cbcf2f20cf0469a77d602d15226e677e7fd 100644
    --- a/core/src/main/java/hudson/model/ModelObject.java
    +++ b/core/src/main/java/hudson/model/ModelObject.java
    @@ -27,7 +27,7 @@ package hudson.model;
      * A model object has a human readable name.
      *
      * And it normally has URL, but this interface doesn't define one.
    - * (Since there're so many classes that define the <tt>getUrl</tt> method
    + * (Since there're so many classes that define the {@code getUrl} method
      * we should have such one.)
      *
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/model/MyViewsProperty.java b/core/src/main/java/hudson/model/MyViewsProperty.java
    index 379b37d15dbecacca90e13de6126601731870c13..c7852d926d82b5379a5482dc27fcba04751996e7 100644
    --- a/core/src/main/java/hudson/model/MyViewsProperty.java
    +++ b/core/src/main/java/hudson/model/MyViewsProperty.java
    @@ -47,6 +47,8 @@ import net.sf.json.JSONObject;
     
     import org.acegisecurity.AccessDeniedException;
     import org.jenkinsci.Symbol;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.HttpRedirect;
     import org.kohsuke.stapler.HttpResponse;
    @@ -87,6 +89,7 @@ public class MyViewsProperty extends UserProperty implements ModifiableViewGroup
             this(null);
         }
     
    +    @Restricted(NoExternalUse.class)
         public Object readResolve() {
             if (views == null)
                 // this shouldn't happen, but an error in 1.319 meant the last view could be deleted
    diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java
    index bcf81c19ad5c7b9e8f941d4c57d5ae3aff8387f6..c61ee7abc138a0dc824f2ae1c76abf916d30e3a7 100644
    --- a/core/src/main/java/hudson/model/Node.java
    +++ b/core/src/main/java/hudson/model/Node.java
    @@ -62,6 +62,7 @@ import java.util.logging.Logger;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     import jenkins.model.Jenkins;
    +import jenkins.util.SystemProperties;
     import jenkins.util.io.OnMaster;
     import net.sf.json.JSONObject;
     import org.acegisecurity.Authentication;
    @@ -97,6 +98,9 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
     
         private static final Logger LOGGER = Logger.getLogger(Node.class.getName());
     
    +    /** @see <a href="https://issues.jenkins-ci.org/browse/JENKINS-46652">JENKINS-46652</a> */
    +    public static /* not final */ boolean SKIP_BUILD_CHECK_ON_FLYWEIGHTS = SystemProperties.getBoolean(Node.class.getName() + ".SKIP_BUILD_CHECK_ON_FLYWEIGHTS", true);
    +
         /**
          * Newly copied agents get this flag set, so that Jenkins doesn't try to start/remove this node until its configuration
          * is saved once.
    @@ -345,6 +349,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
         /**
          * Gets the special label that represents this node itself.
          */
    +    @Nonnull
         @WithBridgeMethods(Label.class)
         public LabelAtom getSelfLabel() {
             return LabelAtom.get(getNodeName());
    @@ -394,7 +399,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
             }
     
             Authentication identity = item.authenticate();
    -        if (!hasPermission(identity,Computer.BUILD)) {
    +        if (!(SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !hasPermission(identity, Computer.BUILD)) {
                 // doesn't have a permission
                 return CauseOfBlockage.fromMessage(Messages._Node_LackingBuildPermission(identity.getName(), getDisplayName()));
             }
    diff --git a/core/src/main/java/hudson/model/ParameterDefinition.java b/core/src/main/java/hudson/model/ParameterDefinition.java
    index fa08b2f0f24ddd71bdb1ac1bcc3d5c8481f0dbe6..d535a90a051433cb5050b96730e05404a1a0f1e4 100644
    --- a/core/src/main/java/hudson/model/ParameterDefinition.java
    +++ b/core/src/main/java/hudson/model/ParameterDefinition.java
    @@ -75,18 +75,18 @@ import org.kohsuke.stapler.export.ExportedBean;
      *
      * <h2>Persistence</h2>
      * <p>
    - * Instances of {@link ParameterDefinition}s are persisted into job <tt>config.xml</tt>
    + * Instances of {@link ParameterDefinition}s are persisted into job {@code config.xml}
      * through XStream.
      *
      *
      * <h2>Associated Views</h2>
      * <h3>config.jelly</h3>
    - * {@link ParameterDefinition} class uses <tt>config.jelly</tt> to contribute a form
    + * {@link ParameterDefinition} class uses {@code config.jelly} to contribute a form
      * fragment in the job configuration screen. Values entered there are fed back to
      * {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)} to create {@link ParameterDefinition}s.
      *
      * <h3>index.jelly</h3>
    - * The <tt>index.jelly</tt> view contributes a form fragment in the page where the user
    + * The {@code index.jelly} view contributes a form fragment in the page where the user
      * enters actual values of parameters for a build. The result of this form submission
      * is then fed to {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} to
      * create {@link ParameterValue}s.
    diff --git a/core/src/main/java/hudson/model/ParameterValue.java b/core/src/main/java/hudson/model/ParameterValue.java
    index 6cd1f46a190d322cb7cf8fbe0cbf6ccba4e3a66a..64fc925a8ec8a76cd27e1ba96056b97a5daa95eb 100644
    --- a/core/src/main/java/hudson/model/ParameterValue.java
    +++ b/core/src/main/java/hudson/model/ParameterValue.java
    @@ -56,12 +56,12 @@ import org.kohsuke.stapler.export.ExportedBean;
      *
      * <h2>Persistence</h2>
      * <p>
    - * Instances of {@link ParameterValue}s are persisted into build's <tt>build.xml</tt>
    + * Instances of {@link ParameterValue}s are persisted into build's {@code build.xml}
      * through XStream (via {@link ParametersAction}), so instances need to be persistable.
      *
      * <h2>Associated Views</h2>
      * <h3>value.jelly</h3>
    - * The <tt>value.jelly</tt> view contributes a UI fragment to display the parameter
    + * The {@code value.jelly} view contributes a UI fragment to display the parameter
      * values used for a build.
      *
      * <h2>Notes</h2>
    diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java
    index a262ae6adbe12c811300774645eba374c01175f4..9de8ef8dcd00e9f390cf59ee2b5d269ce7019e13 100644
    --- a/core/src/main/java/hudson/model/ParametersAction.java
    +++ b/core/src/main/java/hudson/model/ParametersAction.java
    @@ -87,7 +87,7 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
     
         private Set<String> safeParameters;
     
    -    private final List<ParameterValue> parameters;
    +    private @Nonnull List<ParameterValue> parameters;
     
         private List<String> parameterDefinitionNames;
     
    @@ -99,7 +99,7 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
     
         private transient Run<?, ?> run;
     
    -    public ParametersAction(List<ParameterValue> parameters) {
    +    public ParametersAction(@Nonnull List<ParameterValue> parameters) {
             this.parameters = new ArrayList<>(parameters);
             String paramNames = SystemProperties.getString(SAFE_PARAMETERS_SYSTEM_PROPERTY_NAME);
             safeParameters = new TreeSet<>();
    @@ -284,6 +284,9 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
         }
     
         private Object readResolve() {
    +        if (parameters == null) { // JENKINS-39495
    +            parameters = Collections.emptyList();
    +        }
             if (build != null)
                 OldDataMonitor.report(build, "1.283");
             if (safeParameters == null) {
    @@ -296,7 +299,7 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
         public void onAttached(Run<?, ?> r) {
             ParametersDefinitionProperty p = r.getParent().getProperty(ParametersDefinitionProperty.class);
             if (p != null) {
    -            this.parameterDefinitionNames = p.getParameterDefinitionNames();
    +            this.parameterDefinitionNames = new ArrayList<>(p.getParameterDefinitionNames());
             } else {
                 this.parameterDefinitionNames = Collections.emptyList();
             }
    diff --git a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java
    index f256e68ddc5a86c7e6bbf7d3e056053bbeed4bfc..1fa26ed3720af73f034e3325cc80bd5db293e76d 100644
    --- a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java
    +++ b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java
    @@ -34,6 +34,7 @@ import java.util.Arrays;
     import java.util.Collection;
     import java.util.Collections;
     import java.util.List;
    +import java.util.concurrent.TimeUnit;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     import javax.servlet.ServletException;
    @@ -58,7 +59,7 @@ import org.kohsuke.stapler.export.ExportedBean;
      * Keeps a list of the parameters defined for a project.
      *
      * <p>
    - * This class also implements {@link Action} so that <tt>index.jelly</tt> provides
    + * This class also implements {@link Action} so that {@code index.jelly} provides
      * a form to enter build parameters.
      * <p>The owning job needs a {@code sidepanel.jelly} and should have web methods delegating to {@link ParameterizedJobMixIn#doBuild} and {@link ParameterizedJobMixIn#doBuildWithParameters}.
      * The builds also need a {@code sidepanel.jelly}.
    @@ -71,17 +72,11 @@ public class ParametersDefinitionProperty extends OptionalJobProperty<Job<?, ?>>
     
         @DataBoundConstructor
         public ParametersDefinitionProperty(@Nonnull List<ParameterDefinition> parameterDefinitions) {
    -        if (parameterDefinitions == null) {
    -            throw new NullPointerException("ParameterDefinitions is null when this is a not valid value");
    -        }
    -        this.parameterDefinitions = parameterDefinitions;
    +        this.parameterDefinitions = parameterDefinitions != null ? parameterDefinitions : new ArrayList<>();
         }
     
         public ParametersDefinitionProperty(@Nonnull ParameterDefinition... parameterDefinitions) {
    -        if (parameterDefinitions == null) {
    -            throw new NullPointerException("ParameterDefinitions is null when this is a not valid value");
    -        }
    -        this.parameterDefinitions = Arrays.asList(parameterDefinitions) ;
    +        this.parameterDefinitions = parameterDefinitions != null ? Arrays.asList(parameterDefinitions) : new ArrayList<>();
         }
     
         private Object readResolve() {
    @@ -107,15 +102,7 @@ public class ParametersDefinitionProperty extends OptionalJobProperty<Job<?, ?>>
          * Gets the names of all the parameter definitions.
          */
         public List<String> getParameterDefinitionNames() {
    -        return new AbstractList<String>() {
    -            public String get(int index) {
    -                return parameterDefinitions.get(index).getName();
    -            }
    -
    -            public int size() {
    -                return parameterDefinitions.size();
    -            }
    -        };
    +        return new DefinitionsAbstractList(this.parameterDefinitions);
         }
     
         @Nonnull
    @@ -147,7 +134,8 @@ public class ParametersDefinitionProperty extends OptionalJobProperty<Job<?, ?>>
          * This method is supposed to be invoked from {@link ParameterizedJobMixIn#doBuild(StaplerRequest, StaplerResponse, TimeDuration)}.
          */
         public void _doBuild(StaplerRequest req, StaplerResponse rsp, @QueryParameter TimeDuration delay) throws IOException, ServletException {
    -        if (delay==null)    delay=new TimeDuration(getJob().getQuietPeriod());
    +        if (delay==null)
    +            delay=new TimeDuration(TimeUnit.MILLISECONDS.convert(getJob().getQuietPeriod(), TimeUnit.SECONDS));
     
     
             List<ParameterValue> values = new ArrayList<ParameterValue>();
    @@ -196,7 +184,8 @@ public class ParametersDefinitionProperty extends OptionalJobProperty<Job<?, ?>>
             		values.add(value);
             	}
             }
    -        if (delay==null)    delay=new TimeDuration(getJob().getQuietPeriod());
    +        if (delay==null)
    +            delay=new TimeDuration(TimeUnit.MILLISECONDS.convert(getJob().getQuietPeriod(), TimeUnit.SECONDS));
     
             Queue.Item item = Jenkins.getInstance().getQueue().schedule2(
                     getJob(), delay.getTimeInSeconds(), new ParametersAction(values), ParameterizedJobMixIn.getBuildCause(getJob(), req)).getItem();
    @@ -252,4 +241,20 @@ public class ParametersDefinitionProperty extends OptionalJobProperty<Job<?, ?>>
         public String getUrlName() {
             return null;
         }
    +
    +    private static class DefinitionsAbstractList extends AbstractList<String> {
    +        private final List<ParameterDefinition> parameterDefinitions;
    +
    +        public DefinitionsAbstractList(List<ParameterDefinition> parameterDefinitions) {
    +            this.parameterDefinitions = parameterDefinitions;
    +        }
    +
    +        public String get(int index) {
    +            return this.parameterDefinitions.get(index).getName();
    +        }
    +
    +        public int size() {
    +            return this.parameterDefinitions.size();
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/model/PeriodicWork.java b/core/src/main/java/hudson/model/PeriodicWork.java
    index aab83426723546989179c0401803d845cfceccdc..1079553551bf02620ca92ba89ce80444d556a7d7 100644
    --- a/core/src/main/java/hudson/model/PeriodicWork.java
    +++ b/core/src/main/java/hudson/model/PeriodicWork.java
    @@ -23,6 +23,7 @@
      */
     package hudson.model;
     
    +import hudson.ExtensionListListener;
     import hudson.init.Initializer;
     import hudson.triggers.SafeTimerTask;
     import hudson.ExtensionPoint;
    @@ -30,6 +31,8 @@ import hudson.Extension;
     import hudson.ExtensionList;
     import jenkins.util.Timer;
     
    +import java.util.HashSet;
    +import java.util.Set;
     import java.util.concurrent.TimeUnit;
     import java.util.logging.Logger;
     import java.util.Random;
    @@ -99,15 +102,49 @@ public abstract class PeriodicWork extends SafeTimerTask implements ExtensionPoi
         @Initializer(after= JOB_LOADED)
         public static void init() {
             // start all PeriodicWorks
    -        for (PeriodicWork p : PeriodicWork.all()) {
    -            Timer.get().scheduleAtFixedRate(p, p.getInitialDelay(), p.getRecurrencePeriod(), TimeUnit.MILLISECONDS);
    +        ExtensionList<PeriodicWork> extensionList = all();
    +        extensionList.addListener(new PeriodicWorkExtensionListListener(extensionList));
    +        for (PeriodicWork p : extensionList) {
    +            schedulePeriodicWork(p);
             }
         }
     
    +    private static void schedulePeriodicWork(PeriodicWork p) {
    +        Timer.get().scheduleAtFixedRate(p, p.getInitialDelay(), p.getRecurrencePeriod(), TimeUnit.MILLISECONDS);
    +    }
    +
         // time constants
         protected static final long MIN = 1000*60;
         protected static final long HOUR =60*MIN;
         protected static final long DAY = 24*HOUR;
     
         private static final Random RANDOM = new Random();
    +
    +    /**
    +     * ExtensionListener that will kick off any new AperiodWork extensions from plugins that are dynamically
    +     * loaded.
    +     */
    +    private static class PeriodicWorkExtensionListListener extends ExtensionListListener {
    +
    +        private final Set<PeriodicWork> registered = new HashSet<>();
    +
    +        PeriodicWorkExtensionListListener(ExtensionList<PeriodicWork> initiallyRegistered) {
    +            for (PeriodicWork p : initiallyRegistered) {
    +                registered.add(p);
    +            }
    +        }
    +
    +        @Override
    +        public void onChange() {
    +            synchronized (registered) {
    +                for (PeriodicWork p : PeriodicWork.all()) {
    +                    // it is possibly to programatically remove Extensions but that is rarely used.
    +                    if (!registered.contains(p)) {
    +                        schedulePeriodicWork(p);
    +                        registered.add(p);
    +                    }
    +                }
    +            }
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/model/PersistentDescriptor.java b/core/src/main/java/hudson/model/PersistentDescriptor.java
    new file mode 100644
    index 0000000000000000000000000000000000000000..837d154cfb51be87f96d5a40fefa846714ee8d96
    --- /dev/null
    +++ b/core/src/main/java/hudson/model/PersistentDescriptor.java
    @@ -0,0 +1,16 @@
    +package hudson.model;
    +
    +import javax.annotation.PostConstruct;
    +
    +/**
    + * Marker interface for Descriptors which use xml persistent data, and as such need to load from disk when instantiated.
    + * <p>
    + * {@link Descriptor#load()} method is annotated as {@link PostConstruct} so it get automatically invoked after
    + * constructor and field injection.
    + * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
    + */
    +public interface PersistentDescriptor extends Saveable {
    +
    +    @PostConstruct
    +    void load();
    +}
    diff --git a/core/src/main/java/hudson/model/ProxyView.java b/core/src/main/java/hudson/model/ProxyView.java
    index 795b94ba8635d64757a30386b35afaca22308602..a879dfd0b0d9211ce40018b05ade964ea5d65b10 100644
    --- a/core/src/main/java/hudson/model/ProxyView.java
    +++ b/core/src/main/java/hudson/model/ProxyView.java
    @@ -91,6 +91,11 @@ public class ProxyView extends View implements StaplerFallback {
             return getProxiedView().contains(item);
         }
     
    +    @Override
    +    public TopLevelItem getItem(String name) {
    +        return getProxiedView().getItem(name);
    +    }
    +
         @Override
         protected void submit(StaplerRequest req) throws IOException, ServletException, FormException {
             String proxiedViewName = req.getSubmittedForm().getString("proxiedViewName");
    diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java
    index 6ad10d5f818e227d2df1ef623b75963344c71672..1eaeb089310d8be5faf4325af606a43eec149951 100644
    --- a/core/src/main/java/hudson/model/Queue.java
    +++ b/core/src/main/java/hudson/model/Queue.java
    @@ -24,10 +24,12 @@
      */
     package hudson.model;
     
    +import com.google.common.annotations.VisibleForTesting;
     import com.google.common.cache.Cache;
     import com.google.common.cache.CacheBuilder;
     import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
     import hudson.BulkChange;
    +import hudson.Extension;
     import hudson.ExtensionList;
     import hudson.ExtensionPoint;
     import hudson.Util;
    @@ -64,7 +66,10 @@ import hudson.model.queue.WorkUnitContext;
     import hudson.security.ACL;
     import hudson.security.AccessControlled;
     import java.nio.file.Files;
    +
    +import hudson.util.Futures;
     import jenkins.security.QueueItemAuthenticatorProvider;
    +import jenkins.util.SystemProperties;
     import jenkins.util.Timer;
     import hudson.triggers.SafeTimerTask;
     import java.util.concurrent.TimeUnit;
    @@ -93,7 +98,6 @@ import java.util.NoSuchElementException;
     import java.util.Set;
     import java.util.TreeSet;
     import java.util.concurrent.Callable;
    -import java.util.concurrent.TimeUnit;
     import java.util.concurrent.Future;
     import java.util.concurrent.atomic.AtomicLong;
     import java.util.concurrent.locks.Condition;
    @@ -102,6 +106,7 @@ import java.util.logging.Level;
     import java.util.logging.Logger;
     
     import javax.annotation.Nonnull;
    +import javax.annotation.concurrent.GuardedBy;
     import javax.servlet.ServletException;
     
     import jenkins.model.Jenkins;
    @@ -454,6 +459,9 @@ public class Queue extends ResourceController implements Saveable {
          */
         public void save() {
             if(BulkChange.contains(this))  return;
    +        if (Jenkins.getInstanceOrNull() == null) {
    +            return;
    +        }
     
             XmlFile queueFile = new XmlFile(XSTREAM, getXMLQueueFile());
             lock.lock();
    @@ -499,11 +507,11 @@ public class Queue extends ResourceController implements Saveable {
         }
     
         private File getQueueFile() {
    -        return new File(Jenkins.getInstance().getRootDir(), "queue.txt");
    +        return new File(Jenkins.get().getRootDir(), "queue.txt");
         }
     
         /*package*/ File getXMLQueueFile() {
    -        return new File(Jenkins.getInstance().getRootDir(), "queue.xml");
    +        return new File(Jenkins.get().getRootDir(), "queue.xml");
         }
     
         /**
    @@ -752,7 +760,9 @@ public class Queue extends ResourceController implements Saveable {
         public HttpResponse doCancelItem(@QueryParameter long id) throws IOException, ServletException {
             Item item = getItem(id);
             if (item != null) {
    -            cancel(item);
    +            if(item.hasCancelPermission()){
    +                cancel(item);
    +            }
             } // else too late, ignore (JENKINS-14813)
             return HttpResponses.forwardToPreviousPage();
         }
    @@ -1102,7 +1112,7 @@ public class Queue extends ResourceController implements Saveable {
         /**
          * Gets the information about the queue item for the given project.
          *
    -     * @return null if the project is not in the queue.
    +     * @return empty if the project is not in the queue.
          */
         public List<Item> getItems(Task t) {
             Snapshot snapshot = this.snapshot;
    @@ -1445,6 +1455,10 @@ public class Queue extends ResourceController implements Saveable {
          * and it also gets invoked periodically (see {@link Queue.MaintainTask}.)
          */
         public void maintain() {
    +        Jenkins jenkins = Jenkins.getInstanceOrNull();
    +        if (jenkins == null) {
    +            return;
    +        }
             lock.lock();
             try { try {
     
    @@ -1455,8 +1469,8 @@ public class Queue extends ResourceController implements Saveable {
     
                 {// update parked (and identify any pending items whose executor has disappeared)
                     List<BuildableItem> lostPendings = new ArrayList<BuildableItem>(pendings);
    -                for (Computer c : Jenkins.getInstance().getComputers()) {
    -                    for (Executor e : c.getExecutors()) {
    +                for (Computer c : jenkins.getComputers()) {
    +                    for (Executor e : c.getAllExecutors()) {
                             if (e.isInterrupted()) {
                                 // JENKINS-28840 we will deadlock if we try to touch this executor while interrupt flag set
                                 // we need to clear lost pendings as we cannot know what work unit was on this executor
    @@ -1555,8 +1569,15 @@ public class Queue extends ResourceController implements Saveable {
                     }
                 }
     
    -            if (s != null)
    -                s.sortBuildableItems(buildables);
    +            if (s != null) {
    +                try {
    +                    s.sortBuildableItems(buildables);
    +                } catch (Throwable e) {
    +                    // We don't really care if the sort doesn't sort anything, we still should
    +                    // continue to do our job. We'll complain about it and continue.
    +                    LOGGER.log(Level.WARNING, "s.sortBuildableItems() threw Throwable: {0}", e);
    +                }
    +            }
                 
                 // Ensure that identification of blocked tasks is using the live state: JENKINS-27708 & JENKINS-27871
                 updateSnapshot();
    @@ -1875,6 +1896,18 @@ public class Queue extends ResourceController implements Saveable {
              */
             String getFullDisplayName();
     
    +        /**
    +         * Returns task-specific key which is used by the {@link LoadBalancer} to choose one particular executor
    +         * amongst all the free executors on all possibly suitable nodes.
    +         * NOTE: To be able to re-use the same node during the next run this key should not change from one run to
    +         * another. You probably want to compute that key based on the job's name.
    +         * <p>
    +         * @return by default: {@link #getFullDisplayName()}
    +         *
    +         * @see hudson.model.LoadBalancer
    +         */
    +        default String getAffinityKey() { return getFullDisplayName(); }
    +
             /**
              * Checks the permission to see if the current user can abort this executable.
              * Returns normally from this method if it's OK.
    @@ -1983,7 +2016,7 @@ public class Queue extends ResourceController implements Saveable {
          *
          * <h2>Views</h2>
          * <p>
    -     * Implementation must have <tt>executorCell.jelly</tt>, which is
    +     * Implementation must have {@code executorCell.jelly}, which is
          * used to render the HTML that indicates this executable is executing.
          */
         public interface Executable extends Runnable {
    @@ -2184,8 +2217,10 @@ public class Queue extends ResourceController implements Saveable {
                 for (Action action: actions) addAction(action);
             }
     
    +        @SuppressWarnings("deprecation") // JENKINS-51584
             protected Item(Item item) {
    -        	this(item.task, new ArrayList<Action>(item.getAllActions()), item.id, item.future, item.inQueueSince);
    +            // do not use item.getAllActions() here as this will persist actions from a TransientActionFactory
    +            this(item.task, new ArrayList<Action>(item.getActions()), item.id, item.future, item.inQueueSince);
             }
     
             /**
    @@ -2251,7 +2286,9 @@ public class Queue extends ResourceController implements Saveable {
             @Deprecated
             @RequirePOST
             public HttpResponse doCancelQueue() throws IOException, ServletException {
    -        	Jenkins.getInstance().getQueue().cancel(this);
    +            if(hasCancelPermission()){
    +                Jenkins.getInstance().getQueue().cancel(this);
    +            }
                 return HttpResponses.forwardToPreviousPage();
             }
     
    @@ -3009,4 +3046,75 @@ public class Queue extends ResourceController implements Saveable {
         public static void init(Jenkins h) {
             h.getQueue().load();
         }
    +
    +    /**
    +     * Schedule {@code Queue.save()} call for near future once items change. Ignore all changes until the time the save
    +     * takes place.
    +     *
    +     * Once queue is restored after a crash, items stages might not be accurate until the next #maintain() - this is not
    +     * a problem as the items will be reshuffled first and then scheduled during the next maintainance cycle.
    +     *
    +     * Implementation note: Queue.load() calls QueueListener hooks for every item deserialized that can hammer the persistance
    +     * on load. The problem is avoided by delaying the actual save for the time long enough for queue to load so the save
    +     * operations will collapse into one. Also, items are persisted as buildable or blocked in vast majority of cases and
    +     * those stages does not trigger the save here.
    +     */
    +    @Extension
    +    @Restricted(NoExternalUse.class)
    +    public static final class Saver extends QueueListener implements Runnable {
    +
    +        /**
    +         * All negative values will disable periodic saving.
    +         */
    +        @VisibleForTesting
    +        /*package*/ static /*final*/ int DELAY_SECONDS = SystemProperties.getInteger("hudson.model.Queue.Saver.DELAY_SECONDS", 60);
    +
    +        private final Object lock = new Object();
    +        @GuardedBy("lock")
    +        private Future<?> nextSave;
    +
    +        @Override
    +        public void onEnterWaiting(WaitingItem wi) {
    +            push();
    +        }
    +
    +        @Override
    +        public void onLeft(Queue.LeftItem li) {
    +            push();
    +        }
    +
    +        private void push() {
    +            if (DELAY_SECONDS < 0) return;
    +
    +            synchronized (lock) {
    +                // Can be done or canceled in case of a bug or external intervention - do not allow it to hang there forever
    +                if (nextSave != null && !(nextSave.isDone() || nextSave.isCancelled())) return;
    +                nextSave = Timer.get().schedule(this, DELAY_SECONDS, TimeUnit.SECONDS);
    +            }
    +        }
    +
    +        @Override
    +        public void run() {
    +            try {
    +                Jenkins j = Jenkins.getInstanceOrNull();
    +                if (j != null) {
    +                    j.getQueue().save();
    +                }
    +            } finally {
    +                synchronized (lock) {
    +                    nextSave = null;
    +                }
    +            }
    +        }
    +
    +        @VisibleForTesting @Restricted(NoExternalUse.class)
    +        /*package*/ @Nonnull Future<?> getNextSave() {
    +            synchronized (lock) {
    +                return nextSave == null
    +                        ? Futures.precomputed(null)
    +                        : nextSave
    +                ;
    +            }
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java
    index f7e11b1c657f13b6ddfe6d4a5a8a18b561333c5e..6b9c3fbbd97d1e9a8dd1d8b91565fe6e17a73bbe 100644
    --- a/core/src/main/java/hudson/model/Run.java
    +++ b/core/src/main/java/hudson/model/Run.java
    @@ -59,6 +59,7 @@ import hudson.security.Permission;
     import hudson.security.PermissionGroup;
     import hudson.security.PermissionScope;
     import hudson.tasks.BuildWrapper;
    +import hudson.tasks.Fingerprinter.FingerprintAction;
     import hudson.util.FormApply;
     import hudson.util.LogTaskListener;
     import hudson.util.ProcessTree;
    @@ -73,12 +74,14 @@ import java.io.OutputStream;
     import java.io.PrintWriter;
     import java.io.RandomAccessFile;
     import java.io.Reader;
    +import java.io.Serializable;
     import java.nio.charset.Charset;
     import java.text.DateFormat;
     import java.text.SimpleDateFormat;
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Calendar;
    +import java.util.Collection;
     import java.util.Collections;
     import java.util.Comparator;
     import java.util.Date;
    @@ -92,6 +95,8 @@ import java.util.Map;
     import java.util.Set;
     import java.util.logging.Level;
     import static java.util.logging.Level.*;
    +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
    +
     import java.util.logging.Logger;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
    @@ -108,6 +113,7 @@ import jenkins.model.RunAction2;
     import jenkins.model.StandardArtifactManager;
     import jenkins.model.lazy.BuildReference;
     import jenkins.model.lazy.LazyBuildMixIn;
    +import jenkins.security.MasterToSlaveCallable;
     import jenkins.util.VirtualFile;
     import jenkins.util.io.OnMaster;
     import net.sf.json.JSONObject;
    @@ -119,8 +125,10 @@ import org.apache.commons.lang.ArrayUtils;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.HttpResponse;
    +import org.kohsuke.stapler.HttpResponses;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.Stapler;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.export.Exported;
    @@ -140,7 +148,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
      */
     @ExportedBean
     public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>>
    -        extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster {
    +        extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster, StaplerProxy {
     
         /**
          * The original {@link Queue.Item#getId()} has not yet been mapped onto the {@link Run} instance.
    @@ -1090,12 +1098,16 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
          * @return The list can be empty but never null
          */ 
         public @Nonnull List<Artifact> getArtifactsUpTo(int artifactsNumber) {
    -        ArtifactList r = new ArtifactList();
    +        SerializableArtifactList sal;
    +        VirtualFile root = getArtifactManager().root();
             try {
    -            addArtifacts(getArtifactManager().root(), "", "", r, null, artifactsNumber);
    +            sal = root.run(new AddArtifacts(root, artifactsNumber));
             } catch (IOException x) {
                 LOGGER.log(Level.WARNING, null, x);
    +            sal = new SerializableArtifactList();
             }
    +        ArtifactList r = new ArtifactList();
    +        r.updateFrom(sal);
             r.computeDisplayName();
             return r;
         }
    @@ -1109,9 +1121,25 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
             return !getArtifactsUpTo(1).isEmpty();
         }
     
    -    private int addArtifacts(@Nonnull VirtualFile dir, 
    +    private static final class AddArtifacts extends MasterToSlaveCallable<SerializableArtifactList, IOException> {
    +        private static final long serialVersionUID = 1L;
    +        private final VirtualFile root;
    +        private final int artifactsNumber;
    +        AddArtifacts(VirtualFile root, int artifactsNumber) {
    +            this.root = root;
    +            this.artifactsNumber = artifactsNumber;
    +        }
    +        @Override
    +        public SerializableArtifactList call() throws IOException {
    +            SerializableArtifactList sal = new SerializableArtifactList();
    +            addArtifacts(root, "", "", sal, null, artifactsNumber);
    +            return sal;
    +        }
    +    }
    +
    +    private static int addArtifacts(@Nonnull VirtualFile dir,
                 @Nonnull String path, @Nonnull String pathHref, 
    -            @Nonnull ArtifactList r, @Nonnull Artifact parent, int upTo) throws IOException {
    +            @Nonnull SerializableArtifactList r, @CheckForNull SerializableArtifact parent, int upTo) throws IOException {
             VirtualFile[] kids = dir.list();
             Arrays.sort(kids);
     
    @@ -1122,26 +1150,26 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
                 String childHref = pathHref + Util.rawEncode(child);
                 String length = sub.isFile() ? String.valueOf(sub.length()) : "";
                 boolean collapsed = (kids.length==1 && parent!=null);
    -            Artifact a;
    +            SerializableArtifact a;
                 if (collapsed) {
                     // Collapse single items into parent node where possible:
    -                a = new Artifact(parent.getFileName() + '/' + child, childPath,
    +                a = new SerializableArtifact(parent.name + '/' + child, childPath,
                                      sub.isDirectory() ? null : childHref, length,
    -                                 parent.getTreeNodeId());
    +                                 parent.treeNodeId);
                     r.tree.put(a, r.tree.remove(parent));
                 } else {
                     // Use null href for a directory:
    -                a = new Artifact(child, childPath,
    +                a = new SerializableArtifact(child, childPath,
                                      sub.isDirectory() ? null : childHref, length,
                                      "n" + ++r.idSeq);
    -                r.tree.put(a, parent!=null ? parent.getTreeNodeId() : null);
    +                r.tree.put(a, parent!=null ? parent.treeNodeId : null);
                 }
                 if (sub.isDirectory()) {
                     n += addArtifacts(sub, childPath + '/', childHref + '/', r, a, upTo-n);
                     if (n>=upTo) break;
                 } else {
                     // Don't store collapsed path in ArrayList (for correct data in external API)
    -                r.add(collapsed ? new Artifact(child, a.relativePath, a.href, length, a.treeNodeId) : a);
    +                r.add(collapsed ? new SerializableArtifact(child, a.relativePath, a.href, length, a.treeNodeId) : a);
                     if (++n>=upTo) break;
                 }
             }
    @@ -1159,6 +1187,30 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
         public static final int TREE_CUTOFF = Integer.parseInt(SystemProperties.getString("hudson.model.Run.ArtifactList.treeCutoff", "40"));
     
         // ..and then "too many"
    +    
    +    /** {@link Run.Artifact} without the implicit link to {@link Run} */
    +    private static final class SerializableArtifact implements Serializable {
    +        private static final long serialVersionUID = 1L;
    +        final String name;
    +        final String relativePath;
    +        final String href;
    +        final String length;
    +        final String treeNodeId;
    +        SerializableArtifact(String name, String relativePath, String href, String length, String treeNodeId) {
    +            this.name = name;
    +            this.relativePath = relativePath;
    +            this.href = href;
    +            this.length = length;
    +            this.treeNodeId = treeNodeId;
    +        }
    +    }
    +
    +    /** {@link Run.ArtifactList} without the implicit link to {@link Run} */
    +    private static final class SerializableArtifactList extends ArrayList<SerializableArtifact> {
    +        private static final long serialVersionUID = 1L;
    +        private LinkedHashMap<SerializableArtifact, String> tree = new LinkedHashMap<>();
    +        private int idSeq = 0;
    +    }
     
         public final class ArtifactList extends ArrayList<Artifact> {
             private static final long serialVersionUID = 1L;
    @@ -1167,7 +1219,24 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
              * Contains Artifact objects for directories and files (the ArrayList contains only files).
              */
             private LinkedHashMap<Artifact,String> tree = new LinkedHashMap<Artifact,String>();
    -        private int idSeq = 0;
    +
    +        void updateFrom(SerializableArtifactList clone) {
    +            Map<String, Artifact> artifacts = new HashMap<>(); // need to share objects between tree and list, since computeDisplayName mutates displayPath
    +            for (SerializableArtifact sa : clone) {
    +                Artifact a = new Artifact(sa);
    +                artifacts.put(a.relativePath, a);
    +                add(a);
    +            }
    +            tree = new LinkedHashMap<>();
    +            for (Map.Entry<SerializableArtifact, String> entry : clone.tree.entrySet()) {
    +                SerializableArtifact sa = entry.getKey();
    +                Artifact a = artifacts.get(sa.relativePath);
    +                if (a == null) {
    +                    a = new Artifact(sa);
    +                }
    +                tree.put(a, entry.getValue());
    +            }
    +        }
     
             public Map<Artifact,String> getTree() {
                 return tree;
    @@ -1282,6 +1351,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
              */
             private String length;
     
    +        Artifact(SerializableArtifact clone) {
    +            this(clone.name, clone.relativePath, clone.href, clone.length, clone.treeNodeId);
    +        }
    +
             /*package for test*/ Artifact(String name, String relativePath, String href, String len, String treeNodeId) {
                 this.name = name;
                 this.relativePath = relativePath;
    @@ -1334,6 +1407,21 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
             }
         }
     
    +    /**
    +     * get the fingerprints associated with this build
    +     *
    +     * @return The fingerprints
    +     */
    +    @Nonnull
    +    @Exported(name = "fingerprint", inline = true, visibility = -1)
    +    public Collection<Fingerprint> getBuildFingerprints() {
    +        FingerprintAction fingerprintAction = getAction(FingerprintAction.class);
    +        if (fingerprintAction != null) {
    +            return fingerprintAction.getFingerprints().values();
    +        }
    +        return Collections.<Fingerprint>emptyList();
    +    }
    +    
         /**
          * Returns the log file.
          * @return The file may reference both uncompressed or compressed logs
    @@ -1387,21 +1475,12 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
         }
     
         /**
    -     * Used from <tt>console.jelly</tt> to write annotated log to the given output.
    +     * Used from {@code console.jelly} to write annotated log to the given output.
          *
          * @since 1.349
          */
         public void writeLogTo(long offset, @Nonnull XMLOutput out) throws IOException {
    -        try {
    -			getLogText().writeHtmlTo(offset,out.asWriter());
    -		} catch (IOException e) {
    -			// try to fall back to the old getLogInputStream()
    -			// mainly to support .gz compressed files
    -			// In this case, console annotation handling will be turned off.
    -			try (InputStream input = getLogInputStream()) {
    -				IOUtils.copy(input, out.asWriter());
    -			}
    -		}
    +        getLogText().writeHtmlTo(offset, out.asWriter());
         }
     
         /**
    @@ -1487,6 +1566,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
             
             RunListener.fireDeleted(this);
     
    +        if (artifactManager != null) {
    +            deleteArtifacts();
    +        } // for StandardArtifactManager, deleting the whole build dir suffices
    +
             synchronized (this) { // avoid holding a lock while calling plugin impls of onDeleted
             File tmp = new File(rootDir.getParentFile(),'.'+rootDir.getName());
             
    @@ -2491,6 +2574,26 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
             return returnedResult;
         }
     
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            // This is a bit weird, but while the Run's PermissionScope does not have READ, delegate to the parent
    +            if (!getParent().hasPermission(Item.DISCOVER)) {
    +                return null;
    +            }
    +            getParent().checkPermission(Item.READ);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(Run.class.getName() + ".skipPermissionCheck");
    +
    +
         public static class RedirectUp {
             public void doDynamic(StaplerResponse rsp) throws IOException {
                 // Compromise to handle both browsers (auto-redirect) and programmatic access
    diff --git a/core/src/main/java/hudson/model/RunParameterDefinition.java b/core/src/main/java/hudson/model/RunParameterDefinition.java
    index da1e7865e62734ae184fad71e5c0b2b01a53d2df..8f725193f22439c545695924a2445d00664763fd 100644
    --- a/core/src/main/java/hudson/model/RunParameterDefinition.java
    +++ b/core/src/main/java/hudson/model/RunParameterDefinition.java
    @@ -92,7 +92,7 @@ public class RunParameterDefinition extends SimpleParameterDefinition {
         public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) {
             if (defaultValue instanceof RunParameterValue) {
                 RunParameterValue value = (RunParameterValue) defaultValue;
    -            return new RunParameterDefinition(getName(), value.getRunId(), getDescription(), getFilter());
    +            return new RunParameterDefinition(getName(), getProjectName(), value.getRunId(), getDescription(), getFilter());
             } else {
                 return this;
             }
    diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java
    index cde11411fe8a4df976bea76232081b318232e233..f4395847929c2df9356113c0238708f70b9f4951 100644
    --- a/core/src/main/java/hudson/model/Slave.java
    +++ b/core/src/main/java/hudson/model/Slave.java
    @@ -84,7 +84,7 @@ import org.kohsuke.stapler.StaplerResponse;
      * Information about a Hudson agent node.
      *
      * <p>
    - * Ideally this would have been in the <tt>hudson.slaves</tt> package,
    + * Ideally this would have been in the {@code hudson.slaves} package,
      * but for compatibility reasons, it can't.
      *
      * <p>
    @@ -146,8 +146,8 @@ public abstract class Slave extends Node implements Serializable {
          */
         private String label="";
     
    -    private /*almost final*/ DescribableList<NodeProperty<?>,NodePropertyDescriptor> nodeProperties = 
    -                                    new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(Jenkins.getInstance().getNodesObject());
    +    private /*almost final*/ DescribableList<NodeProperty<?>,NodePropertyDescriptor> nodeProperties =
    +            new DescribableList<>(this);
     
         /**
          * Lazily computed set of labels from {@link #label}.
    @@ -493,19 +493,19 @@ public abstract class Slave extends Node implements Serializable {
                 // RemoteLauncher requires an active Channel instance to operate correctly
                 final Channel channel = c.getChannel();
                 if (channel == null) { 
    -                reportLauncerCreateError("The agent has not been fully initialized yet",
    +                reportLauncherCreateError("The agent has not been fully initialized yet",
                                              "No remoting channel to the agent OR it has not been fully initialized yet", listener);
                     return new Launcher.DummyLauncher(listener);
                 }
                 if (channel.isClosingOrClosed()) {
    -                reportLauncerCreateError("The agent is being disconnected",
    +                reportLauncherCreateError("The agent is being disconnected",
                                              "Remoting channel is either in the process of closing down or has closed down", listener);
                     return new Launcher.DummyLauncher(listener);
                 }
                 final Boolean isUnix = c.isUnix();
                 if (isUnix == null) {
                     // isUnix is always set when the channel is not null, so it should never happen
    -                reportLauncerCreateError("The agent has not been fully initialized yet",
    +                reportLauncherCreateError("The agent has not been fully initialized yet",
                                              "Cannot determing if the agent is a Unix one, the System status request has not completed yet. " +
                                              "It is an invalid channel state, please report a bug to Jenkins if you see it.", 
                                              listener);
    @@ -516,7 +516,7 @@ public abstract class Slave extends Node implements Serializable {
             }
         }
         
    -    private void reportLauncerCreateError(@Nonnull String humanReadableMsg, @CheckForNull String exceptionDetails, @Nonnull TaskListener listener) {
    +    private void reportLauncherCreateError(@Nonnull String humanReadableMsg, @CheckForNull String exceptionDetails, @Nonnull TaskListener listener) {
             String message = "Issue with creating launcher for agent " + name + ". " + humanReadableMsg;
             listener.error(message);
             if (LOGGER.isLoggable(Level.WARNING)) {
    @@ -529,7 +529,12 @@ public abstract class Slave extends Node implements Serializable {
     
         /**
          * Gets the corresponding computer object.
    +     *
    +     * @return
    +     *      this method can return null if there's no {@link Computer} object for this node,
    +     *      such as when this node has no executors at all.
          */
    +    @CheckForNull
         public SlaveComputer getComputer() {
             return (SlaveComputer)toComputer();
         }
    @@ -554,7 +559,7 @@ public abstract class Slave extends Node implements Serializable {
          */
         protected Object readResolve() {
             if(nodeProperties==null)
    -            nodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(Jenkins.getInstance().getNodesObject());
    +            nodeProperties = new DescribableList<>(this);
             return this;
         }
     
    diff --git a/core/src/main/java/hudson/model/TaskListener.java b/core/src/main/java/hudson/model/TaskListener.java
    index aeaae6643902a7a67337233d5702dd6dd00e95ab..e5e27f56c5c8cb92189f6161a1aafed79dd2bc83 100644
    --- a/core/src/main/java/hudson/model/TaskListener.java
    +++ b/core/src/main/java/hudson/model/TaskListener.java
    @@ -25,6 +25,7 @@ package hudson.model;
     
     import hudson.console.ConsoleNote;
     import hudson.console.HyperlinkNote;
    +import hudson.remoting.Channel;
     import hudson.util.NullStream;
     import hudson.util.StreamTaskListener;
     
    @@ -59,7 +60,9 @@ import org.kohsuke.accmod.restrictions.ProtectedExternally;
      *
      * <p>
      * {@link StreamTaskListener} is the most typical implementation of this interface.
    - * All the {@link TaskListener} implementations passed to plugins from Hudson core are remotable.
    + *
    + * <p>
    + * Implementations are generally expected to be remotable via {@link Channel}.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/model/TopLevelItemDescriptor.java b/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
    index 32aee86d1c638d9424bb78ab73fb48a4a046f8e8..91488a22cc72f833f45322dd68704dfb0f4e473f 100644
    --- a/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
    +++ b/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
    @@ -125,7 +125,7 @@ public abstract class TopLevelItemDescriptor extends Descriptor<TopLevelItem> im
          *
          * <p>
          * Used as the caption when the user chooses what item type to create.
    -     * The descriptor implementation also needs to have <tt>newInstanceDetail.jelly</tt>
    +     * The descriptor implementation also needs to have {@code newInstanceDetail.jelly}
          * script, which will be used to render the text below the caption
          * that explains the item type.
          */
    @@ -135,10 +135,11 @@ public abstract class TopLevelItemDescriptor extends Descriptor<TopLevelItem> im
         }
     
         /**
    -     * A description of this kind of item type. This description can contain HTML code but it is recommend to use text plain
    -     * in order to avoid how it should be represented.
    +     * A description of this kind of item type. This description can contain HTML code but it is recommended that
    +     * you use plain text in order to be consistent with the rest of Jenkins.
          *
    -     * This method should be called in a thread where Stapler is associated, but it will return an empty string.
    +     * This method should be called from a thread where Stapler is handling an HTTP request, otherwise it will
    +     * return an empty string.
          *
          * @return A string, by default the value from newInstanceDetail view is taken.
          *
    diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java
    index e2686cee56acfd6b335b909113730dbfd59dadb9..68aa1d292f470739476bf317b33490b857fd4233 100644
    --- a/core/src/main/java/hudson/model/UpdateCenter.java
    +++ b/core/src/main/java/hudson/model/UpdateCenter.java
    @@ -23,6 +23,7 @@
      */
     package hudson.model;
     
    +import com.google.common.annotations.VisibleForTesting;
     import hudson.BulkChange;
     import hudson.Extension;
     import hudson.ExtensionPoint;
    @@ -38,6 +39,7 @@ import jenkins.util.SystemProperties;
     import hudson.Util;
     import hudson.XmlFile;
     import static hudson.init.InitMilestone.PLUGINS_STARTED;
    +import static java.util.logging.Level.INFO;
     import static java.util.logging.Level.WARNING;
     
     import hudson.init.Initializer;
    @@ -52,7 +54,6 @@ import hudson.util.DaemonThreadFactory;
     import hudson.util.FormValidation;
     import hudson.util.HttpResponses;
     import hudson.util.NamingThreadFactory;
    -import hudson.util.IOException2;
     import hudson.util.PersistedList;
     import hudson.util.XStream2;
     import jenkins.MissingDependencyException;
    @@ -71,6 +72,7 @@ import org.jenkinsci.Symbol;
     import org.jvnet.localizer.Localizable;
     import org.kohsuke.accmod.restrictions.DoNotUse;
     import org.kohsuke.stapler.HttpResponse;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     
    @@ -147,7 +149,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
      * @since 1.220
      */
     @ExportedBean
    -public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster {
    +public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster, StaplerProxy {
     
         private static final String UPDATE_CENTER_URL = SystemProperties.getString(UpdateCenter.class.getName()+".updateCenterUrl","https://updates.jenkins.io/");
     
    @@ -293,7 +295,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
         }
     
         public Api getApi() {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             return new Api(this);
         }
     
    @@ -365,7 +366,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @Restricted(DoNotUse.class)
         public HttpResponse doConnectionStatus(StaplerRequest request) {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             try {
                 String siteId = request.getParameter("siteId");
                 if (siteId == null) {
    @@ -418,7 +418,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @Restricted(DoNotUse.class) // WebOnly
         public HttpResponse doIncompleteInstallStatus() {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             try {
             Map<String,String> jobs = InstallUtil.getPersistedInstallStatus();
             if(jobs == null) {
    @@ -468,7 +467,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @Restricted(DoNotUse.class)
         public HttpResponse doInstallStatus(StaplerRequest request) {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             try {
                 String correlationId = request.getParameter("correlationId");
                 Map<String,Object> response = new HashMap<>();
    @@ -577,7 +575,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
         }
     
         /**
    -     * Gets the {@link UpdateSite} from which we receive updates for <tt>jenkins.war</tt>.
    +     * Gets the {@link UpdateSite} from which we receive updates for {@code jenkins.war}.
          *
          * @return
          *      {@code null} if no such update center is provided.
    @@ -634,7 +632,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @RequirePOST
         public void doUpgrade(StaplerResponse rsp) throws IOException, ServletException {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             HudsonUpgradeJob job = new HudsonUpgradeJob(getCoreSource(), Jenkins.getAuthentication());
             if(!Lifecycle.get().canRewriteHudsonWar()) {
                 sendError("Jenkins upgrade not supported in this running mode");
    @@ -653,7 +650,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @RequirePOST
         public HttpResponse doInvalidateData() {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             for (UpdateSite site : sites) {
                 site.doInvalidateData();
             }
    @@ -669,7 +665,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
         public void doSafeRestart(StaplerRequest request, StaplerResponse response) throws IOException, ServletException {
             synchronized (jobs) {
                 if (!isRestartScheduled()) {
    -                Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                     addJob(new RestartJenkinsJob(getCoreSource()));
                     LOGGER.info("Scheduling Jenkins reboot");
                 }
    @@ -740,7 +735,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @RequirePOST
         public void doDowngrade(StaplerResponse rsp) throws IOException, ServletException {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             if(!isDowngradable()) {
                 sendError("Jenkins downgrade is not possible, probably backup does not exist");
                 return;
    @@ -757,7 +751,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
          */
         @RequirePOST
         public void doRestart(StaplerResponse rsp) throws IOException, ServletException {
    -        Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication());
             LOGGER.info("Scheduling the core downgrade");
     
    @@ -996,7 +989,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
             return results;
         }
     
    -
         /**
          * {@link AdministrativeMonitor} that checks if there's Jenkins update.
          */
    @@ -1121,12 +1113,15 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
              */
             public File download(DownloadJob job, URL src) throws IOException {
                 MessageDigest sha1 = null;
    +            MessageDigest sha256 = null;
    +            MessageDigest sha512 = null;
                 try {
    +                // Java spec says SHA-1 and SHA-256 exist, and SHA-512 might not, so one try/catch block should be fine
                     sha1 = MessageDigest.getInstance("SHA-1");
    -            } catch (NoSuchAlgorithmException ignored) {
    -                // Irrelevant as the Java spec says SHA-1 must exist. Still, if this fails
    -                // the DownloadJob will just have computedSha1 = null and that is expected
    -                // to be handled by caller
    +                sha256 = MessageDigest.getInstance("SHA-256");
    +                sha512 = MessageDigest.getInstance("SHA-512");
    +            } catch (NoSuchAlgorithmException nsa) {
    +                LOGGER.log(Level.WARNING, "Failed to instantiate message digest algorithm, may only have weak or no verification of downloaded file", nsa);
                 }
     
                 URLConnection con = null;
    @@ -1149,7 +1144,10 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
                     String oldName = t.getName();
                     t.setName(oldName + ": " + src);
                     try (OutputStream _out = Files.newOutputStream(tmp.toPath());
    -                     OutputStream out = sha1 != null ? new DigestOutputStream(_out, sha1) : _out;
    +                     OutputStream out =
    +                             sha1 != null ? new DigestOutputStream(
    +                                     sha256 != null ? new DigestOutputStream(
    +                                             sha512 != null ? new DigestOutputStream(_out, sha512) : _out, sha256) : _out, sha1) : _out;
                          InputStream in = con.getInputStream();
                          CountingInputStream cin = new CountingInputStream(in)) {
                         while ((len = cin.read(buf)) >= 0) {
    @@ -1173,6 +1171,14 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
                         byte[] digest = sha1.digest();
                         job.computedSHA1 = Base64.encodeBase64String(digest);
                     }
    +                if (sha256 != null) {
    +                    byte[] digest = sha256.digest();
    +                    job.computedSHA256 = Base64.encodeBase64String(digest);
    +                }
    +                if (sha512 != null) {
    +                    byte[] digest = sha512.digest();
    +                    job.computedSHA512 = Base64.encodeBase64String(digest);
    +                }
                     return tmp;
                 } catch (IOException e) {
                     // assist troubleshooting in case of e.g. "too many redirects" by printing actual URL
    @@ -1183,7 +1189,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
                         // Also, since it involved name resolution, it'd be an expensive operation.
                         extraMessage = " (redirected to: " + con.getURL() + ")";
                     }
    -                throw new IOException2("Failed to download from "+src+extraMessage,e);
    +                throw new IOException("Failed to download from "+src+extraMessage,e);
                 }
             }
     
    @@ -1226,8 +1232,8 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
              *
              * @deprecated as of 1.333
              *      With the introduction of multiple update center capability, this information
    -         *      is now a part of the <tt>update-center.json</tt> file. See
    -         *      <tt>http://jenkins-ci.org/update-center.json</tt> as an example.
    +         *      is now a part of the {@code update-center.json} file. See
    +         *      {@code http://jenkins-ci.org/update-center.json} as an example.
              */
             @Deprecated
             public String getConnectionCheckUrl() {
    @@ -1253,7 +1259,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
              * Returns the URL of the server that hosts plugins and core updates.
              *
              * @deprecated as of 1.333
    -         *      <tt>update-center.json</tt> is now signed, so we don't have to further make sure that
    +         *      {@code update-center.json} is now signed, so we don't have to further make sure that
              *      we aren't downloading from anywhere unsecure.
              */
             @Deprecated
    @@ -1287,7 +1293,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
         /**
          * Things that {@link UpdateCenter#installerService} executes.
          *
    -     * This object will have the <tt>row.jelly</tt> which renders the job on UI.
    +     * This object will have the {@code row.jelly} which renders the job on UI.
          */
         @ExportedBean
         public abstract class UpdateCenterJob implements Runnable {
    @@ -1409,7 +1415,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
                 status = new Running();
                 try {
                     // safeRestart records the current authentication for the log, so set it to the managing user
    -                try (ACLContext _ = ACL.as(User.get(authentication, false, Collections.emptyMap()))) {
    +                try (ACLContext acl = ACL.as(User.get(authentication, false, Collections.emptyMap()))) {
                         Jenkins.getInstance().safeRestart();
                     }
                 } catch (RestartNotSupportedException exception) {
    @@ -1603,11 +1609,18 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
                 status = new Success();
             }
         }
    +
    +    @Restricted(NoExternalUse.class)
    +    /*package*/ interface WithComputedChecksums {
    +        String getComputedSHA1();
    +        String getComputedSHA256();
    +        String getComputedSHA512();
    +    }
         
         /**
          * Base class for a job that downloads a file from the Jenkins project.
          */
    -    public abstract class DownloadJob extends UpdateCenterJob {
    +    public abstract class DownloadJob extends UpdateCenterJob implements WithComputedChecksums {
             /**
              * Immutable object representing the current state of this job.
              */
    @@ -1634,16 +1647,41 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
     
             /**
              * During download, an attempt is made to compute the SHA-1 checksum of the file.
    +         * This is the base64 encoded SHA-1 checksum.
              *
              * @since 1.641
              */
             @CheckForNull
    -        protected String getComputedSHA1() {
    +        public String getComputedSHA1() {
                 return computedSHA1;
             }
     
             private String computedSHA1;
     
    +        /**
    +         * Base64 encoded SHA-256 checksum of the downloaded file, if it could be computed.
    +         *
    +         * @since 2.130
    +         */
    +        @CheckForNull
    +        public String getComputedSHA256() {
    +            return computedSHA256;
    +        }
    +
    +        private String computedSHA256;
    +
    +        /**
    +         * Base64 encoded SHA-512 checksum of the downloaded file, if it could be computed.
    +         *
    +         * @since 2.130
    +         */
    +        @CheckForNull
    +        public String getComputedSHA512() {
    +            return computedSHA512;
    +        }
    +
    +        private String computedSHA512;
    +
             private Authentication authentication;
     
             /**
    @@ -1808,22 +1846,88 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
         }
     
         /**
    -     * If expectedSHA1 is non-null, ensure that actualSha1 is the same value, otherwise throw.
    -     *
    -     * Utility method for InstallationJob and HudsonUpgradeJob.
    +     * Compare the provided values and return the appropriate {@link VerificationResult}.
          *
    -     * @throws IOException when checksums don't match, or actual checksum was null.
          */
    -    private void verifyChecksums(String expectedSHA1, String actualSha1, File downloadedFile) throws IOException {
    -        if (expectedSHA1 != null) {
    -            if (actualSha1 == null) {
    -                // refuse to install if SHA-1 could not be computed
    +    private static VerificationResult verifyChecksums(String expectedDigest, String actualDigest, boolean caseSensitive) {
    +        if (expectedDigest == null) {
    +            return VerificationResult.NOT_PROVIDED;
    +        }
    +
    +        if (actualDigest == null) {
    +            return VerificationResult.NOT_COMPUTED;
    +        }
    +
    +        if (caseSensitive ? expectedDigest.equals(actualDigest) : expectedDigest.equalsIgnoreCase(actualDigest)) {
    +            return VerificationResult.PASS;
    +        }
    +
    +        return VerificationResult.FAIL;
    +    }
    +
    +    private static enum VerificationResult {
    +        PASS,
    +        NOT_PROVIDED,
    +        NOT_COMPUTED,
    +        FAIL
    +    }
    +
    +    /**
    +     * Throws an {@code IOException} with a message about {@code actual} not matching {@code expected} for {@code file} when using {@code algorithm}.
    +     */
    +    private static void throwVerificationFailure(String expected, String actual, File file, String algorithm) throws IOException {
    +        throw new IOException("Downloaded file " + file.getAbsolutePath() + " does not match expected " + algorithm + ", expected '" + expected + "', actual '" + actual + "'");
    +    }
    +
    +    /**
    +     * Implements the checksum verification logic with fallback to weaker algorithm for {@link DownloadJob}.
    +     * @param job The job downloading the file to check
    +     * @param entry The metadata entry for the file to check
    +     * @param file The downloaded file
    +     * @throws IOException thrown when one of the checks failed, or no checksum could be computed.
    +     */
    +    @VisibleForTesting
    +    @Restricted(NoExternalUse.class)
    +    /* package */ static void verifyChecksums(WithComputedChecksums job, UpdateSite.Entry entry, File file) throws IOException {
    +        VerificationResult result512 = verifyChecksums(entry.getSha512(), job.getComputedSHA512(), false);
    +        switch (result512) {
    +            case PASS:
    +                // this has passed so no reason to check the weaker checksums
    +                return;
    +            case FAIL:
    +                throwVerificationFailure(entry.getSha512(), job.getComputedSHA512(), file, "SHA-512");
    +            case NOT_COMPUTED:
    +                LOGGER.log(WARNING, "Attempt to verify a downloaded file (" + file.getName() + ") using SHA-512 failed since it could not be computed. Falling back to weaker algorithms. Update your JRE.");
    +                break;
    +            case NOT_PROVIDED:
    +                break;
    +        }
    +
    +        VerificationResult result256 = verifyChecksums(entry.getSha256(), job.getComputedSHA256(), false);
    +        switch (result256) {
    +            case PASS:
    +                return;
    +            case FAIL:
    +                throwVerificationFailure(entry.getSha256(), job.getComputedSHA256(), file, "SHA-256");
    +            case NOT_COMPUTED:
    +            case NOT_PROVIDED:
    +                break;
    +        }
    +
    +        if (result512 == VerificationResult.NOT_PROVIDED && result256 == VerificationResult.NOT_PROVIDED) {
    +            LOGGER.log(INFO, "Attempt to verify a downloaded file (" + file.getName() + ") using SHA-512 or SHA-256 failed since your configured update site does not provide either of those checksums. Falling back to SHA-1.");
    +        }
    +
    +        VerificationResult result1 = verifyChecksums(entry.getSha1(), job.getComputedSHA1(), true);
    +        switch (result1) {
    +            case PASS:
    +                return;
    +            case FAIL:
    +                throwVerificationFailure(entry.getSha1(), job.getComputedSHA1(), file, "SHA-1");
    +            case NOT_COMPUTED:
                     throw new IOException("Failed to compute SHA-1 of downloaded file, refusing installation");
    -            }
    -            if (!expectedSHA1.equals(actualSha1)) {
    -                throw new IOException("Downloaded file " + downloadedFile.getAbsolutePath() + " does not match expected SHA-1, expected '" + expectedSHA1 + "', actual '" + actualSha1 + "'");
    -                // keep 'downloadedFile' around for investigating what's going on
    -            }
    +            case NOT_PROVIDED:
    +                throw new IOException("Unable to confirm integrity of downloaded file, refusing installation");
             }
         }
     
    @@ -1973,8 +2077,9 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
              */
             @Override
             protected void replace(File dst, File src) throws IOException {
    -
    -            verifyChecksums(plugin.getSha1(), getComputedSHA1(), src);
    +            if (!site.getId().equals(ID_UPLOAD)) {
    +                verifyChecksums(this, plugin, src);
    +            }
     
                 File bak = Util.changeExtension(dst, ".bak");
                 bak.delete();
    @@ -2108,8 +2213,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
     
             @Override
             protected void replace(File dst, File src) throws IOException {
    -            String expectedSHA1 = site.getData().core.getSha1();
    -            verifyChecksums(expectedSHA1, getComputedSHA1(), src);
    +            verifyChecksums(this, site.getData().core, src);
                 Lifecycle.get().rewriteHudsonWar(src);
             }
         }
    @@ -2241,6 +2345,22 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
             }
         }
     
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(UpdateCenter.class.getName() + ".skipPermissionCheck");
    +
    +
         /**
          * Sequence number generator.
          */
    diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java
    index c7f7899431651d3cbc7e6f7a8f93b0e23feee1ea..86c2233f2a1248c6a7675e6a5d457858b46a2cac 100644
    --- a/core/src/main/java/hudson/model/UpdateSite.java
    +++ b/core/src/main/java/hudson/model/UpdateSite.java
    @@ -35,6 +35,7 @@ import hudson.model.UpdateCenter.UpdateCenterJob;
     import hudson.util.FormValidation;
     import hudson.util.FormValidation.Kind;
     import hudson.util.HttpResponses;
    +import static jenkins.util.MemoryReductionUtil.*;
     import hudson.util.TextFile;
     import static java.util.concurrent.TimeUnit.*;
     import hudson.util.VersionNumber;
    @@ -46,7 +47,6 @@ import java.net.URLEncoder;
     import java.security.GeneralSecurityException;
     import java.util.ArrayList;
     import java.util.Collections;
    -import java.util.HashMap;
     import java.util.HashSet;
     import java.util.List;
     import java.util.Locale;
    @@ -56,6 +56,7 @@ import java.util.TreeMap;
     import java.util.UUID;
     import java.util.concurrent.Callable;
     import java.util.concurrent.Future;
    +import java.util.function.Predicate;
     import java.util.logging.Level;
     import java.util.logging.Logger;
     import java.util.regex.Pattern;
    @@ -120,11 +121,6 @@ public class UpdateSite {
          */
         private transient volatile long retryWindow;
     
    -    /**
    -     * lastModified time of the data file when it was last read.
    -     */
    -    private transient long dataLastReadFromFile;
    -
         /**
          * Latest data as read from the data file.
          */
    @@ -136,7 +132,7 @@ public class UpdateSite {
         private final String id;
     
         /**
    -     * Path to <tt>update-center.json</tt>, like <tt>http://jenkins-ci.org/update-center.json</tt>.
    +     * Path to {@code update-center.json}, like {@code http://jenkins-ci.org/update-center.json}.
          */
         private final String url;
     
    @@ -226,6 +222,7 @@ public class UpdateSite {
             LOGGER.info("Obtained the latest update center data file for UpdateSource " + id);
             retryWindow = 0;
             getDataFile().write(json);
    +        data = new Data(o);
             return FormValidation.ok();
         }
     
    @@ -309,23 +306,20 @@ public class UpdateSite {
         public HttpResponse doInvalidateData() {
             Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
             dataTimestamp = 0;
    +        data = null;
             return HttpResponses.ok();
         }
     
         /**
    -     * Loads the update center data, if any and if modified since last read.
    +     * Loads the update center data, if any.
          *
          * @return  null if no data is available.
          */
         public Data getData() {
    -        TextFile df = getDataFile();
    -        if (df.exists() && dataLastReadFromFile != df.file.lastModified()) {
    +        if (data == null) {
                 JSONObject o = getJSONObject();
    -            if (o!=null) {
    +            if (o != null) {
                     data = new Data(o);
    -                dataLastReadFromFile = df.file.lastModified();
    -            } else {
    -                data = null;
                 }
             }
             return data;
    @@ -492,7 +486,15 @@ public class UpdateSite {
          * Is this the legacy default update center site?
          */
         public boolean isLegacyDefault() {
    -        return id.equals(UpdateCenter.PREDEFINED_UPDATE_SITE_ID) && url.startsWith("http://hudson-ci.org/") || url.startsWith("http://updates.hudson-labs.org/");
    +        return isHudsonCI() || isUpdatesFromHudsonLabs();
    +    }
    +
    +    private boolean isHudsonCI() {
    +        return url != null && UpdateCenter.PREDEFINED_UPDATE_SITE_ID.equals(id) && url.startsWith("http://hudson-ci.org/");
    +    }
    +
    +    private boolean isUpdatesFromHudsonLabs() {
    +        return url != null && url.startsWith("http://updates.hudson-labs.org/");
         }
     
         /**
    @@ -526,7 +528,7 @@ public class UpdateSite {
             public final String connectionCheckUrl;
     
             Data(JSONObject o) {
    -            this.sourceId = (String)o.get("id");
    +            this.sourceId = Util.intern((String)o.get("id"));
                 JSONObject c = o.optJSONObject("core");
                 if (c!=null) {
                     core = new Entry(sourceId, c, url);
    @@ -556,7 +558,7 @@ public class UpdateSite {
                             }
                         }
                     }
    -                plugins.put(e.getKey(), p);
    +                plugins.put(Util.intern(e.getKey()), p);
                 }
     
                 connectionCheckUrl = (String)o.get("connectionCheckUrl");
    @@ -616,18 +618,26 @@ public class UpdateSite {
             @Restricted(NoExternalUse.class)
             /* final */ String sha1;
     
    +        @Restricted(NoExternalUse.class)
    +        /* final */ String sha256;
    +
    +        @Restricted(NoExternalUse.class)
    +        /* final */ String sha512;
    +
             public Entry(String sourceId, JSONObject o) {
                 this(sourceId, o, null);
             }
     
             Entry(String sourceId, JSONObject o, String baseURL) {
                 this.sourceId = sourceId;
    -            this.name = o.getString("name");
    -            this.version = o.getString("version");
    +            this.name = Util.intern(o.getString("name"));
    +            this.version = Util.intern(o.getString("version"));
     
                 // Trim this to prevent issues when the other end used Base64.encodeBase64String that added newlines
                 // to the end in old commons-codec. Not the case on updates.jenkins-ci.org, but let's be safe.
                 this.sha1 = Util.fixEmptyAndTrim(o.optString("sha1"));
    +            this.sha256 = Util.fixEmptyAndTrim(o.optString("sha256"));
    +            this.sha512 = Util.fixEmptyAndTrim(o.optString("sha512"));
     
                 String url = o.getString("url");
                 if (!URI.create(url).isAbsolute()) {
    @@ -649,6 +659,24 @@ public class UpdateSite {
                 return sha1;
             }
     
    +        /**
    +         * The base64 encoded SHA-256 checksum of the file.
    +         * Can be null if not provided by the update site.
    +         * @since 2.130
    +         */
    +        public String getSha256() {
    +            return sha256;
    +        }
    +
    +        /**
    +         * The base64 encoded SHA-512 checksum of the file.
    +         * Can be null if not provided by the update site.
    +         * @since 2.130
    +         */
    +        public String getSha512() {
    +            return sha512;
    +        }
    +
             /**
              * Checks if the specified "current version" is older than the version of this entry.
              *
    @@ -711,8 +739,8 @@ public class UpdateSite {
     
             public WarningVersionRange(JSONObject o) {
                 this.name = Util.fixEmpty(o.optString("name"));
    -            this.firstVersion = Util.fixEmpty(o.optString("firstVersion"));
    -            this.lastVersion = Util.fixEmpty(o.optString("lastVersion"));
    +            this.firstVersion = Util.intern(Util.fixEmpty(o.optString("firstVersion")));
    +            this.lastVersion = Util.intern(Util.fixEmpty(o.optString("lastVersion")));
                 Pattern p;
                 try {
                     p = Pattern.compile(o.getString("pattern"));
    @@ -809,13 +837,13 @@ public class UpdateSite {
                     this.type = Type.UNKNOWN;
                 }
                 this.id = o.getString("id");
    -            this.component = o.getString("name");
    +            this.component = Util.intern(o.getString("name"));
                 this.message = o.getString("message");
                 this.url = o.getString("url");
     
                 if (o.has("versions")) {
    -                List<WarningVersionRange> ranges = new ArrayList<>();
                     JSONArray versions = o.getJSONArray("versions");
    +                List<WarningVersionRange> ranges = new ArrayList<>(versions.size());
                     for (int i = 0; i < versions.size(); i++) {
                         WarningVersionRange range = new WarningVersionRange(versions.getJSONObject(i));
                         ranges.add(range);
    @@ -899,6 +927,16 @@ public class UpdateSite {
             }
         }
     
    +    private static String get(JSONObject o, String prop) {
    +        if(o.has(prop))
    +            return o.getString(prop);
    +        else
    +            return null;
    +    }
    +
    +    static final Predicate<Object> IS_DEP_PREDICATE = x -> x instanceof JSONObject && get(((JSONObject)x), "name") != null;
    +    static final Predicate<Object> IS_NOT_OPTIONAL = x-> "false".equals(get(((JSONObject)x), "optional"));
    +
         public final class Plugin extends Entry {
             /**
              * Optional URL to the Wiki page that discusses this plugin.
    @@ -940,13 +978,13 @@ public class UpdateSite {
              * Dependencies of this plugin, a name -&gt; version mapping.
              */
             @Exported
    -        public final Map<String,String> dependencies = new HashMap<String,String>();
    +        public final Map<String,String> dependencies;
             
             /**
              * Optional dependencies of this plugin.
              */
             @Exported
    -        public final Map<String,String> optionalDependencies = new HashMap<String,String>();
    +        public final Map<String,String> optionalDependencies;
     
             @DataBoundConstructor
             public Plugin(String sourceId, JSONObject o) {
    @@ -954,30 +992,31 @@ public class UpdateSite {
                 this.wiki = get(o,"wiki");
                 this.title = get(o,"title");
                 this.excerpt = get(o,"excerpt");
    -            this.compatibleSinceVersion = get(o,"compatibleSinceVersion");
    -            this.requiredCore = get(o,"requiredCore");
    -            this.categories = o.has("labels") ? (String[])o.getJSONArray("labels").toArray(new String[0]) : null;
    +            this.compatibleSinceVersion = Util.intern(get(o,"compatibleSinceVersion"));
    +            this.requiredCore = Util.intern(get(o,"requiredCore"));
    +            this.categories = o.has("labels") ? internInPlace((String[])o.getJSONArray("labels").toArray(EMPTY_STRING_ARRAY)) : null;
    +            JSONArray ja = o.getJSONArray("dependencies");
    +            int depCount = (int)(ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL)).count());
    +            int optionalDepCount = (int)(ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL.negate())).count());
    +            dependencies = getPresizedMutableMap(depCount);
    +            optionalDependencies = getPresizedMutableMap(optionalDepCount);
    +
                 for(Object jo : o.getJSONArray("dependencies")) {
                     JSONObject depObj = (JSONObject) jo;
                     // Make sure there's a name attribute and that the optional value isn't true.
    -                if (get(depObj,"name")!=null) {
    +                String depName = Util.intern(get(depObj,"name"));
    +                if (depName!=null) {
                         if (get(depObj, "optional").equals("false")) {
    -                        dependencies.put(get(depObj, "name"), get(depObj, "version"));
    +                        dependencies.put(depName, Util.intern(get(depObj, "version")));
                         } else {
    -                        optionalDependencies.put(get(depObj, "name"), get(depObj, "version"));
    +                        optionalDependencies.put(depName, Util.intern(get(depObj, "version")));
                         }
                     }
    -                
                 }
     
             }
     
    -        private String get(JSONObject o, String prop) {
    -            if(o.has(prop))
    -                return o.getString(prop);
    -            else
    -                return null;
    -        }
    +
     
             public String getDisplayName() {
                 String displayName;
    @@ -1094,10 +1133,19 @@ public class UpdateSite {
             }
     
             public boolean isNeededDependenciesForNewerJenkins() {
    -            for (Plugin p: getNeededDependencies()) {
    -                if (p.isForNewerHudson() || p.isNeededDependenciesForNewerJenkins()) return true;
    -            }
    -            return false;
    +            return isNeededDependenciesForNewerJenkins(new PluginManager.MetadataCache());
    +        }
    +
    +        @Restricted(NoExternalUse.class) // table.jelly
    +        public boolean isNeededDependenciesForNewerJenkins(PluginManager.MetadataCache cache) {
    +            return cache.of("isNeededDependenciesForNewerJenkins:" + name, Boolean.class, () -> {
    +                for (Plugin p : getNeededDependencies()) {
    +                    if (p.isForNewerHudson() || p.isNeededDependenciesForNewerJenkins()) {
    +                        return true;
    +                    }
    +                }
    +                return false;
    +            });
             }
     
             /**
    @@ -1109,11 +1157,19 @@ public class UpdateSite {
              * specified, it'll return true.
              */
             public boolean isNeededDependenciesCompatibleWithInstalledVersion() {
    -            for (Plugin p: getNeededDependencies()) {
    -                if (!p.isCompatibleWithInstalledVersion() || !p.isNeededDependenciesCompatibleWithInstalledVersion())
    -                    return false;
    -            }
    -            return true;
    +            return isNeededDependenciesCompatibleWithInstalledVersion(new PluginManager.MetadataCache());
    +        }
    +
    +        @Restricted(NoExternalUse.class) // table.jelly
    +        public boolean isNeededDependenciesCompatibleWithInstalledVersion(PluginManager.MetadataCache cache) {
    +            return cache.of("isNeededDependenciesCompatibleWithInstalledVersion:" + name, Boolean.class, () -> {
    +                for (Plugin p : getNeededDependencies()) {
    +                    if (!p.isCompatibleWithInstalledVersion() || !p.isNeededDependenciesCompatibleWithInstalledVersion()) {
    +                        return false;
    +                    }
    +                }
    +                return true;
    +            });
             }
     
             /**
    diff --git a/core/src/main/java/hudson/model/UsageStatistics.java b/core/src/main/java/hudson/model/UsageStatistics.java
    index 2f78840b63154e07e8824f561db0a4d997d77602..bb791d38e15d82c220d80ca22eba02f8fc5f5c52 100644
    --- a/core/src/main/java/hudson/model/UsageStatistics.java
    +++ b/core/src/main/java/hudson/model/UsageStatistics.java
    @@ -66,7 +66,7 @@ import jenkins.util.SystemProperties;
      * @author Kohsuke Kawaguchi
      */
     @Extension
    -public class UsageStatistics extends PageDecorator {
    +public class UsageStatistics extends PageDecorator implements PersistentDescriptor {
         private final String keyImage;
     
         /**
    @@ -88,7 +88,6 @@ public class UsageStatistics extends PageDecorator {
          */
         public UsageStatistics(String keyImage) {
             this.keyImage = keyImage;
    -        load();
         }
     
         /**
    diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java
    index 7f132023b7f2506b2d2c5e0a9b8517f1ce8a5ea0..654d41432fca3777a21521df3b166fdfe6160b61 100644
    --- a/core/src/main/java/hudson/model/User.java
    +++ b/core/src/main/java/hudson/model/User.java
    @@ -97,6 +97,9 @@ import org.apache.commons.lang.StringUtils;
     import org.jenkinsci.Symbol;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
    +import org.kohsuke.stapler.HttpResponses;
    +import org.kohsuke.stapler.Stapler;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.export.Exported;
    @@ -104,6 +107,8 @@ import org.kohsuke.stapler.export.ExportedBean;
     import org.kohsuke.stapler.interceptor.RequirePOST;
     import org.springframework.dao.DataAccessException;
     
    +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
    +
     /**
      * Represents a user.
      *
    @@ -129,7 +134,7 @@ import org.springframework.dao.DataAccessException;
      * @author Kohsuke Kawaguchi
      */
     @ExportedBean
    -public class User extends AbstractModelObject implements AccessControlled, DescriptorByNameOwner, Saveable, Comparable<User>, ModelObjectWithContextMenu {
    +public class User extends AbstractModelObject implements AccessControlled, DescriptorByNameOwner, Saveable, Comparable<User>, ModelObjectWithContextMenu, StaplerProxy {
     
         /**
          * The username of the 'unknown' user used to avoid null user references.
    @@ -490,7 +495,8 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
                 byNameLock.readLock().unlock();
             }
             final File configFile = getConfigFileFor(id);
    -        if (unsanitizedLegacyConfigFile.exists() && !unsanitizedLegacyConfigFile.equals(configFile)) {
    +        boolean mustMigrateLegacyConfig = isMigrationRequiredForLegacyConfigFile(unsanitizedLegacyConfigFile, configFile);
    +        if (mustMigrateLegacyConfig) {
                 File ancestor = unsanitizedLegacyConfigFile.getParentFile();
                 if (!configFile.exists()) {
                     try {
    @@ -552,6 +558,37 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
             }
             return u;
         }
    +    
    +    private static boolean isMigrationRequiredForLegacyConfigFile(@Nonnull File legacyConfigFile, @Nonnull File newConfigFile){
    +        boolean mustMigrateLegacyConfig = legacyConfigFile.exists() && !legacyConfigFile.equals(newConfigFile);
    +        if(mustMigrateLegacyConfig){
    +            try{
    +                // TODO Could be replace by Util.isDescendant(getRootDir(), legacyConfigFile) in 2.80+
    +                String canonicalLegacy = legacyConfigFile.getCanonicalPath();
    +                String canonicalUserDir = getRootDir().getCanonicalPath();
    +                if(!canonicalLegacy.startsWith(canonicalUserDir + File.separator)){
    +                    // without that check, the application config.xml could be moved (i.e. erased from application PoV)
    +                    mustMigrateLegacyConfig = false;
    +                    LOGGER.log(Level.WARNING, String.format(
    +                            "Attempt to escape from users directory with %s, migration aborted, see SECURITY-897 for more information",
    +                            legacyConfigFile.getAbsolutePath()
    +                    ));
    +                }
    +            }
    +            catch (IOException e){
    +                mustMigrateLegacyConfig = false;
    +                LOGGER.log(
    +                        Level.WARNING,
    +                        String.format(
    +                                "Failed to determine the canonical path of %s, migration aborted, see SECURITY-897 for more information", 
    +                                legacyConfigFile.getAbsolutePath()
    +                        ),
    +                        e
    +                );
    +            }
    +        }
    +        return mustMigrateLegacyConfig;
    +    }
     
         /**
          * Gets the {@link User} object by its id or full name.
    @@ -764,6 +801,16 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
         public @Override String toString() {
             return fullName;
         }
    +    
    +    /**
    +     * Returns the folder that store all the user information
    +     * Useful for plugins to save a user-specific file aside the config.xml
    +     * 
    +     * @since 2.129
    +     */
    +    public File getUserFolder(){
    +        return getUserFolderFor(this.id);
    +    }
     
         /**
          * The file we save our configuration.
    @@ -773,7 +820,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
         }
     
         private static final File getConfigFileFor(String id) {
    -        return new File(getRootDir(), idStrategy().filenameOf(id) +"/config.xml");
    +        return new File(getUserFolderFor(id), "config.xml");
    +    }
    +    
    +    private static File getUserFolderFor(String id){
    +        return new File(getRootDir(), idStrategy().filenameOf(id));
         }
     
         private static File getUnsanitizedLegacyConfigFileFor(String id) {
    @@ -977,14 +1028,10 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
         }
     
         public ACL getACL() {
    -        final ACL base = Jenkins.getInstance().getAuthorizationStrategy().getACL(this);
    +        ACL base = Jenkins.getInstance().getAuthorizationStrategy().getACL(this);
             // always allow a non-anonymous user full control of himself.
    -        return new ACL() {
    -            public boolean hasPermission(Authentication a, Permission permission) {
    -                return (idStrategy().equals(a.getName(), id) && !(a instanceof AnonymousAuthenticationToken))
    -                        || base.hasPermission(a, permission);
    -            }
    -        };
    +        return ACL.lambda((a, permission) -> (idStrategy().equals(a.getName(), id) && !(a instanceof AnonymousAuthenticationToken))
    +                        || base.hasPermission(a, permission));
         }
     
         /**
    @@ -1071,6 +1118,24 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
         public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws Exception {
             return new ContextMenu().from(this,request,response);
         }
    +
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            if (!Jenkins.get().hasPermission(Jenkins.READ)) {
    +                return null;
    +            }
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(User.class.getName() + ".skipPermissionCheck");
    +
         
         /**
          * Gets list of Illegal usernames, for which users should not be created.
    @@ -1096,6 +1161,9 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
                 File[] subdirs = getRootDir().listFiles((FileFilter) DirectoryFileFilter.INSTANCE);
                 if (subdirs != null) {
                     for (File subdir : subdirs) {
    +                    if (subdir.equals(getRootDir())) {
    +                        continue; // ignore the parent directory in case of stray config.xml
    +                    }
                         File configFile = new File(subdir, "config.xml");
                         if (configFile.exists()) {
                             String name = strategy.idFromFilename(subdir.getName());
    diff --git a/core/src/main/java/hudson/model/UserProperty.java b/core/src/main/java/hudson/model/UserProperty.java
    index 48198a28ebbe9400139f48735b15067cf33bbfcc..ec02b1211b99d78fa15d5024b8ce69f6311fb006 100644
    --- a/core/src/main/java/hudson/model/UserProperty.java
    +++ b/core/src/main/java/hudson/model/UserProperty.java
    @@ -41,10 +41,10 @@ import org.kohsuke.stapler.export.ExportedBean;
      * configuration screen, and they are persisted with the user object.
      *
      * <p>
    - * Configuration screen should be defined in <tt>config.jelly</tt>.
    + * Configuration screen should be defined in {@code config.jelly}.
      * Within this page, the {@link UserProperty} instance is available
    - * as <tt>instance</tt> variable (while <tt>it</tt> refers to {@link User}.
    - * See {@link hudson.search.UserSearchProperty}'s <tt>config.jelly</tt> for an example.
    + * as {@code instance} variable (while {@code it} refers to {@link User}.
    + * See {@link hudson.search.UserSearchProperty}'s {@code config.jelly} for an example.
      * <p>A property may also define a {@code summary.jelly} view to show in the main user screen.
      *
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java
    index 83a368250316fcf82c63a9c0145093bde78c46e9..fcab16b90b2043c855c23b0c4accc7ad3f7c071f 100644
    --- a/core/src/main/java/hudson/model/View.java
    +++ b/core/src/main/java/hudson/model/View.java
    @@ -26,7 +26,6 @@ package hudson.model;
     
     import com.thoughtworks.xstream.converters.ConversionException;
     import com.thoughtworks.xstream.io.StreamException;
    -import com.thoughtworks.xstream.io.xml.Xpp3Driver;
     import hudson.DescriptorExtensionList;
     import hudson.Extension;
     import hudson.ExtensionPoint;
    @@ -117,7 +116,6 @@ import java.util.logging.Logger;
     import java.util.stream.Collectors;
     
     import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
    -import static jenkins.scm.RunWithSCM.*;
     
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
    @@ -135,7 +133,7 @@ import org.xml.sax.SAXException;
      * <h2>Note for implementers</h2>
      * <ul>
      * <li>
    - * {@link View} subtypes need the <tt>newViewDetail.jelly</tt> page,
    + * {@link View} subtypes need the {@code newViewDetail.jelly} page,
      * which is included in the "new view" page. This page should have some
      * description of what the view is about. 
      * </ul>
    @@ -173,8 +171,6 @@ public abstract class View extends AbstractModelObject implements AccessControll
          */
         protected boolean filterQueue;
         
    -    protected transient List<Action> transientActions;
    -
         /**
          * List of {@link ViewProperty}s configured for this view.
          * @since 1.406
    @@ -193,6 +189,7 @@ public abstract class View extends AbstractModelObject implements AccessControll
         /**
          * Gets all the items in this collection in a read-only view.
          */
    +    @Nonnull
         @Exported(name="jobs")
         public abstract Collection<TopLevelItem> getItems();
     
    @@ -550,21 +547,20 @@ public abstract class View extends AbstractModelObject implements AccessControll
          * @see Jenkins#getActions()
          */
         public List<Action> getActions() {
    -    	List<Action> result = new ArrayList<Action>();
    -    	result.addAll(getOwner().getViewActions());
    -    	synchronized (this) {
    -    		if (transientActions == null) {
    -                updateTransientActions();
    -    		}
    -    		result.addAll(transientActions);
    -    	}
    -    	return result;
    -    }
    -    
    -    public synchronized void updateTransientActions() {
    -        transientActions = TransientViewActionFactory.createAllFor(this); 
    +        List<Action> result = new ArrayList<>();
    +        result.addAll(getOwner().getViewActions());
    +        result.addAll(TransientViewActionFactory.createAllFor(this));
    +        return result;
         }
    -    
    +
    +    /**
    +     * No-op. Included to maintain backwards compatibility.
    +     * @deprecated This method does nothing and should not be used
    +     */
    +    @Restricted(DoNotUse.class)
    +    @Deprecated
    +    public void updateTransientActions() {}
    +
         public Object getDynamic(String token) {
             for (Action a : getActions()) {
                 String url = a.getUrlName();
    @@ -994,7 +990,6 @@ public abstract class View extends AbstractModelObject implements AccessControll
             rename(req.getParameter("name"));
     
             getProperties().rebuild(req, req.getSubmittedForm(), getApplicablePropertyDescriptors());
    -        updateTransientActions();  
     
             save();
     
    @@ -1070,11 +1065,11 @@ public abstract class View extends AbstractModelObject implements AccessControll
          */
         @Restricted(DoNotUse.class)
         public Categories doItemCategories(StaplerRequest req, StaplerResponse rsp, @QueryParameter String iconStyle) throws IOException, ServletException {
    +        getOwner().checkPermission(Item.CREATE);
     
             rsp.addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
             rsp.addHeader("Pragma", "no-cache");
             rsp.addHeader("Expires", "0");
    -        getOwner().checkPermission(Item.CREATE);
             Categories categories = new Categories();
             int order = 0;
             JellyContext ctx;
    @@ -1155,7 +1150,7 @@ public abstract class View extends AbstractModelObject implements AccessControll
         }
     
         /**
    -     * Accepts <tt>config.xml</tt> submission, as well as serve it.
    +     * Accepts {@code config.xml} submission, as well as serve it.
          */
         @WebMethod(name = "config.xml")
         public HttpResponse doConfigDotXml(StaplerRequest req) throws IOException {
    @@ -1214,7 +1209,7 @@ public abstract class View extends AbstractModelObject implements AccessControll
                 // view in same ViewGroup and might not satisfy Jenkins.checkGoodName.
                 String oldname = name;
                 ViewGroup oldOwner = owner; // oddly, this field is not transient
    -            Object o = Jenkins.XSTREAM2.unmarshal(new Xpp3Driver().createReader(in), this, null, true);
    +            Object o = Jenkins.XSTREAM2.unmarshal(XStream2.getDefaultDriver().createReader(in), this, null, true);
                 if (!o.getClass().equals(getClass())) {
                     // ensure that we've got the same view type. extending this code to support updating
                     // to different view type requires destroying & creating a new view type
    @@ -1368,7 +1363,7 @@ public abstract class View extends AbstractModelObject implements AccessControll
         /**
          * Instantiate View subtype from XML stream.
          *
    -     * @param name Alternative name to use or <tt>null</tt> to keep the one in xml.
    +     * @param name Alternative name to use or {@code null} to keep the one in xml.
          */
         public static View createViewFromXML(String name, InputStream xml) throws IOException {
     
    diff --git a/core/src/main/java/hudson/model/ViewDescriptor.java b/core/src/main/java/hudson/model/ViewDescriptor.java
    index a63484d69e1250aeb4ae2518e47b8e1a2e25b8b7..933ad74dcb68aeccb30cf329781e89ff185ba99e 100644
    --- a/core/src/main/java/hudson/model/ViewDescriptor.java
    +++ b/core/src/main/java/hudson/model/ViewDescriptor.java
    @@ -88,7 +88,6 @@ public abstract class ViewDescriptor extends Descriptor<View> {
          */
         @Restricted(DoNotUse.class)
         public AutoCompletionCandidates doAutoCompleteCopyNewItemFrom(@QueryParameter final String value, @AncestorInPath ItemGroup<?> container) {
    -        // TODO do we need a permissions check here?
             AutoCompletionCandidates candidates = AutoCompletionCandidates.ofJobNames(TopLevelItem.class, value, container);
             if (container instanceof DirectlyModifiableTopLevelItemGroup) {
                 DirectlyModifiableTopLevelItemGroup modifiableContainer = (DirectlyModifiableTopLevelItemGroup) container;
    diff --git a/core/src/main/java/hudson/model/WorkspaceCleanupThread.java b/core/src/main/java/hudson/model/WorkspaceCleanupThread.java
    index 0a01a5f28fe5ca9cd329df55e244f87f76e2436e..8c55c2bc4d8b77b347d8ac3e012784b41f33bb34 100644
    --- a/core/src/main/java/hudson/model/WorkspaceCleanupThread.java
    +++ b/core/src/main/java/hudson/model/WorkspaceCleanupThread.java
    @@ -139,6 +139,15 @@ public class WorkspaceCleanupThread extends AsyncPeriodicWork {
                 }
             }
     
    +        // TODO this may only check the last build in fact:
    +        if (item instanceof Job<?,?>) {
    +            Job<?,?> j = (Job<?,?>) item;
    +            if (j.isBuilding()) {
    +                LOGGER.log(Level.FINE, "Job {0} is building, so not deleting", item.getFullDisplayName());
    +                return false;
    +            }
    +        }
    +
             LOGGER.log(Level.FINER, "Going to delete directory {0}", dir);
             return true;
         }
    diff --git a/core/src/main/java/hudson/model/package.html b/core/src/main/java/hudson/model/package.html
    index 640b328a391dfaf338c5b4e9ffd7dae8d679b5bb..73e34b62da6534d444ef2ca712f87b093383550b 100644
    --- a/core/src/main/java/hudson/model/package.html
    +++ b/core/src/main/java/hudson/model/package.html
    @@ -23,5 +23,5 @@ THE SOFTWARE.
     -->
     
     <html><head/><body>
    -Core object model that are bound to URLs via stapler, rooted at <a href="Hudson.html"><tt>Hudson</tt></a>.
    +Core object model that are bound to URLs via stapler, rooted at <a href="Hudson.html"><code>Hudson</code></a>.
     </body></html>
    \ No newline at end of file
    diff --git a/core/src/main/java/hudson/model/queue/CauseOfBlockage.java b/core/src/main/java/hudson/model/queue/CauseOfBlockage.java
    index e5ba86359771f88092d1ed5982197bc48dad24d7..beb76b824a34b11729ea57fa3d71ab012d480b76 100644
    --- a/core/src/main/java/hudson/model/queue/CauseOfBlockage.java
    +++ b/core/src/main/java/hudson/model/queue/CauseOfBlockage.java
    @@ -19,7 +19,7 @@ import org.jvnet.localizer.Localizable;
      * has expanded beyond queues.
      *
      * <h2>View</h2>
    - * <tt>summary.jelly</tt> should do one-line HTML rendering to be used showing the cause
    + * {@code summary.jelly} should do one-line HTML rendering to be used showing the cause
      * to the user. By default it simply renders {@link #getShortDescription()} text.
      *
      * <p>
    diff --git a/core/src/main/java/hudson/model/queue/Executables.java b/core/src/main/java/hudson/model/queue/Executables.java
    index 2dbcca3f21bb04616dd79efc06229fabd6ad0590..426c12df96e5d3f2af55d15621b7a7c9b50eec9e 100644
    --- a/core/src/main/java/hudson/model/queue/Executables.java
    +++ b/core/src/main/java/hudson/model/queue/Executables.java
    @@ -46,7 +46,7 @@ public class Executables {
                 throws Error, RuntimeException {
             try {
                 return e.getParent();
    -        } catch (AbstractMethodError _) {
    +        } catch (AbstractMethodError ignored) { // will fallback to a private implementation
                 try {
                     Method m = e.getClass().getMethod("getParent");
                     m.setAccessible(true);
    diff --git a/core/src/main/java/hudson/model/queue/MappingWorksheet.java b/core/src/main/java/hudson/model/queue/MappingWorksheet.java
    index a8cdad8ef9e01847b5fc25db291877e84bdcc3bc..561c950e4aa3e17dcbe13aa65c928887b8238ca6 100644
    --- a/core/src/main/java/hudson/model/queue/MappingWorksheet.java
    +++ b/core/src/main/java/hudson/model/queue/MappingWorksheet.java
    @@ -134,7 +134,7 @@ public class MappingWorksheet {
                 if (c.assignedLabel!=null && !c.assignedLabel.contains(node))
                     return false;   // label mismatch
     
    -            if (!nodeAcl.hasPermission(item.authenticate(), Computer.BUILD))
    +            if (!(Node.SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !nodeAcl.hasPermission(item.authenticate(), Computer.BUILD))
                     return false;   // tasks don't have a permission to run on this node
     
                 return true;
    diff --git a/core/src/main/java/hudson/model/queue/WorkUnitContext.java b/core/src/main/java/hudson/model/queue/WorkUnitContext.java
    index 80a402242d5112a6c0962647f70a76c3bffe7d4b..8afce5cbfd0f757816ef82966ad31354a612b68f 100644
    --- a/core/src/main/java/hudson/model/queue/WorkUnitContext.java
    +++ b/core/src/main/java/hudson/model/queue/WorkUnitContext.java
    @@ -67,8 +67,8 @@ public final class WorkUnitContext {
             this.item = item;
             this.task = item.task;
             this.future = (FutureImpl)item.getFuture();
    -        this.actions = new ArrayList<Action>(item.getAllActions());
    -        
    +        // JENKINS-51584 do not use item.getAllActions() here.
    +        this.actions = new ArrayList<Action>(item.getActions());
             // +1 for the main task
             int workUnitSize = task.getSubTasks().size();
             startLatch = new Latch(workUnitSize) {
    diff --git a/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java
    index 8621f37cebe47cd29ba7bceb3aa8b313224aafe9..e35ae80a7e133c7ff6ca20b3d1344b979ced0735 100644
    --- a/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java
    +++ b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java
    @@ -6,10 +6,16 @@ import hudson.remoting.VirtualChannel;
     import jenkins.model.Jenkins;
     
     import javax.annotation.CheckForNull;
    +import javax.annotation.Nonnull;
     import java.io.IOException;
    +import java.util.ArrayList;
    +import java.util.Collection;
     import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.List;
     import java.util.Map;
     import java.util.Map.Entry;
    +import java.util.Set;
     import java.util.concurrent.ExecutionException;
     import java.util.concurrent.Future;
     import java.util.concurrent.TimeoutException;
    @@ -60,10 +66,22 @@ public abstract class AbstractAsyncNodeMonitorDescriptor<T> extends AbstractNode
     
         /**
          * Performs all monitoring concurrently.
    +     *
    +     * @return Mapping from computer to monitored value. The map values can be null for several reasons, see {@link Result}
    +     * for more details.
          */
         @Override
         protected Map<Computer, T> monitor() throws InterruptedException {
    +        // Bridge method to offer original constrained interface.
    +        return monitorDetailed().getMonitoringData();
    +    }
    +
    +    /**
    +     * Perform monitoring with detailed reporting.
    +     */
    +    protected final @Nonnull Result<T> monitorDetailed() throws InterruptedException {
             Map<Computer,Future<T>> futures = new HashMap<Computer,Future<T>>();
    +        Set<Computer> skipped = new HashSet<>();
     
             for (Computer c : Jenkins.getInstance().getComputers()) {
                 try {
    @@ -101,11 +119,53 @@ public abstract class AbstractAsyncNodeMonitorDescriptor<T> extends AbstractNode
                     } catch (TimeoutException x) {
                         LOGGER.log(WARNING, "Failed to monitor " + c.getDisplayName() + " for " + getDisplayName(), x);
                     }
    +            } else {
    +                skipped.add(c);
                 }
             }
     
    -        return data;
    +        return new Result<>(data, skipped);
         }
     
         private static final Logger LOGGER = Logger.getLogger(AbstractAsyncNodeMonitorDescriptor.class.getName());
    +
    +    /**
    +     * Result object for {@link AbstractAsyncNodeMonitorDescriptor#monitorDetailed()} to facilitate extending information
    +     * returned in the future.
    +     *
    +     * The {@link #getMonitoringData()} provides the results of the monitoring as {@link #monitor()} does. Note the value
    +     * in the map can be {@code null} for several reasons:
    +     * <ul>
    +     *     <li>The monitoring {@link Callable} returned {@code null} as a provisioning result.</li>
    +     *     <li>Creating or evaluating that callable has thrown an exception.</li>
    +     *     <li>The computer was not monitored as it was offline.</li>
    +     *     <li>The {@link AbstractAsyncNodeMonitorDescriptor#createCallable} has returned null.</li>
    +     * </ul>
    +     *
    +     * Clients can distinguishing among these states based on the additional data attached to this object. {@link #getSkipped()}
    +     * returns computers that was not monitored as they ware either offline or monitor produced {@code null} {@link Callable}.
    +     */
    +    protected static final class Result<T> {
    +        private static final long serialVersionUID = -7671448355804481216L;
    +
    +        private final @Nonnull Map<Computer, T> data;
    +        private final @Nonnull ArrayList<Computer> skipped;
    +
    +        private Result(@Nonnull Map<Computer, T> data, @Nonnull Collection<Computer> skipped) {
    +            this.data = new HashMap<>(data);
    +            this.skipped = new ArrayList<>(skipped);
    +        }
    +
    +        protected @Nonnull Map<Computer, T> getMonitoringData() {
    +            return data;
    +        }
    +
    +        /**
    +         * Computers that ware skipped during monitoring as they either do not have a a channel (offline) or the monitor
    +         * have not produced the Callable. Computers that caused monitor to throw exception are not returned here.
    +         */
    +        protected @Nonnull List<Computer> getSkipped() {
    +            return skipped;
    +        }
    +    }
     }
    diff --git a/core/src/main/java/hudson/node_monitors/NodeMonitor.java b/core/src/main/java/hudson/node_monitors/NodeMonitor.java
    index 17ddf7343ef29036b32c6eef2ebf26dcb07f59af..665b9cafe6dcec2219880eedc7c15954963472ec 100644
    --- a/core/src/main/java/hudson/node_monitors/NodeMonitor.java
    +++ b/core/src/main/java/hudson/node_monitors/NodeMonitor.java
    @@ -48,7 +48,7 @@ import org.kohsuke.stapler.export.ExportedBean;
      * <dl>
      * <dt>column.jelly</dt>
      * <dd>
    - * Invoked from {@link ComputerSet} <tt>index.jelly</tt> to render a column.
    + * Invoked from {@link ComputerSet} {@code index.jelly} to render a column.
      * The {@link NodeMonitor} instance is accessible through the "from" variable.
      * Also see {@link #getColumnCaption()}.
      *
    diff --git a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
    index d7241e9eff619d13af4c0746e8830854f5b83b4c..9f77f9bb779e3ed08e0ddc750364fa4a2d5bc817 100644
    --- a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
    +++ b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
    @@ -46,20 +46,24 @@ import org.kohsuke.stapler.export.ExportedBean;
     public class ResponseTimeMonitor extends NodeMonitor {
         @Extension
         public static final AbstractNodeMonitorDescriptor<Data> DESCRIPTOR = new AbstractAsyncNodeMonitorDescriptor<Data>() {
    +
             @Override
             protected Callable<Data,IOException> createCallable(Computer c) {
    -            if (c.getChannel() == null) {
    -                return null;
    -            }
                 return new Step1(get(c));
             }
     
             @Override
             protected Map<Computer, Data> monitor() throws InterruptedException {
    -            Map<Computer, Data> base = super.monitor();
    -            for (Entry<Computer, Data> e : base.entrySet()) {
    +            Result<Data> base = monitorDetailed();
    +            Map<Computer, Data> monitoringData = base.getMonitoringData();
    +            for (Entry<Computer, Data> e : monitoringData.entrySet()) {
                     Computer c = e.getKey();
                     Data d = e.getValue();
    +                if (base.getSkipped().contains(c)) {
    +                    assert d == null;
    +                    continue;
    +                }
    +
                     if (d ==null) {
                         // if we failed to monitor, put in the special value that indicates a failure
                         e.setValue(d=new Data(get(c),-1L));
    @@ -74,7 +78,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
                         LOGGER.warning(Messages.ResponseTimeMonitor_MarkedOffline(c.getName()));
                     }
                 }
    -            return base;
    +            return monitoringData;
             }
     
             public String getDisplayName() {
    diff --git a/core/src/main/java/hudson/os/solaris/ZFSInstaller.java b/core/src/main/java/hudson/os/solaris/ZFSInstaller.java
    index a5709e8976ad1b39b17cec622aaee0798f89190b..f22a0b7879d1f6441cf6afef0bc944a75d9b8308 100644
    --- a/core/src/main/java/hudson/os/solaris/ZFSInstaller.java
    +++ b/core/src/main/java/hudson/os/solaris/ZFSInstaller.java
    @@ -169,9 +169,23 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
     
             // this is the actual creation of the file system.
             // return true indicating a success
    -        return SU.execute(listener, rootUsername, rootPassword, new MasterToSlaveCallable<String,IOException>() {
    +        return SU.execute(listener, rootUsername, rootPassword, new Create(listener, home, uid, gid, userName));
    +    }
    +    private static class Create extends MasterToSlaveCallable<String, IOException> {
    +        private final TaskListener listener;
    +        private final File home;
    +        private final int uid;
    +        private final int gid;
    +        private final String userName;
    +        Create(TaskListener listener, File home, int uid, int gid, String userName) {
    +            this.listener = listener;
    +            this.home = home;
    +            this.uid = uid;
    +            this.gid = gid;
    +            this.userName = userName;
    +        }
                 private static final long serialVersionUID = 7731167233498214301L;
    -
    +            @Override
                 public String call() throws IOException {
                     PrintStream out = listener.getLogger();
     
    @@ -205,14 +219,13 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
                         // revert the file system creation
                         try {
                             hudson.destory();
    -                    } catch (Exception _) {
    +                    } catch (Exception ignored) {
                             // but ignore the error and let the original error thrown
                         }
                         throw e;
                     }
                     return hudson.getName();
                 }
    -        });
         }
     
         /**
    diff --git a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java
    index 229b5affb54e7cb9404a0f0c246381e349a0c78d..aeb46745118cd5155753cb218e27ac9bf5d8e09d 100644
    --- a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java
    +++ b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java
    @@ -53,9 +53,11 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
         private final String rootDataset;
     
         public ZFSProvisioner(Node node) throws IOException, InterruptedException {
    -        rootDataset = node.getRootPath().act(new MasterToSlaveFileCallable<String>() {
    +        rootDataset = node.getRootPath().act(new GetName());
    +    }
    +    private static class GetName extends MasterToSlaveFileCallable<String> {
                 private static final long serialVersionUID = -2142349338699797436L;
    -
    +            @Override
                 public String invoke(File f, VirtualChannel channel) throws IOException {
                     ZFSFileSystem fs = libzfs.getFileSystemByMountPoint(f);
                     if(fs!=null)    return fs.getName();
    @@ -63,15 +65,22 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
                     // TODO: for now, only support agents that are already on ZFS.
                     throw new IOException("Not on ZFS");
                 }
    -        });
         }
     
         public void prepareWorkspace(AbstractBuild<?,?> build, FilePath ws, final TaskListener listener) throws IOException, InterruptedException {
             final String name = build.getProject().getFullName();
             
    -        ws.act(new MasterToSlaveFileCallable<Void>() {
    +        ws.act(new PrepareWorkspace(name, listener));
    +    }
    +    private class PrepareWorkspace extends MasterToSlaveFileCallable<Void> {
    +        private final String name;
    +        private final TaskListener listener;
    +        PrepareWorkspace(String name, TaskListener listener) {
    +            this.name = name;
    +            this.listener = listener;
    +        }
                 private static final long serialVersionUID = 2129531727963121198L;
    -
    +            @Override
                 public Void invoke(File f, VirtualChannel channel) throws IOException {
                     ZFSFileSystem fs = libzfs.getFileSystemByMountPoint(f);
                     if(fs!=null)    return null;    // already on ZFS
    @@ -84,20 +93,20 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
                     fs.mount();
                     return null;
                 }
    -        });
         }
     
         public void discardWorkspace(AbstractProject<?, ?> project, FilePath ws) throws IOException, InterruptedException {
    -        ws.act(new MasterToSlaveFileCallable<Void>() {
    +        ws.act(new DiscardWorkspace());
    +    }
    +    private static class DiscardWorkspace extends MasterToSlaveFileCallable<Void> {
                 private static final long serialVersionUID = 1916618107019257530L;
    -
    +            @Override
                 public Void invoke(File f, VirtualChannel channel) throws IOException {
                     ZFSFileSystem fs = libzfs.getFileSystemByMountPoint(f);
                     if(fs!=null)
                         fs.destory(true);
                     return null;
                 }
    -        });
         }
     
         /**
    diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java
    index 14c67b90343201741e8ccbfb726191123119f618..120b8bc2a16bda6ef51a40fa0fec9ab96678d98e 100644
    --- a/core/src/main/java/hudson/scheduler/CronTab.java
    +++ b/core/src/main/java/hudson/scheduler/CronTab.java
    @@ -204,7 +204,7 @@ public final class CronTab {
             }
     
             void setTo(Calendar c, int i) {
    -            c.set(field,i-offset);
    +            c.set(field,Math.min(i-offset, c.getActualMaximum(field)));
             }
     
             void clear(Calendar c) {
    diff --git a/core/src/main/java/hudson/scheduler/RareOrImpossibleDateException.java b/core/src/main/java/hudson/scheduler/RareOrImpossibleDateException.java
    index 7f2cf4e2cfec564549778cd80596da37f7d9df57..bd4d4831b7202a9b3be5da057adb250a6fa911e2 100644
    --- a/core/src/main/java/hudson/scheduler/RareOrImpossibleDateException.java
    +++ b/core/src/main/java/hudson/scheduler/RareOrImpossibleDateException.java
    @@ -35,7 +35,7 @@ import java.util.Calendar;
      * <p>This can typically have a few different reasons:</p>
      *
      * <ul>
    - *   <li>The date is impossible. For example, June 31 does never happen, so <tt>0 0 31 6 *</tt> will never happen</li>
    + *   <li>The date is impossible. For example, June 31 does never happen, so {@code 0 0 31 6 *} will never happen</li>
      *   <li>The date happens only rarely
      *     <ul>
      *       <li>February 29 being the obvious one</li>
    diff --git a/core/src/main/java/hudson/scm/AbstractScmTagAction.java b/core/src/main/java/hudson/scm/AbstractScmTagAction.java
    index c098427ac6de87bf52cdef32b23ab18010a6e8fe..a86ba18382ec498bab920ff0b1256dd13346fed3 100644
    --- a/core/src/main/java/hudson/scm/AbstractScmTagAction.java
    +++ b/core/src/main/java/hudson/scm/AbstractScmTagAction.java
    @@ -37,11 +37,11 @@ import java.io.IOException;
     import jenkins.model.RunAction2;
     
     /**
    - * Common part of <tt>CVSSCM.TagAction</tt> and <tt>SubversionTagAction</tt>.
    + * Common part of {@code CVSSCM.TagAction} and {@code SubversionTagAction}.
      *
      * <p>
      * This class implements the action that tags the modules. Derived classes
    - * need to provide <tt>tagForm.jelly</tt> view that displays a form for
    + * need to provide {@code tagForm.jelly} view that displays a form for
      * letting user start tagging.
      *
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/scm/NullChangeLogParser.java b/core/src/main/java/hudson/scm/NullChangeLogParser.java
    index 19f6aeceb781190449f46915c95147fdb1330672..d78a03cce3e66ea80fd3c79b0b7c320d5bbb4779 100644
    --- a/core/src/main/java/hudson/scm/NullChangeLogParser.java
    +++ b/core/src/main/java/hudson/scm/NullChangeLogParser.java
    @@ -41,7 +41,7 @@ public class NullChangeLogParser extends ChangeLogParser {
             return ChangeLogSet.createEmpty(build);
         }
         
    -    public Object readResolve() {
    +    protected Object readResolve() {
             return INSTANCE;
         }
     }
    diff --git a/core/src/main/java/hudson/scm/SCM.java b/core/src/main/java/hudson/scm/SCM.java
    index c33810a7f8f41964110983f4cfcbcc4b2f230e90..b49f3f10fbcd6a11aa35d1d2bee223a6a914e1e8 100644
    --- a/core/src/main/java/hudson/scm/SCM.java
    +++ b/core/src/main/java/hudson/scm/SCM.java
    @@ -54,6 +54,8 @@ import java.io.IOException;
     import java.util.ArrayList;
     import java.util.List;
     import java.util.Map;
    +import java.util.logging.Level;
    +import java.util.logging.Logger;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
     import javax.annotation.Nullable;
    @@ -86,6 +88,8 @@ import org.kohsuke.stapler.export.ExportedBean;
     @ExportedBean
     public abstract class SCM implements Describable<SCM>, ExtensionPoint {
     
    +    private static final Logger LOGGER = Logger.getLogger(SCM.class.getName());
    +
         /** JENKINS-35098: discouraged */
         @SuppressWarnings("FieldMayBeFinal")
         private static boolean useAutoBrowserHolder = SystemProperties.getBoolean(SCM.class.getName() + ".useAutoBrowserHolder");
    @@ -143,7 +147,12 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
                 }
                 return autoBrowserHolder.get();
             } else {
    -            return guessBrowser();
    +            try {
    +                return guessBrowser();
    +            } catch (RuntimeException x) {
    +                LOGGER.log(Level.WARNING, null, x);
    +                return null;
    +            }
             }
         }
     
    @@ -563,7 +572,7 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
          * <p>
          * Many builders, like Ant or Maven, works off the specific user file
          * at the top of the checked out module (in the above case, that would
    -     * be <tt>xyz/build.xml</tt>), yet the builder doesn't know the "xyz"
    +     * be {@code xyz/build.xml}), yet the builder doesn't know the "xyz"
          * part; that comes from SCM.
          *
          * <p>
    @@ -669,7 +678,7 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
         }
     
         /**
    -     * The returned object will be used to parse <tt>changelog.xml</tt>.
    +     * The returned object will be used to parse {@code changelog.xml}.
          */
         public abstract ChangeLogParser createChangeLogParser();
     
    diff --git a/core/src/main/java/hudson/scm/package.html b/core/src/main/java/hudson/scm/package.html
    index 610f57991d9931e3ee1ea5e658fafa90b5d076ca..d2a0046c665ff9e2217f5ec12e5023989df06616 100644
    --- a/core/src/main/java/hudson/scm/package.html
    +++ b/core/src/main/java/hudson/scm/package.html
    @@ -23,5 +23,5 @@ THE SOFTWARE.
     -->
     
     <html><head/><body>
    -Hudson's interface with source code management systems. Start with <a href="SCM.html"><tt>SCM</tt></a>
    +Hudson's interface with source code management systems. Start with <a href="SCM.html"><code>SCM</code></a>
     </body></html>
    \ No newline at end of file
    diff --git a/core/src/main/java/hudson/search/FixedSet.java b/core/src/main/java/hudson/search/FixedSet.java
    index f9ab1ade2990e273beac460e43ca4a8088b1d37d..eca310184329baf7a1925f447596501f7d89d2e2 100644
    --- a/core/src/main/java/hudson/search/FixedSet.java
    +++ b/core/src/main/java/hudson/search/FixedSet.java
    @@ -50,7 +50,7 @@ public class FixedSet implements SearchIndex {
             boolean caseInsensitive = UserSearchProperty.isCaseInsensitive();
             for (SearchItem i : items) {
                 String name = i.getSearchName();
    -            if (name.equals(token) || (caseInsensitive && name.equalsIgnoreCase(token))) {
    +            if (name != null && (name.equals(token) || (caseInsensitive && name.equalsIgnoreCase(token)))) {
                     result.add(i);
                 }
             }
    @@ -60,7 +60,7 @@ public class FixedSet implements SearchIndex {
             boolean caseInsensitive = UserSearchProperty.isCaseInsensitive();
             for (SearchItem i : items) {
                 String name = i.getSearchName();
    -            if (name.contains(token) || (caseInsensitive && StringUtils.containsIgnoreCase(name, token))) {
    +            if (name != null && (name.contains(token) || (caseInsensitive && StringUtils.containsIgnoreCase(name, token)))) {
                     result.add(i);
                 }
             }
    diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java
    index 627d77fafba4c570c86c12bac722b895764a3822..6b07b99a5d615933f7a3a3a9c44010463c1edd8f 100644
    --- a/core/src/main/java/hudson/search/Search.java
    +++ b/core/src/main/java/hudson/search/Search.java
    @@ -42,10 +42,13 @@ import java.util.logging.Logger;
     
     import javax.servlet.ServletException;
     
    +import jenkins.util.MemoryReductionUtil;
    +import jenkins.model.Jenkins;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.Ancestor;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.StaplerProxy;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.export.DataWriter;
    @@ -63,7 +66,7 @@ import org.kohsuke.stapler.export.Flavor;
      * @author Kohsuke Kawaguchi
      * @see SearchableModelObject
      */
    -public class Search {
    +public class Search implements StaplerProxy {
         @Restricted(NoExternalUse.class) // used from stapler views only
         public static String encodeQuery(String query) throws UnsupportedEncodingException {
             return URLEncoder.encode(query, "UTF-8");
    @@ -140,7 +143,7 @@ public class Search {
         public SearchResult getSuggestions(StaplerRequest req, String query) {
             Set<String> paths = new HashSet<String>();  // paths already added, to control duplicates
             SearchResultImpl r = new SearchResultImpl();
    -        int max = req.hasParameter("max") ? Integer.parseInt(req.getParameter("max")) : 20;
    +        int max = req.hasParameter("max") ? Integer.parseInt(req.getParameter("max")) : 100;
             SearchableModelObject smo = findClosestSearchableModelObject(req);
             for (SuggestedItem i : suggest(makeSuggestIndex(req), query, smo)) {
                 if(r.size()>=max) {
    @@ -323,16 +326,14 @@ public class Search {
         static final class TokenList {
             private final String[] tokens;
     
    -        private final static String[] EMPTY = new String[0];
    -
             public TokenList(String tokenList) {
    -            tokens = tokenList!=null ? tokenList.split("(?<=\\s)(?=\\S)") : EMPTY;
    +            tokens = tokenList!=null ? tokenList.split("(?<=\\s)(?=\\S)") : MemoryReductionUtil.EMPTY_STRING_ARRAY;
             }
     
             public int length() { return tokens.length; }
     
             /**
    -         * Returns {@link List} such that its <tt>get(end)</tt>
    +         * Returns {@link List} such that its {@code get(end)}
              * returns the concatenation of [token_start,...,token_end]
              * (both end inclusive.)
              */
    @@ -406,6 +407,21 @@ public class Search {
     
             return paths[tokens.length()];
         }
    -    
    +
    +    @Override
    +    @Restricted(NoExternalUse.class)
    +    public Object getTarget() {
    +        if (!SKIP_PERMISSION_CHECK) {
    +            Jenkins.getInstance().checkPermission(Jenkins.READ);
    +        }
    +        return this;
    +    }
    +
    +    /**
    +     * Escape hatch for StaplerProxy-based access control
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(Search.class.getName() + ".skipPermissionCheck");
    +
         private final static Logger LOGGER = Logger.getLogger(Search.class.getName());
     }
    diff --git a/core/src/main/java/hudson/security/ACL.java b/core/src/main/java/hudson/security/ACL.java
    index 41f8b5a9bc82430162647394e55b6d21b9ba2539..1f39620c66ebcc353f4f32772cba9cbfa5063744 100644
    --- a/core/src/main/java/hudson/security/ACL.java
    +++ b/core/src/main/java/hudson/security/ACL.java
    @@ -34,6 +34,7 @@ import hudson.model.Item;
     import hudson.remoting.Callable;
     import hudson.model.ItemGroup;
     import hudson.model.TopLevelItemDescriptor;
    +import java.util.function.BiFunction;
     import jenkins.security.NonSerializableSecurityContext;
     import jenkins.model.Jenkins;
     import jenkins.security.NotReallyRoleSensitiveCallable;
    @@ -95,6 +96,21 @@ public abstract class ACL {
          */
         public abstract boolean hasPermission(@Nonnull Authentication a, @Nonnull Permission permission);
     
    +    /**
    +     * Creates a simple {@link ACL} implementation based on a “single-abstract-method” easily implemented via lambda syntax.
    +     * @param impl the implementation of {@link ACL#hasPermission(Authentication, Permission)}
    +     * @return an adapter to that lambda
    +     * @since 2.105
    +     */
    +    public static ACL lambda(final BiFunction<Authentication, Permission, Boolean> impl) {
    +        return new ACL() {
    +            @Override
    +            public boolean hasPermission(Authentication a, Permission permission) {
    +                return impl.apply(a, permission);
    +            }
    +        };
    +    }
    +
         /**
          * Checks if the current security principal has the permission to create top level items within the specified
          * item group.
    diff --git a/core/src/main/resources/jenkins/security/ApiTokenProperty/config.groovy b/core/src/main/java/hudson/security/AccountCreationFailedException.java
    similarity index 69%
    rename from core/src/main/resources/jenkins/security/ApiTokenProperty/config.groovy
    rename to core/src/main/java/hudson/security/AccountCreationFailedException.java
    index 76ab6b5eb5d029557190897fcaca604c62a11cbd..bab5345db1647133d7604a1b48ca0863d74437a0 100644
    --- a/core/src/main/resources/jenkins/security/ApiTokenProperty/config.groovy
    +++ b/core/src/main/java/hudson/security/AccountCreationFailedException.java
    @@ -1,7 +1,7 @@
     /*
      * The MIT License
      *
    - * Copyright (c) 2011, CloudBees, Inc.
    + * Copyright (c) 2017 Jenkins contributors
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy
      * of this software and associated documentation files (the "Software"), to deal
    @@ -21,16 +21,20 @@
      * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      * THE SOFTWARE.
      */
    -package jenkins.security.ApiTokenProperty;
     
    -f=namespace(lib.FormTagLib)
    +package hudson.security;
     
    -f.advanced(title:_("Show API Token"), align:"left") {
    -    f.entry(title: _('User ID')) {
    -        f.readOnlyTextbox(value: my.id)
    -    }
    -    f.entry(title:_("API Token"), field:"apiToken") {
    -        f.readOnlyTextbox(id:"apiToken") // TODO: need to figure out the way to do this without using ID.
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
    +
    +/**
    + * Thrown if an account creation was attempted but failed due to invalid data being entered into a form.
    + *
    + * @author Philipp Nowak
    + */
    +@Restricted(NoExternalUse.class)
    +public class AccountCreationFailedException extends Exception {
    +    public AccountCreationFailedException(String message) {
    +        super(message);
         }
    -    f.validateButton(title:_("Change API Token"),method:"changeToken")
     }
    diff --git a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
    index bda002cf106fd51e1c09345ae1b3f160759e3466..1199e303ef9d54d41f9a98413880d855187e0d09 100644
    --- a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
    +++ b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
    @@ -39,7 +39,7 @@ import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
     
     /**
      * {@link AuthenticationProcessingFilter} with a change for Jenkins so that
    - * we can pick up the hidden "from" form field defined in <tt>login.jelly</tt>
    + * we can pick up the hidden "from" form field defined in {@code login.jelly}
      * to send the user back to where he came from, after a successful authentication.
      * 
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/security/AuthorizationStrategy.java b/core/src/main/java/hudson/security/AuthorizationStrategy.java
    index a875908f212d7ec472f589dad9a27f93090e9260..a273e984b201c96d3c33c86666bb04ac297354e1 100644
    --- a/core/src/main/java/hudson/security/AuthorizationStrategy.java
    +++ b/core/src/main/java/hudson/security/AuthorizationStrategy.java
    @@ -95,9 +95,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl<Auth
          * @since 1.220
          */
         public @Nonnull ACL getACL(final @Nonnull View item) {
    -        return new ACL() {
    -            @Override
    -            public boolean hasPermission(Authentication a, Permission permission) {
    +        return ACL.lambda((a, permission) -> {
                     ACL base = item.getOwner().getACL();
     
                     boolean hasPermission = base.hasPermission(a, permission);
    @@ -106,8 +104,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl<Auth
                     }
     
                     return hasPermission;
    -            }
    -        };
    +        });
         }
         
         /**
    @@ -225,12 +222,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl<Auth
                 return Collections.emptySet();
             }
     
    -        private static final ACL UNSECURED_ACL = new ACL() {
    -            @Override
    -            public boolean hasPermission(Authentication a, Permission permission) {
    -                return true;
    -            }
    -        };
    +        private static final ACL UNSECURED_ACL = ACL.lambda((a, p) -> true);
     
             @Extension @Symbol("unsecured")
             public static final class DescriptorImpl extends Descriptor<AuthorizationStrategy> {
    diff --git a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
    index 1ec0be214f8532bbb2865d0c90e315ec9c84c2c9..737108560bf007e6140edf977e430c431bd1ddfe 100644
    --- a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
    +++ b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
    @@ -29,6 +29,7 @@ import hudson.util.Scrambler;
     import jenkins.security.ApiTokenProperty;
     import jenkins.security.SecurityListener;
     import org.acegisecurity.Authentication;
    +import jenkins.security.BasicApiTokenHelper;
     import org.acegisecurity.context.SecurityContextHolder;
     import org.acegisecurity.userdetails.UserDetails;
     
    @@ -57,14 +58,14 @@ import java.net.URLEncoder;
      *
      * <p>
      * When an HTTP request arrives with an HTTP basic auth header, this filter detects
    - * that and emulate an invocation of <tt>/j_security_check</tt>
    + * that and emulate an invocation of {@code /j_security_check}
      * (see <a href="http://mail-archives.apache.org/mod_mbox/tomcat-users/200105.mbox/%3C9005C0C9C85BD31181B20060085DAC8B10C8EF@tuvi.andmevara.ee%3E">this page</a> for the original technique.)
      *
      * <p>
      * This causes the container to perform authentication, but there's no way
      * to find out whether the user has been successfully authenticated or not.
      * So to find this out, we then redirect the user to
    - * {@link jenkins.model.Jenkins#doSecured(StaplerRequest, StaplerResponse) <tt>/secured/...</tt> page}.
    + * {@link jenkins.model.Jenkins#doSecured(StaplerRequest, StaplerResponse) {@code /secured/...} page}.
      *
      * <p>
      * The handler of the above URL checks if the user is authenticated,
    @@ -78,7 +79,7 @@ import java.net.URLEncoder;
      * <h2>Notes</h2>
      * <ul>
      * <li>
    - * The technique of getting a request dispatcher for <tt>/j_security_check</tt> may not
    + * The technique of getting a request dispatcher for {@code /j_security_check} may not
      * work for all containers, but so far that seems like the only way to make this work.
      * <li>
      * This A → B → A redirect is a cyclic redirection, so we need to watch out for clients
    @@ -135,12 +136,9 @@ public class BasicAuthenticationFilter implements Filter {
                 return;
             }
     
    -        {// attempt to authenticate as API token
    -            // create is true as the user may not have been saved and the default api token may be in use.
    -            // validation of the user will be performed against the underlying realm in impersonate.
    -            User u = User.getById(username, true);
    -            ApiTokenProperty t = u.getProperty(ApiTokenProperty.class);
    -            if (t!=null && t.matchesPassword(password)) {
    +        {
    +            User u = BasicApiTokenHelper.isConnectingUsingApiToken(username, password);
    +            if(u != null){
                     UserDetails userDetails = u.getUserDetailsForImpersonation();
                     Authentication auth = u.impersonate(userDetails);
     
    diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
    index 95ff998a2a4ffcc65d5089a0fa26d92c972905e3..83d31bd1e1c3fab546773cea9507eb7cfc96ceea 100644
    --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
    +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
    @@ -65,6 +65,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
     import org.mindrot.jbcrypt.BCrypt;
     import org.springframework.dao.DataAccessException;
     
    +import javax.annotation.Nonnull;
     import javax.servlet.Filter;
     import javax.servlet.FilterChain;
     import javax.servlet.FilterConfig;
    @@ -73,15 +74,13 @@ import javax.servlet.ServletRequest;
     import javax.servlet.ServletResponse;
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
    +import javax.servlet.http.HttpSession;
    +
     import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
     import java.io.IOException;
     import java.lang.reflect.Constructor;
     import java.security.SecureRandom;
    -import java.util.ArrayList;
    -import java.util.Collections;
    -import java.util.List;
    -import java.util.MissingResourceException;
    -import java.util.ResourceBundle;
    +import java.util.*;
     import java.util.logging.Logger;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
    @@ -96,6 +95,15 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
      * @author Kohsuke Kawaguchi
      */
     public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRealm implements ModelObject, AccessControlled {
    +    private static /* not final */ String ID_REGEX = System.getProperty(HudsonPrivateSecurityRealm.class.getName() + ".ID_REGEX");
    +    
    +    /**
    +     * Default REGEX for the user ID check in case the ID_REGEX is not set
    +     * It allows A-Za-z0-9 + "_-"
    +     * in Java {@code \w} is equivalent to {@code [A-Za-z0-9_]} (take care of "_")
    +     */
    +    private static final String DEFAULT_ID_REGEX = "^[\\w-]+$";
    +    
         /**
          * If true, sign up is not allowed.
          * <p>
    @@ -254,6 +262,13 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
          */
         @SuppressWarnings("ACL.impersonate")
         private void loginAndTakeBack(StaplerRequest req, StaplerResponse rsp, User u) throws ServletException, IOException {
    +        HttpSession session = req.getSession(false);
    +        if (session != null) {
    +            // avoid session fixation
    +            session.invalidate();
    +        }
    +        req.getSession(true);
    +        
             // ... and let him login
             Authentication a = new UsernamePasswordAuthenticationToken(u.getId(),req.getParameter("password1"));
             a = this.getSecurityComponents().manager.authenticate(a);
    @@ -289,6 +304,35 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
             return u;
         }
     
    +    /**
    +     * Creates a user account. Intended to be called from the setup wizard.
    +     * Note that this method does not check whether it is actually called from
    +     * the setup wizard. This requires the {@link Jenkins#ADMINISTER} permission.
    +     *
    +     * @param req the request to retrieve input data from
    +     * @return the created user account, never null
    +     * @throws AccountCreationFailedException if account creation failed due to invalid form input
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public User createAccountFromSetupWizard(StaplerRequest req) throws IOException, AccountCreationFailedException {
    +        checkPermission(Jenkins.ADMINISTER);
    +        SignupInfo si = validateAccountCreationForm(req, false);
    +        if (!si.errors.isEmpty()) {
    +            String messages = getErrorMessages(si);
    +            throw new AccountCreationFailedException(messages);
    +        } else {
    +            return createAccount(si);
    +        }
    +    }
    +
    +    private String getErrorMessages(SignupInfo si) {
    +        StringBuilder messages = new StringBuilder();
    +        for (String message : si.errors.values()) {
    +            messages.append(message).append(" | ");
    +        }
    +        return messages.toString();
    +    }
    +
         /**
          * Creates a first admin user account.
          *
    @@ -321,65 +365,109 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
         }
     
         /**
    +     * @param req the request to get the form data from (is also used for redirection)
    +     * @param rsp the response to use for forwarding if the creation fails
    +     * @param validateCaptcha whether to attempt to validate a captcha in the request
    +     * @param formView the view to redirect to if creation fails
    +     *
          * @return
          *      null if failed. The browser is already redirected to retry by the time this method returns.
          *      a valid {@link User} object if the user creation was successful.
          */
    -    private User createAccount(StaplerRequest req, StaplerResponse rsp, boolean selfRegistration, String formView) throws ServletException, IOException {
    +    private User createAccount(StaplerRequest req, StaplerResponse rsp, boolean validateCaptcha, String formView) throws ServletException, IOException {
    +        SignupInfo si = validateAccountCreationForm(req, validateCaptcha);
    +
    +        if (!si.errors.isEmpty()) {
    +            // failed. ask the user to try again.
    +            req.getView(this, formView).forward(req, rsp);
    +            return null;
    +        }
    +
    +        return createAccount(si);
    +    }
    +
    +    /**
    +     * @param req              the request to process
    +     * @param validateCaptcha  whether to attempt to validate a captcha in the request
    +     *
    +     * @return a {@link SignupInfo#SignupInfo(StaplerRequest) SignupInfo from given request}, with {@link
    +     * SignupInfo#errors} set to a non-null value if any of the supported fields are invalid
    +     */
    +    private SignupInfo validateAccountCreationForm(StaplerRequest req, boolean validateCaptcha) {
             // form field validation
             // this pattern needs to be generalized and moved to stapler
             SignupInfo si = new SignupInfo(req);
     
    -        if(selfRegistration && !validateCaptcha(si.captcha))
    -            si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_TextNotMatchWordInImage();
    -
    -        if(si.password1 != null && !si.password1.equals(si.password2))
    -            si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordNotMatch();
    -
    -        if(!(si.password1 != null && si.password1.length() != 0))
    -            si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordRequired();
    +        if (validateCaptcha && !validateCaptcha(si.captcha)) {
    +            si.errors.put("captcha", Messages.HudsonPrivateSecurityRealm_CreateAccount_TextNotMatchWordInImage());
    +        }
     
    -        if(si.username==null || si.username.length()==0)
    -            si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameRequired();
    -        else {
    +        if (si.username == null || si.username.length() == 0) {
    +            si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameRequired());
    +        } else if(!containsOnlyAcceptableCharacters(si.username)) {
    +            if (ID_REGEX == null) {
    +                si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharacters());
    +            } else {
    +                si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharactersCustom(ID_REGEX));
    +            }
    +        } else {
                 // do not create the user - we just want to check if the user already exists but is not a "login" user.
    -            User user = User.getById(si.username, false); 
    +            User user = User.getById(si.username, false);
                 if (null != user)
                     // Allow sign up. SCM people has no such property.
                     if (user.getProperty(Details.class) != null)
    -                    si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameAlreadyTaken();
    +                    si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameAlreadyTaken());
             }
     
    -        if(si.fullname==null || si.fullname.length()==0)
    -            si.fullname = si.username;
    +        if (si.password1 != null && !si.password1.equals(si.password2)) {
    +            si.errors.put("password1", Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordNotMatch());
    +        }
     
    -        if(isMailerPluginPresent() && (si.email==null || !si.email.contains("@")))
    -            si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_InvalidEmailAddress();
    +        if (!(si.password1 != null && si.password1.length() != 0)) {
    +            si.errors.put("password1", Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordRequired());
    +        }
     
    -        if (! User.isIdOrFullnameAllowed(si.username)) {
    -            si.errorMessage = hudson.model.Messages.User_IllegalUsername(si.username);
    +        if (si.fullname == null || si.fullname.length() == 0) {
    +            si.fullname = si.username;
             }
     
    -        if (! User.isIdOrFullnameAllowed(si.fullname)) {
    -            si.errorMessage = hudson.model.Messages.User_IllegalFullname(si.fullname);
    +        if (isMailerPluginPresent() && (si.email == null || !si.email.contains("@"))) {
    +            si.errors.put("email", Messages.HudsonPrivateSecurityRealm_CreateAccount_InvalidEmailAddress());
             }
     
    -        if(si.errorMessage!=null) {
    -            // failed. ask the user to try again.
    -            req.setAttribute("data",si);
    -            req.getView(this, formView).forward(req,rsp);
    -            return null;
    +        if (!User.isIdOrFullnameAllowed(si.username)) {
    +            si.errors.put("username", hudson.model.Messages.User_IllegalUsername(si.username));
    +        }
    +
    +        if (!User.isIdOrFullnameAllowed(si.fullname)) {
    +            si.errors.put("fullname", hudson.model.Messages.User_IllegalFullname(si.fullname));
             }
    +        req.setAttribute("data", si); // for error messages in the view
    +        return si;
    +    }
     
    +    /**
    +     * Creates a new account from a valid signup info. A signup info is valid if its {@link SignupInfo#errors}
    +     * field is empty.
    +     *
    +     * @param si the valid signup info to create an account from
    +     * @return a valid {@link User} object created from given signup info
    +     * @throws IllegalArgumentException if an invalid signup info is passed
    +     */
    +    private User createAccount(SignupInfo si) throws IOException {
    +        if (!si.errors.isEmpty()) {
    +            String messages = getErrorMessages(si);
    +            throw new IllegalArgumentException("invalid signup info passed to createAccount(si): " + messages);
    +        }
             // register the user
    -        User user = createAccount(si.username,si.password1);
    +        User user = createAccount(si.username, si.password1);
             user.setFullName(si.fullname);
    -        if(isMailerPluginPresent()) {
    +        if (isMailerPluginPresent()) {
                 try {
                     // legacy hack. mail support has moved out to a separate plugin
                     Class<?> up = Jenkins.getInstance().pluginManager.uberClassLoader.loadClass("hudson.tasks.Mailer$UserProperty");
                     Constructor<?> c = up.getDeclaredConstructor(String.class);
    -                user.addProperty((UserProperty)c.newInstance(si.email));
    +                user.addProperty((UserProperty) c.newInstance(si.email));
                 } catch (ReflectiveOperationException e) {
                     throw new RuntimeException(e);
                 }
    @@ -387,7 +475,15 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
             user.save();
             return user;
         }
    -    
    +
    +    private boolean containsOnlyAcceptableCharacters(@Nonnull String value){
    +        if(ID_REGEX == null){
    +            return value.matches(DEFAULT_ID_REGEX);
    +        }else{
    +            return value.matches(ID_REGEX);
    +        }
    +    }
    +
         @Restricted(NoExternalUse.class)
         public boolean isMailerPluginPresent() {
             try {
    @@ -445,8 +541,9 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
          * This is to map users under the security realm URL.
          * This in turn helps us set up the right navigation breadcrumb.
          */
    +    @Restricted(NoExternalUse.class)
         public User getUser(String id) {
    -        return User.getById(id, true);
    +        return User.getById(id, User.ALLOW_USER_CREATION_VIA_URL && hasPermission(Jenkins.ADMINISTER));
         }
     
         // TODO
    @@ -460,6 +557,8 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
              */
             public String errorMessage;
     
    +        public HashMap<String, String> errors = new HashMap<String, String>();
    +
             public SignupInfo() {
             }
     
    @@ -641,7 +740,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
          *
          * <p>
          * The salt is prepended to the hashed password and returned. So the encoded password is of the form
    -     * <tt>SALT ':' hash(PASSWORD,SALT)</tt>.
    +     * {@code SALT ':' hash(PASSWORD,SALT)}.
          *
          * <p>
          * This abbreviates the need to store the salt separately, which in turn allows us to hide the salt handling
    @@ -650,11 +749,11 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
         /*package*/ static final PasswordEncoder CLASSIC = new PasswordEncoder() {
             private final PasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
     
    -        public String encodePassword(String rawPass, Object _) throws DataAccessException {
    +        public String encodePassword(String rawPass, Object obj) throws DataAccessException {
                 return hash(rawPass);
             }
     
    -        public boolean isPasswordValid(String encPass, String rawPass, Object _) throws DataAccessException {
    +        public boolean isPasswordValid(String encPass, String rawPass, Object obj) throws DataAccessException {
                 // pull out the sale from the encoded password
                 int i = encPass.indexOf(':');
                 if(i<0) return false;
    @@ -690,11 +789,11 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
          * {@link PasswordEncoder} that uses jBCrypt.
          */
         private static final PasswordEncoder JBCRYPT_ENCODER = new PasswordEncoder() {
    -        public String encodePassword(String rawPass, Object _) throws DataAccessException {
    +        public String encodePassword(String rawPass, Object obj) throws DataAccessException {
                 return BCrypt.hashpw(rawPass,BCrypt.gensalt());
             }
     
    -        public boolean isPasswordValid(String encPass, String rawPass, Object _) throws DataAccessException {
    +        public boolean isPasswordValid(String encPass, String rawPass, Object obj) throws DataAccessException {
                 return BCrypt.checkpw(rawPass,encPass);
             }
         };
    diff --git a/core/src/main/java/hudson/security/LegacySecurityRealm.java b/core/src/main/java/hudson/security/LegacySecurityRealm.java
    index 116e50b2c9e237aaa3bc409ba060f978f01c14c8..8f4cc84e7a72e4bfee3ae06b79a08af1a84437a7 100644
    --- a/core/src/main/java/hudson/security/LegacySecurityRealm.java
    +++ b/core/src/main/java/hudson/security/LegacySecurityRealm.java
    @@ -29,13 +29,12 @@ import org.acegisecurity.AuthenticationException;
     import org.jenkinsci.Symbol;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
    +import org.kohsuke.stapler.DataBoundConstructor;
     import org.springframework.web.context.WebApplicationContext;
    -import org.kohsuke.stapler.StaplerRequest;
     import groovy.lang.Binding;
     import hudson.model.Descriptor;
     import hudson.util.spring.BeanBuilder;
     import hudson.Extension;
    -import net.sf.json.JSONObject;
     
     import javax.servlet.Filter;
     import javax.servlet.FilterConfig;
    @@ -48,6 +47,10 @@ import javax.servlet.FilterConfig;
      * @author Kohsuke Kawaguchi
      */
     public final class LegacySecurityRealm extends SecurityRealm implements AuthenticationManager {
    +    @DataBoundConstructor
    +    public LegacySecurityRealm() {
    +    }
    +
         public SecurityComponents createSecurityComponents() {
             return new SecurityComponents(this);
         }
    @@ -104,10 +107,6 @@ public final class LegacySecurityRealm extends SecurityRealm implements Authenti
                 DESCRIPTOR = this;
             }
     
    -        public SecurityRealm newInstance(StaplerRequest req, JSONObject formData) throws FormException {
    -            return new LegacySecurityRealm();
    -        }
    -
             public String getDisplayName() {
                 return Messages.LegacySecurityRealm_Displayname();
             }
    diff --git a/core/src/main/java/hudson/security/PermissionGroup.java b/core/src/main/java/hudson/security/PermissionGroup.java
    index 13542cf4a884342883700f8e6d945c83fe95dac7..ef39787662f72a53e45c05882bece5ea4f42d032 100644
    --- a/core/src/main/java/hudson/security/PermissionGroup.java
    +++ b/core/src/main/java/hudson/security/PermissionGroup.java
    @@ -27,6 +27,7 @@ import hudson.model.Hudson;
     import java.util.ArrayList;
     import java.util.Iterator;
     import java.util.List;
    +import java.util.Locale;
     import java.util.SortedSet;
     import java.util.TreeSet;
     import javax.annotation.CheckForNull;
    @@ -51,6 +52,8 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
          */
         public final Localizable title;
     
    +    private final String id;
    +
         /**
          * Both creates a registers a new permission group.
          * @param owner sets {@link #owner}
    @@ -58,12 +61,32 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
          * @throws IllegalStateException if this group was already registered
          */
         public PermissionGroup(@Nonnull Class owner, Localizable title) throws IllegalStateException {
    +        this(title.toString(Locale.ENGLISH), owner, title);
    +    }
    +
    +    /**
    +     * Both creates a registers a new permission group.
    +     * @param owner sets {@link #owner}
    +     * @param title sets {@link #title}
    +     * @throws IllegalStateException if this group was already registered
    +     * @since 2.127
    +     */
    +    public PermissionGroup(String id, @Nonnull Class owner, Localizable title) throws IllegalStateException {
             this.owner = owner;
             this.title = title;
    +        this.id = id;
             register(this);
         }
     
    -    private String id() {
    +    /**
    +     * Gets ID of the permission group.
    +     * @return Non-localizable ID of the permission group.
    +     */
    +    public String getId() {
    +        return id;
    +    }
    +
    +    public String getOwnerClassName() {
             return owner.getName();
         }
     
    @@ -110,7 +133,7 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
     
             // among the permissions of the same group, just sort by their names
             // so that the sort order is consistent regardless of classloading order.
    -        return id().compareTo(that.id());
    +        return getOwnerClassName().compareTo(that.getOwnerClassName());
         }
     
         private int compareOrder() {
    @@ -119,11 +142,11 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
         }
     
         @Override public boolean equals(Object o) {
    -        return o instanceof PermissionGroup && id().equals(((PermissionGroup) o).id());
    +        return o instanceof PermissionGroup && getOwnerClassName().equals(((PermissionGroup) o).getOwnerClassName());
         }
     
         @Override public int hashCode() {
    -        return id().hashCode();
    +        return getOwnerClassName().hashCode();
         }
     
         public synchronized int size() {
    @@ -131,12 +154,12 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
         }
     
         @Override public String toString() {
    -        return "PermissionGroup[" + id() + "]";
    +        return "PermissionGroup[" + getOwnerClassName() + "]";
         }
     
         private static synchronized void register(PermissionGroup g) {
             if (!PERMISSIONS.add(g)) {
    -            throw new IllegalStateException("attempt to register a second PermissionGroup for " + g.id());
    +            throw new IllegalStateException("attempt to register a second PermissionGroup for " + g.getOwnerClassName());
             }
         }
     
    diff --git a/core/src/main/java/hudson/security/SecurityRealm.java b/core/src/main/java/hudson/security/SecurityRealm.java
    index d2d637911a99aa0e63cc0beffbe655e3caba6616..90d60b7deb83ab1daacacfff9243bdc02be89654 100644
    --- a/core/src/main/java/hudson/security/SecurityRealm.java
    +++ b/core/src/main/java/hudson/security/SecurityRealm.java
    @@ -75,7 +75,7 @@ import java.util.logging.Logger;
      *
      * <p>
      * If additional views/URLs need to be exposed,
    - * an active {@link SecurityRealm} is bound to <tt>CONTEXT_ROOT/securityRealm/</tt>
    + * an active {@link SecurityRealm} is bound to {@code CONTEXT_ROOT/securityRealm/}
      * through {@link jenkins.model.Jenkins#getSecurityRealm()}, so you can define additional pages and
      * operations on your {@link SecurityRealm}.
      *
    @@ -143,7 +143,7 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
          * {@link AuthenticationManager} instantiation often depends on the user-specified parameters
          * (for example, if the authentication is based on LDAP, the user needs to specify
          * the host name of the LDAP server.) Such configuration is expected to be
    -     * presented to the user via <tt>config.jelly</tt> and then
    +     * presented to the user via {@code config.jelly} and then
          * captured as instance variables inside the {@link SecurityRealm} implementation.
          *
          * <p>
    @@ -205,8 +205,8 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
          *
          * <p>
          * {@link SecurityRealm} is a singleton resource in Hudson, and therefore
    -     * it's always configured through <tt>config.jelly</tt> and never with
    -     * <tt>global.jelly</tt>. 
    +     * it's always configured through {@code config.jelly} and never with
    +     * {@code global.jelly}.
          */
         @Override
         public Descriptor<SecurityRealm> getDescriptor() {
    @@ -225,7 +225,7 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
          * Gets the target URL of the "login" link.
          * There's no need to override this, except for {@link LegacySecurityRealm}.
          * On legacy implementation this should point to {@code loginEntry}, which
    -     * is protected by <tt>web.xml</tt>, so that the user can be eventually authenticated
    +     * is protected by {@code web.xml}, so that the user can be eventually authenticated
          * by the container.
          *
          * <p>
    @@ -304,6 +304,9 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
     
             // reset remember-me cookie
             Cookie cookie = new Cookie(ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,"");
    +        cookie.setMaxAge(0);
    +        cookie.setSecure(req.isSecure());
    +        cookie.setHttpOnly(true);
             cookie.setPath(req.getContextPath().length()>0 ? req.getContextPath() : "/");
             rsp.addCookie(cookie);
     
    @@ -312,12 +315,12 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
     
         /**
          * Returns true if this {@link SecurityRealm} allows online sign-up.
    -     * This creates a hyperlink that redirects users to <tt>CONTEXT_ROOT/signUp</tt>,
    -     * which will be served by the <tt>signup.jelly</tt> view of this class.
    +     * This creates a hyperlink that redirects users to {@code CONTEXT_ROOT/signUp},
    +     * which will be served by the {@code signup.jelly} view of this class.
          *
          * <p>
          * If the implementation needs to redirect the user to a different URL
    -     * for signing up, use the following jelly script as <tt>signup.jelly</tt>
    +     * for signing up, use the following jelly script as {@code signup.jelly}
          *
          * <pre>{@code <xmp>
          * <st:redirect url="http://www.sun.com/" xmlns:st="jelly:stapler"/>
    diff --git a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
    index 9b81ae5b19126ef2c7c31641a880c06ecaf6d4bb..ff0f374ca4c897796f00853f3b0b1be49baffd58 100644
    --- a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
    +++ b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
    @@ -123,19 +123,39 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices
     
         @Override
         public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
    -        try {
    -            return super.autoLogin(request, response);
    -        } catch (Exception e) {
    -            cancelCookie(request, response, "Failed to handle remember-me cookie: "+Functions.printThrowable(e));
    +        if(Jenkins.getInstance().isDisableRememberMe()){
    +            cancelCookie(request, response, null);
                 return null;
    +        }else {
    +            try {
    +                return super.autoLogin(request, response);
    +            } catch (Exception e) {
    +                cancelCookie(request, response, "Failed to handle remember-me cookie: " + Functions.printThrowable(e));
    +                return null;
    +            }
             }
         }
     
    -	@Override
    -	protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
    -		Cookie cookie = super.makeValidCookie(tokenValueBase64, request, maxAge);
    +    @Override
    +    protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
    +        Cookie cookie = super.makeValidCookie(tokenValueBase64, request, maxAge);
    +        secureCookie(cookie, request);
    +        return cookie;
    +    }
    +
    +    @Override 
    +    protected Cookie makeCancelCookie(HttpServletRequest request) {
    +        Cookie cookie = super.makeCancelCookie(request);
    +        secureCookie(cookie, request);
    +        return cookie;
    +    }
    +    
    +    /**
    +     * Force always the http-only flag and depending on the request, put also the secure flag.
    +     */
    +    private void secureCookie(Cookie cookie, HttpServletRequest request){
             // if we can mark the cookie HTTP only, do so to protect this cookie even in case of XSS vulnerability.
    -		if (SET_HTTP_ONLY!=null) {
    +        if (SET_HTTP_ONLY!=null) {
                 try {
                     SET_HTTP_ONLY.invoke(cookie,true);
                 } catch (IllegalAccessException e) {
    @@ -148,12 +168,10 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices
             // if the user is running Jenkins over HTTPS, we also want to prevent the cookie from leaking in HTTP.
             // whether the login is done over HTTPS or not would be a good enough approximation of whether Jenkins runs in
             // HTTPS or not, so use that.
    -        if (request.isSecure())
    -            cookie.setSecure(true);
    -		return cookie;
    -	}
    +        cookie.setSecure(request.isSecure());
    +    }
     
    -	/**
    +    /**
          * Used to compute the token signature securely.
          */
         private static final HMACConfidentialKey MAC = new HMACConfidentialKey(TokenBasedRememberMeServices.class,"mac");
    diff --git a/core/src/main/java/hudson/security/WhoAmI.java b/core/src/main/java/hudson/security/WhoAmI.java
    index 76faab1948af8148f6b48b3dad262a750b5cbdb3..66e837acd37ffe2fa5ab1f84de0837b15bbae2b4 100644
    --- a/core/src/main/java/hudson/security/WhoAmI.java
    +++ b/core/src/main/java/hudson/security/WhoAmI.java
    @@ -8,6 +8,7 @@ import hudson.model.UnprotectedRootAction;
     import java.util.ArrayList;
     import java.util.List;
     
    +import jenkins.util.MemoryReductionUtil;
     import jenkins.model.Jenkins;
     
     import org.acegisecurity.Authentication;
    @@ -62,7 +63,7 @@ public class WhoAmI implements UnprotectedRootAction {
         @Exported
         public String[] getAuthorities() {
             if (auth().getAuthorities() == null) {
    -            return new String[0];
    +            return MemoryReductionUtil.EMPTY_STRING_ARRAY;
             }
             List <String> authorities = new ArrayList<String>();
             for (GrantedAuthority a : auth().getAuthorities()) {
    diff --git a/core/src/main/java/hudson/security/captcha/CaptchaSupport.java b/core/src/main/java/hudson/security/captcha/CaptchaSupport.java
    index d5edf956637e3778998a3ec0e9c538c080ba6037..fb39e26e59b5754630036a29423f2cdf69a89fa7 100644
    --- a/core/src/main/java/hudson/security/captcha/CaptchaSupport.java
    +++ b/core/src/main/java/hudson/security/captcha/CaptchaSupport.java
    @@ -38,7 +38,7 @@ import jenkins.model.Jenkins;
      * Extension point for adding Captcha Support to User Registration Page {@link CaptchaSupport}.
      *
      * <p>
    - * This object can have an optional <tt>config.jelly</tt> to configure the Captcha Support
    + * This object can have an optional {@code config.jelly} to configure the Captcha Support
      * <p>
      * A default constructor is needed to create CaptchaSupport in
      * the default configuration.
    diff --git a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
    index 7f833c17f3263f7afa4e6ba255702cf049509f30..ae4a4edfab7272adabcf704ed252267e2ac08845 100644
    --- a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
    +++ b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
    @@ -12,6 +12,7 @@ import java.util.logging.Level;
     import java.util.logging.Logger;
     
     import hudson.Extension;
    +import hudson.model.PersistentDescriptor;
     import jenkins.util.SystemProperties;
     import hudson.Util;
     import jenkins.model.Jenkins;
    @@ -121,13 +122,12 @@ public class DefaultCrumbIssuer extends CrumbIssuer {
         }
         
         @Extension @Symbol("standard")
    -    public static final class DescriptorImpl extends CrumbIssuerDescriptor<DefaultCrumbIssuer> implements ModelObject {
    +    public static final class DescriptorImpl extends CrumbIssuerDescriptor<DefaultCrumbIssuer> implements ModelObject, PersistentDescriptor {
     
             private final static HexStringConfidentialKey CRUMB_SALT = new HexStringConfidentialKey(Jenkins.class,"crumbSalt",16);
             
             public DescriptorImpl() {
                 super(CRUMB_SALT.get(), SystemProperties.getString("hudson.security.csrf.requestfield", CrumbIssuer.DEFAULT_CRUMB_NAME));
    -            load();
             }
     
             @Override
    diff --git a/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java b/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java
    index a93c70cc23630bfb4e219ff4b11c6005bab13614..914fba876d4187485068202f2add0a2d8321cfff 100644
    --- a/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java
    +++ b/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java
    @@ -31,6 +31,8 @@ import net.sf.json.JSONObject;
     import org.jenkinsci.Symbol;
     import org.kohsuke.stapler.StaplerRequest;
     
    +import javax.annotation.Nonnull;
    +
     /**
      * Show the crumb configuration to the system config page.
      *
    @@ -39,14 +41,14 @@ import org.kohsuke.stapler.StaplerRequest;
     @Extension(ordinal=195) @Symbol("crumb") // immediately after the security setting
     public class GlobalCrumbIssuerConfiguration extends GlobalConfiguration {
         @Override
    -    public GlobalConfigurationCategory getCategory() {
    +    public @Nonnull GlobalConfigurationCategory getCategory() {
             return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
         }
     
         @Override
         public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
             // for compatibility reasons, the actual value is stored in Jenkins
    -        Jenkins j = Jenkins.getInstance();
    +        Jenkins j = Jenkins.get();
             if (json.has("csrf")) {
                 JSONObject csrf = json.getJSONObject("csrf");
                 j.setCrumbIssuer(CrumbIssuer.all().newInstanceFromRadioList(csrf, "issuer"));
    diff --git a/core/src/main/java/hudson/slaves/ChannelPinger.java b/core/src/main/java/hudson/slaves/ChannelPinger.java
    index 85d6d78675354676898f921a541061012cdb2e33..eac834a76fa437933d8090fa1b460800f51df9e2 100644
    --- a/core/src/main/java/hudson/slaves/ChannelPinger.java
    +++ b/core/src/main/java/hudson/slaves/ChannelPinger.java
    @@ -34,6 +34,8 @@ import hudson.remoting.Channel;
     import hudson.remoting.PingThread;
     import jenkins.security.MasterToSlaveCallable;
     import jenkins.slaves.PingFailureAnalyzer;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     import javax.annotation.CheckForNull;
     import java.io.IOException;
    @@ -119,14 +121,15 @@ public class ChannelPinger extends ComputerListener {
         }
     
         @VisibleForTesting
    -    /*package*/ static class SetUpRemotePing extends MasterToSlaveCallable<Void, IOException> {
    +    @Restricted(NoExternalUse.class)
    +    public static class SetUpRemotePing extends MasterToSlaveCallable<Void, IOException> {
             private static final long serialVersionUID = -2702219700841759872L;
             @Deprecated
             private transient int pingInterval;
             private final int pingTimeoutSeconds;
             private final int pingIntervalSeconds;
     
    -        SetUpRemotePing(int pingTimeoutSeconds, int pingIntervalSeconds) {
    +        public SetUpRemotePing(int pingTimeoutSeconds, int pingIntervalSeconds) {
                 this.pingTimeoutSeconds = pingTimeoutSeconds;
                 this.pingIntervalSeconds = pingIntervalSeconds;
             }
    @@ -177,7 +180,8 @@ public class ChannelPinger extends ComputerListener {
         }
     
         @VisibleForTesting
    -    /*package*/ static void setUpPingForChannel(final Channel channel, final SlaveComputer computer, int timeoutSeconds, int intervalSeconds, final boolean analysis) {
    +    @Restricted(NoExternalUse.class)
    +    public static void setUpPingForChannel(final Channel channel, final SlaveComputer computer, int timeoutSeconds, int intervalSeconds, final boolean analysis) {
             LOGGER.log(Level.FINE, "setting up ping on {0} with a {1} seconds interval and {2} seconds timeout", new Object[] {channel.getName(), intervalSeconds, timeoutSeconds});
             final AtomicBoolean isInClosed = new AtomicBoolean(false);
             final PingThread t = new PingThread(channel, timeoutSeconds * 1000L, intervalSeconds * 1000L) {
    diff --git a/core/src/main/java/hudson/slaves/Cloud.java b/core/src/main/java/hudson/slaves/Cloud.java
    index e436441abece190c59ec24a4e4f6ed6d2f51ad8d..d9cc5f2039ea4c8f690f04e5f0d06972516da231 100644
    --- a/core/src/main/java/hudson/slaves/Cloud.java
    +++ b/core/src/main/java/hudson/slaves/Cloud.java
    @@ -67,7 +67,7 @@ import java.util.concurrent.Future;
      * <p>
      * To do this, have your {@link Slave} subtype remember the necessary handle (such as EC2 instance ID)
      * as a field. Such fields need to survive the user-initiated re-configuration of {@link Slave}, so you'll need to
    - * expose it in your {@link Slave} <tt>configure-entries.jelly</tt> and read it back in through {@link DataBoundConstructor}.
    + * expose it in your {@link Slave} {@code configure-entries.jelly} and read it back in through {@link DataBoundConstructor}.
      *
      * <p>
      * You then implement your own {@link Computer} subtype, override {@link Slave#createComputer()}, and instantiate
    @@ -80,9 +80,9 @@ import java.util.concurrent.Future;
      *
      * <h3>Views</h3>
      *
    - * Since version 2.64, Jenkins clouds are visualized in UI. Implementations can provide <tt>top</tt> or <tt>main</tt> view
    - * to be presented at the top of the page or at the bottom respectively. In the middle, actions have their <tt>summary</tt>
    - * views displayed. Actions further contribute to <tt>sidepanel</tt> with <tt>box</tt> views. All mentioned views are
    + * Since version 2.64, Jenkins clouds are visualized in UI. Implementations can provide {@code top} or {@code main} view
    + * to be presented at the top of the page or at the bottom respectively. In the middle, actions have their {@code summary}
    + * views displayed. Actions further contribute to {@code sidepanel} with {@code box} views. All mentioned views are
      * optional to preserve backward compatibility.
      *
      * @author Kohsuke Kawaguchi
    diff --git a/core/src/main/java/hudson/slaves/DumbSlave.java b/core/src/main/java/hudson/slaves/DumbSlave.java
    index 835cf004ad23d02fb8fc8de08468f2122084a295..8142eb358facee47f8368640b03587fb1e710725 100644
    --- a/core/src/main/java/hudson/slaves/DumbSlave.java
    +++ b/core/src/main/java/hudson/slaves/DumbSlave.java
    @@ -65,8 +65,8 @@ public final class DumbSlave extends Slave {
             super(name, remoteFS, launcher);
         }
     
    -    @Extension @Symbol({"dumb",
    -            "slave"/*because this is in effect the canonical slave type*/})
    +    @Extension @Symbol({"permanent" /*because this is in effect the canonical slave type*/, 
    +            "dumb", "slave"})
         public static final class DescriptorImpl extends SlaveDescriptor {
             public String getDisplayName() {
                 return Messages.DumbSlave_displayName();
    diff --git a/core/src/main/java/hudson/slaves/EnvironmentVariablesNodeProperty.java b/core/src/main/java/hudson/slaves/EnvironmentVariablesNodeProperty.java
    index d41b8c49a12326d819a993dd3e923221004f88d5..90f95efb4e17fa0d773c7c274e905f7e079319fd 100644
    --- a/core/src/main/java/hudson/slaves/EnvironmentVariablesNodeProperty.java
    +++ b/core/src/main/java/hudson/slaves/EnvironmentVariablesNodeProperty.java
    @@ -39,6 +39,9 @@ import org.kohsuke.stapler.Stapler;
     import java.io.IOException;
     import java.util.Arrays;
     import java.util.List;
    +import java.util.Map;
    +import java.util.Set;
    +import java.util.stream.Collectors;
     
     /**
      * {@link NodeProperty} that sets additional environment variables.
    @@ -65,6 +68,14 @@ public class EnvironmentVariablesNodeProperty extends NodeProperty<Node> {
         	return envVars;
         }
     
    +    /**
    +     * @return environment variables using same data type as constructor parameter.
    +     * @since 2.136
    +     */
    +    public List<Entry> getEnv() {
    +        return envVars.entrySet().stream().map(Entry::new).collect(Collectors.toList());
    +    }
    +
         @Override
         public Environment setUp(AbstractBuild build, Launcher launcher,
     			BuildListener listener) throws IOException, InterruptedException {
    @@ -100,6 +111,10 @@ public class EnvironmentVariablesNodeProperty extends NodeProperty<Node> {
     	public static class Entry {
     		public String key, value;
     
    +		private Entry(Map.Entry<String,String> e) {
    +		    this(e.getKey(), e.getValue());
    +        }
    +
     		@DataBoundConstructor
     		public Entry(String key, String value) {
     			this.key = key;
    diff --git a/core/src/main/java/hudson/slaves/JNLPLauncher.java b/core/src/main/java/hudson/slaves/JNLPLauncher.java
    index eeafc2bb7f211e2c2e8631b971327acb59053860..bed08613d8146fa87a5300833e05810a41c52c3a 100644
    --- a/core/src/main/java/hudson/slaves/JNLPLauncher.java
    +++ b/core/src/main/java/hudson/slaves/JNLPLauncher.java
    @@ -31,12 +31,14 @@ import hudson.model.DescriptorVisibilityFilter;
     import hudson.model.TaskListener;
     import javax.annotation.CheckForNull;
     import javax.annotation.Nonnull;
    +
     import jenkins.model.Jenkins;
     import jenkins.slaves.RemotingWorkDirSettings;
     import org.jenkinsci.Symbol;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.NoExternalUse;
     import org.kohsuke.stapler.DataBoundConstructor;
    +import org.kohsuke.stapler.DataBoundSetter;
     
     /**
      * {@link ComputerLauncher} via JNLP.
    @@ -67,21 +69,29 @@ public class JNLPLauncher extends ComputerLauncher {
         public final String vmargs;
     
         @Nonnull
    -    private RemotingWorkDirSettings workDirSettings;
    +    private RemotingWorkDirSettings workDirSettings = RemotingWorkDirSettings.getEnabledDefaults();
    +
    +    /**
    +     * Constructor.
    +     * @param tunnel Tunnel settings
    +     * @param vmargs JVM arguments
    +     * @param workDirSettings Settings for Work Directory management in Remoting.
    +     *                        If {@code null}, {@link RemotingWorkDirSettings#getEnabledDefaults()}
    +     *                        will be used to enable work directories by default in new agents.
    +     * @since 2.68
    +     */
    +    @Deprecated
    +    public JNLPLauncher(@CheckForNull String tunnel, @CheckForNull String vmargs, @CheckForNull RemotingWorkDirSettings workDirSettings) {
    +        this(tunnel, vmargs);
    +        if (workDirSettings != null) {
    +            setWorkDirSettings(workDirSettings);
    +        }
    +    }
         
         @DataBoundConstructor
    -    public JNLPLauncher(@CheckForNull String tunnel, @CheckForNull String vmargs, @Nonnull RemotingWorkDirSettings workDirSettings) {
    +    public JNLPLauncher(@CheckForNull String tunnel, @CheckForNull String vmargs) {
             this.tunnel = Util.fixEmptyAndTrim(tunnel);
             this.vmargs = Util.fixEmptyAndTrim(vmargs);
    -        this.workDirSettings = workDirSettings;
    -    }
    -    
    -    @Deprecated
    -    public JNLPLauncher(String tunnel, String vmargs) {
    -        // TODO: Enable workDir by default in API? Otherwise classes inheriting from JNLPLauncher
    -        // will need to enable the feature by default as well.
    -        // https://github.com/search?q=org%3Ajenkinsci+%22extends+JNLPLauncher%22&type=Code
    -        this(tunnel, vmargs, RemotingWorkDirSettings.getDisabledDefaults());
         }
     
         /**
    @@ -121,6 +131,11 @@ public class JNLPLauncher extends ComputerLauncher {
         public RemotingWorkDirSettings getWorkDirSettings() {
             return workDirSettings;
         }
    +
    +    @DataBoundSetter
    +    public final void setWorkDirSettings(@Nonnull RemotingWorkDirSettings workDirSettings) {
    +        this.workDirSettings = workDirSettings;
    +    }
         
         @Override
         public boolean isLaunchSupported() {
    diff --git a/core/src/main/java/hudson/slaves/NodeDescriptor.java b/core/src/main/java/hudson/slaves/NodeDescriptor.java
    index 6f06289c38d35390d9a9a099b66dabb63965eca2..5262ece687087f14269d7a3a6ccea5602dc6cf37 100644
    --- a/core/src/main/java/hudson/slaves/NodeDescriptor.java
    +++ b/core/src/main/java/hudson/slaves/NodeDescriptor.java
    @@ -50,8 +50,8 @@ import javax.servlet.ServletException;
      *
      * <h2>Views</h2>
      * <p>
    - * This object needs to have <tt>newInstanceDetail.jelly</tt> view, which shows up in
    - * <tt>http://server/hudson/computers/new</tt> page as an explanation of this job type.
    + * This object needs to have {@code newInstanceDetail.jelly} view, which shows up in
    + * {@code http://server/hudson/computers/new} page as an explanation of this job type.
      *
      * <h2>Other Implementation Notes</h2>
      *
    diff --git a/core/src/main/java/hudson/slaves/OfflineCause.java b/core/src/main/java/hudson/slaves/OfflineCause.java
    index 3d711dd5f0ccd710fd267d94172871879b88aa36..a158ce2b8c15da689b612fc22634561eae5bd181 100644
    --- a/core/src/main/java/hudson/slaves/OfflineCause.java
    +++ b/core/src/main/java/hudson/slaves/OfflineCause.java
    @@ -44,7 +44,7 @@ import java.util.Date;
      *
      * <h2>Views</h2>
      * <p>
    - * {@link OfflineCause} must have <tt>cause.jelly</tt> that renders a cause
    + * {@link OfflineCause} must have {@code cause.jelly} that renders a cause
      * into HTML. This is used to tell users why the node is put offline.
      * This view should render a block element like DIV.
      *
    @@ -103,7 +103,6 @@ public abstract class OfflineCause {
          * Caused by unexpected channel termination.
          */
         public static class ChannelTermination extends OfflineCause {
    -        @Exported
             public final Exception cause;
     
             public ChannelTermination(Exception cause) {
    diff --git a/core/src/main/java/hudson/slaves/RetentionStrategy.java b/core/src/main/java/hudson/slaves/RetentionStrategy.java
    index 0e33a9dc6c4ed93ac349c1278fb993baf85623cd..9eb95290c7e9d7175fb4ab1efff0909745a45dae 100644
    --- a/core/src/main/java/hudson/slaves/RetentionStrategy.java
    +++ b/core/src/main/java/hudson/slaves/RetentionStrategy.java
    @@ -97,12 +97,7 @@ public abstract class RetentionStrategy<T extends Computer> extends AbstractDesc
          * @since 1.275
          */
         public void start(final @Nonnull T c) {
    -        Queue.withLock(new Runnable() {
    -            @Override
    -            public void run() {
    -                check(c);
    -            }
    -        });
    +        Queue.withLock((Runnable) () -> check(c));
         }
     
         /**
    @@ -123,26 +118,27 @@ public abstract class RetentionStrategy<T extends Computer> extends AbstractDesc
         /**
          * Dummy instance that doesn't do any attempt to retention.
          */
    -    public static final RetentionStrategy<Computer> NOOP = new RetentionStrategy<Computer>() {
    +    public static final RetentionStrategy<Computer> NOOP = new NoOp();
    +    private static final class NoOp extends RetentionStrategy<Computer> {
             @GuardedBy("hudson.model.Queue.lock")
    +        @Override
             public long check(Computer c) {
                 return 60;
             }
    -
             @Override
             public void start(Computer c) {
                 c.connect(false);
             }
    -
             @Override
             public Descriptor<RetentionStrategy<?>> getDescriptor() {
                 return DESCRIPTOR;
             }
    -
    -        private final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
    -
    -        class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {}
    -    };
    +        private Object readResolve() {
    +            return NOOP;
    +        }
    +        private static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
    +        private static final class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {}
    +    }
     
         /**
          * Convenient singleton instance, since this {@link RetentionStrategy} is stateless.
    diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java
    index 9b87bf5dec3f2c1110067e0863ba162ace222d23..31650d18aa7e3ab0636d228c257fd9e9cc38e293 100644
    --- a/core/src/main/java/hudson/slaves/SlaveComputer.java
    +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java
    @@ -39,6 +39,7 @@ import hudson.model.User;
     import hudson.remoting.Channel;
     import hudson.remoting.ChannelBuilder;
     import hudson.remoting.ChannelClosedException;
    +import hudson.remoting.CommandTransport;
     import hudson.remoting.Launcher;
     import hudson.remoting.VirtualChannel;
     import hudson.security.ACL;
    @@ -47,6 +48,7 @@ import hudson.util.Futures;
     import hudson.util.NullStream;
     import hudson.util.RingBufferLogHandler;
     import hudson.util.StreamTaskListener;
    +import hudson.util.VersionNumber;
     import hudson.util.io.RewindableFileOutputStream;
     import hudson.util.io.RewindableRotatingFileOutputStream;
     import jenkins.model.Jenkins;
    @@ -54,19 +56,25 @@ import jenkins.security.ChannelConfigurator;
     import jenkins.security.MasterToSlaveCallable;
     import jenkins.slaves.EncryptedSlaveAgentJnlpFile;
     import jenkins.slaves.JnlpSlaveAgentProtocol;
    +import jenkins.slaves.RemotingVersionInfo;
     import jenkins.slaves.systemInfo.SlaveSystemInfo;
     import jenkins.util.SystemProperties;
     import org.acegisecurity.context.SecurityContext;
     import org.acegisecurity.context.SecurityContextHolder;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.Beta;
    +import org.kohsuke.accmod.restrictions.DoNotUse;
     import org.kohsuke.stapler.HttpRedirect;
     import org.kohsuke.stapler.HttpResponse;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     import org.kohsuke.stapler.WebMethod;
    +import org.kohsuke.stapler.export.Exported;
     import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import javax.annotation.CheckForNull;
    +import javax.annotation.Nonnull;
     import javax.annotation.OverridingMethodsMustInvokeSuper;
     import javax.servlet.ServletException;
     import java.io.File;
    @@ -86,6 +94,7 @@ import java.util.logging.LogRecord;
     import java.util.logging.Logger;
     
     import static hudson.slaves.SlaveComputer.LogHolder.SLAVE_LOG_HANDLER;
    +import org.jenkinsci.remoting.util.LoggingChannelListener;
     
     
     /**
    @@ -377,7 +386,15 @@ public class SlaveComputer extends Computer {
     
         private final Object channelLock = new Object();
     
    -    public void setChannel(InputStream in, OutputStream out, TaskListener taskListener, Channel.Listener listener) throws IOException, InterruptedException {
    +    /**
    +     * Creates a {@link Channel} from the given stream and sets that to this agent.
    +     *
    +     * Same as {@link #setChannel(InputStream, OutputStream, OutputStream, Channel.Listener)}, but for
    +     * {@link TaskListener}.
    +     */
    +    public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out,
    +                           @Nonnull TaskListener taskListener,
    +                           @CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
             setChannel(in,out,taskListener.getLogger(),listener);
         }
     
    @@ -391,7 +408,7 @@ public class SlaveComputer extends Computer {
          *      Stream connected to the remote peer. It's the caller's responsibility to do
          *      buffering on this stream, if that's necessary.
          * @param launchLog
    -     *      If non-null, receive the portion of data in <tt>is</tt> before
    +     *      If non-null, receive the portion of data in {@code is} before
          *      the data goes into the "binary mode". This is useful
          *      when the established communication channel might include some data that might
          *      be useful for debugging/trouble-shooting.
    @@ -400,7 +417,9 @@ public class SlaveComputer extends Computer {
          *      By the time this method is called, the cause of the termination is reported to the user,
          *      so the implementation of the listener doesn't need to do that again.
          */
    -    public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
    +    public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out,
    +                           @CheckForNull OutputStream launchLog,
    +                           @CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
             ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting)
                 .withMode(Channel.Mode.NEGOTIATE)
                 .withHeaderStream(launchLog);
    @@ -413,6 +432,39 @@ public class SlaveComputer extends Computer {
             setChannel(channel,launchLog,listener);
         }
     
    +    /**
    +     * Creates a {@link Channel} from the given Channel Builder and Command Transport.
    +     * This method can be used to allow {@link ComputerLauncher}s to create channels not based on I/O streams.
    +     *
    +     * @param cb
    +     *      Channel Builder.
    +     *      To print launch logs this channel builder should have a Header Stream defined
    +     *      (see {@link ChannelBuilder#getHeaderStream()}) in this argument or by one of {@link ChannelConfigurator}s.
    +     * @param commandTransport
    +     *      Command Transport
    +     * @param listener
    +     *      Gets a notification when the channel closes, to perform clean up. Can be {@code null}.
    +     *      By the time this method is called, the cause of the termination is reported to the user,
    +     *      so the implementation of the listener doesn't need to do that again.
    +     * @since 2.127
    +     */
    +    @Restricted(Beta.class)
    +    public void setChannel(@Nonnull ChannelBuilder cb,
    +                           @Nonnull CommandTransport commandTransport,
    +                           @CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
    +        for (ChannelConfigurator cc : ChannelConfigurator.all()) {
    +            cc.onChannelBuilding(cb,this);
    +        }
    +
    +        OutputStream headerStream = cb.getHeaderStream();
    +        if (headerStream == null) {
    +            LOGGER.log(Level.WARNING, "No header stream defined when setting channel for computer {0}. " +
    +                    "Launch log won't be printed", this);
    +        }
    +        Channel channel = cb.build(commandTransport);
    +        setChannel(channel, headerStream, listener);
    +    }
    +
         /**
          * Shows {@link Channel#classLoadingCount}.
          * @since 1.495
    @@ -470,6 +522,26 @@ public class SlaveComputer extends Computer {
             return channel == null ? null : absoluteRemoteFs;
         }
     
    +    /**
    +     * Just for restFul api.
    +     * Returns the remote FS root absolute path or {@code null} if the agent is off-line. The absolute path may change
    +     * between connections if the connection method does not provide a consistent working directory and the node's
    +     * remote FS is specified as a relative path.
    +     * @see #getAbsoluteRemoteFs()
    +     * @return the remote FS root absolute path or {@code null} if the agent is off-line or don't have connect permission.
    +     * @since 2.125
    +     */
    +    @Exported
    +    @Restricted(DoNotUse.class)
    +    @CheckForNull
    +    public String getAbsoluteRemotePath() {
    +        if(hasPermission(CONNECT)) {
    +            return getAbsoluteRemoteFs();
    +        } else {
    +            return null;
    +        }
    +    }
    +
         static class LoadingCount extends MasterToSlaveCallable<Integer,RuntimeException> {
             private final boolean resource;
             LoadingCount(boolean resource) {
    @@ -506,19 +578,23 @@ public class SlaveComputer extends Computer {
     
         /**
          * Sets up the connection through an existing channel.
    -     * @param channel the channel to use; <strong>warning:</strong> callers are expected to have called {@link ChannelConfigurator} already
    +     * @param channel the channel to use; <strong>warning:</strong> callers are expected to have called {@link ChannelConfigurator} already.
    +     * @param launchLog Launch log. If not {@code null}, will receive launch log messages
    +     * @param listener Channel event listener to be attached (if not {@code null})
          * @since 1.444
          */
    -    public void setChannel(Channel channel, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
    +    public void setChannel(@Nonnull Channel channel,
    +                           @CheckForNull OutputStream launchLog,
    +                           @CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
             if(this.channel!=null)
                 throw new IllegalStateException("Already connected");
     
    -        final TaskListener taskListener = new StreamTaskListener(launchLog);
    +        final TaskListener taskListener = launchLog != null ? new StreamTaskListener(launchLog) : TaskListener.NULL;
             PrintStream log = taskListener.getLogger();
     
             channel.setProperty(SlaveComputer.class, this);
     
    -        channel.addListener(new Channel.Listener() {
    +        channel.addListener(new LoggingChannelListener(logger, Level.FINEST) {
                 @Override
                 public void onClosed(Channel c, IOException cause) {
                     // Orderly shutdown will have null exception
    @@ -545,6 +621,12 @@ public class SlaveComputer extends Computer {
     
             String slaveVersion = channel.call(new SlaveVersion());
             log.println("Remoting version: " + slaveVersion);
    +        VersionNumber agentVersion = new VersionNumber(slaveVersion);
    +        if (agentVersion.isOlderThan(RemotingVersionInfo.getMinimumSupportedVersion())) {
    +            log.println(String.format("WARNING: Remoting version is older than a minimum required one (%s). " +
    +                    "Connection will not be rejected, but the compatibility is NOT guaranteed",
    +                    RemotingVersionInfo.getMinimumSupportedVersion()));
    +        }
     
             boolean _isUnix = channel.call(new DetectOS());
             log.println(_isUnix? hudson.model.Messages.Slave_UnixSlave():hudson.model.Messages.Slave_WindowsSlave());
    @@ -660,6 +742,8 @@ public class SlaveComputer extends Computer {
     
         @RequirePOST
         public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
    +        checkPermission(CONNECT);
    +            
             if(channel!=null) {
                 req.getView(this,"already-launched.jelly").forward(req, rsp);
                 return;
    @@ -717,7 +801,7 @@ public class SlaveComputer extends Computer {
     
         public RetentionStrategy getRetentionStrategy() {
             Slave n = getNode();
    -        return n==null ? RetentionStrategy.INSTANCE : n.getRetentionStrategy();
    +        return n==null ? RetentionStrategy.NOOP : n.getRetentionStrategy();
         }
     
         /**
    diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java
    index d42a4a3298f58640ef28914433c3df2d626e7b58..b90fe33a3ab6d5f9a7ece2980c645b5e9dea181d 100644
    --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java
    +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java
    @@ -141,9 +141,9 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
         }
     
         // Backwards compatibility for older builds
    -    @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", 
    +    @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE",
                 justification = "Null checks in readResolve are valid since we deserialize and upgrade objects")
    -    public Object readResolve() {
    +    protected Object readResolve() {
             if (allowEmptyArchive == null) {
                 this.allowEmptyArchive = SystemProperties.getBoolean(ArtifactArchiver.class.getName()+".warnOnEmpty");
             }
    @@ -214,20 +214,10 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
             this.caseSensitive = caseSensitive;
         }
     
    -    private void listenerWarnOrError(TaskListener listener, String message) {
    -    	if (allowEmptyArchive) {
    -    		listener.getLogger().println(String.format("WARN: %s", message));
    -    	} else {
    -    		listener.error(message);
    -    	}
    -    }
    -
         @Override
    -    public void perform(Run<?,?> build, FilePath ws, Launcher launcher, TaskListener listener) throws InterruptedException, AbortException {
    +    public void perform(Run<?,?> build, FilePath ws, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
             if(artifacts.length()==0) {
    -            listener.error(Messages.ArtifactArchiver_NoIncludes());
    -            build.setResult(Result.FAILURE);
    -            return;
    +            throw new AbortException(Messages.ArtifactArchiver_NoIncludes());
             }
     
             Result result = build.getResult();
    @@ -249,29 +239,29 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
                 } else {
                     result = build.getResult();
                     if (result == null || result.isBetterOrEqualTo(Result.UNSTABLE)) {
    -                    // If the build failed, don't complain that there was no matching artifact.
    -                    // The build probably didn't even get to the point where it produces artifacts. 
    -                    listenerWarnOrError(listener, Messages.ArtifactArchiver_NoMatchFound(artifacts));
    -                    String msg = null;
                         try {
    -                    	msg = ws.validateAntFileMask(artifacts, FilePath.VALIDATE_ANT_FILE_MASK_BOUND, caseSensitive);
    +                    	String msg = ws.validateAntFileMask(artifacts, FilePath.VALIDATE_ANT_FILE_MASK_BOUND, caseSensitive);
    +                        if (msg != null) {
    +                            listener.getLogger().println(msg);
    +                        }
                         } catch (Exception e) {
    -                    	listenerWarnOrError(listener, e.getMessage());
    +                        Functions.printStackTrace(e, listener.getLogger());
                         }
    -                    if(msg!=null)
    -                        listenerWarnOrError(listener, msg);
    -                }
    -                if (!allowEmptyArchive) {
    -                	build.setResult(Result.FAILURE);
    +                    if (allowEmptyArchive) {
    +                        listener.getLogger().println(Messages.ArtifactArchiver_NoMatchFound(artifacts));
    +                    } else {
    +                        throw new AbortException(Messages.ArtifactArchiver_NoMatchFound(artifacts));
    +                    }
    +                } else {
    +                    // If a freestyle build failed, do not complain that there was no matching artifact:
    +                    // the build probably did not even get to the point where it produces artifacts.
    +                    // For Pipeline, the program ought not be *trying* to archive anything after a failure,
    +                    // but anyway most likely result == null above so we would not be here.
                     }
                 }
             } catch (java.nio.file.AccessDeniedException e) {
                 LOG.log(Level.FINE, "Diagnosing anticipated Exception", e);
                 throw new AbortException(e.toString()); // Message is not enough as that is the filename only
    -        } catch (IOException e) {
    -            Util.displayIOException(e,listener);
    -            Functions.printStackTrace(e, listener.error(Messages.ArtifactArchiver_FailedToArchive(artifacts)));
    -            build.setResult(Result.FAILURE);
             }
         }
     
    diff --git a/core/src/main/java/hudson/tasks/BuildStep.java b/core/src/main/java/hudson/tasks/BuildStep.java
    index 16f0c0268cc7223ba33c748a416a6d6070473adb..ad81455335c1db7049e11347484230207b4988ac 100644
    --- a/core/src/main/java/hudson/tasks/BuildStep.java
    +++ b/core/src/main/java/hudson/tasks/BuildStep.java
    @@ -65,7 +65,7 @@ import jenkins.model.Jenkins;
      * So generally speaking, derived classes should use instance variables
      * only for keeping configuration. You can still store objects you use
      * for processing, like a parser of some sort, but they need to be marked
    - * as <tt>transient</tt>, and the code needs to be aware that they might
    + * as {@code transient}, and the code needs to be aware that they might
      * be null (which is the case when you access the field for the first time
      * the object is restored.)
      *
    @@ -145,7 +145,7 @@ public interface BuildStep {
          * it owns when the rendering is requested.
          *
          * <p>
    -     * This action can have optional <tt>jobMain.jelly</tt> view, which will be
    +     * This action can have optional {@code jobMain.jelly} view, which will be
          * aggregated into the main panel of the job top page. The jelly file
          * should have an {@code <h2>} tag that shows the section title, followed by some
          * block elements to render the details of the section.
    diff --git a/core/src/main/java/hudson/tasks/BuildTrigger.java b/core/src/main/java/hudson/tasks/BuildTrigger.java
    index 00615dbc79a7e9649d39d0a2b3c977c715494fed..78f8ef13f8a10df1597782725e84af532db3e4df 100644
    --- a/core/src/main/java/hudson/tasks/BuildTrigger.java
    +++ b/core/src/main/java/hudson/tasks/BuildTrigger.java
    @@ -431,7 +431,7 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer {
             public static class ItemListenerImpl extends ItemListener {
                 @Override
                 public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) {
    -                try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
    +                try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
                         locationChanged(item, oldFullName, newFullName);
                     }
                 }
    diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java
    index fb45ef9cc2cba11f4147b863d92b9c29b1081909..c563180e0130d1c4060d335942979db68779ff25 100644
    --- a/core/src/main/java/hudson/tasks/Fingerprinter.java
    +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java
    @@ -188,59 +188,69 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
             }
         }
     
    -    private void record(Run<?,?> build, FilePath ws, TaskListener listener, Map<String,String> record, final String targets) throws IOException, InterruptedException {
    -        final class Record implements Serializable {
    -            final boolean produced;
    -            final String relativePath;
    -            final String fileName;
    -            final String md5sum;
    -
    -            public Record(boolean produced, String relativePath, String fileName, String md5sum) {
    -                this.produced = produced;
    -                this.relativePath = relativePath;
    -                this.fileName = fileName;
    -                this.md5sum = md5sum;
    -            }
    -
    -            Fingerprint addRecord(Run build) throws IOException {
    -                FingerprintMap map = Jenkins.getInstance().getFingerprintMap();
    -                return map.getOrCreate(produced?build:null, fileName, md5sum);
    -            }
    +    private static final class Record implements Serializable {
    +
    +        final boolean produced;
    +        final String relativePath;
    +        final String fileName;
    +        final String md5sum;
    +
    +        public Record(boolean produced, String relativePath, String fileName, String md5sum) {
    +            this.produced = produced;
    +            this.relativePath = relativePath;
    +            this.fileName = fileName;
    +            this.md5sum = md5sum;
    +        }
     
    -            private static final long serialVersionUID = 1L;
    +        Fingerprint addRecord(Run build) throws IOException {
    +            FingerprintMap map = Jenkins.getInstance().getFingerprintMap();
    +            return map.getOrCreate(produced?build:null, fileName, md5sum);
             }
     
    -        final long buildTimestamp = build.getTimeInMillis();
    +        private static final long serialVersionUID = 1L;
    +    }
     
    -        List<Record> records = ws.act(new MasterToSlaveFileCallable<List<Record>>() {
    -            public List<Record> invoke(File baseDir, VirtualChannel channel) throws IOException {
    -                List<Record> results = new ArrayList<Record>();
    +    private static final class FindRecords extends MasterToSlaveFileCallable<List<Record>> {
     
    -                FileSet src = Util.createFileSet(baseDir,targets);
    +        private final String targets;
    +        private final long buildTimestamp;
     
    -                DirectoryScanner ds = src.getDirectoryScanner();
    -                for( String f : ds.getIncludedFiles() ) {
    -                    File file = new File(baseDir,f);
    +        FindRecords(String targets, long buildTimestamp) {
    +            this.targets = targets;
    +            this.buildTimestamp = buildTimestamp;
    +        }
     
    -                    // consider the file to be produced by this build only if the timestamp
    -                    // is newer than when the build has started.
    -                    // 2000ms is an error margin since since VFAT only retains timestamp at 2sec precision
    -                    boolean produced = buildTimestamp <= file.lastModified()+2000;
    +        @Override
    +        public List<Record> invoke(File baseDir, VirtualChannel channel) throws IOException {
    +            List<Record> results = new ArrayList<Record>();
     
    -                    try {
    -                        results.add(new Record(produced,f,file.getName(),new FilePath(file).digest()));
    -                    } catch (IOException e) {
    -                        throw new IOException(Messages.Fingerprinter_DigestFailed(file),e);
    -                    } catch (InterruptedException e) {
    -                        throw new IOException(Messages.Fingerprinter_Aborted(),e);
    -                    }
    -                }
    +            FileSet src = Util.createFileSet(baseDir,targets);
     
    -                return results;
    +            DirectoryScanner ds = src.getDirectoryScanner();
    +            for( String f : ds.getIncludedFiles() ) {
    +                File file = new File(baseDir,f);
    +
    +                // consider the file to be produced by this build only if the timestamp
    +                // is newer than when the build has started.
    +                // 2000ms is an error margin since since VFAT only retains timestamp at 2sec precision
    +                boolean produced = buildTimestamp <= file.lastModified()+2000;
    +
    +                try {
    +                    results.add(new Record(produced,f,file.getName(),new FilePath(file).digest()));
    +                } catch (IOException e) {
    +                    throw new IOException(Messages.Fingerprinter_DigestFailed(file),e);
    +                } catch (InterruptedException e) {
    +                    throw new IOException(Messages.Fingerprinter_Aborted(),e);
    +                }
                 }
    -        });
     
    -        for (Record r : records) {
    +            return results;
    +        }
    +
    +    }
    +
    +    private void record(Run<?,?> build, FilePath ws, TaskListener listener, Map<String,String> record, final String targets) throws IOException, InterruptedException {
    +        for (Record r : ws.act(new FindRecords(targets, build.getTimeInMillis()))) {
                 Fingerprint fp = r.addRecord(build);
                 if(fp==null) {
                     listener.error(Messages.Fingerprinter_FailedFor(r.relativePath));
    diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java
    index c1e4a610fe18d5fa8e070a9ecaded3458995c9a7..5bccbfe78548651ed390c081f0e986bf3c53249a 100644
    --- a/core/src/main/java/hudson/tasks/Maven.java
    +++ b/core/src/main/java/hudson/tasks/Maven.java
    @@ -24,6 +24,7 @@
     package hudson.tasks;
     
     import hudson.Extension;
    +import hudson.model.PersistentDescriptor;
     import jenkins.MasterToSlaveFileCallable;
     import hudson.Launcher;
     import hudson.Functions;
    @@ -245,7 +246,7 @@ public class Maven extends Builder {
         }
     
         /**
    -     * Looks for <tt>pom.xlm</tt> or <tt>project.xml</tt> to determine the maven executable
    +     * Looks for {@code pom.xlm} or {@code project.xml} to determine the maven executable
          * name.
          */
         private static final class DecideDefaultMavenCommand extends MasterToSlaveFileCallable<String> {
    @@ -424,13 +425,12 @@ public class Maven extends Builder {
         public static DescriptorImpl DESCRIPTOR;
     
         @Extension @Symbol("maven")
    -    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
    +    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> implements PersistentDescriptor {
             @CopyOnWrite
             private volatile MavenInstallation[] installations = new MavenInstallation[0];
     
             public DescriptorImpl() {
                 DESCRIPTOR = this;
    -            load();
             }
     
             public boolean isApplicable(Class<? extends AbstractProject> jobType) {
    @@ -553,29 +553,7 @@ public class Maven extends Builder {
             public boolean meetsMavenReqVersion(Launcher launcher, int mavenReqVersion) throws IOException, InterruptedException {
                 // FIXME using similar stuff as in the maven plugin could be better 
                 // olamy : but will add a dependency on maven in core -> so not so good 
    -            String mavenVersion = launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
    -                    private static final long serialVersionUID = -4143159957567745621L;
    -
    -                    public String call() throws IOException {
    -                        File[] jars = new File(getHomeDir(),"lib").listFiles();
    -                        if(jars!=null) { // be defensive
    -                            for (File jar : jars) {
    -                                if (jar.getName().startsWith("maven-")) {
    -                                    JarFile jf = null;
    -                                    try {
    -                                        jf = new JarFile(jar);
    -                                        Manifest manifest = jf.getManifest();
    -                                        String version = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
    -                                        if(version != null) return version;
    -                                    } finally {
    -                                        if(jf != null) jf.close();
    -                                    }
    -                                }
    -                            }
    -                        }
    -                        return "";
    -                    }
    -                });
    +            String mavenVersion = launcher.getChannel().call(new GetMavenVersion());
     
                 if (!mavenVersion.equals("")) {
                     if (mavenReqVersion == MAVEN_20) {
    @@ -594,6 +572,33 @@ public class Maven extends Builder {
                 return false;
                 
             }
    +        private class GetMavenVersion extends MasterToSlaveCallable<String, IOException> {
    +            private static final long serialVersionUID = -4143159957567745621L;
    +            @Override
    +            public String call() throws IOException {
    +                File[] jars = new File(getHomeDir(), "lib").listFiles();
    +                if (jars != null) { // be defensive
    +                    for (File jar : jars) {
    +                        if (jar.getName().startsWith("maven-")) {
    +                            JarFile jf = null;
    +                            try {
    +                                jf = new JarFile(jar);
    +                                Manifest manifest = jf.getManifest();
    +                                String version = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
    +                                if (version != null) {
    +                                    return version;
    +                                }
    +                            } finally {
    +                                if (jf != null) {
    +                                    jf.close();
    +                                }
    +                            }
    +                        }
    +                    }
    +                }
    +                return "";
    +            }
    +        }
             
             /**
              * Is this Maven 2.1.x or 2.2.x - but not Maven 3.x?
    @@ -609,9 +614,11 @@ public class Maven extends Builder {
              * Gets the executable path of this maven on the given target system.
              */
             public String getExecutable(Launcher launcher) throws IOException, InterruptedException {
    -            return launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
    +            return launcher.getChannel().call(new GetExecutable());
    +        }
    +        private class GetExecutable extends MasterToSlaveCallable<String, IOException> {
                     private static final long serialVersionUID = 2373163112639943768L;
    -
    +                @Override
                     public String call() throws IOException {
                         File exe = getExeFile("mvn");
                         if(exe.exists())
    @@ -621,7 +628,6 @@ public class Maven extends Builder {
                             return exe.getPath();
                         return null;
                     }
    -            });
             }
     
             private File getExeFile(String execName) {
    @@ -773,7 +779,7 @@ public class Maven extends Builder {
              * If the Maven installation can not be uniquely determined,
              * it's often better to return just one of them, rather than returning
              * null, since this method is currently ultimately only used to
    -         * decide where to parse <tt>conf/settings.xml</tt> from.
    +         * decide where to parse {@code conf/settings.xml} from.
              */
             MavenInstallation inferMavenInstallation();
         }
    diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java
    index 4360f4ce94c9bf74e8c83ecd164765a4c23d8148..3455076d5bfcc9550dd910fdc8e4ba9962113f33 100644
    --- a/core/src/main/java/hudson/tasks/Shell.java
    +++ b/core/src/main/java/hudson/tasks/Shell.java
    @@ -27,6 +27,7 @@ import hudson.FilePath;
     import hudson.Util;
     import hudson.Extension;
     import hudson.model.AbstractProject;
    +import hudson.model.PersistentDescriptor;
     import hudson.remoting.VirtualChannel;
     import hudson.util.FormValidation;
     import java.io.IOException;
    @@ -131,16 +132,12 @@ public class Shell extends CommandInterpreter {
         }
     
         @Extension @Symbol("shell")
    -    public static class DescriptorImpl extends BuildStepDescriptor<Builder> {
    +    public static class DescriptorImpl extends BuildStepDescriptor<Builder> implements PersistentDescriptor {
             /**
              * Shell executable, or null to default.
              */
             private String shell;
     
    -        public DescriptorImpl() {
    -            load();
    -        }
    -
             public boolean isApplicable(Class<? extends AbstractProject> jobType) {
                 return true;
             }
    diff --git a/core/src/main/java/hudson/tasks/package.html b/core/src/main/java/hudson/tasks/package.html
    index 9a85792fab9ecd3e347cf3c850ac37bb8e42ccc2..3ecc377e257b81333f1027a871b92d26d27adca9 100644
    --- a/core/src/main/java/hudson/tasks/package.html
    +++ b/core/src/main/java/hudson/tasks/package.html
    @@ -23,6 +23,6 @@ THE SOFTWARE.
     -->
     
     <html><head/><body>
    -Built-in <a href="Builder.html"><tt>Builder</tt></a>s and <a href="Publisher.html"><tt>Publisher</tt></a>s
    +Built-in <a href="Builder.html"><code>Builder</code></a>s and <a href="Publisher.html"><code>Publisher</code></a>s
     that perform the actual heavy-lifting of a build. 
    -</body></html>
    \ No newline at end of file
    +</body></html>
    diff --git a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java
    index e60facbf6851c3365b20be2dcb963615cd35bbcb..c421b041a9f5288657ffd28a04158706ed4a5652 100644
    --- a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java
    +++ b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java
    @@ -108,7 +108,7 @@ public abstract class DownloadFromUrlInstaller extends ToolInstaller {
          *
          * @return
          *      Return the real top directory inside {@code root} that contains the meat. In the above example,
    -     *      <tt>root.child("jakarta-ant")</tt> should be returned. If there's no directory to pull up,
    +     *      {@code root.child("jakarta-ant")} should be returned. If there's no directory to pull up,
          *      return null. 
          */
         protected FilePath findPullUpDirectory(FilePath root) throws IOException, InterruptedException {
    diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java
    deleted file mode 100644
    index ce4f9f2eebc69de02a47f3a8f6344b4ca8947304..0000000000000000000000000000000000000000
    --- a/core/src/main/java/hudson/tools/JDKInstaller.java
    +++ /dev/null
    @@ -1,967 +0,0 @@
    -/*
    - * The MIT License
    - *
    - * Copyright (c) 2009-2010, Sun Microsystems, Inc., CloudBees, Inc.
    - *
    - * 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 hudson.tools;
    -
    -import hudson.AbortException;
    -import hudson.Extension;
    -import hudson.FilePath;
    -import hudson.Launcher;
    -import hudson.Launcher.ProcStarter;
    -import hudson.ProxyConfiguration;
    -import hudson.Util;
    -import hudson.model.DownloadService.Downloadable;
    -import hudson.model.JDK;
    -import hudson.model.Node;
    -import hudson.model.TaskListener;
    -import hudson.util.ArgumentListBuilder;
    -import hudson.util.FormValidation;
    -import hudson.util.HttpResponses;
    -import hudson.util.Secret;
    -import java.io.OutputStream;
    -import java.nio.file.Files;
    -import java.nio.file.InvalidPathException;
    -import jenkins.model.Jenkins;
    -import jenkins.security.MasterToSlaveCallable;
    -import net.sf.json.JSONObject;
    -import net.sf.json.JsonConfig;
    -import org.apache.commons.httpclient.Cookie;
    -import org.apache.commons.httpclient.HttpClient;
    -import org.apache.commons.httpclient.HttpMethodBase;
    -import org.apache.commons.httpclient.URI;
    -import org.apache.commons.httpclient.UsernamePasswordCredentials;
    -import org.apache.commons.httpclient.auth.AuthScope;
    -import org.apache.commons.httpclient.methods.GetMethod;
    -import org.apache.commons.httpclient.methods.PostMethod;
    -import org.apache.commons.httpclient.protocol.Protocol;
    -import org.apache.commons.io.IOUtils;
    -import org.jenkinsci.Symbol;
    -import org.kohsuke.stapler.DataBoundConstructor;
    -import org.kohsuke.stapler.HttpResponse;
    -import org.kohsuke.stapler.QueryParameter;
    -import org.kohsuke.stapler.Stapler;
    -
    -import javax.servlet.ServletException;
    -import java.io.ByteArrayInputStream;
    -import java.io.DataInputStream;
    -import java.io.File;
    -import java.io.IOException;
    -import java.io.InputStream;
    -import java.io.InputStreamReader;
    -import java.io.OutputStreamWriter;
    -import java.io.PrintStream;
    -import java.net.URL;
    -import java.util.ArrayList;
    -import java.util.Arrays;
    -import java.util.Iterator;
    -import java.util.LinkedList;
    -import java.util.List;
    -import java.util.Locale;
    -import java.util.logging.Logger;
    -import java.util.regex.Matcher;
    -import java.util.regex.Pattern;
    -
    -import static hudson.tools.JDKInstaller.Preference.*;
    -import org.kohsuke.stapler.interceptor.RequirePOST;
    -
    -/**
    - * Install JDKs from java.sun.com.
    - *
    - * @author Kohsuke Kawaguchi
    - * @since 1.305
    - */
    -public class JDKInstaller extends ToolInstaller {
    -
    -    static {
    -        // this socket factory will not attempt to bind to the client interface
    -        Protocol.registerProtocol("http", new Protocol("http", new hudson.util.NoClientBindProtocolSocketFactory(), 80));
    -        Protocol.registerProtocol("https", new Protocol("https", new hudson.util.NoClientBindSSLProtocolSocketFactory(), 443));
    -    }
    -
    -    /**
    -     * The release ID that Sun assigns to each JDK, such as "jdk-6u13-oth-JPR@CDS-CDS_Developer"
    -     *
    -     * <p>
    -     * This ID can be seen in the "ProductRef" query parameter of the download page, like
    -     * https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u13-oth-JPR@CDS-CDS_Developer
    -     */
    -    public final String id;
    -
    -    /**
    -     * We require that the user accepts the license by clicking a checkbox, to make up for the part
    -     * that we auto-accept cds.sun.com license click through.
    -     */
    -    public final boolean acceptLicense;
    -
    -    @DataBoundConstructor
    -    public JDKInstaller(String id, boolean acceptLicense) {
    -        super(null);
    -        this.id = id;
    -        this.acceptLicense = acceptLicense;
    -    }
    -
    -    public FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException {
    -        FilePath expectedLocation = preferredLocation(tool, node);
    -        PrintStream out = log.getLogger();
    -        try {
    -            if(!acceptLicense) {
    -                out.println(Messages.JDKInstaller_UnableToInstallUntilLicenseAccepted());
    -                return expectedLocation;
    -            }
    -            // already installed?
    -            FilePath marker = expectedLocation.child(".installedByHudson");
    -            if (marker.exists() && marker.readToString().equals(id)) {
    -                return expectedLocation;
    -            }
    -            expectedLocation.deleteRecursive();
    -            expectedLocation.mkdirs();
    -
    -            Platform p = Platform.of(node);
    -            URL url = locate(log, p, CPU.of(node));
    -
    -//            out.println("Downloading "+url);
    -            FilePath file = expectedLocation.child(p.bundleFileName);
    -            file.copyFrom(url);
    -
    -            // JDK6u13 on Windows doesn't like path representation like "/tmp/foo", so make it a strict platform native format by doing 'absolutize'
    -            install(node.createLauncher(log), p, new FilePathFileSystem(node), log, expectedLocation.absolutize().getRemote(), file.getRemote());
    -
    -            // successfully installed
    -            file.delete();
    -            marker.write(id, null);
    -
    -        } catch (DetectionFailedException e) {
    -            out.println("JDK installation skipped: "+e.getMessage());
    -        }
    -
    -        return expectedLocation;
    -    }
    -
    -    /**
    -     * Performs the JDK installation to a system, provided that the bundle was already downloaded.
    -     *
    -     * @param launcher
    -     *      Used to launch processes on the system.
    -     * @param p
    -     *      Platform of the system. This determines how the bundle is installed.
    -     * @param fs
    -     *      Abstraction of the file system manipulation on this system.
    -     * @param log
    -     *      Where the output from the installation will be written.
    -     * @param expectedLocation
    -     *      Path to install JDK to. Must be absolute and in the native file system notation.
    -     * @param jdkBundle
    -     *      Path to the installed JDK bundle. (The bundle to download can be determined by {@link #locate(TaskListener, Platform, CPU)} call.)
    -     */
    -    public void install(Launcher launcher, Platform p, FileSystem fs, TaskListener log, String expectedLocation, String jdkBundle) throws IOException, InterruptedException {
    -        PrintStream out = log.getLogger();
    -
    -        out.println("Installing "+ jdkBundle);
    -        FilePath parent = new FilePath(launcher.getChannel(), expectedLocation).getParent();
    -        switch (p) {
    -        case LINUX:
    -        case SOLARIS:
    -            // JDK on Unix up to 6 was distributed as shell script installer, but in JDK7 it switched to a plain tgz.
    -            // so check if the file is gzipped, and if so, treat it accordingly
    -            byte[] header = new byte[2];
    -            {
    -                try (InputStream is = fs.read(jdkBundle);
    -                     DataInputStream in = new DataInputStream(is)) {
    -                    in.readFully(header);
    -                }
    -            }
    -
    -            ProcStarter starter;
    -            if (header[0]==0x1F && header[1]==(byte)0x8B) {// gzip
    -                starter = launcher.launch().cmds("tar", "xvzf", jdkBundle);
    -            } else {
    -                fs.chmod(jdkBundle,0755);
    -                starter = launcher.launch().cmds(jdkBundle, "-noregister");
    -            }
    -
    -            int exit = starter
    -                    .stdin(new ByteArrayInputStream("yes".getBytes())).stdout(out)
    -                    .pwd(new FilePath(launcher.getChannel(), expectedLocation)).join();
    -            if (exit != 0)
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -
    -            // JDK creates its own sub-directory, so pull them up
    -            List<String> paths = fs.listSubDirectories(expectedLocation);
    -            for (Iterator<String> itr = paths.iterator(); itr.hasNext();) {
    -                String s =  itr.next();
    -                if (!s.matches("j(2s)?dk.*"))
    -                    itr.remove();
    -            }
    -            if(paths.size()!=1)
    -                throw new AbortException("Failed to find the extracted JDKs: "+paths);
    -
    -            // remove the intermediate directory
    -            fs.pullUp(expectedLocation+'/'+paths.get(0),expectedLocation);
    -            break;
    -        case WINDOWS:
    -            /*
    -                Windows silent installation is full of bad know-how.
    -
    -                On Windows, command line argument to a process at the OS level is a single string,
    -                not a string array like POSIX. When we pass arguments as string array, JRE eventually
    -                turn it into a single string with adding quotes to "the right place". Unfortunately,
    -                with the strange argument layout of InstallShield (like /v/qn" INSTALLDIR=foobar"),
    -                it appears that the escaping done by JRE gets in the way, and prevents the installation.
    -                Presumably because of this, my attempt to use /q/vn" INSTALLDIR=foo" didn't work with JDK5.
    -
    -                I tried to locate exactly how InstallShield parses the arguments (and why it uses
    -                awkward option like /qn, but couldn't find any. Instead, experiments revealed that
    -                "/q/vn ARG ARG ARG" works just as well. This is presumably due to the Visual C++ runtime library
    -                (which does single string -> string array conversion to invoke the main method in most Win32 process),
    -                and this consistently worked on JDK5 and JDK4.
    -
    -                Some of the official documentations are available at
    -                - http://java.sun.com/j2se/1.5.0/sdksilent.html
    -                - http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/silent.html
    -             */
    -
    -            expectedLocation = expectedLocation.trim();
    -            if (expectedLocation.endsWith("\\")) {
    -                // Prevent a trailing slash from escaping quotes
    -                expectedLocation = expectedLocation.substring(0, expectedLocation.length() - 1);
    -            }
    -            String logFile = parent.createTempFile("install", "log").getRemote();
    -
    -
    -            ArgumentListBuilder args = new ArgumentListBuilder();
    -            assert (new File(expectedLocation).exists()) : expectedLocation
    -                    + " must exist, otherwise /L will cause the installer to fail with error 1622";
    -            if (isJava15() || isJava14()) {
    -                // Installer uses InstallShield.
    -                args.add("CMD.EXE", "/C");
    -
    -                // see http://docs.oracle.com/javase/1.5.0/docs/guide/deployment/deployment-guide/silent.html
    -                // CMD.EXE /C must be followed by a single parameter (do not split it!)
    -                args.add(jdkBundle + " /s /v\"/qn REBOOT=ReallySuppress INSTALLDIR=\\\""
    -                        + expectedLocation + "\\\" /L \\\"" + logFile + "\\\"\"");
    -            } else {
    -                // Installed uses Windows Installer (MSI)
    -                args.add(jdkBundle, "/s");
    -
    -                // Create a private JRE by omitting "PublicjreFeature"
    -                // @see http://docs.oracle.com/javase/7/docs/webnotes/install/windows/jdk-installation-windows.html#jdk-silent-installation
    -                args.add("ADDLOCAL=\"ToolsFeature\"",
    -                        "REBOOT=ReallySuppress", "INSTALLDIR=" + expectedLocation,
    -                        "/L",  logFile);
    -            }
    -            int r = launcher.launch().cmds(args).stdout(out)
    -                    .pwd(new FilePath(launcher.getChannel(), expectedLocation)).join();
    -            if (r != 0) {
    -                out.println(Messages.JDKInstaller_FailedToInstallJDK(r));
    -                // log file is in UTF-16
    -                try (InputStreamReader in = new InputStreamReader(fs.read(logFile), "UTF-16")) {
    -                    IOUtils.copy(in, new OutputStreamWriter(out));
    -                }
    -                throw new AbortException();
    -            }
    -
    -            fs.delete(logFile);
    -
    -            break;
    -
    -        case OSX:
    -            // Mount the DMG distribution bundle
    -            FilePath dmg = parent.createTempDir("jdk", "dmg");
    -            exit = launcher.launch()
    -                    .cmds("hdiutil", "attach", "-puppetstrings", "-mountpoint", dmg.getRemote(), jdkBundle)
    -                    .stdout(log)
    -                    .join();
    -            if (exit != 0)
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -
    -            // expand the installation PKG
    -            FilePath[] list = dmg.list("*.pkg");
    -            if (list.length != 1) {
    -                log.getLogger().println("JDK dmg bundle does not contain expected pkg installer");
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -            }
    -            String installer = list[0].getRemote();
    -
    -            FilePath pkg = parent.createTempDir("jdk", "pkg");
    -            pkg.deleteRecursive(); // pkgutil fails if target directory exists
    -            exit = launcher.launch()
    -                    .cmds("pkgutil", "--expand", installer, pkg.getRemote())
    -                    .stdout(log)
    -                    .join();
    -            if (exit != 0)
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -
    -            exit = launcher.launch()
    -                    .cmds("umount", dmg.getRemote())
    -                    .stdout(log)
    -                    .join();
    -            if (exit != 0)
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -
    -            // We only want the actual JDK sub-package, which "Payload" is actually a tar.gz archive
    -            list = pkg.list("jdk*.pkg/Payload");
    -            if (list.length != 1) {
    -                log.getLogger().println("JDK pkg installer does not contain expected JDK Payload archive");
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -            }
    -            String payload = list[0].getRemote();
    -            exit = launcher.launch()
    -                    .pwd(parent).cmds("tar", "xzf", payload)
    -                    .stdout(log)
    -                    .join();
    -            if (exit != 0)
    -                throw new AbortException(Messages.JDKInstaller_FailedToInstallJDK(exit));
    -
    -            parent.child("Contents/Home").moveAllChildrenTo(new FilePath(launcher.getChannel(), expectedLocation));
    -            parent.child("Contents").deleteRecursive();
    -
    -            pkg.deleteRecursive();
    -            dmg.deleteRecursive();
    -            break;
    -        }
    -    }
    -
    -    private boolean isJava15() {
    -        return id.contains("-1.5");
    -    }
    -
    -    private boolean isJava14() {
    -        return id.contains("-1.4");
    -    }
    -
    -    /**
    -     * Abstraction of the file system to perform JDK installation.
    -     * Consider {@link JDKInstaller.FilePathFileSystem} as the canonical documentation of the contract.
    -     */
    -    public interface FileSystem {
    -        void delete(String file) throws IOException, InterruptedException;
    -        void chmod(String file,int mode) throws IOException, InterruptedException;
    -        InputStream read(String file) throws IOException, InterruptedException;
    -        /**
    -         * List sub-directories of the given directory and just return the file name portion.
    -         */
    -        List<String> listSubDirectories(String dir) throws IOException, InterruptedException;
    -        void pullUp(String from, String to) throws IOException, InterruptedException;
    -    }
    -
    -    /*package*/ static final class FilePathFileSystem implements FileSystem {
    -        private final Node node;
    -
    -        FilePathFileSystem(Node node) {
    -            this.node = node;
    -        }
    -
    -        public void delete(String file) throws IOException, InterruptedException {
    -            $(file).delete();
    -        }
    -
    -        public void chmod(String file, int mode) throws IOException, InterruptedException {
    -            $(file).chmod(mode);
    -        }
    -
    -        public InputStream read(String file) throws IOException, InterruptedException {
    -            return $(file).read();
    -        }
    -
    -        public List<String> listSubDirectories(String dir) throws IOException, InterruptedException {
    -            List<String> r = new ArrayList<String>();
    -            for( FilePath f : $(dir).listDirectories())
    -                r.add(f.getName());
    -            return r;
    -        }
    -
    -        public void pullUp(String from, String to) throws IOException, InterruptedException {
    -            $(from).moveAllChildrenTo($(to));
    -        }
    -
    -        private FilePath $(String file) {
    -            return node.createPath(file);
    -        }
    -    }
    -
    -    /**
    -     * This is where we locally cache this JDK.
    -     */
    -    private File getLocalCacheFile(Platform platform, CPU cpu) {
    -        return new File(Jenkins.getInstance().getRootDir(),"cache/jdks/"+platform+"/"+cpu+"/"+id);
    -    }
    -
    -    /**
    -     * Performs a license click through and obtains the one-time URL for downloading bits.
    -     */
    -    public URL locate(TaskListener log, Platform platform, CPU cpu) throws IOException {
    -        File cache = getLocalCacheFile(platform, cpu);
    -        if (cache.exists() && cache.length()>1*1024*1024) return cache.toURL(); // if the file is too small, don't trust it. In the past, the download site served error message in 200 status code
    -
    -        log.getLogger().println("Installing JDK "+id);
    -        JDKFamilyList families = JDKList.all().get(JDKList.class).toList();
    -        if (families.isEmpty())
    -            throw new IOException("JDK data is empty.");
    -
    -        JDKRelease release = families.getRelease(id);
    -        if (release==null)
    -            throw new IOException("Unable to find JDK with ID="+id);
    -
    -        JDKFile primary=null,secondary=null;
    -        for (JDKFile f : release.files) {
    -            String vcap = f.name.toUpperCase(Locale.ENGLISH);
    -
    -            // JDK files have either 'windows', 'linux', or 'solaris' in its name, so that allows us to throw
    -            // away unapplicable stuff right away
    -            if(!platform.is(vcap))
    -                continue;
    -
    -            switch (cpu.accept(vcap)) {
    -            case PRIMARY:   primary = f;break;
    -            case SECONDARY: secondary=f;break;
    -            case UNACCEPTABLE:  break;
    -            }
    -        }
    -
    -        if(primary==null)   primary=secondary;
    -        if(primary==null)
    -            throw new AbortException("Couldn't find the right download for "+platform+" and "+ cpu +" combination");
    -        LOGGER.fine("Platform choice:"+primary);
    -
    -        log.getLogger().println("Downloading JDK from "+primary.filepath);
    -
    -        HttpClient hc = new HttpClient();
    -        hc.getParams().setParameter("http.useragent","Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)");
    -        ProxyConfiguration jpc = Jenkins.getInstance().proxy;
    -        if(jpc != null) {
    -            hc.getHostConfiguration().setProxy(jpc.name, jpc.port);
    -            if(jpc.getUserName() != null)
    -                hc.getState().setProxyCredentials(AuthScope.ANY,new UsernamePasswordCredentials(jpc.getUserName(),jpc.getPassword()));
    -        }
    -
    -        int authCount=0, totalPageCount=0;  // counters for avoiding infinite loop
    -
    -        HttpMethodBase m = new GetMethod(primary.filepath);
    -        hc.getState().addCookie(new Cookie(".oracle.com","gpw_e24",".", "/", -1, false));
    -        hc.getState().addCookie(new Cookie(".oracle.com","oraclelicense","accept-securebackup-cookie", "/", -1, false));
    -        try {
    -            while (true) {
    -                if (totalPageCount++>16) // looping too much
    -                    throw new IOException("Unable to find the login form");
    -
    -                LOGGER.fine("Requesting " + m.getURI());
    -                int r = hc.executeMethod(m);
    -                if (r/100==3) {
    -                    // redirect?
    -                    String loc = m.getResponseHeader("Location").getValue();
    -                    m.releaseConnection();
    -                    m = new GetMethod(loc);
    -                    continue;
    -                }
    -                if (r!=200)
    -                    throw new IOException("Failed to request " + m.getURI() +" exit code="+r);
    -
    -                if (m.getURI().getHost().equals("login.oracle.com")) {
    -                    /* Oracle switched from old to new, and then back to old. This code should work for either.
    -                     * Old Login flow:
    -                     * 1. /mysso/signon.jsp: Form for username + password: Submit actions is:
    -                     * 2. /oam/server/sso/auth_cred_submit: Returns a 302 to:
    -                     * 3. https://edelivery.oracle.com/osso_login_success: Returns a 302 to the download.
    -                     * New Login flow:
    -                     * 1. /oaam_server/oamLoginPage.jsp: Form for username + password. Submit action is:
    -                     * 2. /oaam_server/login.do: Returns a 302 to:
    -                     * 3. /oaam_server/loginAuth.do: After 2 seconds, JS sets window.location to:
    -                     * 4. /oaam_server/authJump.do: Contains a single form with hidden inputs and JS that submits the form to:
    -                     * 5. /oam/server/dap/cred_submit: Returns a 302 to:
    -                     * 6. https://edelivery.oracle.com/osso_login_success: Returns a 302 to the download.
    -                     */
    -                    if (m.getURI().getPath().contains("/loginAuth.do")) {
    -                        try {
    -                            Thread.sleep(2000);
    -                            m.releaseConnection();
    -                            m = new GetMethod(new URI(m.getURI(), "/oaam_server/authJump.do?jump=false", true).toString());
    -                            continue;
    -                        } catch (InterruptedException x) {
    -                            throw new IOException("Interrupted while logging in", x);
    -                        }
    -                    }
    -
    -                    LOGGER.fine("Appears to be a login page");
    -                    String resp = IOUtils.toString(m.getResponseBodyAsStream(), m.getResponseCharSet());
    -                    m.releaseConnection();
    -                    Matcher pm = Pattern.compile("<form .*?action=\"([^\"]*)\".*?</form>", Pattern.DOTALL).matcher(resp);
    -                    if (!pm.find())
    -                        throw new IllegalStateException("Unable to find a form in the response:\n"+resp);
    -
    -                    String form = pm.group();
    -                    PostMethod post = new PostMethod(
    -                            new URL(new URL(m.getURI().getURI()),pm.group(1)).toExternalForm());
    -
    -                    if (m.getURI().getPath().contains("/authJump.do")) {
    -                        m = post;
    -                        continue;
    -                    }
    -
    -                    String u = getDescriptor().getUsername();
    -                    Secret p = getDescriptor().getPassword();
    -                    if (u==null || p==null) {
    -                        log.hyperlink(getCredentialPageUrl(),"Oracle now requires Oracle account to download previous versions of JDK. Please specify your Oracle account username/password.\n");
    -                        throw new AbortException("Unable to install JDK unless a valid Oracle account username/password is provided in the system configuration.");
    -                    }
    -
    -                    for (String fragment : form.split("<input")) {
    -                        String n = extractAttribute(fragment,"name");
    -                        String v = extractAttribute(fragment,"value");
    -                        if (n==null || v==null)     continue;
    -                        if (n.equals("userid") || n.equals("ssousername"))
    -                            v = u;
    -                        if (n.equals("pass") || n.equals("password")) {
    -                            v = p.getPlainText();
    -                            if (authCount++ > 3) {
    -                                log.hyperlink(getCredentialPageUrl(),"Your Oracle account doesn't appear valid. Please specify a valid username/password\n");
    -                                throw new AbortException("Unable to install JDK unless a valid username/password is provided.");
    -                            }
    -                        }
    -                        post.addParameter(n, v);
    -                    }
    -
    -                    m = post;
    -                } else {
    -                    log.getLogger().println("Downloading " + m.getResponseContentLength() + " bytes");
    -
    -                    // download to a temporary file and rename it in to handle concurrency and failure correctly,
    -                    File tmp = new File(cache.getPath()+".tmp");
    -                    try {
    -                        tmp.getParentFile().mkdirs();
    -                        try (OutputStream out = Files.newOutputStream(tmp.toPath())) {
    -                            IOUtils.copy(m.getResponseBodyAsStream(), out);
    -                        } catch (InvalidPathException e) {
    -                            throw new IOException(e);
    -                        }
    -
    -                        tmp.renameTo(cache);
    -                        return cache.toURL();
    -                    } finally {
    -                        tmp.delete();
    -                    }
    -                }
    -            }
    -        } finally {
    -            m.releaseConnection();
    -        }
    -    }
    -
    -    private static String extractAttribute(String s, String name) {
    -        String h = name + "=\"";
    -        int si = s.indexOf(h);
    -        if (si<0)   return null;
    -        int ei = s.indexOf('\"',si+h.length());
    -        return s.substring(si+h.length(),ei);
    -    }
    -
    -    private String getCredentialPageUrl() {
    -        return "/"+getDescriptor().getDescriptorUrl()+"/enterCredential";
    -    }
    -
    -    public enum Preference {
    -        PRIMARY, SECONDARY, UNACCEPTABLE
    -    }
    -
    -    /**
    -     * Supported platform.
    -     */
    -    public enum Platform {
    -        LINUX("jdk.sh"), SOLARIS("jdk.sh"), WINDOWS("jdk.exe"), OSX("jdk.dmg");
    -
    -        /**
    -         * Choose the file name suitable for the downloaded JDK bundle.
    -         */
    -        public final String bundleFileName;
    -
    -        Platform(String bundleFileName) {
    -            this.bundleFileName = bundleFileName;
    -        }
    -
    -        public boolean is(String line) {
    -            return line.contains(name());
    -        }
    -
    -        /**
    -         * Determines the platform of the given node.
    -         */
    -        public static Platform of(Node n) throws IOException,InterruptedException,DetectionFailedException {
    -            return n.getChannel().call(new GetCurrentPlatform());
    -        }
    -
    -        public static Platform current() throws DetectionFailedException {
    -            String arch = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
    -            if(arch.contains("linux"))  return LINUX;
    -            if(arch.contains("windows"))   return WINDOWS;
    -            if(arch.contains("sun") || arch.contains("solaris"))    return SOLARIS;
    -            if(arch.contains("mac")) return OSX;
    -            throw new DetectionFailedException("Unknown CPU name: "+arch);
    -        }
    -
    -        static class GetCurrentPlatform extends MasterToSlaveCallable<Platform,DetectionFailedException> {
    -            private static final long serialVersionUID = 1L;
    -            public Platform call() throws DetectionFailedException {
    -                return current();
    -            }
    -        }
    -
    -    }
    -
    -    /**
    -     * CPU type.
    -     */
    -    public enum CPU {
    -        i386, amd64, Sparc, Itanium;
    -
    -        /**
    -         * In JDK5u3, I see platform like "Linux AMD64", while JDK6u3 refers to "Linux x64", so
    -         * just use "64" for locating bits.
    -         */
    -        public Preference accept(String line) {
    -            switch (this) {
    -            // these two guys are totally incompatible with everything else, so no fallback
    -            case Sparc:     return must(line.contains("SPARC"));
    -            case Itanium:   return must(line.contains("IA64"));
    -
    -            // 64bit Solaris, Linux, and Windows can all run 32bit executable, so fall back to 32bit if 64bit bundle is not found
    -            case amd64:
    -                if(line.contains("SPARC") || line.contains("IA64"))  return UNACCEPTABLE;
    -                if(line.contains("64"))     return PRIMARY;
    -                return SECONDARY;
    -            case i386:
    -                if(line.contains("64") || line.contains("SPARC") || line.contains("IA64"))     return UNACCEPTABLE;
    -                return PRIMARY;
    -            }
    -            return UNACCEPTABLE;
    -        }
    -
    -        private static Preference must(boolean b) {
    -             return b ? PRIMARY : UNACCEPTABLE;
    -        }
    -
    -        /**
    -         * Determines the CPU of the given node.
    -         */
    -        public static CPU of(Node n) throws IOException,InterruptedException, DetectionFailedException {
    -            return n.getChannel().call(new GetCurrentCPU());
    -        }
    -
    -        /**
    -         * Determines the CPU of the current JVM.
    -         *
    -         * http://lopica.sourceforge.net/os.html was useful in writing this code.
    -         */
    -        public static CPU current() throws DetectionFailedException {
    -            String arch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
    -            if(arch.contains("sparc"))  return Sparc;
    -            if(arch.contains("ia64"))   return Itanium;
    -            if(arch.contains("amd64") || arch.contains("86_64"))    return amd64;
    -            if(arch.contains("86"))    return i386;
    -            throw new DetectionFailedException("Unknown CPU architecture: "+arch);
    -        }
    -
    -        static class GetCurrentCPU extends MasterToSlaveCallable<CPU,DetectionFailedException> {
    -            private static final long serialVersionUID = 1L;
    -            public CPU call() throws DetectionFailedException {
    -                return current();
    -            }
    -        }
    -
    -    }
    -
    -    /**
    -     * Indicates the failure to detect the OS or CPU.
    -     */
    -    private static final class DetectionFailedException extends Exception {
    -        private DetectionFailedException(String message) {
    -            super(message);
    -        }
    -    }
    -
    -    public static final class JDKFamilyList {
    -        public JDKFamily[] data = new JDKFamily[0];
    -        public int version;
    -
    -        public boolean isEmpty() {
    -            for (JDKFamily f : data) {
    -                if (f.releases.length>0)
    -                    return false;
    -            }
    -            return true;
    -        }
    -
    -        public JDKRelease getRelease(String productCode) {
    -            for (JDKFamily f : data) {
    -                for (JDKRelease r : f.releases) {
    -                    if (r.matchesId(productCode))
    -                        return r;
    -                }
    -            }
    -            return null;
    -        }
    -    }
    -
    -    public static final class JDKFamily {
    -        public String name;
    -        public JDKRelease[] releases;
    -    }
    -
    -    public static final class JDKRelease {
    -        /**
    -         * the list of {@link JDKFile}s
    -         */
    -        public JDKFile[] files;
    -        /**
    -         * the license path
    -         */
    -        public String licpath;
    -        /**
    -         * the license title
    -         */
    -        public String lictitle;
    -        /**
    -         * This maps to the former product code, like "jdk-6u13-oth-JPR"
    -         */
    -        public String name;
    -        /**
    -         * This is human readable.
    -         */
    -        public String title;
    -
    -        /**
    -         * We used to use IDs like "jdk-6u13-oth-JPR@CDS-CDS_Developer", but Oracle switched to just "jdk-6u13-oth-JPR".
    -         * This method matches if the specified string matches the name, and it accepts both the old and the new format.
    -         */
    -        public boolean matchesId(String rhs) {
    -            return rhs!=null && (rhs.equals(name) || rhs.startsWith(name+"@"));
    -        }
    -    }
    -
    -    public static final class JDKFile {
    -        public String filepath;
    -        public String name;
    -        public String title;
    -    }
    -
    -    @Override
    -    public DescriptorImpl getDescriptor() {
    -        return (DescriptorImpl)super.getDescriptor();
    -    }
    -
    -    @Extension @Symbol("jdkInstaller")
    -    public static final class DescriptorImpl extends ToolInstallerDescriptor<JDKInstaller> {
    -        private String username;
    -        private Secret password;
    -
    -        public DescriptorImpl() {
    -            load();
    -        }
    -
    -        public String getDisplayName() {
    -            return Messages.JDKInstaller_DescriptorImpl_displayName();
    -        }
    -
    -        @Override
    -        public boolean isApplicable(Class<? extends ToolInstallation> toolType) {
    -            return toolType==JDK.class;
    -        }
    -
    -        public String getUsername() {
    -            return username;
    -        }
    -
    -        public Secret getPassword() {
    -            return password;
    -        }
    -
    -        public FormValidation doCheckId(@QueryParameter String value) {
    -            if (Util.fixEmpty(value) == null)
    -                return FormValidation.error(Messages.JDKInstaller_DescriptorImpl_doCheckId()); // improve message
    -            return FormValidation.ok();
    -        }
    -
    -        /**
    -         * List of installable JDKs.
    -         * @return never null.
    -         */
    -        public List<JDKFamily> getInstallableJDKs() throws IOException {
    -            return Arrays.asList(JDKList.all().get(JDKList.class).toList().data);
    -        }
    -
    -        public FormValidation doCheckAcceptLicense(@QueryParameter boolean value) {
    -            if (username==null || password==null)
    -                return FormValidation.errorWithMarkup(Messages.JDKInstaller_RequireOracleAccount(Stapler.getCurrentRequest().getContextPath()+'/'+getDescriptorUrl()+"/enterCredential"));
    -            if (value) {
    -                return FormValidation.ok();
    -            } else {
    -                return FormValidation.error(Messages.JDKInstaller_DescriptorImpl_doCheckAcceptLicense());
    -            }
    -        }
    -
    -        /**
    -         * Submits the Oracle account username/password.
    -         */
    -        @RequirePOST
    -        public HttpResponse doPostCredential(@QueryParameter String username, @QueryParameter String password) throws IOException, ServletException {
    -            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    -            this.username = username;
    -            this.password = Secret.fromString(password);
    -            save();
    -            return HttpResponses.redirectTo("credentialOK");
    -        }
    -    }
    -
    -    /**
    -     * JDK list.
    -     */
    -    @Extension @Symbol("jdk")
    -    public static final class JDKList extends Downloadable {
    -        public JDKList() {
    -            super(JDKInstaller.class);
    -        }
    -
    -        public JDKFamilyList toList() throws IOException {
    -            JSONObject d = getData();
    -            if(d==null) return new JDKFamilyList();
    -            return (JDKFamilyList)JSONObject.toBean(d,JDKFamilyList.class);
    -        }
    -
    -        /**
    -         * {@inheritDoc}
    -         */
    -        @Override
    -        public JSONObject reduce (List<JSONObject> jsonObjectList) {
    -            List<JDKFamily> reducedFamilies = new LinkedList<>();
    -            int version = 0;
    -            JsonConfig jsonConfig = new JsonConfig();
    -            jsonConfig.registerPropertyExclusion(JDKFamilyList.class, "empty");
    -            jsonConfig.setRootClass(JDKFamilyList.class);
    -            //collect all JDKFamily objects from the multiple json objects
    -            for (JSONObject jsonJdkFamilyList : jsonObjectList) {
    -                JDKFamilyList jdkFamilyList = (JDKFamilyList)JSONObject.toBean(jsonJdkFamilyList, jsonConfig);
    -                if (version == 0) {
    -                    //we set as version the version of the first update center
    -                    version = jdkFamilyList.version;
    -                }
    -                JDKFamily[] jdkFamilies = jdkFamilyList.data;
    -                reducedFamilies.addAll(Arrays.asList(jdkFamilies));
    -            }
    -            //we  iterate on the list and reduce it until there are no more duplicates
    -            //this could be made recursive
    -            while (hasDuplicates(reducedFamilies, "name")) {
    -                //create a temporary list to store the tmp result
    -                List<JDKFamily> tmpReducedFamilies = new LinkedList<>();
    -                //we need to skip the processed families
    -                boolean processed [] = new boolean[reducedFamilies.size()];
    -                for (int i = 0; i < reducedFamilies.size(); i ++ ) {
    -                    if (processed [i] == true) {
    -                        continue;
    -                    }
    -                    JDKFamily data1 = reducedFamilies.get(i);
    -                    boolean hasDuplicate = false;
    -                    for (int j = i + 1; j < reducedFamilies.size(); j ++ ) {
    -                        JDKFamily data2 = reducedFamilies.get(j);
    -                        //if we found a duplicate we need to merge the families
    -                        if (data1.name.equals(data2.name)) {
    -                            hasDuplicate = true;
    -                            processed [j] = true;
    -                            JDKFamily reducedData = reduceData(data1.name, new LinkedList<JDKRelease>(Arrays.asList(data1.releases)), new LinkedList<JDKRelease>(Arrays.asList(data2.releases)));
    -                            tmpReducedFamilies.add(reducedData);
    -                            //after the first duplicate has been found we break the loop since the duplicates are
    -                            //processed two by two
    -                            break;
    -                        }
    -                    }
    -                    //if no duplicate has been found we just insert the whole family in the tmp list
    -                    if (!hasDuplicate) {
    -                        tmpReducedFamilies.add(data1);
    -                    }
    -                }
    -                reducedFamilies = tmpReducedFamilies;
    -            }
    -            JDKFamilyList jdkFamilyList = new JDKFamilyList();
    -            jdkFamilyList.version = version;
    -            jdkFamilyList.data = new JDKFamily[reducedFamilies.size()];
    -            reducedFamilies.toArray(jdkFamilyList.data);
    -            JSONObject reducedJdkFamilyList = JSONObject.fromObject(jdkFamilyList, jsonConfig);
    -            //return the list with no duplicates
    -            return reducedJdkFamilyList;
    -        }
    -
    -        private JDKFamily reduceData(String name, List<JDKRelease> releases1, List<JDKRelease> releases2) {
    -            LinkedList<JDKRelease> reducedReleases = new LinkedList<>();
    -            for (Iterator<JDKRelease> iterator = releases1.iterator(); iterator.hasNext(); ) {
    -                JDKRelease release1 = iterator.next();
    -                boolean hasDuplicate = false;
    -                for (Iterator<JDKRelease> iterator2 = releases2.iterator(); iterator2.hasNext(); ) {
    -                    JDKRelease release2 = iterator2.next();
    -                    if (release1.name.equals(release2.name)) {
    -                        hasDuplicate = true;
    -                        JDKRelease reducedRelease = reduceReleases(release1, new LinkedList<JDKFile>(Arrays.asList(release1.files)), new LinkedList<JDKFile>(Arrays.asList(release2.files)));
    -                        iterator2.remove();
    -                        reducedReleases.add(reducedRelease);
    -                        //we assume that in one release list there are no duplicates so we stop at the first one
    -                        break;
    -                    }
    -                }
    -                if (!hasDuplicate) {
    -                    reducedReleases.add(release1);
    -                }
    -            }
    -            reducedReleases.addAll(releases2);
    -            JDKFamily reducedFamily = new JDKFamily();
    -            reducedFamily.name = name;
    -            reducedFamily.releases = new JDKRelease[reducedReleases.size()];
    -            reducedReleases.toArray(reducedFamily.releases);
    -            return reducedFamily;
    -        }
    -
    -        private JDKRelease reduceReleases(JDKRelease release, List<JDKFile> files1, List<JDKFile> files2) {
    -            LinkedList<JDKFile> reducedFiles = new LinkedList<>();
    -            for (Iterator<JDKFile> iterator1 = files1.iterator(); iterator1.hasNext(); ) {
    -                JDKFile file1 = iterator1.next();
    -                for (Iterator<JDKFile> iterator2 = files2.iterator(); iterator2.hasNext(); ) {
    -                    JDKFile file2 = iterator2.next();
    -                    if (file1.name.equals(file2.name)) {
    -                        iterator2.remove();
    -                        //we assume the in one file list there are no duplicates so we break after we find the
    -                        //first match
    -                        break;
    -                    }
    -                }
    -            }
    -            reducedFiles.addAll(files1);
    -            reducedFiles.addAll(files2);
    -
    -            JDKRelease jdkRelease = new JDKRelease();
    -            jdkRelease.files = new JDKFile[reducedFiles.size()];
    -            reducedFiles.toArray(jdkRelease.files);
    -            jdkRelease.name = release.name;
    -            jdkRelease.licpath = release.licpath;
    -            jdkRelease.lictitle = release.lictitle;
    -            jdkRelease.title = release.title;
    -            return jdkRelease;
    -        }
    -    }
    -
    -    private static final Logger LOGGER = Logger.getLogger(JDKInstaller.class.getName());
    -}
    diff --git a/core/src/main/java/hudson/tools/ToolDescriptor.java b/core/src/main/java/hudson/tools/ToolDescriptor.java
    index ad4ac1734a8635b136a470085cd2ea714ade3e33..c39149ceebe3dd675ea7c9572a1b3106a30ae141 100644
    --- a/core/src/main/java/hudson/tools/ToolDescriptor.java
    +++ b/core/src/main/java/hudson/tools/ToolDescriptor.java
    @@ -44,6 +44,8 @@ import org.jvnet.tiger_types.Types;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
     
    +import javax.annotation.Nonnull;
    +
     /**
      * {@link Descriptor} for {@link ToolInstallation}.
      *
    @@ -57,7 +59,7 @@ public abstract class ToolDescriptor<T extends ToolInstallation> extends Descrip
         protected ToolDescriptor() { }
     
         /**
    -     * @since FIXME
    +     * @since 2.102
          */
         protected ToolDescriptor(Class<T> clazz) {
             super(clazz);
    @@ -109,12 +111,12 @@ public abstract class ToolDescriptor<T extends ToolInstallation> extends Descrip
          * Lists up {@link ToolPropertyDescriptor}s that are applicable to this {@link ToolInstallation}.
          */
         public List<ToolPropertyDescriptor> getPropertyDescriptors() {
    -        return PropertyDescriptor.<ToolPropertyDescriptor, ToolInstallation>for_(ToolProperty.all(), clazz);
    +        return PropertyDescriptor.for_(ToolProperty.all(), clazz);
         }
     
     
         @Override
    -    public GlobalConfigurationCategory getCategory() {
    +    public @Nonnull GlobalConfigurationCategory getCategory() {
             return GlobalConfigurationCategory.get(ToolConfigurationCategory.class);
         }
     
    diff --git a/core/src/main/java/hudson/tools/ToolInstaller.java b/core/src/main/java/hudson/tools/ToolInstaller.java
    index 2aaec73d1d3787244e1e9b776479a2fdf2634698..0a6ef5d07c74d5e5436fc56652556fdb8c71c66b 100644
    --- a/core/src/main/java/hudson/tools/ToolInstaller.java
    +++ b/core/src/main/java/hudson/tools/ToolInstaller.java
    @@ -42,12 +42,14 @@ import org.kohsuke.stapler.DataBoundConstructor;
     
     /**
      * An object which can ensure that a generic {@link ToolInstallation} in fact exists on a node.
    + * The properties can be added to {@link ToolInstallation} using the {@link InstallSourceProperty}.
      *
      * The subclass should have a {@link ToolInstallerDescriptor}.
      * A {@code config.jelly} should be provided to customize specific fields;
      * {@code <t:label xmlns:t="/hudson/tools"/>} to customize {@code label}.
      * @see <a href="http://wiki.jenkins-ci.org/display/JENKINS/Tool+Auto-Installation">Tool Auto-Installation</a>
      * @since 1.305
    + * @see InstallSourceProperty
      */
     public abstract class ToolInstaller implements Describable<ToolInstaller>, ExtensionPoint {
     
    diff --git a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java
    index ba91cfe45763c984eb0e6de05ee817ed51546031..af9bffda1126090623f361f0919fe74e94769d27 100644
    --- a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java
    +++ b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java
    @@ -42,9 +42,11 @@ import java.net.MalformedURLException;
     import java.net.URL;
     import java.net.URLConnection;
     
    +import jenkins.model.Jenkins;
     import org.jenkinsci.Symbol;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     /**
      * Installs a tool into the Hudson working area by downloading and unpacking a ZIP file.
    @@ -95,7 +97,10 @@ public class ZipExtractionInstaller extends ToolInstaller {
                 return Messages.ZipExtractionInstaller_DescriptorImpl_displayName();
             }
     
    +        @RequirePOST
             public FormValidation doCheckUrl(@QueryParameter String value) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
    +            
                 try {
                     URLConnection conn = ProxyConfiguration.open(new URL(value));
                     conn.connect();
    diff --git a/core/src/main/java/hudson/triggers/SCMTrigger.java b/core/src/main/java/hudson/triggers/SCMTrigger.java
    index 682ce987fb813699794044b9e540474d420f351e..e45aa979c13a97a4c5a9ff0be192c2fcb80ec56c 100644
    --- a/core/src/main/java/hudson/triggers/SCMTrigger.java
    +++ b/core/src/main/java/hudson/triggers/SCMTrigger.java
    @@ -37,6 +37,7 @@ import hudson.model.AdministrativeMonitor;
     import hudson.model.Cause;
     import hudson.model.CauseAction;
     import hudson.model.Item;
    +import hudson.model.PersistentDescriptor;
     import hudson.model.Run;
     import hudson.scm.SCM;
     import hudson.scm.SCMDescriptor;
    @@ -84,6 +85,8 @@ import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
     
    +import javax.annotation.PostConstruct;
    +
     import static java.util.logging.Level.WARNING;
     
     
    @@ -211,7 +214,7 @@ public class SCMTrigger extends Trigger<Item> {
         }
     
         @Extension @Symbol("pollSCM")
    -    public static class DescriptorImpl extends TriggerDescriptor {
    +    public static class DescriptorImpl extends TriggerDescriptor implements PersistentDescriptor {
     
             private static ThreadFactory threadFactory() {
                 return new NamingThreadFactory(Executors.defaultThreadFactory(), "SCMTrigger");
    @@ -235,13 +238,18 @@ public class SCMTrigger extends Trigger<Item> {
     
             /**
              * Max number of threads for SCM polling.
    -         * 0 for unbounded.
              */
    -        private int maximumThreads;
    +        private int maximumThreads = 10;
     
    -        public DescriptorImpl() {
    -            load();
    -            resizeThreadPool();
    +        private static final int THREADS_LOWER_BOUND = 5;
    +        private static final int THREADS_UPPER_BOUND = 100;
    +        private static final int THREADS_DEFAULT= 10;
    +
    +        private Object readResolve() {
    +            if (maximumThreads == 0) {
    +                maximumThreads = THREADS_DEFAULT;
    +            }
    +            return this;
             }
     
             public boolean isApplicable(Item item) {
    @@ -290,8 +298,6 @@ public class SCMTrigger extends Trigger<Item> {
             /**
              * Gets the number of concurrent threads used for polling.
              *
    -         * @return
    -         *      0 if unlimited.
              */
             public int getPollingThreadCount() {
                 return maximumThreads;
    @@ -299,12 +305,16 @@ public class SCMTrigger extends Trigger<Item> {
     
             /**
              * Sets the number of concurrent threads used for SCM polling and resizes the thread pool accordingly
    -         * @param n number of concurrent threads, zero or less means unlimited, maximum is 100
    +         * @param n number of concurrent threads in the range 5..100, outside values will set the to the nearest bound
              */
             public void setPollingThreadCount(int n) {
                 // fool proof
    -            if(n<0)     n=0;
    -            if(n>100)   n=100;
    +            if (n < THREADS_LOWER_BOUND) {
    +                n = THREADS_LOWER_BOUND;
    +            }
    +            if (n > THREADS_UPPER_BOUND) {
    +                n = THREADS_UPPER_BOUND;
    +            }
     
                 maximumThreads = n;
     
    @@ -313,7 +323,7 @@ public class SCMTrigger extends Trigger<Item> {
     
             @Restricted(NoExternalUse.class)
             public boolean isPollingThreadCountOptionVisible() {
    -            if (getPollingThreadCount() != 0) {
    +            if (getPollingThreadCount() != THREADS_DEFAULT) {
                     // this is a user who already configured the option
                     return true;
                 }
    @@ -336,18 +346,19 @@ public class SCMTrigger extends Trigger<Item> {
             /**
              * Update the {@link ExecutorService} instance.
              */
    +        @PostConstruct
             /*package*/ synchronized void resizeThreadPool() {
    -            queue.setExecutors(
    -                    (maximumThreads==0 ? Executors.newCachedThreadPool(threadFactory()) : Executors.newFixedThreadPool(maximumThreads, threadFactory())));
    +            queue.setExecutors(Executors.newFixedThreadPool(maximumThreads, threadFactory()));
             }
     
             @Override
             public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
                 String t = json.optString("pollingThreadCount",null);
    -            if(t==null || t.length()==0)
    -                setPollingThreadCount(0);
    -            else
    +            if (doCheckPollingThreadCount(t).kind != FormValidation.Kind.OK) {
    +                setPollingThreadCount(THREADS_DEFAULT);
    +            } else {
                     setPollingThreadCount(Integer.parseInt(t));
    +            }
     
                 // Save configuration
                 save();
    @@ -356,9 +367,7 @@ public class SCMTrigger extends Trigger<Item> {
             }
     
             public FormValidation doCheckPollingThreadCount(@QueryParameter String value) {
    -            if (value != null && "".equals(value.trim()))
    -                return FormValidation.ok();
    -            return FormValidation.validateNonNegativeInteger(value);
    +            return FormValidation.validateIntegerInRange(value, THREADS_LOWER_BOUND, THREADS_UPPER_BOUND);
             }
     
             /**
    @@ -462,7 +471,7 @@ public class SCMTrigger extends Trigger<Item> {
             }
             
             /**
    -         * Used from <tt>polling.jelly</tt> to write annotated polling log to the given output.
    +         * Used from {@code polling.jelly} to write annotated polling log to the given output.
              */
             public void writePollingLogTo(long offset, XMLOutput out) throws IOException {
                 // TODO: resurrect compressed log file support
    diff --git a/core/src/main/java/hudson/triggers/SafeTimerTask.java b/core/src/main/java/hudson/triggers/SafeTimerTask.java
    index e47dfc61e62b52872beaede1f3a03317cf5f5bfe..7e6f89236c74319e6d6475d01c318caf23fc72fb 100644
    --- a/core/src/main/java/hudson/triggers/SafeTimerTask.java
    +++ b/core/src/main/java/hudson/triggers/SafeTimerTask.java
    @@ -24,11 +24,18 @@
     package hudson.triggers;
     
     import hudson.model.AperiodicWork;
    +import hudson.model.AsyncAperiodicWork;
    +import hudson.model.AsyncPeriodicWork;
     import hudson.model.PeriodicWork;
     import hudson.security.ACL;
    +
    +import java.io.File;
     import java.util.TimerTask;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    +
    +import jenkins.model.Jenkins;
    +import jenkins.util.SystemProperties;
     import jenkins.util.Timer;
     import org.acegisecurity.context.SecurityContext;
     import org.acegisecurity.context.SecurityContextHolder;
    @@ -43,6 +50,20 @@ import org.acegisecurity.context.SecurityContextHolder;
      * @since 1.124
      */
     public abstract class SafeTimerTask extends TimerTask {
    +
    +    /**
    +     * System property to change the location where (tasks) logging should be sent.
    +     * <p><strong>Beware: changing it while Jenkins is running gives no guarantee logs will be sent to the new location
    +     * until it is restarted.</strong></p>
    +     */
    +    static final String LOGS_ROOT_PATH_PROPERTY = SafeTimerTask.class.getName()+".logsTargetDir";
    +
    +    /**
    +     * Local marker to know if the information about using non default root directory for logs has already been logged at least once.
    +     * @see #LOGS_ROOT_PATH_PROPERTY
    +     */
    +    private static boolean ALREADY_LOGGED = false;
    +
         public final void run() {
             // background activity gets system credential,
             // just like executors get it.
    @@ -58,5 +79,31 @@ public abstract class SafeTimerTask extends TimerTask {
     
         protected abstract void doRun() throws Exception;
     
    +
    +    /**
    +     * The root path that should be used to put logs related to the tasks running in Jenkins.
    +     *
    +     * @see AsyncAperiodicWork#getLogFile()
    +     * @see AsyncPeriodicWork#getLogFile()
    +     * @return the path where the logs should be put.
    +     * @since 2.114
    +     */
    +    public static File getLogsRoot() {
    +        String tagsLogsPath = SystemProperties.getString(LOGS_ROOT_PATH_PROPERTY);
    +        if (tagsLogsPath == null) {
    +            return new File(Jenkins.get().getRootDir(), "logs");
    +        } else {
    +            Level logLevel = Level.INFO;
    +            if (ALREADY_LOGGED) {
    +                logLevel = Level.FINE;
    +            }
    +            LOGGER.log(logLevel,
    +                       "Using non default root path for tasks logging: {0}. (Beware: no automated migration if you change or remove it again)",
    +                       LOGS_ROOT_PATH_PROPERTY);
    +            ALREADY_LOGGED = true;
    +            return new File(tagsLogsPath);
    +        }
    +    }
    +
         private static final Logger LOGGER = Logger.getLogger(SafeTimerTask.class.getName());
     }
    diff --git a/core/src/main/java/hudson/triggers/package.html b/core/src/main/java/hudson/triggers/package.html
    index 61cb24e2f8c9e33741387c93eec0dc8471891c38..dd0f97ed8b139052e9766ec36ef5102be207faa3 100644
    --- a/core/src/main/java/hudson/triggers/package.html
    +++ b/core/src/main/java/hudson/triggers/package.html
    @@ -23,5 +23,5 @@ THE SOFTWARE.
     -->
     
     <html><head/><body>
    -Built-in <a href="Trigger.html"><tt>Trigger</tt></a>s that run periodically to kick a new build.
    +Built-in <a href="Trigger.html"><code>Trigger</code></a>s that run periodically to kick a new build.
     </body></html>
    \ No newline at end of file
    diff --git a/core/src/main/java/hudson/util/AbstractTaskListener.java b/core/src/main/java/hudson/util/AbstractTaskListener.java
    index b40cfde838793854586317cbc45030795589294f..bb02e99cd000b8c4577e3eedd4502405cc720989 100644
    --- a/core/src/main/java/hudson/util/AbstractTaskListener.java
    +++ b/core/src/main/java/hudson/util/AbstractTaskListener.java
    @@ -13,4 +13,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
     @Restricted(NoExternalUse.class)
     @RestrictedSince("2.91")
     public abstract class AbstractTaskListener implements TaskListener {
    +
    +    private static final long serialVersionUID = 7217626701881006422L;
    +
     }
    diff --git a/core/src/main/java/hudson/util/ArgumentListBuilder.java b/core/src/main/java/hudson/util/ArgumentListBuilder.java
    index 4388a7abb4a54c3a7d9162c08a40603f3567d72e..acc86c16d3804ac92d33a391cabb9d7d7a87028a 100644
    --- a/core/src/main/java/hudson/util/ArgumentListBuilder.java
    +++ b/core/src/main/java/hudson/util/ArgumentListBuilder.java
    @@ -165,7 +165,7 @@ public class ArgumentListBuilder implements Serializable, Cloneable {
         /**
          * Adds key value pairs as "-Dkey=value -Dkey=value ..."
          *
    -     * <tt>-D</tt> portion is configurable as the 'prefix' parameter.
    +     * {@code -D} portion is configurable as the 'prefix' parameter.
          * @since 1.114
          */
         public ArgumentListBuilder addKeyValuePairs(String prefix, Map<String,String> props) {
    diff --git a/core/src/main/java/hudson/util/ClassLoaderSanityThreadFactory.java b/core/src/main/java/hudson/util/ClassLoaderSanityThreadFactory.java
    new file mode 100644
    index 0000000000000000000000000000000000000000..2977e9bfbd45f5588f180b3cc3d62f18312cfad9
    --- /dev/null
    +++ b/core/src/main/java/hudson/util/ClassLoaderSanityThreadFactory.java
    @@ -0,0 +1,27 @@
    +package hudson.util;
    +
    +import java.util.concurrent.ThreadFactory;
    +import java.util.concurrent.TimeUnit;
    +
    +/**
    + *  Explicitly sets the {@link Thread#contextClassLoader} for threads it creates to its own classloader.
    + *  This avoids issues where threads are lazily created (ex by invoking {@link java.util.concurrent.ScheduledExecutorService#schedule(Runnable, long, TimeUnit)})
    + *   in a context where they would receive a customized {@link Thread#contextClassLoader} that was never meant to be used.
    + *
    + *  Commonly this is a problem for Groovy use, where this may result in memory leaks.
    + *  @see <a href="https://issues.jenkins-ci.org/browse/JENKINS-49206">JENKINS-49206</a>
    + * @since 2.105
    + */
    +public class ClassLoaderSanityThreadFactory implements ThreadFactory {
    +    private final ThreadFactory delegate;
    +
    +    public ClassLoaderSanityThreadFactory(ThreadFactory delegate) {
    +        this.delegate = delegate;
    +    }
    +
    +    @Override public Thread newThread(Runnable r) {
    +        Thread t = delegate.newThread(r);
    +        t.setContextClassLoader(ClassLoaderSanityThreadFactory.class.getClassLoader());
    +        return t;
    +    }
    +}
    diff --git a/core/src/main/java/hudson/util/DirScanner.java b/core/src/main/java/hudson/util/DirScanner.java
    index a694c7c21b820db408775953416c3b3b6535e133..2551aa0b081a0d6512be0e44d745de9063e60e35 100644
    --- a/core/src/main/java/hudson/util/DirScanner.java
    +++ b/core/src/main/java/hudson/util/DirScanner.java
    @@ -7,7 +7,6 @@ import org.apache.tools.ant.types.FileSet;
     import java.io.File;
     import java.io.FileFilter;
     import java.io.IOException;
    -import java.io.InterruptedIOException;
     import java.io.Serializable;
     
     import static hudson.Util.fixEmpty;
    @@ -31,19 +30,15 @@ public abstract class DirScanner implements Serializable {
          */
         protected final void scanSingle(File f, String relative, FileVisitor visitor) throws IOException {
             if (visitor.understandsSymlink()) {
    +            String target;
                 try {
    -                String target;
    -                try {
    -                    target = Util.resolveSymlink(f);
    -                } catch (IOException x) { // JENKINS-13202
    -                    target = null;
    -                }
    -                if (target != null) {
    -                    visitor.visitSymlink(f, target, relative);
    -                    return;
    -                }
    -            } catch (InterruptedException e) {
    -                throw (IOException) new InterruptedIOException().initCause(e);
    +                target = Util.resolveSymlink(f);
    +            } catch (IOException x) { // JENKINS-13202
    +                target = null;
    +            }
    +            if (target != null) {
    +                visitor.visitSymlink(f, target, relative);
    +                return;
                 }
             }
             visitor.visit(f, relative);
    diff --git a/core/src/main/java/hudson/util/DoubleLaunchChecker.java b/core/src/main/java/hudson/util/DoubleLaunchChecker.java
    index 8bbcdcab08e32bea00469c84195a7772c2d54ae2..6fff568d048914c3657d4b329d22edb8965a927d 100644
    --- a/core/src/main/java/hudson/util/DoubleLaunchChecker.java
    +++ b/core/src/main/java/hudson/util/DoubleLaunchChecker.java
    @@ -47,7 +47,7 @@ import java.lang.management.ManagementFactory;
     import java.lang.reflect.Method;
     
     /**
    - * Makes sure that no other Hudson uses our <tt>JENKINS_HOME</tt> directory,
    + * Makes sure that no other Hudson uses our {@code JENKINS_HOME} directory,
      * to forestall the problem of running multiple instances of Hudson that point to the same data directory.
      *
      * <p>
    diff --git a/core/src/main/java/hudson/util/ErrorObject.java b/core/src/main/java/hudson/util/ErrorObject.java
    index def3df9adb9b8b2357ad25ebc69448ef28f9e622..5d4a8593570c0b549b5a364a5bac25e0b5011e48 100644
    --- a/core/src/main/java/hudson/util/ErrorObject.java
    +++ b/core/src/main/java/hudson/util/ErrorObject.java
    @@ -35,7 +35,7 @@ import java.io.IOException;
      * Basis for error model objects.
      *
      * This implementation serves error pages for any requests under its domain. Subclasses are responsible for providing
    - * <tt>index</tt> view.
    + * {@code index} view.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java
    index 47ba955121b9daf1f2b29e0d0e42103e040fec74..0595afa4a68a80f12ec60235467f3923f7109430 100644
    --- a/core/src/main/java/hudson/util/FormFieldValidator.java
    +++ b/core/src/main/java/hudson/util/FormFieldValidator.java
    @@ -170,8 +170,8 @@ public abstract class FormFieldValidator {
          * Sends out a string error message that indicates an error.
          *
          * @param message
    -     *      Human readable message to be sent. <tt>error(null)</tt>
    -     *      can be used as <tt>ok()</tt>.
    +     *      Human readable message to be sent. {@code error(null)}
    +     *      can be used as {@code ok()}.
          */
         public void error(String message) throws IOException, ServletException {
             errorWithMarkup(message==null?null:Util.escape(message));
    @@ -209,8 +209,8 @@ public abstract class FormFieldValidator {
          * attack.
          *
          * @param message
    -     *      Human readable message to be sent. <tt>error(null)</tt>
    -     *      can be used as <tt>ok()</tt>.
    +     *      Human readable message to be sent. {@code error(null)}
    +     *      can be used as {@code ok()}.
          */
         public void errorWithMarkup(String message) throws IOException, ServletException {
             _errorWithMarkup(message,"error");
    diff --git a/core/src/main/java/hudson/util/FormFillFailure.java b/core/src/main/java/hudson/util/FormFillFailure.java
    index 8fcf650b7ff9cdd183169a8d7bf04052faade015..16b8e6435a2fc918fe48a731d327694c49d16f68 100644
    --- a/core/src/main/java/hudson/util/FormFillFailure.java
    +++ b/core/src/main/java/hudson/util/FormFillFailure.java
    @@ -40,7 +40,7 @@ import org.kohsuke.stapler.StaplerResponse;
      * Represents a failure in a form field doFillXYZ method.
      *
      * <p>
    - * Use one of the factory methods to create an instance, then throw it from your <tt>doFillXyz</tt>
    + * Use one of the factory methods to create an instance, then throw it from your {@code doFillXyz}
      * method.
      *
      * @since 2.50
    @@ -117,8 +117,8 @@ public abstract class FormFillFailure extends IOException implements HttpRespons
          * This method must be used with care to avoid cross-site scripting
          * attack.
          *
    -     * @param message Human readable message to be sent. <tt>error(null)</tt>
    -     *                can be used as <tt>ok()</tt>.
    +     * @param message Human readable message to be sent. {@code error(null)}
    +     *                can be used as {@code ok()}.
          */
         public static FormFillFailure errorWithMarkup(String message) {
             return _errorWithMarkup(message, FormValidation.Kind.ERROR);
    diff --git a/core/src/main/java/hudson/util/FormValidation.java b/core/src/main/java/hudson/util/FormValidation.java
    index c95e5d1da81cd3a17c6b5d69f28626429c4b4eeb..90748cf224df2774fcf99d9a8a553f109072a529 100644
    --- a/core/src/main/java/hudson/util/FormValidation.java
    +++ b/core/src/main/java/hudson/util/FormValidation.java
    @@ -66,7 +66,7 @@ import static hudson.Util.*;
      * Represents the result of the form field validation.
      *
      * <p>
    - * Use one of the factory methods to create an instance, then return it from your <tt>doCheckXyz</tt>
    + * Use one of the factory methods to create an instance, then return it from your {@code doCheckXyz}
      * method. (Via {@link HttpResponse}, the returned object will render the result into {@link StaplerResponse}.)
      * This way of designing form field validation allows you to reuse {@code doCheckXyz()} methods
      * programmatically as well (by using {@link #kind}.
    @@ -77,7 +77,7 @@ import static hudson.Util.*;
      * that you may be able to reuse.
      *
      * <p>
    - * Also see <tt>doCheckCvsRoot</tt> in <tt>CVSSCM</tt> as an example.
    + * Also see {@code doCheckCvsRoot} in {@code CVSSCM} as an example.
      *
      * <p>
      * This class extends {@link IOException} so that it can be thrown from a method. This allows one to reuse
    @@ -136,8 +136,8 @@ public abstract class FormValidation extends IOException implements HttpResponse
          * Sends out a string error message that indicates an error.
          *
          * @param message
    -     *      Human readable message to be sent. <tt>error(null)</tt>
    -     *      can be used as <tt>ok()</tt>.
    +     *      Human readable message to be sent. {@code error(null)}
    +     *      can be used as {@code ok()}.
          */
         public static FormValidation error(String message) {
             return errorWithMarkup(message==null?null: Util.escape(message));
    @@ -245,8 +245,8 @@ public abstract class FormValidation extends IOException implements HttpResponse
          * attack.
          *
          * @param message
    -     *      Human readable message to be sent. <tt>error(null)</tt>
    -     *      can be used as <tt>ok()</tt>.
    +     *      Human readable message to be sent. {@code error(null)}
    +     *      can be used as {@code ok()}.
          */
         public static FormValidation errorWithMarkup(String message) {
             return _errorWithMarkup(message,Kind.ERROR);
    @@ -393,6 +393,30 @@ public abstract class FormValidation extends IOException implements HttpResponse
             }
         }
     
    +    /**
    +     * Make sure that the given string is an integer in the range specified by the lower and upper bounds (both inclusive)
    +     *
    +     * @param value the value to check
    +     * @param lower the lower bound (inclusive)
    +     * @param upper the upper bound (inclusive)
    +     *
    +     * @since 2.104
    +     */
    +    public static FormValidation validateIntegerInRange(String value, int lower, int upper) {
    +        try {
    +            int intValue = Integer.parseInt(value);
    +            if (intValue < lower) {
    +                return error(hudson.model.Messages.Hudson_MustBeAtLeast(lower));
    +            }
    +            if (intValue > upper) {
    +                return error(hudson.model.Messages.Hudson_MustBeAtMost(upper));
    +            }
    +            return ok();
    +        } catch (NumberFormatException e) {
    +            return error(hudson.model.Messages.Hudson_NotANumber());
    +        }
    +    }
    +
         /**
          * Makes sure that the given string is a positive integer.
          */
    diff --git a/core/src/main/java/hudson/util/Function1.java b/core/src/main/java/hudson/util/Function1.java
    index c200c28c3a2c7fcee3a829fe7d225eb028c32900..aa7535d5903f75a572c99f39139e526567b7fc17 100644
    --- a/core/src/main/java/hudson/util/Function1.java
    +++ b/core/src/main/java/hudson/util/Function1.java
    @@ -24,7 +24,7 @@
     package hudson.util;
     
     /**
    - * Unary function <tt>y=f(x)</tt>.
    + * Unary function {@code y=f(x)}.
      * 
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/HttpResponses.java b/core/src/main/java/hudson/util/HttpResponses.java
    index d6b8fb225700ee5860b869d8a5cca84e13b95643..ef731f3ff8252407ef48beeebe6109c2214ae32d 100644
    --- a/core/src/main/java/hudson/util/HttpResponses.java
    +++ b/core/src/main/java/hudson/util/HttpResponses.java
    @@ -89,16 +89,52 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses {
             return new JSONObjectResponse(data);
         }
     
    -        /**
    -         * Set the response as an error response.
    -         * @param message The error "message" set on the response.
    -         * @return {@code this} object.
    -         *
    -         * @since 2.0
    -         */
    +    /**
    +     * Set the response as an error response.
    +     * @param message The error "message" set on the response.
    +     * @return {@code this} object.
    +     *
    +     * @since 2.0
    +     */
         public static HttpResponse errorJSON(@Nonnull String message) {
             return new JSONObjectResponse().error(message);
         }
    +    
    +    /**
    +     * Set the response as an error response plus some data.
    +     * @param message The error "message" set on the response.
    +     * @param data The data.
    +     * @return {@code this} object.
    +     *
    +     * @since 2.119
    +     */
    +    public static HttpResponse errorJSON(@Nonnull String message, @Nonnull Map<?,?> data) {
    +        return new JSONObjectResponse(data).error(message);
    +    }
    +
    +    /**
    +     * Set the response as an error response plus some data.
    +     * @param message The error "message" set on the response.
    +     * @param data The data.
    +     * @return {@code this} object.
    +     *
    +     * @since 2.115
    +     */
    +    public static HttpResponse errorJSON(@Nonnull String message, @Nonnull JSONObject data) {
    +        return new JSONObjectResponse(data).error(message);
    +    }
    +
    +    /**
    +     * Set the response as an error response plus some data.
    +     * @param message The error "message" set on the response.
    +     * @param data The data.
    +     * @return {@code this} object.
    +     *
    +     * @since 2.115
    +     */
    +    public static HttpResponse errorJSON(@Nonnull String message, @Nonnull JSONArray data) {
    +        return new JSONObjectResponse(data).error(message);
    +    }
     
         /**
          * {@link net.sf.json.JSONObject} response.
    diff --git a/core/src/main/java/hudson/util/HudsonFailedToLoad.java b/core/src/main/java/hudson/util/HudsonFailedToLoad.java
    index 72ad0a4f9c629508703a772b9e56885600958496..8347c7a1cf3f5a3e83d795236a634876c72755f9 100644
    --- a/core/src/main/java/hudson/util/HudsonFailedToLoad.java
    +++ b/core/src/main/java/hudson/util/HudsonFailedToLoad.java
    @@ -30,7 +30,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
      * Model object used to display the generic error when Jenkins start up fails fatally during initialization.
      *
      * <p>
    - * <tt>index.jelly</tt> would display a nice friendly error page.
    + * {@code index.jelly} would display a nice friendly error page.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/IOUtils.java b/core/src/main/java/hudson/util/IOUtils.java
    index 5022dc021f7ffb0a48e19e6118290d21b61b3000..2bcdf90acbdcea173c3b8f82db7ea0a1cf71f07d 100644
    --- a/core/src/main/java/hudson/util/IOUtils.java
    +++ b/core/src/main/java/hudson/util/IOUtils.java
    @@ -117,7 +117,7 @@ public class IOUtils {
          * execute permissions for the owner, group, and others, i.e. the max return value
          * is 0777. Consider using {@link Files#getPosixFilePermissions} instead if you only
          * care about access permissions.
    -     *
    +     * <p>If the file is symlink, the mode is that of the link target, not the link itself.
          * @return a file mode, or -1 if not on Unix
          * @throws PosixException if the file could not be statted, e.g. broken symlink
          */
    diff --git a/core/src/main/java/hudson/util/IncompatibleAntVersionDetected.java b/core/src/main/java/hudson/util/IncompatibleAntVersionDetected.java
    index 1a89b82e6f2f877759f9a8135667ffbf16ea7ddb..c3d34afd4edfbea54d0a2bfe71a5f20b4c297152 100644
    --- a/core/src/main/java/hudson/util/IncompatibleAntVersionDetected.java
    +++ b/core/src/main/java/hudson/util/IncompatibleAntVersionDetected.java
    @@ -33,7 +33,7 @@ import java.net.URL;
      * we find out that the container is picking up its own Ant and that's not 1.7.
      *
      * <p>
    - * <tt>index.jelly</tt> would display a nice friendly error page.
    + * {@code index.jelly} would display a nice friendly error page.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/IncompatibleServletVersionDetected.java b/core/src/main/java/hudson/util/IncompatibleServletVersionDetected.java
    index 92d17d185df650cfbdccf593b35b58bd4c67a6f8..f2e4931d6ab9265a0f3dae05a056953a03dfa3f4 100644
    --- a/core/src/main/java/hudson/util/IncompatibleServletVersionDetected.java
    +++ b/core/src/main/java/hudson/util/IncompatibleServletVersionDetected.java
    @@ -33,7 +33,7 @@ import java.net.URL;
      * we find out that the container doesn't support servlet 2.4.
      *
      * <p>
    - * <tt>index.jelly</tt> would display a nice friendly error page.
    + * {@code index.jelly} would display a nice friendly error page.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/IncompatibleVMDetected.java b/core/src/main/java/hudson/util/IncompatibleVMDetected.java
    index ae690c162bb77dc14ca31b2deeaad13e6bcd71b9..651e5b124950c33efb76cc85e8f057c646df5008 100644
    --- a/core/src/main/java/hudson/util/IncompatibleVMDetected.java
    +++ b/core/src/main/java/hudson/util/IncompatibleVMDetected.java
    @@ -30,7 +30,7 @@ import java.util.Map;
      * we find out that XStream is running in pure-java mode.
      *
      * <p>
    - * <tt>index.jelly</tt> would display a nice friendly error page.
    + * {@code index.jelly} would display a nice friendly error page.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/InsufficientPermissionDetected.java b/core/src/main/java/hudson/util/InsufficientPermissionDetected.java
    index 739e930bb9fed64598d22773c8e496f40e848406..57aea00f22e6479abe2cf45f23b1d0c91695eb15 100644
    --- a/core/src/main/java/hudson/util/InsufficientPermissionDetected.java
    +++ b/core/src/main/java/hudson/util/InsufficientPermissionDetected.java
    @@ -31,7 +31,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
      * we find that we don't have enough permissions to run.
      *
      * <p>
    - * <tt>index.jelly</tt> would display a nice friendly error page.
    + * {@code index.jelly} would display a nice friendly error page.
      *
      * @author Kohsuke Kawaguchi
      */
    diff --git a/core/src/main/java/hudson/util/Iterators.java b/core/src/main/java/hudson/util/Iterators.java
    index 74d82950dc298e8d79b0ccc471dd95d340e12163..8d93d7226212ef01aba818caf5beb60050914ffb 100644
    --- a/core/src/main/java/hudson/util/Iterators.java
    +++ b/core/src/main/java/hudson/util/Iterators.java
    @@ -23,6 +23,7 @@
      */
     package hudson.util;
     
    +import com.google.common.annotations.Beta;
     import com.google.common.base.Predicates;
     import com.google.common.collect.ImmutableList;
     
    @@ -35,6 +36,9 @@ import java.util.AbstractList;
     import java.util.Arrays;
     import java.util.Set;
     import java.util.HashSet;
    +import javax.annotation.Nonnull;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     /**
      * Varios {@link Iterator} implementations.
    @@ -403,4 +407,20 @@ public class Iterators {
         public interface CountingPredicate<T> {
             boolean apply(int index, T input);
         }
    +
    +    /**
    +     * Similar to {@link com.google.common.collect.Iterators#skip} except not {@link Beta}.
    +     * @param iterator some iterator
    +     * @param count a nonnegative count
    +     */
    +    @Restricted(NoExternalUse.class)
    +    public static void skip(@Nonnull Iterator<?> iterator, int count) {
    +        if (count < 0) {
    +            throw new IllegalArgumentException();
    +        }
    +        while (iterator.hasNext() && count-- > 0) {
    +            iterator.next();
    +        }
    +    }
    +
     }
    diff --git a/core/src/main/java/hudson/util/KeyedDataStorage.java b/core/src/main/java/hudson/util/KeyedDataStorage.java
    index 49a220b1b245db200a356c90f7bb0c41ea7ed986..18344394551520136eeffd6bc165ac9b9aed3079 100644
    --- a/core/src/main/java/hudson/util/KeyedDataStorage.java
    +++ b/core/src/main/java/hudson/util/KeyedDataStorage.java
    @@ -254,7 +254,7 @@ public abstract class KeyedDataStorage<T,P> {
          * Among cache misses, number of times when we had {@link SoftReference}
          * but lost its value due to GC.
          *
    -     * <tt>totalQuery-cacheHit-weakRefLost</tt> means cache miss.
    +     * {@code totalQuery-cacheHit-weakRefLost} means cache miss.
          */
         public final AtomicInteger weakRefLost = new AtomicInteger();
         /**
    diff --git a/core/src/main/java/hudson/util/LineEndNormalizingWriter.java b/core/src/main/java/hudson/util/LineEndNormalizingWriter.java
    index 2a0edee1d031c5124e8cb7f6d3b416f59b45137a..480f71d45973dd8519c6d27536ef47a90fe1a88e 100644
    --- a/core/src/main/java/hudson/util/LineEndNormalizingWriter.java
    +++ b/core/src/main/java/hudson/util/LineEndNormalizingWriter.java
    @@ -31,7 +31,7 @@ import java.io.IOException;
      * Finds the lone LF and converts that to CR+LF.
      *
      * <p>
    - * Internet Explorer's <tt>XmlHttpRequest.responseText</tt> seems to
    + * Internet Explorer's {@code XmlHttpRequest.responseText} seems to
      * normalize the line end, and if we only send LF without CR, it will
      * not recognize that as a new line. To work around this problem,
      * we use this filter to always convert LF to CR+LF.
    diff --git a/core/src/main/java/hudson/util/ListBoxModel.java b/core/src/main/java/hudson/util/ListBoxModel.java
    index 63d7ee7795be2c8e80c99c816b6a2c638d413ef2..fc717cc7ef606d98b6dae9cfacbf7ababd5e0af4 100644
    --- a/core/src/main/java/hudson/util/ListBoxModel.java
    +++ b/core/src/main/java/hudson/util/ListBoxModel.java
    @@ -62,7 +62,7 @@ import java.util.Collection;
      *
      * <p>
      * Other parts of the HTML can initiate the SELECT element update by using the "updateListBox"
    - * function, defined in <tt>hudson-behavior.js</tt>. The following example does it
    + * function, defined in {@code hudson-behavior.js}. The following example does it
      * when the value of the textbox changes:
      *
      * <pre>{@code <xmp>
    @@ -70,11 +70,11 @@ import java.util.Collection;
      * }
    * *

    - * The first argument is the SELECT element or the ID of it (see Prototype.js $(...) function.) + * The first argument is the SELECT element or the ID of it (see Prototype.js {@code $(...)} function.) * The second argument is the URL that returns the options list. * *

    - * The URL usually maps to the doXXX method on the server, which uses {@link ListBoxModel} + * The URL usually maps to the {@code doXXX} method on the server, which uses {@link ListBoxModel} * for producing option values. See the following example: * *

    diff --git a/core/src/main/java/hudson/util/NoHomeDir.java b/core/src/main/java/hudson/util/NoHomeDir.java
    index cae6089554a16dd95e2af0d1399862daf15422e3..c3d15ea367762b52a65592fa721f5bb176a5cf38 100644
    --- a/core/src/main/java/hudson/util/NoHomeDir.java
    +++ b/core/src/main/java/hudson/util/NoHomeDir.java
    @@ -30,7 +30,7 @@ import java.io.File;
      * we couldn't create the home directory.
      *
      * 

    - * index.jelly would display a nice friendly error page. + * {@code index.jelly} would display a nice friendly error page. * * @author Kohsuke Kawaguchi */ diff --git a/core/src/main/java/hudson/util/NoTempDir.java b/core/src/main/java/hudson/util/NoTempDir.java index 10cfceb5ac04e446d2bb0c45c1836a8b2130a341..2e6209463dc814ed173c25e477e2665aab46e5b8 100644 --- a/core/src/main/java/hudson/util/NoTempDir.java +++ b/core/src/main/java/hudson/util/NoTempDir.java @@ -33,7 +33,7 @@ import java.io.IOException; * there appears to be no temporary directory. * *

    - * index.jelly would display a nice friendly error page. + * {@code index.jelly} would display a nice friendly error page. * * @author Kohsuke Kawaguchi */ diff --git a/core/src/main/java/hudson/util/PluginServletFilter.java b/core/src/main/java/hudson/util/PluginServletFilter.java index 7f7172240acfa70fd74b0bc80b4415f175d402a9..08ab06151193d420d91da07871fb951671301e41 100644 --- a/core/src/main/java/hudson/util/PluginServletFilter.java +++ b/core/src/main/java/hudson/util/PluginServletFilter.java @@ -166,7 +166,11 @@ public class PluginServletFilter implements Filter, ExtensionPoint { @Restricted(NoExternalUse.class) public static void cleanUp() { - PluginServletFilter instance = getInstance(Jenkins.getInstance().servletContext); + Jenkins jenkins = Jenkins.getInstanceOrNull(); + if (jenkins == null) { + return; + } + PluginServletFilter instance = getInstance(jenkins.servletContext); if (instance != null) { // While we could rely on the current implementation of list being a CopyOnWriteArrayList // safer to just take an explicit copy of the list and operate on the copy diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 95fe237fc32253811d9d308dab3da0aab181218a..e8a24b51b0b3927a40a9914a67df623d89f1aff1 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -43,7 +43,16 @@ import jenkins.security.SlaveToMasterCallable; import org.jvnet.winp.WinProcess; import org.jvnet.winp.WinpException; -import java.io.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -56,6 +65,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.SortedMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,7 +74,6 @@ import javax.annotation.CheckForNull; import static com.sun.jna.Pointer.NULL; import jenkins.util.SystemProperties; import static hudson.util.jna.GNUCLibrary.LIBC; -import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINER; import static java.util.logging.Level.FINEST; import javax.annotation.Nonnull; @@ -94,9 +103,20 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * Lazily obtained {@link ProcessKiller}s to be applied on this process tree. */ private transient volatile List killers; + + /** + * Flag to skip the veto check since there aren't any. + */ + private boolean skipVetoes; // instantiation only allowed for subtypes in this class - private ProcessTree() {} + private ProcessTree() { + skipVetoes = false; + } + + private ProcessTree(boolean vetoesExist) { + skipVetoes = !vetoesExist; + } /** * Gets the process given a specific ID, or null if no such process exists. @@ -133,6 +153,8 @@ public abstract class ProcessTree implements Iterable, IProcessTree, */ public abstract void killAll(Map modelEnvVars) throws InterruptedException; + private final long softKillWaitSeconds = Integer.getInteger("SoftKillWaitSeconds", 2 * 60); // by default processes get at most 2 minutes to respond to SIGTERM (JENKINS-17116) + /** * Convenience method that does {@link #killAll(Map)} and {@link OSProcess#killRecursively()}. * This is necessary to reliably kill the process and its descendants, as some OS @@ -156,22 +178,24 @@ public abstract class ProcessTree implements Iterable, IProcessTree, try { VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); if (channelToMaster!=null) { - killers = channelToMaster.call(new SlaveToMasterCallable, IOException>() { - public List call() throws IOException { - return new ArrayList(ProcessKiller.all()); - } - }); + killers = channelToMaster.call(new ListAll()); } else { // used in an environment that doesn't support talk-back to the master. // let's do with what we have. killers = Collections.emptyList(); } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to obtain killers",e); + } catch (IOException | Error e) { + LOGGER.log(Level.WARNING, "Failed to obtain killers", e); killers = Collections.emptyList(); } return killers; } + private static class ListAll extends SlaveToMasterCallable, IOException> { + @Override + public List call() throws IOException { + return new ArrayList<>(ProcessKiller.all()); + } + } /** * Represents a process. @@ -217,10 +241,11 @@ public abstract class ProcessTree implements Iterable, IProcessTree, void killByKiller() throws InterruptedException { for (ProcessKiller killer : getKillers()) try { - if (killer.kill(this)) + if (killer.kill(this)) { break; - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to kill pid="+getPid(),e); + } + } catch (IOException | Error e) { + LOGGER.log(Level.WARNING, "Failed to kill pid=" + getPid(), e); } } @@ -239,14 +264,26 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * null if no one objects killing the process. */ protected @CheckForNull VetoCause getVeto() { - for (ProcessKillingVeto vetoExtension : ProcessKillingVeto.all()) { - VetoCause cause = vetoExtension.vetoProcessKilling(this); - if (cause != null) { - if (LOGGER.isLoggable(FINEST)) - LOGGER.finest("Killing of pid " + getPid() + " vetoed by " + vetoExtension.getClass().getName() + ": " + cause.getMessage()); - return cause; + String causeMessage = null; + + // Quick check, does anything exist to check against + if (!skipVetoes) { + try { + VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); + if (channelToMaster!=null) { + CheckVetoes vetoCheck = new CheckVetoes(this); + causeMessage = channelToMaster.call(vetoCheck); + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "I/O Exception while checking for vetoes", e); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Interrupted Exception while checking for vetoes", e); } } + + if (causeMessage != null) { + return new VetoCause(causeMessage); + } return null; } @@ -298,6 +335,27 @@ public abstract class ProcessTree implements Iterable, IProcessTree, Object writeReplace() { return new SerializedProcess(pid); } + + private class CheckVetoes extends SlaveToMasterCallable { + private IOSProcess process; + + public CheckVetoes(IOSProcess processToCheck) { + process = processToCheck; + } + + @Override + public String call() throws IOException { + for (ProcessKillingVeto vetoExtension : ProcessKillingVeto.all()) { + VetoCause cause = vetoExtension.vetoProcessKilling(process); + if (cause != null) { + if (LOGGER.isLoggable(FINEST)) + LOGGER.info("Killing of pid " + getPid() + " vetoed by " + vetoExtension.getClass().getName() + ": " + cause.getMessage()); + return cause.getMessage(); + } + } + return null; + } + } } /** @@ -336,6 +394,8 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } + /* package */ static Boolean vetoersExist; + /** * Gets the {@link ProcessTree} of the current system * that JVM runs in, or in the worst case return the default one @@ -345,17 +405,33 @@ public abstract class ProcessTree implements Iterable, IProcessTree, if(!enabled) return DEFAULT; + // Check for the existance of vetoers if I don't know already + if (vetoersExist == null) { + try { + VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); + if (channelToMaster != null) { + vetoersExist = channelToMaster.call(new DoVetoersExist()); + } + } + catch (Exception e) { + LOGGER.log(Level.WARNING, "Error while determining if vetoers exist", e); + } + } + + // Null-check in case the previous call worked + boolean vetoes = (vetoersExist == null ? true : vetoersExist); + try { if(File.pathSeparatorChar==';') - return new Windows(); + return new Windows(vetoes); String os = Util.fixNull(System.getProperty("os.name")); if(os.equals("Linux")) - return new Linux(); + return new Linux(vetoes); if(os.equals("SunOS")) - return new Solaris(); + return new Solaris(vetoes); if(os.equals("Mac OS X")) - return new Darwin(); + return new Darwin(vetoes); } catch (LinkageError e) { LOGGER.log(Level.WARNING,"Failed to load winp. Reverting to the default",e); enabled = false; @@ -363,6 +439,13 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return DEFAULT; } + + private static class DoVetoersExist extends SlaveToMasterCallable { + @Override + public Boolean call() throws IOException { + return ProcessKillingVeto.all().size() > 0; + } + } // // @@ -430,6 +513,8 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return; LOGGER.log(FINER, "Killing recursively {0}", getPid()); + // Firstly try to kill the root process gracefully, then do a forcekill if it does not help (algorithm is described in JENKINS-17116) + killSoftly(); p.killRecursively(); killByKiller(); } @@ -441,10 +526,39 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } LOGGER.log(FINER, "Killing {0}", getPid()); + // Firstly try to kill it gracefully, then do a forcekill if it does not help (algorithm is described in JENKINS-17116) + killSoftly(); p.kill(); killByKiller(); } + private void killSoftly() throws InterruptedException { + // send Ctrl+C to the process + try { + if (!p.sendCtrlC()) { + return; + } + } + catch (WinpException e) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "Failed to send CTRL+C to pid=" + getPid(), e); + } + return; + } + + // after that wait for it to cease to exist + long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000; + int sleepTime = 10; // initially we sleep briefly, then sleep up to 1sec + do { + if (!p.isRunning()) { + break; + } + + Thread.sleep(sleepTime); + sleepTime = Math.min(sleepTime * 2, 1000); + } while (System.nanoTime() < deadline); + } + @Override public synchronized List getArguments() { if(args==null) { @@ -510,7 +624,9 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } private static final class Windows extends Local { - Windows() { + Windows(boolean vetoesExist) { + super(vetoesExist); + for (final WinProcess p : WinProcess.all()) { int pid = p.getPid(); if(pid == 0 || pid == 4) continue; // skip the System Idle and System processes @@ -572,15 +688,13 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } static abstract class Unix extends Local { + public Unix(boolean vetoersExist) { + super(vetoersExist); + } + @Override public OSProcess get(Process proc) { - try { - return get((Integer) UnixReflection.PID_FIELD.get(proc)); - } catch (IllegalAccessException e) { // impossible - IllegalAccessError x = new IllegalAccessError(); - x.initCause(e); - throw x; - } + return get(UnixReflection.pid(proc)); } public void killAll(Map modelEnvVars) throws InterruptedException { @@ -593,7 +707,9 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * {@link ProcessTree} based on /proc. */ static abstract class ProcfsUnix extends Unix { - ProcfsUnix() { + ProcfsUnix(boolean vetoersExist) { + super(vetoersExist); + File[] processes = new File("/proc").listFiles(new FileFilter() { public boolean accept(File f) { return f.isDirectory(); @@ -639,12 +755,29 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * Tries to kill this process. */ public void kill() throws InterruptedException { + // after sending SIGTERM, wait for the process to cease to exist + long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000; + kill(deadline); + } + + private void kill(long deadline) throws InterruptedException { if (getVeto() != null) return; try { int pid = getPid(); LOGGER.fine("Killing pid="+pid); UnixReflection.destroy(pid); + // after sending SIGTERM, wait for the process to cease to exist + int sleepTime = 10; // initially we sleep briefly, then sleep up to 1sec + File status = getFile("status"); + do { + if (!status.exists()) { + break; // status is gone, process therefore as well + } + + Thread.sleep(sleepTime); + sleepTime = Math.min(sleepTime * 2, 1000); + } while (System.nanoTime() < deadline); } catch (IllegalAccessException e) { // this is impossible IllegalAccessError x = new IllegalAccessError(); @@ -661,11 +794,22 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } public void killRecursively() throws InterruptedException { + // after sending SIGTERM, wait for the processes to cease to exist until the deadline + long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000; + killRecursively(deadline); + } + + private void killRecursively(long deadline) throws InterruptedException { // We kill individual processes of a tree, so handling vetoes inside #kill() is enough for UnixProcess es LOGGER.fine("Recursively killing pid="+getPid()); - for (OSProcess p : getChildren()) - p.killRecursively(); - kill(); + for (OSProcess p : getChildren()) { + if (p instanceof UnixProcess) { + ((UnixProcess)p).killRecursively(deadline); + } else { + p.killRecursively(); // should not happen, fallback to non-deadline version + } + } + kill(deadline); } /** @@ -678,65 +822,103 @@ public abstract class ProcessTree implements Iterable, IProcessTree, public abstract List getArguments(); } + //TODO: can be replaced by multi-release JAR /** * Reflection used in the Unix support. */ private static final class UnixReflection { /** * Field to access the PID of the process. + * Required for Java 8 and older JVMs. */ - private static final Field PID_FIELD; + private static final Field JAVA8_PID_FIELD; + + /** + * Field to access the PID of the process. + * Required for Java 9 and above until this is replaced by multi-release JAR. + */ + private static final Method JAVA9_PID_METHOD; /** * Method to destroy a process, given pid. * * Looking at the JavaSE source code, this is using SIGTERM (15) */ - private static final Method DESTROY_PROCESS; + private static final Method JAVA8_DESTROY_PROCESS; + private static final Method JAVA_9_PROCESSHANDLE_OF; + private static final Method JAVA_9_PROCESSHANDLE_DESTROY; static { try { - Class clazz = Class.forName("java.lang.UNIXProcess"); - PID_FIELD = clazz.getDeclaredField("pid"); - PID_FIELD.setAccessible(true); - - if (isPreJava8()) { - DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess",int.class); + if (isPostJava8()) { // Java 9+ + Class clazz = Process.class; + JAVA9_PID_METHOD = clazz.getMethod("pid"); + JAVA8_PID_FIELD = null; + Class processHandleClazz = Class.forName("java.lang.ProcessHandle"); + JAVA_9_PROCESSHANDLE_OF = processHandleClazz.getMethod("of", long.class); + JAVA_9_PROCESSHANDLE_DESTROY = processHandleClazz.getMethod("destroy"); + JAVA8_DESTROY_PROCESS = null; } else { - DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess",int.class, boolean.class); + Class clazz = Class.forName("java.lang.UNIXProcess"); + JAVA8_PID_FIELD = clazz.getDeclaredField("pid"); + JAVA8_PID_FIELD.setAccessible(true); + JAVA9_PID_METHOD = null; + + JAVA8_DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess", int.class, boolean.class); + JAVA8_DESTROY_PROCESS.setAccessible(true); + JAVA_9_PROCESSHANDLE_OF = null; + JAVA_9_PROCESSHANDLE_DESTROY = null; } - DESTROY_PROCESS.setAccessible(true); - } catch (ClassNotFoundException e) { - LinkageError x = new LinkageError(); - x.initCause(e); - throw x; - } catch (NoSuchFieldException e) { - LinkageError x = new LinkageError(); - x.initCause(e); - throw x; - } catch (NoSuchMethodException e) { - LinkageError x = new LinkageError(); - x.initCause(e); + } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { + LinkageError x = new LinkageError("Cannot initialize reflection for Unix Processes", e); throw x; } } - public static void destroy(int pid) throws IllegalAccessException, InvocationTargetException { - if (isPreJava8()) { - DESTROY_PROCESS.invoke(null, pid); + public static void destroy(int pid) throws IllegalAccessException, + InvocationTargetException { + if (JAVA8_DESTROY_PROCESS != null) { + JAVA8_DESTROY_PROCESS.invoke(null, pid, false); } else { - DESTROY_PROCESS.invoke(null, pid, false); + final Optional handle = (Optional)JAVA_9_PROCESSHANDLE_OF.invoke(null, pid); + if (handle.isPresent()) { + JAVA_9_PROCESSHANDLE_DESTROY.invoke(handle.get()); + } + } + } + + //TODO: We ideally need to update ProcessTree APIs to Support Long (JENKINS-53799). + public static int pid(@Nonnull Process proc) { + try { + if (JAVA8_PID_FIELD != null) { + return JAVA8_PID_FIELD.getInt(proc); + } else { + long pid = (long)JAVA9_PID_METHOD.invoke(proc); + if (pid > Integer.MAX_VALUE) { + throw new IllegalAccessError("Java 9+ support error (JENKINS-53799). PID is out of Jenkins API bounds: " + pid); + } + return (int)pid; + } + } catch (IllegalAccessException | InvocationTargetException e) { // impossible + IllegalAccessError x = new IllegalAccessError(); + x.initCause(e); + throw x; } } - private static boolean isPreJava8() { - int javaVersionAsAnInteger = Integer.parseInt(System.getProperty("java.version").replaceAll("\\.", "").replaceAll("_", "").substring(0, 2)); - return javaVersionAsAnInteger < 18; + // Java 9 uses new version format + private static boolean isPostJava8() { + String javaVersion = System.getProperty("java.version"); + return !javaVersion.startsWith("1."); } } static class Linux extends ProcfsUnix { + public Linux(boolean vetoersExist) { + super(vetoersExist); + } + protected LinuxProcess createProcess(int pid) throws IOException { return new LinuxProcess(pid); } @@ -825,7 +1007,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } /** - * Implementation for Solaris that uses /proc. + * Implementation for Solaris that uses {@code /proc}. * * /proc/PID/psinfo contains a psinfo_t struct. We use it to determine where the * process arguments and environment are located in PID's address space. @@ -851,6 +1033,10 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * when accessing this file. */ static class Solaris extends ProcfsUnix { + public Solaris(boolean vetoersExist) { + super(vetoersExist); + } + protected OSProcess createProcess(final int pid) throws IOException { return new SolarisProcess(pid); } @@ -1091,7 +1277,9 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * Implementation for Mac OS X based on sysctl(3). */ private static class Darwin extends Unix { - Darwin() { + Darwin(boolean vetoersExist) { + super(vetoersExist); + String arch = System.getProperty("sun.arch.data.model"); if ("64".equals(arch)) { sizeOf_kinfo_proc = sizeOf_kinfo_proc_64; @@ -1103,18 +1291,18 @@ public abstract class ProcessTree implements Iterable, IProcessTree, kinfo_proc_ppid_offset = kinfo_proc_ppid_offset_32; } try { - IntByReference _ = new IntByReference(sizeOfInt); + IntByReference ref = new IntByReference(sizeOfInt); IntByReference size = new IntByReference(sizeOfInt); Memory m; int nRetry = 0; while(true) { // find out how much memory we need to do this - if(LIBC.sysctl(MIB_PROC_ALL,3, NULL, size, NULL, _)!=0) + if(LIBC.sysctl(MIB_PROC_ALL,3, NULL, size, NULL, ref)!=0) throw new IOException("Failed to obtain memory requirement: "+LIBC.strerror(Native.getLastError())); // now try the real call m = new Memory(size.getValue()); - if(LIBC.sysctl(MIB_PROC_ALL,3, m, size, NULL, _)!=0) { + if(LIBC.sysctl(MIB_PROC_ALL,3, m, size, NULL, ref)!=0) { if(Native.getLastError()==ENOMEM && nRetry++<16) continue; // retry throw new IOException("Failed to call kern.proc.all: "+LIBC.strerror(Native.getLastError())); @@ -1174,14 +1362,14 @@ public abstract class ProcessTree implements Iterable, IProcessTree, arguments = new ArrayList(); envVars = new EnvVars(); - IntByReference _ = new IntByReference(); + IntByReference intByRef = new IntByReference(); IntByReference argmaxRef = new IntByReference(0); IntByReference size = new IntByReference(sizeOfInt); // for some reason, I was never able to get sysctlbyname work. // if(LIBC.sysctlbyname("kern.argmax", argmaxRef.getPointer(), size, NULL, _)!=0) - if(LIBC.sysctl(new int[]{CTL_KERN,KERN_ARGMAX},2, argmaxRef.getPointer(), size, NULL, _)!=0) + if(LIBC.sysctl(new int[]{CTL_KERN,KERN_ARGMAX},2, argmaxRef.getPointer(), size, NULL, intByRef)!=0) throw new IOException("Failed to get kern.argmax: "+LIBC.strerror(Native.getLastError())); int argmax = argmaxRef.getValue(); @@ -1219,7 +1407,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } StringArrayMemory m = new StringArrayMemory(argmax); size.setValue(argmax); - if(LIBC.sysctl(new int[]{CTL_KERN,KERN_PROCARGS2,pid},3, m, size, NULL, _)!=0) + if(LIBC.sysctl(new int[]{CTL_KERN,KERN_PROCARGS2,pid},3, m, size, NULL, intByRef)!=0) throw new IOException("Failed to obtain ken.procargs2: "+LIBC.strerror(Native.getLastError())); @@ -1310,8 +1498,13 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * (The opposite of {@link Remote}.) */ public static abstract class Local extends ProcessTree { + @Deprecated Local() { } + + Local(boolean vetoesExist) { + super(vetoesExist); + } } /** @@ -1320,11 +1513,20 @@ public abstract class ProcessTree implements Iterable, IProcessTree, public static class Remote extends ProcessTree implements Serializable { private final IProcessTree proxy; + @Deprecated public Remote(ProcessTree proxy, Channel ch) { this.proxy = ch.export(IProcessTree.class,proxy); for (Entry e : proxy.processes.entrySet()) processes.put(e.getKey(),new RemoteProcess(e.getValue(),ch)); } + + public Remote(ProcessTree proxy, Channel ch, boolean vetoersExist) { + super(vetoersExist); + + this.proxy = ch.export(IProcessTree.class,proxy); + for (Entry e : proxy.processes.entrySet()) + processes.put(e.getKey(),new RemoteProcess(e.getValue(),ch)); + } @Override public OSProcess get(Process proc) { diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index d38fa7170e9b6629263850f0722b6afcedcf9420..447b19a5c99e486617b8305099498ed531e63e48 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -153,7 +153,10 @@ public final class RemotingDiagnostics { * Obtains the heap dump in an HPROF file. */ public static FilePath getHeapDump(VirtualChannel channel) throws IOException, InterruptedException { - return channel.call(new MasterToSlaveCallable() { + return channel.call(new GetHeapDump()); + } + private static class GetHeapDump extends MasterToSlaveCallable { + @Override public FilePath call() throws IOException { final File hprof = File.createTempFile("hudson-heapdump", "hprof"); hprof.delete(); @@ -169,7 +172,6 @@ public final class RemotingDiagnostics { } private static final long serialVersionUID = 1L; - }); } /** diff --git a/core/src/main/java/hudson/util/RunList.java b/core/src/main/java/hudson/util/RunList.java index d9b8adb7dc54fbb56e446337c8e75d97dc5729ea..bdc9cb61f4d6f092178150a51f2f6d0067510705 100644 --- a/core/src/main/java/hudson/util/RunList.java +++ b/core/src/main/java/hudson/util/RunList.java @@ -150,7 +150,7 @@ public class RunList extends AbstractList { public List subList(int fromIndex, int toIndex) { List r = new ArrayList(); Iterator itr = iterator(); - Iterators.skip(itr,fromIndex); + hudson.util.Iterators.skip(itr, fromIndex); for (int i=toIndex-fromIndex; i>0; i--) { r.add(itr.next()); } diff --git a/core/src/main/java/hudson/util/Secret.java b/core/src/main/java/hudson/util/Secret.java index 599467414e1e6ecbd45cfa8944ba086bb9e7ccba..09c7c683e94ff0c70221d227e2ea75b14160f47c 100644 --- a/core/src/main/java/hudson/util/Secret.java +++ b/core/src/main/java/hudson/util/Secret.java @@ -42,6 +42,7 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.util.logging.Logger; import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -64,6 +65,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; * @author Kohsuke Kawaguchi */ public final class Secret implements Serializable { + private static final Logger LOGGER = Logger.getLogger(Secret.class.getName()); + private static final byte PAYLOAD_V1 = 1; /** * Unencrypted secret text. @@ -92,6 +95,8 @@ public final class Secret implements Serializable { @Override @Deprecated public String toString() { + final String from = new Throwable().getStackTrace()[1].toString(); + LOGGER.warning("Use of toString() on hudson.util.Secret from "+from+". Prefer getPlainText() or getEncryptedValue() depending your needs. see https://jenkins.io/redirect/hudson.util.Secret/"); return value; } diff --git a/core/src/main/java/hudson/util/Service.java b/core/src/main/java/hudson/util/Service.java index a979324f33036cfb43a035bed8295e1ff753f077..9d33249909e370cea5405ca77245e00be9e3e5c2 100644 --- a/core/src/main/java/hudson/util/Service.java +++ b/core/src/main/java/hudson/util/Service.java @@ -37,7 +37,7 @@ import java.util.logging.Logger; import static java.util.logging.Level.WARNING; /** - * Load classes by looking up META-INF/services. + * Load classes by looking up {@code META-INF/services}. * * @author Kohsuke Kawaguchi * @deprecated use {@link ServiceLoader} instead. @@ -76,7 +76,7 @@ public class Service { } /** - * Look up META-INF/service/SPICLASSNAME from the classloader + * Look up {@code META-INF/service/SPICLASSNAME} from the classloader * and all the discovered classes into the given collection. */ public static void load(Class spi, ClassLoader cl, Collection> result) { diff --git a/core/src/main/java/hudson/util/StreamTaskListener.java b/core/src/main/java/hudson/util/StreamTaskListener.java index bd2b6dc87e81e689dace83165463abc4b11c176d..b2b2e4fd62e39e11d2764b4cc8350ea54c7566cf 100644 --- a/core/src/main/java/hudson/util/StreamTaskListener.java +++ b/core/src/main/java/hudson/util/StreamTaskListener.java @@ -44,6 +44,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.stapler.framework.io.WriterOutputStream; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + // TODO: AbstractTaskListener is empty now, but there are dependencies on that e.g. Ruby Runtime - JENKINS-48116) // The change needs API deprecation policy or external usages cleanup. @@ -56,7 +59,9 @@ import org.kohsuke.stapler.framework.io.WriterOutputStream; * @author Kohsuke Kawaguchi */ public class StreamTaskListener extends AbstractTaskListener implements TaskListener, Closeable { + @Nonnull private PrintStream out; + @CheckForNull private Charset charset; /** @@ -66,15 +71,15 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList * or use {@link #fromStdout()} or {@link #fromStderr()}. */ @Deprecated - public StreamTaskListener(PrintStream out) { + public StreamTaskListener(@Nonnull PrintStream out) { this(out,null); } - public StreamTaskListener(OutputStream out) { + public StreamTaskListener(@Nonnull OutputStream out) { this(out,null); } - public StreamTaskListener(OutputStream out, Charset charset) { + public StreamTaskListener(@Nonnull OutputStream out, @CheckForNull Charset charset) { try { if (charset == null) this.out = (out instanceof PrintStream) ? (PrintStream)out : new PrintStream(out, false); @@ -87,18 +92,18 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList } } - public StreamTaskListener(File out) throws IOException { + public StreamTaskListener(@Nonnull File out) throws IOException { this(out,null); } - public StreamTaskListener(File out, Charset charset) throws IOException { + public StreamTaskListener(@Nonnull File out, @CheckForNull Charset charset) throws IOException { // don't do buffering so that what's written to the listener // gets reflected to the file immediately, which can then be // served to the browser immediately this(Files.newOutputStream(asPath(out)), charset); } - private static Path asPath(File out) throws IOException { + private static Path asPath(@Nonnull File out) throws IOException { try { return out.toPath(); } catch (InvalidPathException e) { @@ -115,7 +120,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList * @throws IOException if the file could not be opened. * @since 1.651 */ - public StreamTaskListener(File out, boolean append, Charset charset) throws IOException { + public StreamTaskListener(@Nonnull File out, boolean append, @CheckForNull Charset charset) throws IOException { // don't do buffering so that what's written to the listener // gets reflected to the file immediately, which can then be // served to the browser immediately @@ -127,7 +132,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList ); } - public StreamTaskListener(Writer w) throws IOException { + public StreamTaskListener(@Nonnull Writer w) throws IOException { this(new WriterOutputStream(w)); } @@ -155,7 +160,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList @Override public Charset getCharset() { - return charset; + return charset != null ? charset : Charset.defaultCharset(); } private void writeObject(ObjectOutputStream out) throws IOException { diff --git a/core/src/main/java/hudson/util/TextFile.java b/core/src/main/java/hudson/util/TextFile.java index 0ac1326ce7e754699ff0806b0bc204561046060b..bd6904add0960800b060048ddba96fc2d58d162d 100644 --- a/core/src/main/java/hudson/util/TextFile.java +++ b/core/src/main/java/hudson/util/TextFile.java @@ -23,25 +23,23 @@ */ package hudson.util; -import com.google.common.collect.*; +import edu.umd.cs.findbugs.annotations.CreatesObligation; import hudson.Util; +import jenkins.util.io.LinesStream; import java.nio.file.Files; -import java.nio.file.InvalidPathException; import javax.annotation.Nonnull; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.io.Reader; import java.io.StringWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.Iterator; /** * Represents a text file. @@ -51,9 +49,10 @@ import java.util.Iterator; * @author Kohsuke Kawaguchi */ public class TextFile { - public final File file; - public TextFile(File file) { + public final @Nonnull File file; + + public TextFile(@Nonnull File file) { this.file = file; } @@ -75,41 +74,38 @@ public class TextFile { String line; while ((line = in.readLine()) != null) w.println(line); + } catch (Exception e) { + throw new IOException("Failed to fully read " + file, e); } return out.toString(); } /** - * Parse text file line by line. + * @throws RuntimeException in the case of {@link IOException} in {@link #linesStream()} + * @deprecated This method does not properly propagate errors and may lead to file descriptor leaks + * if the collection is not fully iterated. Use {@link #linesStream()} instead. */ - public Iterable lines() { - return new Iterable() { - @Override - public Iterator iterator() { - try { - final BufferedReader in = new BufferedReader(new InputStreamReader( - Files.newInputStream(file.toPath()),"UTF-8")); - - return new AbstractIterator() { - @Override - protected String computeNext() { - try { - String r = in.readLine(); - if (r==null) { - in.close(); - return endOfData(); - } - return r; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }; - } catch (IOException | InvalidPathException e) { - throw new RuntimeException(e); - } - } - }; + @Deprecated + public @Nonnull Iterable lines() { + try { + return linesStream(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Creates a new {@link jenkins.util.io.LinesStream} of the file. + *

    + * Note: The caller is responsible for closing the returned + * LinesStream. + * @throws IOException if the file cannot be converted to a + * {@link java.nio.file.Path} or if the file cannot be opened for reading + * @since 2.111 + */ + @CreatesObligation + public @Nonnull LinesStream linesStream() throws IOException { + return new LinesStream(Util.fileToPath(file)); } /** diff --git a/core/src/main/java/hudson/util/TimeUnit2.java b/core/src/main/java/hudson/util/TimeUnit2.java index 36b465764de3cd869544f7aaec26843c46173a11..d2aa5eb3fd27f36e20600582f55dfc6504266945 100644 --- a/core/src/main/java/hudson/util/TimeUnit2.java +++ b/core/src/main/java/hudson/util/TimeUnit2.java @@ -36,10 +36,10 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.concurrent.TimeUnit; /** - * A TimeUnit represents time durations at a given unit of + * A {@code TimeUnit} represents time durations at a given unit of * granularity and provides utility methods to convert across units, * and to perform timing and delay operations in these units. A - * TimeUnit does not maintain time information, but only + * {@code TimeUnit} does not maintain time information, but only * helps organize and use time representations that may be maintained * separately across various contexts. A nanosecond is defined as one * thousandth of a microsecond, a microsecond as one thousandth of a @@ -47,7 +47,7 @@ import java.util.concurrent.TimeUnit; * as sixty seconds, an hour as sixty minutes, and a day as twenty four * hours. * - *

    A TimeUnit is mainly used to inform time-based methods + *

    A {@code TimeUnit} is mainly used to inform time-based methods * how a given timing parameter should be interpreted. For example, * the following code will timeout in 50 milliseconds if the {@link * java.util.concurrent.locks.Lock lock} is not available: @@ -63,7 +63,7 @@ import java.util.concurrent.TimeUnit; * * Note however, that there is no guarantee that a particular timeout * implementation will be able to notice the passage of time at the - * same granularity as the given TimeUnit. + * same granularity as the given {@code TimeUnit}. * * @author Doug Lea * @deprecated use {@link TimeUnit}. (Java 5 did not have all the units required, so {@link TimeUnit2} was introduced @@ -188,20 +188,20 @@ public enum TimeUnit2 { * Convert the given time duration in the given unit to this * unit. Conversions from finer to coarser granularities * truncate, so lose precision. For example converting - * 999 milliseconds to seconds results in - * 0. Conversions from coarser to finer granularities + * {@code 999} milliseconds to seconds results in + * {@code 0}. Conversions from coarser to finer granularities * with arguments that would numerically overflow saturate to - * Long.MIN_VALUE if negative or Long.MAX_VALUE + * {@code Long.MIN_VALUE} if negative or {@code Long.MAX_VALUE} * if positive. * *

    For example, to convert 10 minutes to milliseconds, use: - * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * {@code TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)} * - * @param sourceDuration the time duration in the given sourceUnit - * @param sourceUnit the unit of the sourceDuration argument + * @param sourceDuration the time duration in the given {@code sourceUnit} + * @param sourceUnit the unit of the {@code sourceDuration} argument * @return the converted duration in this unit, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. */ public long convert(long sourceDuration, TimeUnit2 sourceUnit) { throw new AbstractMethodError(); @@ -211,31 +211,31 @@ public enum TimeUnit2 { * Convert the given time duration in the given unit to this * unit. Conversions from finer to coarser granularities * truncate, so lose precision. For example converting - * 999 milliseconds to seconds results in - * 0. Conversions from coarser to finer granularities + * {@code 999} milliseconds to seconds results in + * {@code 0}. Conversions from coarser to finer granularities * with arguments that would numerically overflow saturate to - * Long.MIN_VALUE if negative or Long.MAX_VALUE + * {@code Long.MIN_VALUE} if negative or {@code Long.MAX_VALUE} * if positive. * *

    For example, to convert 10 minutes to milliseconds, use: - * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * {@code TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)} * - * @param sourceDuration the time duration in the given sourceUnit - * @param sourceUnit the unit of the sourceDuration argument + * @param sourceDuration the time duration in the given {@code sourceUnit} + * @param sourceUnit the unit of the {@code sourceDuration} argument * @return the converted duration in this unit, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. */ public long convert(long sourceDuration, TimeUnit sourceUnit) { throw new AbstractMethodError(); } /** - * Equivalent to NANOSECONDS.convert(duration, this). + * Equivalent to {@code NANOSECONDS.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toNanos(long duration) { @@ -243,11 +243,11 @@ public enum TimeUnit2 { } /** - * Equivalent to MICROSECONDS.convert(duration, this). + * Equivalent to {@code MICROSECONDS.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toMicros(long duration) { @@ -255,11 +255,11 @@ public enum TimeUnit2 { } /** - * Equivalent to MILLISECONDS.convert(duration, this). + * Equivalent to {@code MILLISECONDS.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toMillis(long duration) { @@ -267,11 +267,11 @@ public enum TimeUnit2 { } /** - * Equivalent to SECONDS.convert(duration, this). + * Equivalent to {@code SECONDS.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toSeconds(long duration) { @@ -279,11 +279,11 @@ public enum TimeUnit2 { } /** - * Equivalent to MINUTES.convert(duration, this). + * Equivalent to {@code MINUTES.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toMinutes(long duration) { @@ -291,11 +291,11 @@ public enum TimeUnit2 { } /** - * Equivalent to HOURS.convert(duration, this). + * Equivalent to {@code HOURS.convert(duration, this)}. * @param duration the duration * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. * @see #convert */ public long toHours(long duration) { @@ -303,7 +303,7 @@ public enum TimeUnit2 { } /** - * Equivalent to DAYS.convert(duration, this). + * Equivalent to {@code DAYS.convert(duration, this)}. * @param duration the duration * @return the converted duration * @see #convert @@ -322,11 +322,11 @@ public enum TimeUnit2 { abstract int excessNanos(long d, long m); /** - * Performs a timed Object.wait using this time unit. + * Performs a timed {@code Object.wait} using this time unit. * This is a convenience method that converts timeout arguments - * into the form required by the Object.wait method. + * into the form required by the {@code Object.wait} method. * - *

    For example, you could implement a blocking poll + *

    For example, you could implement a blocking {@code poll} * method (see {@link java.util.concurrent.BlockingQueue#poll BlockingQueue.poll}) * using: * @@ -353,9 +353,9 @@ public enum TimeUnit2 { } /** - * Performs a timed Thread.join using this time unit. + * Performs a timed {@code Thread.join} using this time unit. * This is a convenience method that converts time arguments into the - * form required by the Thread.join method. + * form required by the {@code Thread.join} method. * @param thread the thread to wait for * @param timeout the maximum time to wait. If less than * or equal to zero, do not wait at all. @@ -372,9 +372,9 @@ public enum TimeUnit2 { } /** - * Performs a Thread.sleep using this unit. + * Performs a {@code Thread.sleep} using this unit. * This is a convenience method that converts time arguments into the - * form required by the Thread.sleep method. + * form required by the {@code Thread.sleep} method. * @param timeout the minimum time to sleep. If less than * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping. diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 41b462df29c5b6d1b65a8737b3a2909af59309f7..d4124ae147442b38fe1160bd2d90bae954c6d04c 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -26,6 +26,7 @@ package hudson.util; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.KXml2Driver; import com.thoughtworks.xstream.mapper.AnnotationMapper; import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.MapperWrapper; @@ -53,6 +54,7 @@ import hudson.diagnosis.OldDataMonitor; import hudson.remoting.ClassFilter; import hudson.util.xstream.ImmutableSetConverter; import hudson.util.xstream.ImmutableSortedSetConverter; +import jenkins.util.xstream.SafeURLConverter; import jenkins.model.Jenkins; import hudson.model.Label; import hudson.model.Result; @@ -97,7 +99,18 @@ public class XStream2 extends XStream { */ private MapperInjectionPoint mapperInjectionPoint; + /** + * Convenience method so we only have to change the driver in one place + * if we switch to something new in the future + * + * @return a new instance of the HierarchicalStreamDriver we want to use + */ + public static HierarchicalStreamDriver getDefaultDriver() { + return new KXml2Driver(); + } + public XStream2() { + super(getDefaultDriver()); init(); classOwnership = null; } @@ -109,6 +122,7 @@ public class XStream2 extends XStream { } XStream2(ClassOwnership classOwnership) { + super(getDefaultDriver()); init(); this.classOwnership = classOwnership; } @@ -235,6 +249,8 @@ public class XStream2 extends XStream { registerConverter(new CopyOnWriteMap.Tree.ConverterImpl(getMapper()),10); // needs to override MapConverter registerConverter(new DescribableList.ConverterImpl(getMapper()),10); // explicitly added to handle subtypes registerConverter(new Label.ConverterImpl(),10); + // SECURITY-637 against URL deserialization + registerConverter(new SafeURLConverter(),10); // this should come after all the XStream's default simpler converters, // but before reflection-based one kicks in. @@ -293,7 +309,7 @@ public class XStream2 extends XStream { */ public void toXMLUTF8(Object obj, OutputStream out) throws IOException { Writer w = new OutputStreamWriter(out, Charset.forName("UTF-8")); - w.write("\n"); + w.write("\n"); toXML(obj, w); } diff --git a/core/src/main/java/hudson/util/io/ParserConfigurator.java b/core/src/main/java/hudson/util/io/ParserConfigurator.java index 878fba535b030361fdc9be6e527b1c691c621aa9..149e82ba1d799c64aed358b1467ef09b587b1f7b 100644 --- a/core/src/main/java/hudson/util/io/ParserConfigurator.java +++ b/core/src/main/java/hudson/util/io/ParserConfigurator.java @@ -78,17 +78,17 @@ public abstract class ParserConfigurator implements ExtensionPoint, Serializable if (Jenkins.getInstanceOrNull()==null) { Channel ch = Channel.current(); if (ch!=null) - all = ch.call(new SlaveToMasterCallable, IOException>() { - - private static final long serialVersionUID = -2178106894481500733L; - - public Collection call() throws IOException { - return new ArrayList(all()); - } - }); + all = ch.call(new GetParserConfigurators()); } else all = all(); for (ParserConfigurator pc : all) pc.configure(reader,context); } + private static class GetParserConfigurators extends SlaveToMasterCallable, IOException> { + private static final long serialVersionUID = -2178106894481500733L; + @Override + public Collection call() throws IOException { + return new ArrayList<>(all()); + } + } } diff --git a/core/src/main/java/hudson/util/io/ZipArchiver.java b/core/src/main/java/hudson/util/io/ZipArchiver.java index 9bd58808b28315d5c7468d8ce401fda92d5ede8b..20da13cbc3a842c64586eea98333ff434cf6b134 100644 --- a/core/src/main/java/hudson/util/io/ZipArchiver.java +++ b/core/src/main/java/hudson/util/io/ZipArchiver.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.InvalidPathException; import org.apache.tools.zip.ZipEntry; +import org.apache.tools.zip.Zip64Mode; import org.apache.tools.zip.ZipOutputStream; import java.io.File; @@ -49,6 +50,7 @@ final class ZipArchiver extends Archiver { ZipArchiver(OutputStream out) { zip = new ZipOutputStream(out); zip.setEncoding(System.getProperty("file.encoding")); + zip.setUseZip64(Zip64Mode.AsNeeded); } public void visit(final File f, final String _relativePath) throws IOException { diff --git a/core/src/main/java/hudson/util/spring/package.html b/core/src/main/java/hudson/util/spring/package.html index ef210c114e8f4808a4d41910dc1bad25939b9a6b..fec56708535de67dfa7df2c39f76d320252ff68c 100644 --- a/core/src/main/java/hudson/util/spring/package.html +++ b/core/src/main/java/hudson/util/spring/package.html @@ -32,10 +32,10 @@ but modifications are made since then to make the syntax more consistent.

    Changes to the original code

    Our version has support for getting rid of surrounding "bb.beans { ... }" if the script - is parsed via the BeanBuilder.parse() method. + is parsed via the BeanBuilder.parse() method.

    - Anonymous bean definition syntax is changed to bean(CLASS) {...} from - {CLASS _ -> ...} to increase consistency with named bean definition. + Anonymous bean definition syntax is changed to bean(CLASS) {...} from + {CLASS _ -> ...} to increase consistency with named bean definition.

    \ No newline at end of file diff --git a/core/src/main/java/hudson/views/GlobalDefaultViewConfiguration.java b/core/src/main/java/hudson/views/GlobalDefaultViewConfiguration.java index c76ae404c383f3e7d98019429fc71795253bf792..36b8aea6a6dfe033e3a1d597e72274395421b501 100644 --- a/core/src/main/java/hudson/views/GlobalDefaultViewConfiguration.java +++ b/core/src/main/java/hudson/views/GlobalDefaultViewConfiguration.java @@ -41,7 +41,7 @@ public class GlobalDefaultViewConfiguration extends GlobalConfiguration { @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { // for compatibility reasons, the actual value is stored in Jenkins - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); if (json.has("primaryView")) { final String viewName = json.getString("primaryView"); final View newPrimaryView = j.getView(viewName); diff --git a/core/src/main/java/hudson/views/ListViewColumn.java b/core/src/main/java/hudson/views/ListViewColumn.java index f5fdd6e4687d961c7b801a5bab9e0a1c079382d5..4b1581a2f967743a114c48e26a722218e7a8e170 100644 --- a/core/src/main/java/hudson/views/ListViewColumn.java +++ b/core/src/main/java/hudson/views/ListViewColumn.java @@ -48,13 +48,13 @@ import net.sf.json.JSONObject; * Extension point for adding a column to a table rendering of {@link Item}s, such as {@link ListView}. * *

    - * This object must have the column.jelly. This view + * This object must have the {@code column.jelly}. This view * is called for each cell of this column. The {@link Item} object * is passed in the "job" variable. The view should render * the {@code } tag. * *

    - * This object may have an additional columnHeader.jelly. The default ColumnHeader + * This object may have an additional {@code columnHeader.jelly}. The default ColumnHeader * will render {@link #getColumnCaption()}. * *

    diff --git a/core/src/main/java/hudson/views/MyViewsTabBar.java b/core/src/main/java/hudson/views/MyViewsTabBar.java index 1d2fe44db1fc2e0b080759dabba478d4b88220d6..c3b49c268f7c7fba864476a2d89cb04eca3e1099 100644 --- a/core/src/main/java/hudson/views/MyViewsTabBar.java +++ b/core/src/main/java/hudson/views/MyViewsTabBar.java @@ -47,7 +47,7 @@ import org.kohsuke.stapler.StaplerRequest; * Extension point for adding a MyViewsTabBar header to Projects {@link MyViewsProperty}. * *

    - * This object must have the myViewTabs.jelly. This view + * This object must have the {@code myViewTabs.jelly}. This view * is called once when the My Views main panel is built. * The "views" attribute is set to the "Collection of views". * @@ -100,13 +100,13 @@ public abstract class MyViewsTabBar extends AbstractDescribableImpl - * This object must have the viewTabs.jelly. This view + * This object must have the {@code viewTabs.jelly}. This view * is called once when the project views main panel is built. * The "views" attribute is set to the "Collection of views". * @@ -102,13 +102,13 @@ public abstract class ViewsTabBar extends AbstractDescribableImpl i @Extension(ordinal=310) @Symbol("viewsTabBar") public static class GlobalConfigurationImpl extends GlobalConfiguration { public ViewsTabBar getViewsTabBar() { - return Jenkins.getInstance().getViewsTabBar(); + return Jenkins.get().getViewsTabBar(); } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { // for compatibility reasons, the actual value is stored in Jenkins - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); if (json.has("viewsTabBar")) { j.setViewsTabBar(req.bindJSON(ViewsTabBar.class,json.getJSONObject("viewsTabBar"))); diff --git a/core/src/main/java/jenkins/CLI.java b/core/src/main/java/jenkins/CLI.java index 2338e0fb4ae3b958bbddd3f6e7b33501606f4f79..e0a7f38957ad990d887aac585a821bcbd14495e9 100644 --- a/core/src/main/java/jenkins/CLI.java +++ b/core/src/main/java/jenkins/CLI.java @@ -4,6 +4,8 @@ import hudson.Extension; import hudson.model.AdministrativeMonitor; import java.io.IOException; import javax.annotation.Nonnull; + +import hudson.model.PersistentDescriptor; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import org.jenkinsci.Symbol; @@ -23,7 +25,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; */ @Restricted(NoExternalUse.class) @Extension @Symbol("remotingCLI") -public class CLI extends GlobalConfiguration { +public class CLI extends GlobalConfiguration implements PersistentDescriptor { /** * Supersedes {@link #isEnabled} if set. @@ -34,22 +36,13 @@ public class CLI extends GlobalConfiguration { @Nonnull public static CLI get() { - CLI instance = GlobalConfiguration.all().get(CLI.class); - if (instance == null) { - // should not happen - return new CLI(); - } - return instance; + return GlobalConfiguration.all().getInstance(CLI.class); } private boolean enabled = true; // historical default, but overridden in SetupWizard - public CLI() { - load(); - } - @Override - public GlobalConfigurationCategory getCategory() { + public @Nonnull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } diff --git a/core/src/main/java/jenkins/ExtensionFilter.java b/core/src/main/java/jenkins/ExtensionFilter.java index 1f3ec5c8865f0bc0873f8c50f8315950f4a813e1..1b4b55ffda568d8920606b1d3f3358017dd09013 100644 --- a/core/src/main/java/jenkins/ExtensionFilter.java +++ b/core/src/main/java/jenkins/ExtensionFilter.java @@ -63,8 +63,9 @@ public abstract class ExtensionFilter implements ExtensionPoint { * @param type * The type of the extension that we are discovering. This is not the actual instance * type, but the contract type, such as {@link Descriptor}, {@link AdministrativeMonitor}, etc. + * @param component the actual discovered {@link hudson.Extension} object. * @return - * true to let the component into Jenkins. false to drop it and pretend + * true to let the component into Jenkins. false to drop it and pretend * as if it didn't exist. When any one of {@link ExtensionFilter}s veto * a component, it gets dropped. */ diff --git a/core/src/main/java/jenkins/MasterToSlaveFileCallable.java b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java index 9b5c88dcb6be0a6c89115defe038580c090aa584..ce7d8df28ef5c2e77a6ed2fb63040047c37fced5 100644 --- a/core/src/main/java/jenkins/MasterToSlaveFileCallable.java +++ b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java @@ -1,12 +1,19 @@ package jenkins; import hudson.FilePath.FileCallable; +import hudson.remoting.VirtualChannel; import jenkins.security.Roles; +import jenkins.slaves.RemotingVersionInfo; import org.jenkinsci.remoting.RoleChecker; +import java.io.File; + /** * {@link FileCallable}s that are meant to be only used on the master. * + * Note that the logic within {@link #invoke(File, VirtualChannel)} should use API of a minimum supported Remoting version. + * See {@link RemotingVersionInfo#getMinimumSupportedVersion()}. + * * @since 1.587 / 1.580.1 * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist */ diff --git a/core/src/main/java/jenkins/SoloFilePathFilter.java b/core/src/main/java/jenkins/SoloFilePathFilter.java index ce135759824434df1e830e7c44556649c2060075..aa0ebbefc8592ec60fe1a27b0edb5e50cccc74dd 100644 --- a/core/src/main/java/jenkins/SoloFilePathFilter.java +++ b/core/src/main/java/jenkins/SoloFilePathFilter.java @@ -1,5 +1,7 @@ package jenkins; +import hudson.FilePath; + import javax.annotation.Nullable; import java.io.File; @@ -31,39 +33,43 @@ public final class SoloFilePathFilter extends FilePathFilter { throw new SecurityException("agent may not " + op + " " + f+"\nSee https://jenkins.io/redirect/security-144 for more details"); return true; } + + private File normalize(File file){ + return new File(FilePath.normalize(file.getAbsolutePath())); + } @Override public boolean read(File f) throws SecurityException { - return noFalse("read",f,base.read(f)); + return noFalse("read",f,base.read(normalize(f))); } @Override public boolean write(File f) throws SecurityException { - return noFalse("write",f,base.write(f)); + return noFalse("write",f,base.write(normalize(f))); } @Override public boolean symlink(File f) throws SecurityException { - return noFalse("symlink",f,base.write(f)); + return noFalse("symlink",f,base.write(normalize(f))); } @Override public boolean mkdirs(File f) throws SecurityException { - return noFalse("mkdirs",f,base.mkdirs(f)); + return noFalse("mkdirs",f,base.mkdirs(normalize(f))); } @Override public boolean create(File f) throws SecurityException { - return noFalse("create",f,base.create(f)); + return noFalse("create",f,base.create(normalize(f))); } @Override public boolean delete(File f) throws SecurityException { - return noFalse("delete",f,base.delete(f)); + return noFalse("delete",f,base.delete(normalize(f))); } @Override public boolean stat(File f) throws SecurityException { - return noFalse("stat",f,base.stat(f)); + return noFalse("stat",f,base.stat(normalize(f))); } } diff --git a/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..bb4a252409d7bd10cce9091e2c6eda73c1a5058f --- /dev/null +++ b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.diagnostics; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import jenkins.model.JenkinsLocationConfiguration; +import jenkins.util.UrlHelper; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Jenkins root URL is required for a lot of operations in both core and plugins. + * There is a default behavior (infer the URL from the request object), but inaccurate in some scenarios. + * Normally this root URL is set during SetupWizard phase, this monitor is there to ensure that behavior. + * Potential exceptions are the dev environment, if someone disable the wizard or + * the administrator put an empty string on the configuration page. + * + * @since 2.119 + */ +@Extension +@Symbol("rootUrlNotSet") +@Restricted(NoExternalUse.class) +public class RootUrlNotSetMonitor extends AdministrativeMonitor { + @Override + public String getDisplayName() { + return Messages.RootUrlNotSetMonitor_DisplayName(); + } + + @Override + public boolean isActivated() { + JenkinsLocationConfiguration loc = JenkinsLocationConfiguration.get(); + return loc.getUrl() == null || !UrlHelper.isValidRootUrl(loc.getUrl()); + } + + // used by jelly to determined if it's a null url or invalid one + @Restricted(NoExternalUse.class) + public boolean isUrlNull(){ + JenkinsLocationConfiguration loc = JenkinsLocationConfiguration.get(); + return loc.getUrl() == null; + } +} diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index 939c45f828a9f3b4c412d541a2e9901044973c6f..df091a08eabc3e2c2e3c6ccc0d50e91f497574cf 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -32,6 +32,7 @@ import hudson.ExtensionPoint; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; +import jenkins.model.JenkinsLocationConfiguration; import org.apache.commons.lang.StringUtils; /** * Jenkins install state. @@ -45,16 +46,42 @@ import org.apache.commons.lang.StringUtils; * @author tom.fennelly@gmail.com */ public class InstallState implements ExtensionPoint { + + /** + * Only here for XStream compatibility.

    + * + * Please DO NOT ADD ITEM TO THIS LIST.

    + * If you add an item here, the deserialization process will break + * because it is used for serialized state like "jenkins.install.InstallState$4" + * before the change from anonymous class to named class. If you need to add a new InstallState, you can just add a new inner named class but nothing to change in this list. + * + * @see #readResolve + */ + @Deprecated + @SuppressWarnings("MismatchedReadAndWriteOfArray") + private static final InstallState[] UNUSED_INNER_CLASSES = { + new InstallState("UNKNOWN", false) {}, + new InstallState("INITIAL_SETUP_COMPLETED", false) {}, + new InstallState("CREATE_ADMIN_USER", false) {}, + new InstallState("INITIAL_SECURITY_SETUP", false) {}, + new InstallState("RESTART", false) {}, + new InstallState("DOWNGRADE", false) {}, + }; + /** * Need InstallState != NEW for tests by default */ @Extension - public static final InstallState UNKNOWN = new InstallState("UNKNOWN", true) { + public static final InstallState UNKNOWN = new Unknown(); + private static class Unknown extends InstallState { + Unknown() { + super("UNKNOWN", true); + } @Override public void initializeState() { InstallUtil.proceedToNextStateFrom(this); } - }; + } /** * After any setup / restart / etc. hooks are done, states should be running @@ -66,9 +93,13 @@ public class InstallState implements ExtensionPoint { * The initial set up has been completed */ @Extension - public static final InstallState INITIAL_SETUP_COMPLETED = new InstallState("INITIAL_SETUP_COMPLETED", true) { + public static final InstallState INITIAL_SETUP_COMPLETED = new InitialSetupCompleted(); + private static final class InitialSetupCompleted extends InstallState { + InitialSetupCompleted() { + super("INITIAL_SETUP_COMPLETED", true); + } public void initializeState() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); try { j.getSetupWizard().completeSetup(); } catch (Exception e) { @@ -76,22 +107,41 @@ public class InstallState implements ExtensionPoint { } j.setInstallState(RUNNING); } - }; + } /** * Creating an admin user for an initial Jenkins install. */ @Extension - public static final InstallState CREATE_ADMIN_USER = new InstallState("CREATE_ADMIN_USER", false) { + public static final InstallState CREATE_ADMIN_USER = new CreateAdminUser(); + private static final class CreateAdminUser extends InstallState { + CreateAdminUser() { + super("CREATE_ADMIN_USER", false); + } public void initializeState() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); // Skip this state if not using the security defaults // e.g. in an init script set up security already if (!j.getSetupWizard().isUsingSecurityDefaults()) { InstallUtil.proceedToNextStateFrom(this); } } - }; + } + + @Extension + public static final InstallState CONFIGURE_INSTANCE = new ConfigureInstance(); + private static final class ConfigureInstance extends InstallState { + ConfigureInstance() { + super("CONFIGURE_INSTANCE", false); + } + public void initializeState() { + // Skip this state if a boot script already configured the root URL + // in case we add more fields in this page, this should be adapted + if (StringUtils.isNotBlank(JenkinsLocationConfiguration.getOrDie().getUrl())) { + InstallUtil.proceedToNextStateFrom(this); + } + } + } /** * New Jenkins install. The user has kicked off the process of installing an @@ -104,17 +154,21 @@ public class InstallState implements ExtensionPoint { * Security setup for a new Jenkins install. */ @Extension - public static final InstallState INITIAL_SECURITY_SETUP = new InstallState("INITIAL_SECURITY_SETUP", false) { + public static final InstallState INITIAL_SECURITY_SETUP = new InitialSecuritySetup(); + private static final class InitialSecuritySetup extends InstallState { + InitialSecuritySetup() { + super("INITIAL_SECURITY_SETUP", false); + } public void initializeState() { try { - Jenkins.getInstance().getSetupWizard().init(true); + Jenkins.get().getSetupWizard().init(true); } catch (Exception e) { throw new RuntimeException(e); } InstallUtil.proceedToNextStateFrom(INITIAL_SECURITY_SETUP); } - }; + } /** * New Jenkins install. @@ -126,11 +180,15 @@ public class InstallState implements ExtensionPoint { * Restart of an existing Jenkins install. */ @Extension - public static final InstallState RESTART = new InstallState("RESTART", true) { + public static final InstallState RESTART = new Restart(); + private static final class Restart extends InstallState { + Restart() { + super("RESTART", true); + } public void initializeState() { InstallUtil.saveLastExecVersion(); } - }; + } /** * Upgrade of an existing Jenkins install. @@ -142,11 +200,15 @@ public class InstallState implements ExtensionPoint { * Downgrade of an existing Jenkins install. */ @Extension - public static final InstallState DOWNGRADE = new InstallState("DOWNGRADE", true) { + public static final InstallState DOWNGRADE = new Downgrade(); + private static final class Downgrade extends InstallState { + Downgrade() { + super("DOWNGRADE", true); + } public void initializeState() { InstallUtil.saveLastExecVersion(); } - }; + } private static final Logger LOGGER = Logger.getLogger(InstallState.class.getName()); @@ -161,7 +223,11 @@ public class InstallState implements ExtensionPoint { */ public static final InstallState DEVELOPMENT = new InstallState("DEVELOPMENT", true); - private final boolean isSetupComplete; + private final transient boolean isSetupComplete; + + /** + * Link with the pluginSetupWizardGui.js map: "statsHandlers" + */ private final String name; public InstallState(@Nonnull String name, boolean isSetupComplete) { @@ -174,8 +240,16 @@ public class InstallState implements ExtensionPoint { */ public void initializeState() { } - - public Object readResolve() { + + /** + * The actual class name is irrelevant; this is functionally an enum. + *

    Creating a {@code writeReplace} does not help much since XStream then just saves: + * {@code } + * @see #UNUSED_INNER_CLASSES + * @deprecated Should no longer be used, as {@link Jenkins} now saves only {@link #name}. + */ + @Deprecated + protected Object readResolve() { // If we get invalid state from the configuration, fallback to unknown if (StringUtils.isBlank(name)) { LOGGER.log(Level.WARNING, "Read install state with blank name: ''{0}''. It will be ignored", name); diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index 48b8d33ab6acaaff6bb27eaee6f499593278e584..7552f9fa35ea5f43b4be51cfe5c7f6ead95c61bf 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -101,37 +101,30 @@ public class InstallUtil { /** * Returns the next state during a transition from the current install state */ - /*package*/ static InstallState getNextInstallState(final InstallState current) { + /*package*/ static InstallState getNextInstallState(InstallState current) { List,InstallState>> installStateFilterChain = new ArrayList<>(); - for (final InstallStateFilter setupExtension : InstallStateFilter.all()) { - installStateFilterChain.add(new Function, InstallState>() { - @Override - public InstallState apply(Provider next) { - return setupExtension.getNextInstallState(current, next); - } - }); + for (InstallStateFilter setupExtension : InstallStateFilter.all()) { + installStateFilterChain.add(next -> setupExtension.getNextInstallState(current, next)); } // Terminal condition: getNextState() on the current install state - installStateFilterChain.add(new Function, InstallState>() { - @Override - public InstallState apply(Provider input) { - // Initially, install state is unknown and - // needs to be determined - if (current == null || InstallState.UNKNOWN.equals(current)) { - return getDefaultInstallState(); - } - final Map states = new HashMap(); - { - states.put(InstallState.CREATE_ADMIN_USER, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.INITIAL_PLUGINS_INSTALLING, InstallState.CREATE_ADMIN_USER); - states.put(InstallState.INITIAL_SECURITY_SETUP, InstallState.NEW); - states.put(InstallState.RESTART, InstallState.RUNNING); - states.put(InstallState.UPGRADE, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.DOWNGRADE, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.INITIAL_SETUP_COMPLETED, InstallState.RUNNING); - } - return states.get(current); + installStateFilterChain.add(input -> { + // Initially, install state is unknown and + // needs to be determined + if (current == null || InstallState.UNKNOWN.equals(current)) { + return getDefaultInstallState(); + } + Map states = new HashMap<>(); + { + states.put(InstallState.CONFIGURE_INSTANCE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.CREATE_ADMIN_USER, InstallState.CONFIGURE_INSTANCE); + states.put(InstallState.INITIAL_PLUGINS_INSTALLING, InstallState.CREATE_ADMIN_USER); + states.put(InstallState.INITIAL_SECURITY_SETUP, InstallState.NEW); + states.put(InstallState.RESTART, InstallState.RUNNING); + states.put(InstallState.UPGRADE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.DOWNGRADE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.INITIAL_SETUP_COMPLETED, InstallState.RUNNING); } + return states.get(current); }); ProviderChain chain = new ProviderChain<>(installStateFilterChain.iterator()); diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 6c70482369770855aa6e1bfe486d1efa3911cee9..3ae5a0ea24b96a4acf29e32ad81e6eedacee2bef 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -4,7 +4,9 @@ import static org.apache.commons.io.FileUtils.readFileToString; import static org.apache.commons.lang.StringUtils.defaultIfBlank; import java.io.IOException; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -20,7 +22,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; +import jenkins.model.JenkinsLocationConfiguration; import jenkins.util.SystemProperties; +import jenkins.util.UrlHelper; import org.acegisecurity.Authentication; import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; @@ -28,6 +32,7 @@ import org.acegisecurity.userdetails.UsernameNotFoundException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -39,6 +44,7 @@ import hudson.model.PageDecorator; import hudson.model.UpdateCenter; import hudson.model.UpdateSite; import hudson.model.User; +import hudson.security.AccountCreationFailedException; import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; import hudson.security.HudsonPrivateSecurityRealm; import hudson.security.SecurityRealm; @@ -91,7 +97,7 @@ public class SetupWizard extends PageDecorator { * Initialize the setup wizard, this will process any current state initializations */ /*package*/ void init(boolean newInstall) throws IOException, InterruptedException { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); if(newInstall) { // this was determined to be a new install, don't run the update wizard here @@ -127,16 +133,9 @@ public class SetupWizard extends PageDecorator { // Disable CLI over Remoting CLI.get().setEnabled(false); - - // Disable old Non-Encrypted protocols () - HashSet newProtocols = new HashSet<>(jenkins.getAgentProtocols()); - newProtocols.removeAll(Arrays.asList( - "JNLP2-connect", "JNLP-connect", "CLI-connect" - )); - jenkins.setAgentProtocols(newProtocols); // require a crumb issuer - jenkins.setCrumbIssuer(new DefaultCrumbIssuer(false)); + jenkins.setCrumbIssuer(new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility",false))); // set master -> slave security: jenkins.getInjector().getInstance(AdminWhitelistRule.class) @@ -195,14 +194,14 @@ public class SetupWizard extends PageDecorator { throw new RuntimeException("Unable to remove PluginServletFilter for the SetupWizard", e); } } - + /** * Indicates a generated password should be used - e.g. this is a new install, no security realm set up */ @SuppressWarnings("unused") // used by jelly public boolean isUsingSecurityToken() { try { - return !Jenkins.getInstance().getInstallState().isSetupComplete() + return !Jenkins.get().getInstallState().isSetupComplete() && isUsingSecurityDefaults(); } catch (Exception e) { // ignore @@ -216,7 +215,7 @@ public class SetupWizard extends PageDecorator { * Other settings are irrelevant. */ /*package*/ boolean isUsingSecurityDefaults() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); if (j.getSecurityRealm() instanceof HudsonPrivateSecurityRealm) { HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm)j.getSecurityRealm(); try { @@ -240,53 +239,104 @@ public class SetupWizard extends PageDecorator { * Called during the initial setup to create an admin user */ @RequirePOST - public HttpResponse doCreateAdminUser(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + @Restricted(NoExternalUse.class) + public HttpResponse doCreateAdminUser(StaplerRequest req, StaplerResponse rsp) throws IOException { Jenkins j = Jenkins.getInstance(); + j.checkPermission(Jenkins.ADMINISTER); - + // This will be set up by default. if not, something changed, ok to fail - HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm)j.getSecurityRealm(); - + HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm) j.getSecurityRealm(); + User admin = securityRealm.getUser(SetupWizard.initialSetupAdminUserName); try { - if(admin != null) { + if (admin != null) { admin.delete(); // assume the new user may well be 'admin' } - - User u = securityRealm.createAccountByAdmin(req, rsp, "/jenkins/install/SetupWizard/setupWizardFirstUser.jelly", null); - if (u != null) { - if(admin != null) { - admin = null; - } - - // Success! Delete the temporary password file: - try { - getInitialAdminPasswordFile().delete(); - } catch (InterruptedException e) { - throw new IOException(e); - } - - InstallUtil.proceedToNextStateFrom(InstallState.CREATE_ADMIN_USER); - - // ... and then login - Authentication a = new UsernamePasswordAuthenticationToken(u.getId(),req.getParameter("password1")); - a = securityRealm.getSecurityComponents().manager.authenticate(a); - SecurityContextHolder.getContext().setAuthentication(a); - CrumbIssuer crumbIssuer = Jenkins.getInstance().getCrumbIssuer(); - JSONObject data = new JSONObject(); - if (crumbIssuer != null) { - data.accumulate("crumbRequestField", crumbIssuer.getCrumbRequestField()).accumulate("crumb", crumbIssuer.getCrumb(req)); - } - return HttpResponses.okJSON(data); - } else { - return HttpResponses.okJSON(); + + User newUser = securityRealm.createAccountFromSetupWizard(req); + if (admin != null) { + admin = null; + } + + // Success! Delete the temporary password file: + try { + getInitialAdminPasswordFile().delete(); + } catch (InterruptedException e) { + throw new IOException(e); } + + InstallUtil.proceedToNextStateFrom(InstallState.CREATE_ADMIN_USER); + + // ... and then login + Authentication auth = new UsernamePasswordAuthenticationToken(newUser.getId(), req.getParameter("password1")); + auth = securityRealm.getSecurityComponents().manager.authenticate(auth); + SecurityContextHolder.getContext().setAuthentication(auth); + CrumbIssuer crumbIssuer = Jenkins.getInstance().getCrumbIssuer(); + JSONObject data = new JSONObject(); + if (crumbIssuer != null) { + data.accumulate("crumbRequestField", crumbIssuer.getCrumbRequestField()).accumulate("crumb", crumbIssuer.getCrumb(req)); + } + return HttpResponses.okJSON(data); + } catch (AccountCreationFailedException e) { + /* + Return Unprocessable Entity from WebDAV. While this is not technically in the HTTP/1.1 standard, browsers + seem to accept this. 400 Bad Request is technically inappropriate because that implies invalid *syntax*, + not incorrect data. The client only cares about it being >200 anyways. + */ + rsp.setStatus(422); + return HttpResponses.forwardToView(securityRealm, "/jenkins/install/SetupWizard/setupWizardFirstUser.jelly"); } finally { - if(admin != null) { + if (admin != null) { admin.save(); // recreate this initial user if something failed } } } + + @RequirePOST + @Restricted(NoExternalUse.class) + public HttpResponse doConfigureInstance(StaplerRequest req, @QueryParameter String rootUrl) { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + + Map errors = new HashMap<>(); + // pre-check data + checkRootUrl(errors, rootUrl); + + if(!errors.isEmpty()){ + return HttpResponses.errorJSON(Messages.SetupWizard_ConfigureInstance_ValidationErrors(), errors); + } + + // use the parameters to configure the instance + useRootUrl(errors, rootUrl); + + if(!errors.isEmpty()){ + return HttpResponses.errorJSON(Messages.SetupWizard_ConfigureInstance_ValidationErrors(), errors); + } + + InstallUtil.proceedToNextStateFrom(InstallState.CONFIGURE_INSTANCE); + + CrumbIssuer crumbIssuer = Jenkins.get().getCrumbIssuer(); + JSONObject data = new JSONObject(); + if (crumbIssuer != null) { + data.accumulate("crumbRequestField", crumbIssuer.getCrumbRequestField()).accumulate("crumb", crumbIssuer.getCrumb(req)); + } + return HttpResponses.okJSON(data); + } + + private void checkRootUrl(Map errors, @CheckForNull String rootUrl){ + if(rootUrl == null){ + errors.put("rootUrl", Messages.SetupWizard_ConfigureInstance_RootUrl_Empty()); + return; + } + if(!UrlHelper.isValidRootUrl(rootUrl)){ + errors.put("rootUrl", Messages.SetupWizard_ConfigureInstance_RootUrl_Invalid()); + } + } + + private void useRootUrl(Map errors, @CheckForNull String rootUrl){ + LOGGER.log(Level.FINE, "Root URL set during SetupWizard to {0}", new Object[]{ rootUrl }); + JenkinsLocationConfiguration.getOrDie().setUrl(rootUrl); + } /*package*/ void setCurrentLevel(VersionNumber v) throws IOException { FileUtils.writeStringToFile(getUpdateStateFile(), v.toString()); @@ -298,7 +348,7 @@ public class SetupWizard extends PageDecorator { * This file records the version number that the installation has upgraded to. */ /*package*/ static File getUpdateStateFile() { - return new File(Jenkins.getInstance().getRootDir(),"jenkins.install.UpgradeWizard.state"); + return new File(Jenkins.get().getRootDir(),"jenkins.install.UpgradeWizard.state"); } /** @@ -327,9 +377,9 @@ public class SetupWizard extends PageDecorator { */ @Restricted(DoNotUse.class) // WebOnly public HttpResponse doPlatformPluginList() throws IOException { - jenkins.install.SetupWizard setupWizard = Jenkins.getInstance().getSetupWizard(); + SetupWizard setupWizard = Jenkins.get().getSetupWizard(); if (setupWizard != null) { - if (InstallState.UPGRADE.equals(Jenkins.getInstance().getInstallState())) { + if (InstallState.UPGRADE.equals(Jenkins.get().getInstallState())) { JSONArray initialPluginData = getPlatformPluginUpdates(); if(initialPluginData != null) { return HttpResponses.okJSON(initialPluginData); @@ -351,7 +401,7 @@ public class SetupWizard extends PageDecorator { @Restricted(DoNotUse.class) // WebOnly public HttpResponse doRestartStatus() throws IOException { JSONObject response = new JSONObject(); - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); response.put("restartRequired", jenkins.getUpdateCenter().isRestartRequiredForCompletion()); response.put("restartSupported", jenkins.getLifecycle().canRestart()); return HttpResponses.okJSON(response); @@ -377,9 +427,9 @@ public class SetupWizard extends PageDecorator { */ @CheckForNull /*package*/ JSONArray getPlatformPluginList() { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); JSONArray initialPluginList = null; - updateSiteList: for (UpdateSite updateSite : Jenkins.getInstance().getUpdateCenter().getSiteList()) { + updateSiteList: for (UpdateSite updateSite : Jenkins.get().getUpdateCenter().getSiteList()) { String updateCenterJsonUrl = updateSite.getUrl(); String suggestedPluginUrl = updateCenterJsonUrl.replace("/update-center.json", "/platform-plugins.json"); try { @@ -423,7 +473,7 @@ public class SetupWizard extends PageDecorator { * Get the platform plugins added in the version range */ /*package*/ JSONArray getPlatformPluginsForUpdate(VersionNumber from, VersionNumber to) { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); JSONArray pluginCategories = JSONArray.fromObject(getPlatformPluginList().toString()); for (Iterator categoryIterator = pluginCategories.iterator(); categoryIterator.hasNext();) { Object category = categoryIterator.next(); @@ -480,7 +530,7 @@ public class SetupWizard extends PageDecorator { * Gets the file used to store the initial admin password */ public FilePath getInitialAdminPasswordFile() { - return Jenkins.getInstance().getRootPath().child("secrets/initialAdminPassword"); + return Jenkins.get().getRootPath().child("secrets/initialAdminPassword"); } /** @@ -493,7 +543,7 @@ public class SetupWizard extends PageDecorator { } /*package*/ void completeSetup() throws IOException, ServletException { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); InstallUtil.saveLastExecVersion(); setCurrentLevel(Jenkins.getVersion()); InstallUtil.proceedToNextStateFrom(InstallState.INITIAL_SETUP_COMPLETED); @@ -555,7 +605,7 @@ public class SetupWizard extends PageDecorator { ((HttpServletResponse) response).sendRedirect(req.getContextPath() + "/"); return; } else if (req.getRequestURI().equals(req.getContextPath() + "/")) { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); chain.doFilter(new HttpServletRequestWrapper(req) { public String getRequestURI() { return getContextPath() + "/setupWizard/"; @@ -577,7 +627,7 @@ public class SetupWizard extends PageDecorator { * Sets up the Setup Wizard filter if the current state requires it. */ private void checkFilter() { - if (!Jenkins.getInstance().getInstallState().isSetupComplete()) { + if (!Jenkins.get().getInstallState().isSetupComplete()) { setUpFilter(); } } diff --git a/core/src/main/java/jenkins/install/UpgradeWizard.java b/core/src/main/java/jenkins/install/UpgradeWizard.java index ab7e243a7039e125d757e5e2748d38e456bd13dd..57aef99ccd4e9ce4ce4447bc49702b0605f366f0 100644 --- a/core/src/main/java/jenkins/install/UpgradeWizard.java +++ b/core/src/main/java/jenkins/install/UpgradeWizard.java @@ -11,6 +11,7 @@ import java.util.logging.Logger; import javax.inject.Provider; import javax.servlet.http.HttpSession; +import jenkins.security.apitoken.ApiTokenPropertyConfiguration; import org.apache.commons.io.FileUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -56,6 +57,8 @@ public class UpgradeWizard extends InstallState { @Override public void initializeState() { + applyForcedChanges(); + // Initializing this state is directly related to // running the detached plugin checks, these should be consolidated somehow updateUpToDate(); @@ -68,6 +71,21 @@ public class UpgradeWizard extends InstallState { } } + /** + * Put here the different changes that are enforced after an update. + */ + private void applyForcedChanges(){ + // Disable the legacy system of API Token only if the new system was not installed + // in such case it means there was already an upgrade before + // and potentially the admin has re-enabled the features + ApiTokenPropertyConfiguration apiTokenPropertyConfiguration = ApiTokenPropertyConfiguration.get(); + if(!apiTokenPropertyConfiguration.hasExistingConfigFile()){ + LOGGER.log(Level.INFO, "New API token system configured with insecure options to keep legacy behavior"); + apiTokenPropertyConfiguration.setCreationOfLegacyTokenEnabled(false); + apiTokenPropertyConfiguration.setTokenGenerationOnCreationEnabled(false); + } + } + @Override public boolean isSetupComplete() { return !isDue(); diff --git a/core/src/main/java/jenkins/model/ArtifactManager.java b/core/src/main/java/jenkins/model/ArtifactManager.java index a7b569417ea82f7babd6f6ab485fc2af057950c5..874d4e3a5841d11abd436d31819b342d40837cb6 100644 --- a/core/src/main/java/jenkins/model/ArtifactManager.java +++ b/core/src/main/java/jenkins/model/ArtifactManager.java @@ -33,6 +33,7 @@ import hudson.model.TaskListener; import hudson.tasks.ArtifactArchiver; import java.io.IOException; import java.util.Map; +import javax.annotation.Nonnull; import jenkins.util.VirtualFile; /** @@ -47,7 +48,7 @@ public abstract class ArtifactManager { * The selected manager will be persisted inside a build, so the build reference should be {@code transient} (quasi-{@code final}) and restored here. * @param build a historical build with which this manager was associated */ - public abstract void onLoad(Run build); + public abstract void onLoad(@Nonnull Run build); /** * Archive all configured artifacts from a build. diff --git a/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java b/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java index 43f98aae8dbbb53afd121a5046f67391594e0a13..cb411800d954c984293e44cf6dcef4b467c7d44d 100644 --- a/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java +++ b/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java @@ -25,29 +25,28 @@ package jenkins.model; import hudson.Extension; +import hudson.model.PersistentDescriptor; import hudson.util.DescribableList; import java.io.IOException; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; +import javax.annotation.Nonnull; + /** * List of configured {@link ArtifactManagerFactory}s. * @since 1.532 */ @Extension @Symbol("artifactManager") -public class ArtifactManagerConfiguration extends GlobalConfiguration { +public class ArtifactManagerConfiguration extends GlobalConfiguration implements PersistentDescriptor { - public static ArtifactManagerConfiguration get() { - return Jenkins.getInstance().getInjector().getInstance(ArtifactManagerConfiguration.class); + public static @Nonnull ArtifactManagerConfiguration get() { + return GlobalConfiguration.all().getInstance(ArtifactManagerConfiguration.class); } private final DescribableList artifactManagerFactories = new DescribableList(this); - public ArtifactManagerConfiguration() { - load(); - } - private Object readResolve() { artifactManagerFactories.setOwner(this); return this; diff --git a/core/src/main/java/jenkins/model/CauseOfInterruption.java b/core/src/main/java/jenkins/model/CauseOfInterruption.java index 7a8c173f212198258c2327347490a1c2a8229636..b14a02ca2752b0b6b3d904ea3123e933c742045a 100644 --- a/core/src/main/java/jenkins/model/CauseOfInterruption.java +++ b/core/src/main/java/jenkins/model/CauseOfInterruption.java @@ -39,7 +39,7 @@ import javax.annotation.Nonnull; * Records why an {@linkplain Executor#interrupt() executor is interrupted}. * *

    View

    - * summary.groovy/.jelly should do one-line HTML rendering to be used while rendering + * {@code summary.groovy/.jelly} should do one-line HTML rendering to be used while rendering * "build history" widget, next to the blocking build. By default it simply renders * {@link #getShortDescription()} text. * diff --git a/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java b/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..6a4a9278a389132cc05bb85cca80893965d8db51 --- /dev/null +++ b/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * + * Copyright 2018, CloudBees, Inc. + * + * 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; + +import hudson.Extension; + +/** + * In case there are no other implementations we will fallback to this implementation. + * + * To make sure that we load this extension last (or at least very late) we use a negative ordinal. + * This allows custom implementation to be "first" + */ +@Extension(ordinal=-9999) +public class DefaultSimplePageDecorator extends SimplePageDecorator { +} diff --git a/core/src/main/java/jenkins/model/DownloadSettings.java b/core/src/main/java/jenkins/model/DownloadSettings.java index e4c78c2ef3fe1cc54270b6b55caa895733745052..6f5e6aeabbad6a753a09de3a11483c3b9b48f545 100644 --- a/core/src/main/java/jenkins/model/DownloadSettings.java +++ b/core/src/main/java/jenkins/model/DownloadSettings.java @@ -30,6 +30,7 @@ import hudson.model.AdministrativeMonitor; import hudson.model.AsyncPeriodicWork; import hudson.model.DownloadService; import hudson.model.DownloadService.Downloadable; +import hudson.model.PersistentDescriptor; import hudson.model.TaskListener; import hudson.model.UpdateSite; import hudson.util.FormValidation; @@ -42,6 +43,8 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; +import javax.annotation.Nonnull; + /** * Lets user configure how metadata files should be downloaded. * @see UpdateSite @@ -49,18 +52,14 @@ import org.kohsuke.stapler.HttpResponse; */ @Restricted(NoExternalUse.class) // no clear reason for this to be an API @Extension @Symbol("downloadSettings") -public final class DownloadSettings extends GlobalConfiguration { +public final class DownloadSettings extends GlobalConfiguration implements PersistentDescriptor { - public static DownloadSettings get() { - return Jenkins.getInstance().getInjector().getInstance(DownloadSettings.class); + public static @Nonnull DownloadSettings get() { + return GlobalConfiguration.all().getInstance(DownloadSettings.class); } private boolean useBrowser = false; - public DownloadSettings() { - load(); - } - public boolean isUseBrowser() { return useBrowser; } @@ -70,19 +69,19 @@ public final class DownloadSettings extends GlobalConfiguration { save(); } - @Override public GlobalConfigurationCategory getCategory() { + @Override public @Nonnull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } public static boolean usePostBack() { - return get().isUseBrowser() && Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER); + return get().isUseBrowser() && Jenkins.get().hasPermission(Jenkins.ADMINISTER); } public static void checkPostBackAccess() throws AccessDeniedException { if (!get().isUseBrowser()) { throw new AccessDeniedException("browser-based download disabled"); } - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); } @Extension @Symbol("updateCenterCheck") @@ -106,7 +105,7 @@ public final class DownloadSettings extends GlobalConfiguration { return; } boolean due = false; - for (UpdateSite site : Jenkins.getInstance().getUpdateCenter().getSites()) { + for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) { if (site.isDue()) { due = true; break; @@ -128,7 +127,7 @@ public final class DownloadSettings extends GlobalConfiguration { return; } // This checks updates of the update sites and downloadables. - HttpResponse rsp = Jenkins.getInstance().getPluginManager().doCheckUpdatesServer(); + HttpResponse rsp = Jenkins.get().getPluginManager().doCheckUpdatesServer(); if (rsp instanceof FormValidation) { listener.error(((FormValidation) rsp).renderHtml()); } diff --git a/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java b/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java index a4aa432e20765e28010cbed48405bf95dbef72f2..9c6635ab05e1a4ae09898adaf722e5d6b1a58779 100644 --- a/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java @@ -21,7 +21,7 @@ public class GlobalCloudConfiguration extends GlobalConfiguration { @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { - Jenkins.getInstance().clouds.rebuildHetero(req,json, Cloud.all(), "cloud"); + Jenkins.get().clouds.rebuildHetero(req,json, Cloud.all(), "cloud"); return true; } catch (IOException e) { throw new FormException(e,"clouds"); diff --git a/core/src/main/java/jenkins/model/GlobalConfiguration.java b/core/src/main/java/jenkins/model/GlobalConfiguration.java index f751404bed11274f520ea1037a00775b0d763f1c..76320768b0d5cd1f1b2f92f9f0de0f25d27a578e 100644 --- a/core/src/main/java/jenkins/model/GlobalConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalConfiguration.java @@ -7,6 +7,8 @@ import hudson.model.Descriptor; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; +import javax.annotation.Nonnull; + /** * Convenient base class for extensions that contributes to the system configuration page but nothing * else, or to manage the global configuration of a plugin implementing several extension points. @@ -69,8 +71,8 @@ public abstract class GlobalConfiguration extends Descriptor all() { - return Jenkins.getInstance().getDescriptorList(GlobalConfiguration.class); + public static @Nonnull ExtensionList all() { + return Jenkins.get().getDescriptorList(GlobalConfiguration.class); // pointless type parameters help work around bugs in javac in earlier versions http://codepad.org/m1bbFRrH } } diff --git a/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java b/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java index d46329a404882bfff7d5505cb7509fe51a60c97b..5164f171e468741212c2da86358a350ef203b011 100644 --- a/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java +++ b/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java @@ -4,10 +4,10 @@ import hudson.Extension; import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.model.ModelObject; -import hudson.security.*; -import hudson.security.Messages; import org.jenkinsci.Symbol; +import javax.annotation.Nonnull; + /** * Grouping of related {@link GlobalConfiguration}s. * @@ -41,8 +41,12 @@ public abstract class GlobalConfigurationCategory implements ExtensionPoint, Mod return ExtensionList.lookup(GlobalConfigurationCategory.class); } - public static T get(Class type) { - return all().get(type); + public static @Nonnull T get(Class type) { + T category = all().get(type); + if(category == null){ + throw new AssertionError("Category not found. It seems the " + type + " is not annotated with @Extension and so not registered"); + } + return category; } /** diff --git a/core/src/main/java/jenkins/model/GlobalNodePropertiesConfiguration.java b/core/src/main/java/jenkins/model/GlobalNodePropertiesConfiguration.java index 68bfc2bf0127c96a330964f06e5f988fb3e35635..b4cd78f46a70e5b5771c105ea5ee4b6e0b760505 100644 --- a/core/src/main/java/jenkins/model/GlobalNodePropertiesConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalNodePropertiesConfiguration.java @@ -19,7 +19,7 @@ public class GlobalNodePropertiesConfiguration extends GlobalConfiguration { @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); JSONObject np = json.getJSONObject("globalNodeProperties"); if (!np.isNullObject()) { j.getGlobalNodeProperties().rebuild(req, np, NodeProperty.for_(j)); diff --git a/core/src/main/java/jenkins/model/GlobalPluginConfiguration.java b/core/src/main/java/jenkins/model/GlobalPluginConfiguration.java index a706573c0782ef2fee4c43899b1218abb93719a4..0454001f2a75f957d9ec24d26b42bf5cc62862e8 100644 --- a/core/src/main/java/jenkins/model/GlobalPluginConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalPluginConfiguration.java @@ -24,7 +24,7 @@ public class GlobalPluginConfiguration extends GlobalConfiguration { public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { for( JSONObject o : StructuredForm.toList(json, "plugin")) - Jenkins.getInstance().pluginManager.getPlugin(o.getString("name")).getPlugin().configure(req, o); + Jenkins.get().pluginManager.getPlugin(o.getString("name")).getPlugin().configure(req, o); return true; } catch (IOException | ServletException e) { throw new FormException(e,"plugin"); diff --git a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java index ca07798af16ff9462546b704a6a7936f0da6e2ee..2502df1b6e1a98b99945ea20646176cd6666e488 100644 --- a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java @@ -41,13 +41,13 @@ public class GlobalProjectNamingStrategyConfiguration extends GlobalConfiguratio @Override public boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException { // for compatibility reasons, the actual value is stored in Jenkins - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); final JSONObject optJSONObject = json.optJSONObject("useProjectNamingStrategy"); if (optJSONObject != null) { final JSONObject strategyObject = optJSONObject.getJSONObject("namingStrategy"); final String className = strategyObject.getString("$class"); try { - Class clazz = Class.forName(className, true, Jenkins.getInstance().getPluginManager().uberClassLoader); + Class clazz = Class.forName(className, true, j.getPluginManager().uberClassLoader); final ProjectNamingStrategy strategy = (ProjectNamingStrategy) req.bindJSON(clazz, strategyObject); j.setProjectNamingStrategy(strategy); } catch (ClassNotFoundException e) { diff --git a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java index cf1001793ac53d3d4e4fe4fc036980388852ae29..b0cdd84d3d3dd9916eaa89713ced86300312e90c 100644 --- a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java @@ -38,7 +38,7 @@ import java.io.IOException; @Extension(ordinal=400) @Symbol("quietPeriod") public class GlobalQuietPeriodConfiguration extends GlobalConfiguration { public int getQuietPeriod() { - return Jenkins.getInstance().getQuietPeriod(); + return Jenkins.get().getQuietPeriod(); } @Override @@ -51,7 +51,7 @@ public class GlobalQuietPeriodConfiguration extends GlobalConfiguration { } try { // for compatibility reasons, this value is stored in Jenkins - Jenkins.getInstance().setQuietPeriod(i); + Jenkins.get().setQuietPeriod(i); return true; } catch (IOException e) { throw new FormException(e,"quietPeriod"); diff --git a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java index 3e4d3afafa3a28d92c56f776755d7e2bbdbb8698..32d0c155c600f7bf3129c2e15be282bdfc7908a4 100644 --- a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java @@ -39,14 +39,14 @@ import java.io.IOException; @Extension(ordinal=395) @Symbol("scmRetryCount") public class GlobalSCMRetryCountConfiguration extends GlobalConfiguration { public int getScmCheckoutRetryCount() { - return Jenkins.getInstance().getScmCheckoutRetryCount(); + return Jenkins.get().getScmCheckoutRetryCount(); } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { // for compatibility reasons, this value is stored in Jenkins - Jenkins.getInstance().setScmCheckoutRetryCount(json.getInt("scmCheckoutRetryCount")); + Jenkins.get().setScmCheckoutRetryCount(json.getInt("scmCheckoutRetryCount")); return true; } catch (IOException e) { throw new FormException(e,"quietPeriod"); diff --git a/core/src/main/java/jenkins/model/InvalidBuildsDir.java b/core/src/main/java/jenkins/model/InvalidBuildsDir.java new file mode 100644 index 0000000000000000000000000000000000000000..7e9b8f348dc7a56935058a3c50900dfa82f46c7f --- /dev/null +++ b/core/src/main/java/jenkins/model/InvalidBuildsDir.java @@ -0,0 +1,16 @@ +package jenkins.model; + +import hudson.util.BootFailure; + +public class InvalidBuildsDir extends BootFailure { + private String message; + + public InvalidBuildsDir(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 01951e03f9ba682735ffd80d1e305c1d071960c8..c5ccf765f3c9cfb293df22dec5c980add0ef6a01 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -27,6 +27,7 @@ package jenkins.model; import antlr.ANTLRException; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; @@ -37,6 +38,7 @@ import hudson.*; import hudson.Launcher.LocalLauncher; import jenkins.AgentProtocol; import jenkins.diagnostics.URICheckEncodingMonitor; +import jenkins.security.RedactSecretJsonInErrorMessageSanitizer; import jenkins.util.SystemProperties; import hudson.cli.declarative.CLIMethod; import hudson.cli.declarative.CLIResolver; @@ -290,6 +292,7 @@ import static hudson.init.InitMilestone.*; import hudson.init.Initializer; import hudson.util.LogTaskListener; import static java.util.logging.Level.*; +import javax.annotation.Nonnegative; import static javax.servlet.http.HttpServletResponse.*; import org.kohsuke.stapler.WebMethod; @@ -326,6 +329,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * The Jenkins instance startup type i.e. NEW, UPGRADE etc */ + private String installStateName; + + @Deprecated private InstallState installState; /** @@ -398,7 +404,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * This value will be variable-expanded as per {@link #expandVariablesForDirectory}. * @see #getWorkspaceFor(TopLevelItem) */ - private String workspaceDir = "${ITEM_ROOTDIR}/"+WORKSPACE_DIRNAME; + private String workspaceDir = OLD_DEFAULT_WORKSPACES_DIR; /** * Root directory for the builds. @@ -792,7 +798,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Secret key generated once and used for a long time, beyond - * container start/stop. Persisted outside config.xml to avoid + * container start/stop. Persisted outside {@code config.xml} to avoid * accidental exposure. */ private transient final String secretKey; @@ -845,7 +851,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (!new File(root,"jobs").exists()) { // if this is a fresh install, use more modern default layout that's consistent with agents - workspaceDir = "${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}"; + workspaceDir = DEFAULT_WORKSPACES_DIR; } // doing this early allows InitStrategy to set environment upfront @@ -888,8 +894,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (pluginManager==null) pluginManager = PluginManager.createDefault(this); this.pluginManager = pluginManager; + WebApp webApp = WebApp.get(servletContext); // JSON binding needs to be able to see all the classes from all the plugins - WebApp.get(servletContext).setClassLoader(pluginManager.uberClassLoader); + webApp.setClassLoader(pluginManager.uberClassLoader); + webApp.setJsonInErrorMessageSanitizer(RedactSecretJsonInErrorMessageSanitizer.INSTANCE); adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit.DAYS.toMillis(365)); @@ -917,9 +925,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if(KILL_AFTER_LOAD) // TODO cleanUp? System.exit(0); - - setupWizard = new SetupWizard(); - getInstallState().initializeState(); + save(); launchTcpSlaveAgentListener(); @@ -1012,24 +1018,24 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @return The Jenkins {@link jenkins.install.InstallState install state}. */ @Nonnull - @Restricted(NoExternalUse.class) public InstallState getInstallState() { - if (installState == null || installState.name() == null) { - return InstallState.UNKNOWN; + if (installState != null) { + installStateName = installState.name(); + installState = null; } - return installState; + InstallState is = installStateName != null ? InstallState.valueOf(installStateName) : InstallState.UNKNOWN; + return is != null ? is : InstallState.UNKNOWN; } /** * Update the current install state. This will invoke state.initializeState() * when the state has been transitioned. */ - @Restricted(NoExternalUse.class) public void setInstallState(@Nonnull InstallState newState) { - InstallState prior = installState; - installState = newState; - LOGGER.log(Main.isDevelopmentMode ? Level.INFO : Level.FINE, "Install state transitioning from: {0} to : {1}", new Object[] { prior, installState }); - if (!newState.equals(prior)) { + String prior = installStateName; + installStateName = newState.name(); + LOGGER.log(Main.isDevelopmentMode ? Level.INFO : Level.FINE, "Install state transitioning from: {0} to : {1}", new Object[] { prior, installStateName }); + if (!installStateName.equals(prior)) { getSetupWizard().onInstallStateUpdate(newState); newState.initializeState(); } @@ -1505,6 +1511,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * If the descriptor is missing. * @since 1.326 */ + @Nonnull public Descriptor getDescriptorOrDie(Class type) { Descriptor d = getDescriptor(type); if (d==null) @@ -1554,7 +1561,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Gets the plugin object from its short name. - * This allows URL hudson/plugin/ID to be served by the views + * This allows URL {@code hudson/plugin/ID} to be served by the views * of the plugin class. * @param shortName Short name of the plugin * @return The plugin singleton or {@code null} if for some reason the plugin is not loaded. @@ -2136,46 +2143,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return FormValidation.validateNonNegativeInteger(value); } - public FormValidation doCheckRawBuildsDir(@QueryParameter String value) { - // do essentially what expandVariablesForDirectory does, without an Item - String replacedValue = expandVariablesForDirectory(value, - "doCheckRawBuildsDir-Marker:foo", - Jenkins.getInstance().getRootDir().getPath() + "/jobs/doCheckRawBuildsDir-Marker$foo"); - - File replacedFile = new File(replacedValue); - if (!replacedFile.isAbsolute()) { - return FormValidation.error(value + " does not resolve to an absolute path"); - } - - if (!replacedValue.contains("doCheckRawBuildsDir-Marker")) { - return FormValidation.error(value + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); - } - - if (replacedValue.contains("doCheckRawBuildsDir-Marker:foo")) { - // make sure platform can handle colon - try { - File tmp = File.createTempFile("Jenkins-doCheckRawBuildsDir", "foo:bar"); - tmp.delete(); - } catch (IOException e) { - return FormValidation.error(value + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); - } - } - - File d = new File(replacedValue); - if (!d.isDirectory()) { - // if dir does not exist (almost guaranteed) need to make sure nearest existing ancestor can be written to - d = d.getParentFile(); - while (!d.exists()) { - d = d.getParentFile(); - } - if (!d.canWrite()) { - return FormValidation.error(value + " does not exist and probably cannot be created"); - } - } - - return FormValidation.ok(); - } - // to route /descriptor/FQCN/xxx to getDescriptor(FQCN).xxx public Object getDynamic(String token) { return Jenkins.getInstance().getDescriptor(token); @@ -2403,6 +2370,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return DEFAULT_BUILDS_DIR.equals(buildsDir); } + @Restricted(NoExternalUse.class) + boolean isDefaultWorkspaceDir() { + return OLD_DEFAULT_WORKSPACES_DIR.equals(workspaceDir) || DEFAULT_WORKSPACES_DIR.equals(workspaceDir); + } + private File expandVariablesForDirectory(String base, Item item) { return new File(expandVariablesForDirectory(base, item.getFullName(), item.getRootDir().getPath())); } @@ -2445,11 +2417,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @Override public Callable getClockDifferenceCallable() { - return new MasterToSlaveCallable() { - public ClockDifference call() throws IOException { - return new ClockDifference(0); - } - }; + return new ClockDifferenceCallable(); + } + private static class ClockDifferenceCallable extends MasterToSlaveCallable { + @Override + public ClockDifference call() throws IOException { + return new ClockDifference(0); + } } /** @@ -2574,7 +2548,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * * @since 1.433 */ - public Injector getInjector() { + public @CheckForNull Injector getInjector() { return lookup(Injector.class); } @@ -2611,7 +2585,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Can be an empty list but never null. */ @SuppressWarnings({"unchecked"}) - public ,D extends Descriptor> DescriptorExtensionList getDescriptorList(Class type) { + public @Nonnull ,D extends Descriptor> DescriptorExtensionList getDescriptorList(Class type) { return descriptorLists.computeIfAbsent(type, key -> DescriptorExtensionList.createDescriptorList(this, key)); } @@ -2713,7 +2687,16 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return initLevel; } - public void setNumExecutors(int n) throws IOException { + /** + * Sets a number of executors. + * @param n Number of executors + * @throws IOException Failed to save the configuration + * @throws IllegalArgumentException Negative value has been passed + */ + public void setNumExecutors(@Nonnegative int n) throws IOException, IllegalArgumentException { + if (n < 0) { + throw new IllegalArgumentException("Incorrect field \"# of executors\": " + n +". It should be a non-negative number."); + } if (this.numExecutors != n) { this.numExecutors = n; updateComputerList(); @@ -3025,6 +3008,87 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // load from disk cfg.unmarshal(Jenkins.this); } + + try { + checkRawBuildsDir(buildsDir); + setBuildsAndWorkspacesDir(); + } catch (InvalidBuildsDir invalidBuildsDir) { + throw new IOException(invalidBuildsDir); + } + + } + + private void setBuildsAndWorkspacesDir() throws IOException, InvalidBuildsDir { + boolean mustSave = false; + String newBuildsDir = SystemProperties.getString(BUILDS_DIR_PROP); + if (newBuildsDir != null && !buildsDir.equals(newBuildsDir)) { + + checkRawBuildsDir(newBuildsDir); + LOGGER.log(Level.WARNING, "Changing builds directories from {0} to {1}. Beware that no automated data migration will occur.", + new String[]{buildsDir, newBuildsDir}); + buildsDir = newBuildsDir; + mustSave = true; + } else if (!isDefaultBuildDir()) { + LOGGER.log(Level.INFO, "Using non default builds directories: {0}.", buildsDir); + } + + String newWorkspacesDir = SystemProperties.getString(WORKSPACES_DIR_PROP); + if (newWorkspacesDir != null && !workspaceDir.equals(newWorkspacesDir)) { + LOGGER.log(Level.WARNING, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", + new String[]{workspaceDir, newWorkspacesDir}); + workspaceDir = newWorkspacesDir; + mustSave = true; + } else if (!isDefaultWorkspaceDir()) { + LOGGER.log(Level.INFO, "Using non default workspaces directories: {0}.", workspaceDir); + } + + if (mustSave) { + save(); + } + } + + /** + * Checks the correctness of the newBuildsDirValue for use as {@link #buildsDir}. + * @param newBuildsDirValue the candidate newBuildsDirValue for updating {@link #buildsDir}. + */ + @VisibleForTesting + /*private*/ static void checkRawBuildsDir(String newBuildsDirValue) throws InvalidBuildsDir { + + // do essentially what expandVariablesForDirectory does, without an Item + String replacedValue = expandVariablesForDirectory(newBuildsDirValue, + "doCheckRawBuildsDir-Marker:foo", + Jenkins.getInstance().getRootDir().getPath() + "/jobs/doCheckRawBuildsDir-Marker$foo"); + + File replacedFile = new File(replacedValue); + if (!replacedFile.isAbsolute()) { + throw new InvalidBuildsDir(newBuildsDirValue + " does not resolve to an absolute path"); + } + + if (!replacedValue.contains("doCheckRawBuildsDir-Marker")) { + throw new InvalidBuildsDir(newBuildsDirValue + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); + } + + if (replacedValue.contains("doCheckRawBuildsDir-Marker:foo")) { + // make sure platform can handle colon + try { + File tmp = File.createTempFile("Jenkins-doCheckRawBuildsDir", "foo:bar"); + tmp.delete(); + } catch (IOException e) { + throw new InvalidBuildsDir(newBuildsDirValue + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); + } + } + + File d = new File(replacedValue); + if (!d.isDirectory()) { + // if dir does not exist (almost guaranteed) need to make sure nearest existing ancestor can be written to + d = d.getParentFile(); + while (!d.exists()) { + d = d.getParentFile(); + } + if (!d.canWrite()) { + throw new InvalidBuildsDir(newBuildsDirValue + " does not exist and probably cannot be created"); + } + } } private synchronized TaskBuilder loadTasks() throws IOException { @@ -3136,6 +3200,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // auto register root actions for (Action a : getExtensionList(RootAction.class)) if (!actions.contains(a)) actions.add(a); + + setupWizard = new SetupWizard(); + getInstallState().initializeState(); } }); @@ -3625,9 +3692,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve JSONObject json = req.getSubmittedForm(); - workspaceDir = json.getString("rawWorkspaceDir"); - buildsDir = json.getString("rawBuildsDir"); - systemMessage = Util.nullify(req.getParameter("system_message")); boolean result = true; @@ -4136,7 +4200,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // give some time for the browser to load the "reloading" page Thread.sleep(5000); - LOGGER.severe(String.format("Restarting VM as requested by %s",exitUser)); + LOGGER.info(String.format("Restarting VM as requested by %s",exitUser)); for (RestartListener listener : RestartListener.all()) listener.onRestart(); lifecycle.restart(); @@ -4173,7 +4237,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // give some time for the browser to load the "reloading" page LOGGER.info("Restart in 10 seconds"); Thread.sleep(10000); - LOGGER.severe(String.format("Restarting VM as requested by %s",exitUser)); + LOGGER.info(String.format("Restarting VM as requested by %s",exitUser)); for (RestartListener listener : RestartListener.all()) listener.onRestart(); lifecycle.restart(); @@ -4220,8 +4284,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @RequirePOST public void doExit( StaplerRequest req, StaplerResponse rsp ) throws IOException { checkPermission(ADMINISTER); - LOGGER.severe(String.format("Shutting down VM as requested by %s from %s", - getAuthentication().getName(), req!=null?req.getRemoteAddr():"???")); if (rsp!=null) { rsp.setStatus(HttpServletResponse.SC_OK); rsp.setContentType("text/plain"); @@ -4230,10 +4292,22 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } } - cleanUp(); - System.exit(0); - } + new Thread("exit thread") { + @Override + public void run() { + try { + ACL.impersonate(ACL.SYSTEM); + LOGGER.info(String.format("Shutting down VM as requested by %s from %s", + getAuthentication().getName(), req != null ? req.getRemoteAddr() : "???")); + cleanUp(); + System.exit(0); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Failed to shut down Jenkins", e); + } + } + }.start(); + } /** * Shutdown the system safely. @@ -4251,7 +4325,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve public void run() { try { ACL.impersonate(ACL.SYSTEM); - LOGGER.severe(String.format("Shutting down VM as requested by %s from %s", + LOGGER.info(String.format("Shutting down VM as requested by %s from %s", exitUser, exitAddr)); // Wait 'til we have no active executors. doQuietDown(true, 0); @@ -4558,7 +4632,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } /** - * Exposes the current user to /me URL. + * Exposes the current user to {@code /me} URL. */ public User getMe() { User u = User.current(); @@ -4885,7 +4959,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } String ver = props.getProperty("version"); if(ver==null) ver = UNCOMPUTED_VERSION; - if(Main.isDevelopmentMode && "${build.version}".equals(ver)) { + if(Main.isDevelopmentMode && "${project.version}".equals(ver)) { // in dev mode, unable to get version (ahem Eclipse) try { File dir = new File(".").getAbsoluteFile(); @@ -4970,7 +5044,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (idx > 0) { return new VersionNumber(versionString.substring(0,idx)); } - } catch (NumberFormatException _) { + } catch (NumberFormatException ignored) { // fall through } @@ -5044,6 +5118,29 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @see #getRawBuildsDir() */ private static final String DEFAULT_BUILDS_DIR = "${ITEM_ROOTDIR}/builds"; + /** + * Old layout for workspaces. + * @see #DEFAULT_WORKSPACES_DIR + */ + private static final String OLD_DEFAULT_WORKSPACES_DIR = "${ITEM_ROOTDIR}/" + WORKSPACE_DIRNAME; + + /** + * Default value for the workspace's directories layout. + * @see #workspaceDir + */ + private static final String DEFAULT_WORKSPACES_DIR = "${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}"; + + /** + * System property name to set {@link #buildsDir}. + * @see #getRawBuildsDir() + */ + static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".buildsDir"; + + /** + * System property name to set {@link #workspaceDir}. + * @see #getRawWorkspaceDir() + */ + static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".workspacesDir"; /** * Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created. diff --git a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java index b7390ad3d3ee4baf5731090dd2b2bb1965de49df..263b482b0f5a443bb811994100b59f96a50188b8 100644 --- a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java +++ b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java @@ -3,9 +3,12 @@ package jenkins.model; import hudson.Extension; import hudson.Util; import hudson.XmlFile; +import hudson.model.PersistentDescriptor; import hudson.util.FormValidation; import hudson.util.XStream2; import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; import javax.mail.internet.AddressException; @@ -29,7 +32,7 @@ import javax.annotation.Nonnull; * @since 1.494 */ @Extension @Symbol("location") -public class JenkinsLocationConfiguration extends GlobalConfiguration { +public class JenkinsLocationConfiguration extends GlobalConfiguration implements PersistentDescriptor { /** * @deprecated replaced by {@link #jenkinsUrl} */ @@ -41,18 +44,20 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { // just to suppress warnings private transient String charset,useSsl; + public static @Nonnull JenkinsLocationConfiguration get() { + return GlobalConfiguration.all().getInstance(JenkinsLocationConfiguration.class); + } + /** - * Gets local configuration. - * - * @return {@code null} if the {@link GlobalConfiguration#all()} list does not contain this extension. - * Most likely it means that the Jenkins instance has not been fully loaded yet. + * Gets local configuration. For explanation when it could die, see {@link #get()} */ - public static @CheckForNull JenkinsLocationConfiguration get() { - return GlobalConfiguration.all().get(JenkinsLocationConfiguration.class); - } - - public JenkinsLocationConfiguration() { - load(); + @Restricted(NoExternalUse.class) + public static @Nonnull JenkinsLocationConfiguration getOrDie(){ + JenkinsLocationConfiguration config = JenkinsLocationConfiguration.get(); + if (config == null) { + throw new IllegalStateException("JenkinsLocationConfiguration instance is missing. Probably the Jenkins instance is not fully loaded at this time."); + } + return config; } @Override @@ -63,7 +68,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { if(!file.exists()) { XStream2 xs = new XStream2(); xs.addCompatibilityAlias("hudson.tasks.Mailer$DescriptorImpl",JenkinsLocationConfiguration.class); - file = new XmlFile(xs,new File(Jenkins.getInstance().getRootDir(),"hudson.tasks.Mailer.xml")); + file = new XmlFile(xs,new File(Jenkins.get().getRootDir(),"hudson.tasks.Mailer.xml")); if (file.exists()) { try { file.unmarshal(this); @@ -125,7 +130,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { */ private void updateSecureSessionFlag() { try { - ServletContext context = Jenkins.getInstance().servletContext; + ServletContext context = Jenkins.get().servletContext; Method m; try { m = context.getClass().getMethod("getSessionCookieConfig"); @@ -152,7 +157,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { } /** - * Checks the URL in global.jelly + * Checks the URL in {@code global.jelly} */ public FormValidation doCheckUrl(@QueryParameter String value) { if(value.startsWith("http://localhost")) diff --git a/core/src/main/java/jenkins/model/MasterBuildConfiguration.java b/core/src/main/java/jenkins/model/MasterBuildConfiguration.java index 6ae797adf2a0addc4c7984d498292b539267f099..e656a6d9bf4eeb92553b18d8020b5581cbdc13ae 100644 --- a/core/src/main/java/jenkins/model/MasterBuildConfiguration.java +++ b/core/src/main/java/jenkins/model/MasterBuildConfiguration.java @@ -39,18 +39,23 @@ import java.io.IOException; @Extension(ordinal=500) @Symbol("masterBuild") public class MasterBuildConfiguration extends GlobalConfiguration { public int getNumExecutors() { - return Jenkins.getInstance().getNumExecutors(); + return Jenkins.get().getNumExecutors(); } public String getLabelString() { - return Jenkins.getInstance().getLabelString(); + return Jenkins.get().getLabelString(); } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); try { // for compatibility reasons, this value is stored in Jenkins + String num = json.getString("numExecutors"); + if (!num.matches("\\d+")) { + throw new FormException(Messages.Hudson_Computer_IncorrectNumberOfExecutors(),"numExecutors"); + } + j.setNumExecutors(json.getInt("numExecutors")); if (req.hasParameter("master.mode")) j.setMode(Mode.valueOf(req.getParameter("master.mode"))); diff --git a/core/src/main/java/jenkins/model/NewViewLink.java b/core/src/main/java/jenkins/model/NewViewLink.java index 5f4e404167ca96b2f2c0c9e6ebdd96b43e751e1e..618dc826dfdc125064f13c5709127e25d515ccde 100644 --- a/core/src/main/java/jenkins/model/NewViewLink.java +++ b/core/src/main/java/jenkins/model/NewViewLink.java @@ -9,10 +9,10 @@ import java.util.Collections; import java.util.List; import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; @Extension -@Restricted(DoNotUse.class) +@Restricted(NoExternalUse.class) public class NewViewLink extends TransientViewActionFactory { @VisibleForTesting diff --git a/core/src/main/java/jenkins/model/Nodes.java b/core/src/main/java/jenkins/model/Nodes.java index 2b1116bb86b1f802bb356f45bbe15886c74fb648..24f9ac7431c0adef6cbd72dc6c11402b53087423 100644 --- a/core/src/main/java/jenkins/model/Nodes.java +++ b/core/src/main/java/jenkins/model/Nodes.java @@ -127,7 +127,8 @@ public class Nodes implements Saveable { * @throws IOException if the list of nodes could not be persisted. */ public void addNode(final @Nonnull Node node) throws IOException { - if (node != nodes.get(node.getNodeName())) { + Node oldNode = nodes.get(node.getNodeName()); + if (node != oldNode) { // TODO we should not need to lock the queue for adding nodes but until we have a way to update the // computer list for just the new node Queue.withLock(new Runnable() { @@ -139,7 +140,21 @@ public class Nodes implements Saveable { } }); // TODO there is a theoretical race whereby the node instance is updated/removed after lock release - persistNode(node); + try { + persistNode(node); + } catch (IOException | RuntimeException e) { + // JENKINS-50599: If persisting the node throws an exception, we need to remove the node from + // memory before propagating the exception. + Queue.withLock(new Runnable() { + @Override + public void run() { + nodes.compute(node.getNodeName(), (ignoredNodeName, ignoredNode) -> oldNode); + jenkins.updateComputerList(); + jenkins.trimLabels(); + } + }); + throw e; + } NodeListener.fireOnCreated(node); } } diff --git a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java index 55301e4f8636ae60f62433fc8059daf5be053a41..e83f3526efb6614e2b63ee6c75efaaab77cdcd4d 100644 --- a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java +++ b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java @@ -54,6 +54,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; import javax.servlet.ServletException; import static javax.servlet.http.HttpServletResponse.SC_CREATED; @@ -190,7 +191,7 @@ public abstract class ParameterizedJobMixIn & Param @SuppressWarnings("deprecation") public final void doBuild(StaplerRequest req, StaplerResponse rsp, @QueryParameter TimeDuration delay) throws IOException, ServletException { if (delay == null) { - delay = new TimeDuration(asJob().getQuietPeriod()); + delay=new TimeDuration(TimeUnit.MILLISECONDS.convert(asJob().getQuietPeriod(), TimeUnit.SECONDS)); } if (!asJob().isBuildable()) { diff --git a/core/src/main/java/jenkins/model/RenameAction.java b/core/src/main/java/jenkins/model/RenameAction.java new file mode 100644 index 0000000000000000000000000000000000000000..45ddbd5290401125c247e1da57462a5d00f8971e --- /dev/null +++ b/core/src/main/java/jenkins/model/RenameAction.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright 2018 CloudBees, Inc. + * + * 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; + +import hudson.Extension; +import hudson.model.AbstractItem; +import hudson.model.Action; +import java.util.Collection; +import java.util.Collections; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +@Restricted(NoExternalUse.class) +public class RenameAction implements Action { + + @Override + public String getIconFileName() { + return "notepad.png"; + } + + @Override + public String getDisplayName() { + return "Rename"; + } + + @Override + public String getUrlName() { + return "confirm-rename"; + } + + @Extension + public static class TransientActionFactoryImpl extends TransientActionFactory { + + @Override + public Class type() { + return AbstractItem.class; + } + + @Override + public Collection createFor(AbstractItem target) { + if (target.isNameEditable()) { + return Collections.singleton(new RenameAction()); + } else { + return Collections.emptyList(); + } + } + } +} diff --git a/core/src/main/java/jenkins/model/SimplePageDecorator.java b/core/src/main/java/jenkins/model/SimplePageDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3b0682bda322cf6770912fce140a8aa930fccd --- /dev/null +++ b/core/src/main/java/jenkins/model/SimplePageDecorator.java @@ -0,0 +1,72 @@ +/* + * The MIT License + * + * Copyright 2018, CloudBees, Inc. + * + * 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; + +import hudson.DescriptorExtensionList; +import hudson.ExtensionPoint; +import hudson.model.Describable; +import hudson.model.Descriptor; +/** + * Participates in the rendering of the login page + * + *

    + * This class provides a few hooks to augment the HTML of the login page. + * + * @since 2.128 + */ +public class SimplePageDecorator extends Descriptor implements ExtensionPoint, Describable { + + protected SimplePageDecorator() { + super(self()); + } + + @Override + public final Descriptor getDescriptor() { + return this; + } + /** + * Obtains the URL of this object, excluding the context path. + * + *

    + * Every {@link SimplePageDecorator} is bound to URL via {@link Jenkins#getDescriptor()}. + * This method returns such an URL. + */ + public final String getUrl() { + return "descriptor/"+clazz.getName(); + } + + /** + * The first found LoginDecarator, there can only be one. + * @return the first found {@link SimplePageDecorator} + */ + public static SimplePageDecorator first(){ + DescriptorExtensionList descriptorList = Jenkins.getInstanceOrNull().getDescriptorList(SimplePageDecorator.class); + if (descriptorList.size() >= 1) { + return descriptorList.get(0); + } else { + return null; + } + } + +} diff --git a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java index a1f2eaecc32ba686bb4af7ed261c581a57ba8bb8..b1d7ae6afe98f60466a97dd35a8ea6ef86b26495 100644 --- a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java +++ b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java @@ -29,10 +29,9 @@ import hudson.model.RunMap; import java.io.File; import java.io.IOException; import java.util.AbstractMap; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -41,6 +40,8 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.CheckForNull; + +import jenkins.util.MemoryReductionUtil; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -189,7 +190,7 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i String[] kids = dir.list(); if (kids == null) { // the job may have just been created - kids = EMPTY_STRING_ARRAY; + kids = MemoryReductionUtil.EMPTY_STRING_ARRAY; } SortedIntList list = new SortedIntList(kids.length / 2); for (String s : kids) { @@ -336,9 +337,9 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i return null; case DESC: // TODO again could be made more efficient - List reversed = new ArrayList(numberOnDisk); - Collections.reverse(reversed); - for (int m : reversed) { + ListIterator iterator = numberOnDisk.listIterator(numberOnDisk.size()); + while(iterator.hasPrevious()) { + int m = iterator.previous(); if (m > n) { continue; } @@ -587,8 +588,6 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i ASC, DESC, EXACT } - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private static final SortedMap EMPTY_SORTED_MAP = Collections.unmodifiableSortedMap(new TreeMap()); static final Logger LOGGER = Logger.getLogger(AbstractLazyLoadRunMap.class.getName()); diff --git a/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java b/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java index 7ed081657283862c29d17d52d16fab93a1b7e914..c99dd40de67399cfadd0538f193fdeef6a7ce5a3 100644 --- a/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java +++ b/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java @@ -39,6 +39,7 @@ import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; /** @@ -61,7 +62,7 @@ public abstract class AsynchronousExecution extends RuntimeException { /** * Initially null, and usually stays null. - * If {@link #completed} is called before {@link #setExecutor}, then either {@link #NULL} for success, or the error. + * If {@link #completed} is called before {@link #setExecutorWithoutCompleting}, then either {@link #NULL} for success, or the error. */ @GuardedBy("this") private @CheckForNull Throwable result; @@ -98,7 +99,7 @@ public abstract class AsynchronousExecution extends RuntimeException { /** * Obtains the associated executor. - * @return Associated Executor. May be {@code null} if {@link #setExecutor(hudson.model.Executor)} + * @return Associated Executor. May be {@code null} if {@link #setExecutorWithoutCompleting(hudson.model.Executor)} * has not been called yet. */ @CheckForNull @@ -106,13 +107,26 @@ public abstract class AsynchronousExecution extends RuntimeException { return executor; } + /** + * Set the executor without notifying it about task completion. + * The caller must also call {@link #maybeComplete()} + * after releasing any problematic locks. + */ @Restricted(NoExternalUse.class) - public synchronized final void setExecutor(@Nonnull Executor executor) { - assert this.executor==null; - + public synchronized final void setExecutorWithoutCompleting(@Nonnull Executor executor) { + assert this.executor == null; this.executor = executor; - if (result!=null) { - executor.completedAsynchronous( result!=NULL ? result : null ); + } + + /** + * If there is a pending completion notification, deliver it to the executor. + * Must be called after {@link #setExecutorWithoutCompleting(Executor)}. + */ + @Restricted(NoExternalUse.class) + public synchronized final void maybeComplete() { + assert this.executor != null; + if (result != null) { + executor.completedAsynchronous(result != NULL ? result : null); result = null; } } diff --git a/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java b/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java index b8d0ebe4a5f0b6b7d8c9afe7ae252d68429cc734..477aa34afd57000f4e638bf079c107ca86739519 100644 --- a/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java +++ b/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java @@ -1,24 +1,23 @@ package jenkins.mvn; import hudson.Extension; +import hudson.model.PersistentDescriptor; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import jenkins.tools.ToolConfigurationCategory; import org.jenkinsci.Symbol; +import javax.annotation.Nonnull; + //as close as it gets to the global Maven Project configuration @Extension(ordinal = 50) @Symbol("maven") -public class GlobalMavenConfig extends GlobalConfiguration { +public class GlobalMavenConfig extends GlobalConfiguration implements PersistentDescriptor { private SettingsProvider settingsProvider; private GlobalSettingsProvider globalSettingsProvider; - public GlobalMavenConfig() { - load(); - } - @Override - public ToolConfigurationCategory getCategory() { + public @Nonnull ToolConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(ToolConfigurationCategory.class); } @@ -40,8 +39,8 @@ public class GlobalMavenConfig extends GlobalConfiguration { return settingsProvider != null ? settingsProvider : new DefaultSettingsProvider(); } - public static GlobalMavenConfig get() { - return GlobalConfiguration.all().get(GlobalMavenConfig.class); + public static @Nonnull GlobalMavenConfig get() { + return GlobalConfiguration.all().getInstance(GlobalMavenConfig.class); } } diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..e23c8f15a1cc7be718660c5f0557b5a1000e4ee4 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java @@ -0,0 +1,2074 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import jenkins.util.MemoryReductionUtil; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.net.IDN; +import java.util.Arrays; +import java.util.Locale; + +/** + *

    Domain name validation routines.

    + * + *

    + * This validator provides methods for validating Internet domain names + * and top-level domains. + *

    + * + *

    Domain names are evaluated according + * to the standards RFC1034, + * section 3, and RFC1123, + * section 2.1. No accommodation is provided for the specialized needs of + * other applications; if the domain name has been URL-encoded, for example, + * validation will fail even though the equivalent plaintext version of the + * same name would have passed. + *

    + * + *

    + * Validation is also provided for top-level domains (TLDs) as defined and + * maintained by the Internet Assigned Numbers Authority (IANA): + *

    + * + *
      + *
    • {@link #isValidInfrastructureTld} - validates infrastructure TLDs + * (.arpa, etc.)
    • + *
    • {@link #isValidGenericTld} - validates generic TLDs + * (.com, .org, etc.)
    • + *
    • {@link #isValidCountryCodeTld} - validates country code TLDs + * (.us, .uk, .cn, etc.)
    • + *
    + * + *

    + * (NOTE: This class does not provide IP address lookup for domain names or + * methods to ensure that a given domain name matches a specific IP; see + * {@link java.net.InetAddress} for that functionality.) + *

    + * + * @version $Revision: 1781829 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class DomainValidator implements Serializable { + + private static final int MAX_DOMAIN_LENGTH = 253; + + private static final long serialVersionUID = -4407125112880174009L; + + // Regular expression strings for hostnames (derived from RFC2396 and RFC 1123) + + // RFC2396: domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + // Max 63 characters + private static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; + + // RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum + // Max 63 characters + private static final String TOP_LABEL_REGEX = "\\p{Alpha}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; + + // RFC2396 hostname = *( domainlabel "." ) toplabel [ "." ] + // Note that the regex currently requires both a domain label and a top level label, whereas + // the RFC does not. This is because the regex is used to detect if a TLD is present. + // If the match fails, input is checked against DOMAIN_LABEL_REGEX (hostnameRegex) + // RFC1123 sec 2.1 allows hostnames to start with a digit + private static final String DOMAIN_NAME_REGEX = + "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")\\.?$"; + + private final boolean allowLocal; + + /** + * Singleton instance of this validator, which + * doesn't consider local addresses as valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false); + + /** + * Singleton instance of this validator, which does + * consider local addresses valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true); + + /** + * RegexValidator for matching domains. + */ + private final RegexValidator domainRegex = + new RegexValidator(DOMAIN_NAME_REGEX); + /** + * RegexValidator for matching a local hostname + */ + // RFC1123 sec 2.1 allows hostnames to start with a digit + private final RegexValidator hostnameRegex = + new RegexValidator(DOMAIN_LABEL_REGEX); + + /** + * Returns the singleton instance of this validator. It + * will not consider local addresses as valid. + * @return the singleton instance of this validator + */ + public static synchronized DomainValidator getInstance() { + inUse = true; + return DOMAIN_VALIDATOR; + } + + /** + * Returns the singleton instance of this validator, + * with local validation as required. + * @param allowLocal Should local addresses be considered valid? + * @return the singleton instance of this validator + */ + public static synchronized DomainValidator getInstance(boolean allowLocal) { + inUse = true; + if(allowLocal) { + return DOMAIN_VALIDATOR_WITH_LOCAL; + } + return DOMAIN_VALIDATOR; + } + + /** Private constructor. */ + private DomainValidator(boolean allowLocal) { + this.allowLocal = allowLocal; + } + + /** + * Returns true if the specified String parses + * as a valid domain name with a recognized top-level domain. + * The parsing is case-insensitive. + * @param domain the parameter to check for domain name syntax + * @return true if the parameter is a valid domain name + */ + public boolean isValid(String domain) { + if (domain == null) { + return false; + } + domain = unicodeToASCII(domain); + // hosts must be equally reachable via punycode and Unicode; + // Unicode is never shorter than punycode, so check punycode + // if domain did not convert, then it will be caught by ASCII + // checks in the regexes below + if (domain.length() > MAX_DOMAIN_LENGTH) { + return false; + } + String[] groups = domainRegex.match(domain); + if (groups != null && groups.length > 0) { + return isValidTld(groups[0]); + } + return allowLocal && hostnameRegex.isValid(domain); + } + + // package protected for unit test access + // must agree with isValidRootUrl() above + final boolean isValidDomainSyntax(String domain) { + if (domain == null) { + return false; + } + domain = unicodeToASCII(domain); + // hosts must be equally reachable via punycode and Unicode; + // Unicode is never shorter than punycode, so check punycode + // if domain did not convert, then it will be caught by ASCII + // checks in the regexes below + if (domain.length() > MAX_DOMAIN_LENGTH) { + return false; + } + String[] groups = domainRegex.match(domain); + return (groups != null && groups.length > 0) + || hostnameRegex.isValid(domain); + } + + /** + * Returns true if the specified String matches any + * IANA-defined top-level domain. Leading dots are ignored if present. + * The search is case-insensitive. + * @param tld the parameter to check for TLD status, not null + * @return true if the parameter is a TLD + */ + public boolean isValidTld(String tld) { + tld = unicodeToASCII(tld); + if(allowLocal && isValidLocalTld(tld)) { + return true; + } + return isValidInfrastructureTld(tld) + || isValidGenericTld(tld) + || isValidCountryCodeTld(tld); + } + + /** + * Returns true if the specified String matches any + * IANA-defined infrastructure top-level domain. Leading dots are + * ignored if present. The search is case-insensitive. + * @param iTld the parameter to check for infrastructure TLD status, not null + * @return true if the parameter is an infrastructure TLD + */ + public boolean isValidInfrastructureTld(String iTld) { + final String key = chompLeadingDot(unicodeToASCII(iTld).toLowerCase(Locale.ENGLISH)); + return arrayContains(INFRASTRUCTURE_TLDS, key); + } + + /** + * Returns true if the specified String matches any + * IANA-defined generic top-level domain. Leading dots are ignored + * if present. The search is case-insensitive. + * @param gTld the parameter to check for generic TLD status, not null + * @return true if the parameter is a generic TLD + */ + public boolean isValidGenericTld(String gTld) { + final String key = chompLeadingDot(unicodeToASCII(gTld).toLowerCase(Locale.ENGLISH)); + return (arrayContains(GENERIC_TLDS, key) || arrayContains(genericTLDsPlus, key)) + && !arrayContains(genericTLDsMinus, key); + } + + /** + * Returns true if the specified String matches any + * IANA-defined country code top-level domain. Leading dots are + * ignored if present. The search is case-insensitive. + * @param ccTld the parameter to check for country code TLD status, not null + * @return true if the parameter is a country code TLD + */ + public boolean isValidCountryCodeTld(String ccTld) { + final String key = chompLeadingDot(unicodeToASCII(ccTld).toLowerCase(Locale.ENGLISH)); + return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(countryCodeTLDsPlus, key)) + && !arrayContains(countryCodeTLDsMinus, key); + } + + /** + * Returns true if the specified String matches any + * widely used "local" domains (localhost or localdomain). Leading dots are + * ignored if present. The search is case-insensitive. + * @param lTld the parameter to check for local TLD status, not null + * @return true if the parameter is an local TLD + */ + public boolean isValidLocalTld(String lTld) { + final String key = chompLeadingDot(unicodeToASCII(lTld).toLowerCase(Locale.ENGLISH)); + return arrayContains(LOCAL_TLDS, key); + } + + private String chompLeadingDot(String str) { + if (str.startsWith(".")) { + return str.substring(1); + } + return str; + } + + // --------------------------------------------- + // ----- TLDs defined by IANA + // ----- Authoritative and comprehensive list at: + // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt + + // Note that the above list is in UPPER case. + // The code currently converts strings to lower case (as per the tables below) + + // IANA also provide an HTML list at http://www.iana.org/domains/root/db + // Note that this contains several country code entries which are NOT in + // the text file. These all have the "Not assigned" in the "Sponsoring Organisation" column + // For example (as of 2015-01-02): + // .bl country-code Not assigned + // .um country-code Not assigned + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] INFRASTRUCTURE_TLDS = new String[] { + "arpa", // internet infrastructure + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] GENERIC_TLDS = new String[] { + // Taken from Version 2017020400, Last Updated Sat Feb 4 07:07:01 2017 UTC + "aaa", // aaa American Automobile Association, Inc. + "aarp", // aarp AARP + "abarth", // abarth Fiat Chrysler Automobiles N.V. + "abb", // abb ABB Ltd + "abbott", // abbott Abbott Laboratories, Inc. + "abbvie", // abbvie AbbVie Inc. + "abc", // abc Disney Enterprises, Inc. + "able", // able Able Inc. + "abogado", // abogado Top Level Domain Holdings Limited + "abudhabi", // abudhabi Abu Dhabi Systems and Information Centre + "academy", // academy Half Oaks, LLC + "accenture", // accenture Accenture plc + "accountant", // accountant dot Accountant Limited + "accountants", // accountants Knob Town, LLC + "aco", // aco ACO Severin Ahlmann GmbH & Co. KG + "active", // active The Active Network, Inc + "actor", // actor United TLD Holdco Ltd. + "adac", // adac Allgemeiner Deutscher Automobil-Club e.V. (ADAC) + "ads", // ads Charleston Road Registry Inc. + "adult", // adult ICM Registry AD LLC + "aeg", // aeg Aktiebolaget Electrolux + "aero", // aero Societe Internationale de Telecommunications Aeronautique (SITA INC USA) + "aetna", // aetna Aetna Life Insurance Company + "afamilycompany", // afamilycompany Johnson Shareholdings, Inc. + "afl", // afl Australian Football League + "agakhan", // agakhan Fondation Aga Khan (Aga Khan Foundation) + "agency", // agency Steel Falls, LLC + "aig", // aig American International Group, Inc. + "aigo", // aigo aigo Digital Technology Co,Ltd. + "airbus", // airbus Airbus S.A.S. + "airforce", // airforce United TLD Holdco Ltd. + "airtel", // airtel Bharti Airtel Limited + "akdn", // akdn Fondation Aga Khan (Aga Khan Foundation) + "alfaromeo", // alfaromeo Fiat Chrysler Automobiles N.V. + "alibaba", // alibaba Alibaba Group Holding Limited + "alipay", // alipay Alibaba Group Holding Limited + "allfinanz", // allfinanz Allfinanz Deutsche Vermögensberatung Aktiengesellschaft + "allstate", // allstate Allstate Fire and Casualty Insurance Company + "ally", // ally Ally Financial Inc. + "alsace", // alsace REGION D ALSACE + "alstom", // alstom ALSTOM + "americanexpress", // americanexpress American Express Travel Related Services Company, Inc. + "americanfamily", // americanfamily AmFam, Inc. + "amex", // amex American Express Travel Related Services Company, Inc. + "amfam", // amfam AmFam, Inc. + "amica", // amica Amica Mutual Insurance Company + "amsterdam", // amsterdam Gemeente Amsterdam + "analytics", // analytics Campus IP LLC + "android", // android Charleston Road Registry Inc. + "anquan", // anquan QIHOO 360 TECHNOLOGY CO. LTD. + "anz", // anz Australia and New Zealand Banking Group Limited + "aol", // aol AOL Inc. + "apartments", // apartments June Maple, LLC + "app", // app Charleston Road Registry Inc. + "apple", // apple Apple Inc. + "aquarelle", // aquarelle Aquarelle.com + "aramco", // aramco Aramco Services Company + "archi", // archi STARTING DOT LIMITED + "army", // army United TLD Holdco Ltd. + "art", // art UK Creative Ideas Limited + "arte", // arte Association Relative à la Télévision Européenne G.E.I.E. + "asda", // asda Wal-Mart Stores, Inc. + "asia", // asia DotAsia Organisation Ltd. + "associates", // associates Baxter Hill, LLC + "athleta", // athleta The Gap, Inc. + "attorney", // attorney United TLD Holdco, Ltd + "auction", // auction United TLD HoldCo, Ltd. + "audi", // audi AUDI Aktiengesellschaft + "audible", // audible Amazon Registry Services, Inc. + "audio", // audio Uniregistry, Corp. + "auspost", // auspost Australian Postal Corporation + "author", // author Amazon Registry Services, Inc. + "auto", // auto Uniregistry, Corp. + "autos", // autos DERAutos, LLC + "avianca", // avianca Aerovias del Continente Americano S.A. Avianca + "aws", // aws Amazon Registry Services, Inc. + "axa", // axa AXA SA + "azure", // azure Microsoft Corporation + "baby", // baby Johnson & Johnson Services, Inc. + "baidu", // baidu Baidu, Inc. + "banamex", // banamex Citigroup Inc. + "bananarepublic", // bananarepublic The Gap, Inc. + "band", // band United TLD Holdco, Ltd + "bank", // bank fTLD Registry Services, LLC + "bar", // bar Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable + "barcelona", // barcelona Municipi de Barcelona + "barclaycard", // barclaycard Barclays Bank PLC + "barclays", // barclays Barclays Bank PLC + "barefoot", // barefoot Gallo Vineyards, Inc. + "bargains", // bargains Half Hallow, LLC + "baseball", // baseball MLB Advanced Media DH, LLC + "basketball", // basketball Fédération Internationale de Basketball (FIBA) + "bauhaus", // bauhaus Werkhaus GmbH + "bayern", // bayern Bayern Connect GmbH + "bbc", // bbc British Broadcasting Corporation + "bbt", // bbt BB&T Corporation + "bbva", // bbva BANCO BILBAO VIZCAYA ARGENTARIA, S.A. + "bcg", // bcg The Boston Consulting Group, Inc. + "bcn", // bcn Municipi de Barcelona + "beats", // beats Beats Electronics, LLC + "beauty", // beauty L'Oréal + "beer", // beer Top Level Domain Holdings Limited + "bentley", // bentley Bentley Motors Limited + "berlin", // berlin dotBERLIN GmbH & Co. KG + "best", // best BestTLD Pty Ltd + "bestbuy", // bestbuy BBY Solutions, Inc. + "bet", // bet Afilias plc + "bharti", // bharti Bharti Enterprises (Holding) Private Limited + "bible", // bible American Bible Society + "bid", // bid dot Bid Limited + "bike", // bike Grand Hollow, LLC + "bing", // bing Microsoft Corporation + "bingo", // bingo Sand Cedar, LLC + "bio", // bio STARTING DOT LIMITED + "biz", // biz Neustar, Inc. + "black", // black Afilias Limited + "blackfriday", // blackfriday Uniregistry, Corp. + "blanco", // blanco BLANCO GmbH + Co KG + "blockbuster", // blockbuster Dish DBS Corporation + "blog", // blog Knock Knock WHOIS There, LLC + "bloomberg", // bloomberg Bloomberg IP Holdings LLC + "blue", // blue Afilias Limited + "bms", // bms Bristol-Myers Squibb Company + "bmw", // bmw Bayerische Motoren Werke Aktiengesellschaft + "bnl", // bnl Banca Nazionale del Lavoro + "bnpparibas", // bnpparibas BNP Paribas + "boats", // boats DERBoats, LLC + "boehringer", // boehringer Boehringer Ingelheim International GmbH + "bofa", // bofa NMS Services, Inc. + "bom", // bom Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "bond", // bond Bond University Limited + "boo", // boo Charleston Road Registry Inc. + "book", // book Amazon Registry Services, Inc. + "booking", // booking Booking.com B.V. + "boots", // boots THE BOOTS COMPANY PLC + "bosch", // bosch Robert Bosch GMBH + "bostik", // bostik Bostik SA + "boston", // boston Boston TLD Management, LLC + "bot", // bot Amazon Registry Services, Inc. + "boutique", // boutique Over Galley, LLC + "box", // box NS1 Limited + "bradesco", // bradesco Banco Bradesco S.A. + "bridgestone", // bridgestone Bridgestone Corporation + "broadway", // broadway Celebrate Broadway, Inc. + "broker", // broker DOTBROKER REGISTRY LTD + "brother", // brother Brother Industries, Ltd. + "brussels", // brussels DNS.be vzw + "budapest", // budapest Top Level Domain Holdings Limited + "bugatti", // bugatti Bugatti International SA + "build", // build Plan Bee LLC + "builders", // builders Atomic Madison, LLC + "business", // business Spring Cross, LLC + "buy", // buy Amazon Registry Services, INC + "buzz", // buzz DOTSTRATEGY CO. + "bzh", // bzh Association www.bzh + "cab", // cab Half Sunset, LLC + "cafe", // cafe Pioneer Canyon, LLC + "cal", // cal Charleston Road Registry Inc. + "call", // call Amazon Registry Services, Inc. + "calvinklein", // calvinklein PVH gTLD Holdings LLC + "cam", // cam AC Webconnecting Holding B.V. + "camera", // camera Atomic Maple, LLC + "camp", // camp Delta Dynamite, LLC + "cancerresearch", // cancerresearch Australian Cancer Research Foundation + "canon", // canon Canon Inc. + "capetown", // capetown ZA Central Registry NPC trading as ZA Central Registry + "capital", // capital Delta Mill, LLC + "capitalone", // capitalone Capital One Financial Corporation + "car", // car Cars Registry Limited + "caravan", // caravan Caravan International, Inc. + "cards", // cards Foggy Hollow, LLC + "care", // care Goose Cross, LLC + "career", // career dotCareer LLC + "careers", // careers Wild Corner, LLC + "cars", // cars Uniregistry, Corp. + "cartier", // cartier Richemont DNS Inc. + "casa", // casa Top Level Domain Holdings Limited + "case", // case CNH Industrial N.V. + "caseih", // caseih CNH Industrial N.V. + "cash", // cash Delta Lake, LLC + "casino", // casino Binky Sky, LLC + "cat", // cat Fundacio puntCAT + "catering", // catering New Falls. LLC + "catholic", // catholic Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "cba", // cba COMMONWEALTH BANK OF AUSTRALIA + "cbn", // cbn The Christian Broadcasting Network, Inc. + "cbre", // cbre CBRE, Inc. + "cbs", // cbs CBS Domains Inc. + "ceb", // ceb The Corporate Executive Board Company + "center", // center Tin Mill, LLC + "ceo", // ceo CEOTLD Pty Ltd + "cern", // cern European Organization for Nuclear Research ("CERN") + "cfa", // cfa CFA Institute + "cfd", // cfd DOTCFD REGISTRY LTD + "chanel", // chanel Chanel International B.V. + "channel", // channel Charleston Road Registry Inc. + "chase", // chase JPMorgan Chase & Co. + "chat", // chat Sand Fields, LLC + "cheap", // cheap Sand Cover, LLC + "chintai", // chintai CHINTAI Corporation + "chloe", // chloe Richemont DNS Inc. + "christmas", // christmas Uniregistry, Corp. + "chrome", // chrome Charleston Road Registry Inc. + "chrysler", // chrysler FCA US LLC. + "church", // church Holly Fileds, LLC + "cipriani", // cipriani Hotel Cipriani Srl + "circle", // circle Amazon Registry Services, Inc. + "cisco", // cisco Cisco Technology, Inc. + "citadel", // citadel Citadel Domain LLC + "citi", // citi Citigroup Inc. + "citic", // citic CITIC Group Corporation + "city", // city Snow Sky, LLC + "cityeats", // cityeats Lifestyle Domain Holdings, Inc. + "claims", // claims Black Corner, LLC + "cleaning", // cleaning Fox Shadow, LLC + "click", // click Uniregistry, Corp. + "clinic", // clinic Goose Park, LLC + "clinique", // clinique The Estée Lauder Companies Inc. + "clothing", // clothing Steel Lake, LLC + "cloud", // cloud ARUBA S.p.A. + "club", // club .CLUB DOMAINS, LLC + "clubmed", // clubmed Club Méditerranée S.A. + "coach", // coach Koko Island, LLC + "codes", // codes Puff Willow, LLC + "coffee", // coffee Trixy Cover, LLC + "college", // college XYZ.COM LLC + "cologne", // cologne NetCologne Gesellschaft für Telekommunikation mbH + "com", // com VeriSign Global Registry Services + "comcast", // comcast Comcast IP Holdings I, LLC + "commbank", // commbank COMMONWEALTH BANK OF AUSTRALIA + "community", // community Fox Orchard, LLC + "company", // company Silver Avenue, LLC + "compare", // compare iSelect Ltd + "computer", // computer Pine Mill, LLC + "comsec", // comsec VeriSign, Inc. + "condos", // condos Pine House, LLC + "construction", // construction Fox Dynamite, LLC + "consulting", // consulting United TLD Holdco, LTD. + "contact", // contact Top Level Spectrum, Inc. + "contractors", // contractors Magic Woods, LLC + "cooking", // cooking Top Level Domain Holdings Limited + "cookingchannel", // cookingchannel Lifestyle Domain Holdings, Inc. + "cool", // cool Koko Lake, LLC + "coop", // coop DotCooperation LLC + "corsica", // corsica Collectivité Territoriale de Corse + "country", // country Top Level Domain Holdings Limited + "coupon", // coupon Amazon Registry Services, Inc. + "coupons", // coupons Black Island, LLC + "courses", // courses OPEN UNIVERSITIES AUSTRALIA PTY LTD + "credit", // credit Snow Shadow, LLC + "creditcard", // creditcard Binky Frostbite, LLC + "creditunion", // creditunion CUNA Performance Resources, LLC + "cricket", // cricket dot Cricket Limited + "crown", // crown Crown Equipment Corporation + "crs", // crs Federated Co-operatives Limited + "cruise", // cruise Viking River Cruises (Bermuda) Ltd. + "cruises", // cruises Spring Way, LLC + "csc", // csc Alliance-One Services, Inc. + "cuisinella", // cuisinella SALM S.A.S. + "cymru", // cymru Nominet UK + "cyou", // cyou Beijing Gamease Age Digital Technology Co., Ltd. + "dabur", // dabur Dabur India Limited + "dad", // dad Charleston Road Registry Inc. + "dance", // dance United TLD Holdco Ltd. + "data", // data Dish DBS Corporation + "date", // date dot Date Limited + "dating", // dating Pine Fest, LLC + "datsun", // datsun NISSAN MOTOR CO., LTD. + "day", // day Charleston Road Registry Inc. + "dclk", // dclk Charleston Road Registry Inc. + "dds", // dds Minds + Machines Group Limited + "deal", // deal Amazon Registry Services, Inc. + "dealer", // dealer Dealer Dot Com, Inc. + "deals", // deals Sand Sunset, LLC + "degree", // degree United TLD Holdco, Ltd + "delivery", // delivery Steel Station, LLC + "dell", // dell Dell Inc. + "deloitte", // deloitte Deloitte Touche Tohmatsu + "delta", // delta Delta Air Lines, Inc. + "democrat", // democrat United TLD Holdco Ltd. + "dental", // dental Tin Birch, LLC + "dentist", // dentist United TLD Holdco, Ltd + "desi", // desi Desi Networks LLC + "design", // design Top Level Design, LLC + "dev", // dev Charleston Road Registry Inc. + "dhl", // dhl Deutsche Post AG + "diamonds", // diamonds John Edge, LLC + "diet", // diet Uniregistry, Corp. + "digital", // digital Dash Park, LLC + "direct", // direct Half Trail, LLC + "directory", // directory Extra Madison, LLC + "discount", // discount Holly Hill, LLC + "discover", // discover Discover Financial Services + "dish", // dish Dish DBS Corporation + "diy", // diy Lifestyle Domain Holdings, Inc. + "dnp", // dnp Dai Nippon Printing Co., Ltd. + "docs", // docs Charleston Road Registry Inc. + "doctor", // doctor Brice Trail, LLC + "dodge", // dodge FCA US LLC. + "dog", // dog Koko Mill, LLC + "doha", // doha Communications Regulatory Authority (CRA) + "domains", // domains Sugar Cross, LLC +// "doosan", // doosan Doosan Corporation (retired) + "dot", // dot Dish DBS Corporation + "download", // download dot Support Limited + "drive", // drive Charleston Road Registry Inc. + "dtv", // dtv Dish DBS Corporation + "dubai", // dubai Dubai Smart Government Department + "duck", // duck Johnson Shareholdings, Inc. + "dunlop", // dunlop The Goodyear Tire & Rubber Company + "duns", // duns The Dun & Bradstreet Corporation + "dupont", // dupont E. I. du Pont de Nemours and Company + "durban", // durban ZA Central Registry NPC trading as ZA Central Registry + "dvag", // dvag Deutsche Vermögensberatung Aktiengesellschaft DVAG + "dvr", // dvr Hughes Satellite Systems Corporation + "earth", // earth Interlink Co., Ltd. + "eat", // eat Charleston Road Registry Inc. + "eco", // eco Big Room Inc. + "edeka", // edeka EDEKA Verband kaufmännischer Genossenschaften e.V. + "edu", // edu EDUCAUSE + "education", // education Brice Way, LLC + "email", // email Spring Madison, LLC + "emerck", // emerck Merck KGaA + "energy", // energy Binky Birch, LLC + "engineer", // engineer United TLD Holdco Ltd. + "engineering", // engineering Romeo Canyon + "enterprises", // enterprises Snow Oaks, LLC + "epost", // epost Deutsche Post AG + "epson", // epson Seiko Epson Corporation + "equipment", // equipment Corn Station, LLC + "ericsson", // ericsson Telefonaktiebolaget L M Ericsson + "erni", // erni ERNI Group Holding AG + "esq", // esq Charleston Road Registry Inc. + "estate", // estate Trixy Park, LLC + "esurance", // esurance Esurance Insurance Company + "eurovision", // eurovision European Broadcasting Union (EBU) + "eus", // eus Puntueus Fundazioa + "events", // events Pioneer Maple, LLC + "everbank", // everbank EverBank + "exchange", // exchange Spring Falls, LLC + "expert", // expert Magic Pass, LLC + "exposed", // exposed Victor Beach, LLC + "express", // express Sea Sunset, LLC + "extraspace", // extraspace Extra Space Storage LLC + "fage", // fage Fage International S.A. + "fail", // fail Atomic Pipe, LLC + "fairwinds", // fairwinds FairWinds Partners, LLC + "faith", // faith dot Faith Limited + "family", // family United TLD Holdco Ltd. + "fan", // fan Asiamix Digital Ltd + "fans", // fans Asiamix Digital Limited + "farm", // farm Just Maple, LLC + "farmers", // farmers Farmers Insurance Exchange + "fashion", // fashion Top Level Domain Holdings Limited + "fast", // fast Amazon Registry Services, Inc. + "fedex", // fedex Federal Express Corporation + "feedback", // feedback Top Level Spectrum, Inc. + "ferrari", // ferrari Fiat Chrysler Automobiles N.V. + "ferrero", // ferrero Ferrero Trading Lux S.A. + "fiat", // fiat Fiat Chrysler Automobiles N.V. + "fidelity", // fidelity Fidelity Brokerage Services LLC + "fido", // fido Rogers Communications Canada Inc. + "film", // film Motion Picture Domain Registry Pty Ltd + "final", // final Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "finance", // finance Cotton Cypress, LLC + "financial", // financial Just Cover, LLC + "fire", // fire Amazon Registry Services, Inc. + "firestone", // firestone Bridgestone Corporation + "firmdale", // firmdale Firmdale Holdings Limited + "fish", // fish Fox Woods, LLC + "fishing", // fishing Top Level Domain Holdings Limited + "fit", // fit Minds + Machines Group Limited + "fitness", // fitness Brice Orchard, LLC + "flickr", // flickr Yahoo! Domain Services Inc. + "flights", // flights Fox Station, LLC + "flir", // flir FLIR Systems, Inc. + "florist", // florist Half Cypress, LLC + "flowers", // flowers Uniregistry, Corp. +// "flsmidth", // flsmidth FLSmidth A/S retired 2016-07-22 + "fly", // fly Charleston Road Registry Inc. + "foo", // foo Charleston Road Registry Inc. + "food", // food Lifestyle Domain Holdings, Inc. + "foodnetwork", // foodnetwork Lifestyle Domain Holdings, Inc. + "football", // football Foggy Farms, LLC + "ford", // ford Ford Motor Company + "forex", // forex DOTFOREX REGISTRY LTD + "forsale", // forsale United TLD Holdco, LLC + "forum", // forum Fegistry, LLC + "foundation", // foundation John Dale, LLC + "fox", // fox FOX Registry, LLC + "free", // free Amazon Registry Services, Inc. + "fresenius", // fresenius Fresenius Immobilien-Verwaltungs-GmbH + "frl", // frl FRLregistry B.V. + "frogans", // frogans OP3FT + "frontdoor", // frontdoor Lifestyle Domain Holdings, Inc. + "frontier", // frontier Frontier Communications Corporation + "ftr", // ftr Frontier Communications Corporation + "fujitsu", // fujitsu Fujitsu Limited + "fujixerox", // fujixerox Xerox DNHC LLC + "fun", // fun DotSpace, Inc. + "fund", // fund John Castle, LLC + "furniture", // furniture Lone Fields, LLC + "futbol", // futbol United TLD Holdco, Ltd. + "fyi", // fyi Silver Tigers, LLC + "gal", // gal Asociación puntoGAL + "gallery", // gallery Sugar House, LLC + "gallo", // gallo Gallo Vineyards, Inc. + "gallup", // gallup Gallup, Inc. + "game", // game Uniregistry, Corp. + "games", // games United TLD Holdco Ltd. + "gap", // gap The Gap, Inc. + "garden", // garden Top Level Domain Holdings Limited + "gbiz", // gbiz Charleston Road Registry Inc. + "gdn", // gdn Joint Stock Company "Navigation-information systems" + "gea", // gea GEA Group Aktiengesellschaft + "gent", // gent COMBELL GROUP NV/SA + "genting", // genting Resorts World Inc. Pte. Ltd. + "george", // george Wal-Mart Stores, Inc. + "ggee", // ggee GMO Internet, Inc. + "gift", // gift Uniregistry, Corp. + "gifts", // gifts Goose Sky, LLC + "gives", // gives United TLD Holdco Ltd. + "giving", // giving Giving Limited + "glade", // glade Johnson Shareholdings, Inc. + "glass", // glass Black Cover, LLC + "gle", // gle Charleston Road Registry Inc. + "global", // global Dot Global Domain Registry Limited + "globo", // globo Globo Comunicação e Participações S.A + "gmail", // gmail Charleston Road Registry Inc. + "gmbh", // gmbh Extra Dynamite, LLC + "gmo", // gmo GMO Internet, Inc. + "gmx", // gmx 1&1 Mail & Media GmbH + "godaddy", // godaddy Go Daddy East, LLC + "gold", // gold June Edge, LLC + "goldpoint", // goldpoint YODOBASHI CAMERA CO.,LTD. + "golf", // golf Lone Falls, LLC + "goo", // goo NTT Resonant Inc. + "goodhands", // goodhands Allstate Fire and Casualty Insurance Company + "goodyear", // goodyear The Goodyear Tire & Rubber Company + "goog", // goog Charleston Road Registry Inc. + "google", // google Charleston Road Registry Inc. + "gop", // gop Republican State Leadership Committee, Inc. + "got", // got Amazon Registry Services, Inc. + "gov", // gov General Services Administration Attn: QTDC, 2E08 (.gov Domain Registration) + "grainger", // grainger Grainger Registry Services, LLC + "graphics", // graphics Over Madison, LLC + "gratis", // gratis Pioneer Tigers, LLC + "green", // green Afilias Limited + "gripe", // gripe Corn Sunset, LLC + "group", // group Romeo Town, LLC + "guardian", // guardian The Guardian Life Insurance Company of America + "gucci", // gucci Guccio Gucci S.p.a. + "guge", // guge Charleston Road Registry Inc. + "guide", // guide Snow Moon, LLC + "guitars", // guitars Uniregistry, Corp. + "guru", // guru Pioneer Cypress, LLC + "hair", // hair L'Oreal + "hamburg", // hamburg Hamburg Top-Level-Domain GmbH + "hangout", // hangout Charleston Road Registry Inc. + "haus", // haus United TLD Holdco, LTD. + "hbo", // hbo HBO Registry Services, Inc. + "hdfc", // hdfc HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED + "hdfcbank", // hdfcbank HDFC Bank Limited + "health", // health DotHealth, LLC + "healthcare", // healthcare Silver Glen, LLC + "help", // help Uniregistry, Corp. + "helsinki", // helsinki City of Helsinki + "here", // here Charleston Road Registry Inc. + "hermes", // hermes Hermes International + "hgtv", // hgtv Lifestyle Domain Holdings, Inc. + "hiphop", // hiphop Uniregistry, Corp. + "hisamitsu", // hisamitsu Hisamitsu Pharmaceutical Co.,Inc. + "hitachi", // hitachi Hitachi, Ltd. + "hiv", // hiv dotHIV gemeinnuetziger e.V. + "hkt", // hkt PCCW-HKT DataCom Services Limited + "hockey", // hockey Half Willow, LLC + "holdings", // holdings John Madison, LLC + "holiday", // holiday Goose Woods, LLC + "homedepot", // homedepot Homer TLC, Inc. + "homegoods", // homegoods The TJX Companies, Inc. + "homes", // homes DERHomes, LLC + "homesense", // homesense The TJX Companies, Inc. + "honda", // honda Honda Motor Co., Ltd. + "honeywell", // honeywell Honeywell GTLD LLC + "horse", // horse Top Level Domain Holdings Limited + "hospital", // hospital Ruby Pike, LLC + "host", // host DotHost Inc. + "hosting", // hosting Uniregistry, Corp. + "hot", // hot Amazon Registry Services, Inc. + "hoteles", // hoteles Travel Reservations SRL + "hotmail", // hotmail Microsoft Corporation + "house", // house Sugar Park, LLC + "how", // how Charleston Road Registry Inc. + "hsbc", // hsbc HSBC Holdings PLC + "htc", // htc HTC corporation + "hughes", // hughes Hughes Satellite Systems Corporation + "hyatt", // hyatt Hyatt GTLD, L.L.C. + "hyundai", // hyundai Hyundai Motor Company + "ibm", // ibm International Business Machines Corporation + "icbc", // icbc Industrial and Commercial Bank of China Limited + "ice", // ice IntercontinentalExchange, Inc. + "icu", // icu One.com A/S + "ieee", // ieee IEEE Global LLC + "ifm", // ifm ifm electronic gmbh +// "iinet", // iinet Connect West Pty. Ltd. (Retired) + "ikano", // ikano Ikano S.A. + "imamat", // imamat Fondation Aga Khan (Aga Khan Foundation) + "imdb", // imdb Amazon Registry Services, Inc. + "immo", // immo Auburn Bloom, LLC + "immobilien", // immobilien United TLD Holdco Ltd. + "industries", // industries Outer House, LLC + "infiniti", // infiniti NISSAN MOTOR CO., LTD. + "info", // info Afilias Limited + "ing", // ing Charleston Road Registry Inc. + "ink", // ink Top Level Design, LLC + "institute", // institute Outer Maple, LLC + "insurance", // insurance fTLD Registry Services LLC + "insure", // insure Pioneer Willow, LLC + "int", // int Internet Assigned Numbers Authority + "intel", // intel Intel Corporation + "international", // international Wild Way, LLC + "intuit", // intuit Intuit Administrative Services, Inc. + "investments", // investments Holly Glen, LLC + "ipiranga", // ipiranga Ipiranga Produtos de Petroleo S.A. + "irish", // irish Dot-Irish LLC + "iselect", // iselect iSelect Ltd + "ismaili", // ismaili Fondation Aga Khan (Aga Khan Foundation) + "ist", // ist Istanbul Metropolitan Municipality + "istanbul", // istanbul Istanbul Metropolitan Municipality / Medya A.S. + "itau", // itau Itau Unibanco Holding S.A. + "itv", // itv ITV Services Limited + "iveco", // iveco CNH Industrial N.V. + "iwc", // iwc Richemont DNS Inc. + "jaguar", // jaguar Jaguar Land Rover Ltd + "java", // java Oracle Corporation + "jcb", // jcb JCB Co., Ltd. + "jcp", // jcp JCP Media, Inc. + "jeep", // jeep FCA US LLC. + "jetzt", // jetzt New TLD Company AB + "jewelry", // jewelry Wild Bloom, LLC + "jio", // jio Affinity Names, Inc. + "jlc", // jlc Richemont DNS Inc. + "jll", // jll Jones Lang LaSalle Incorporated + "jmp", // jmp Matrix IP LLC + "jnj", // jnj Johnson & Johnson Services, Inc. + "jobs", // jobs Employ Media LLC + "joburg", // joburg ZA Central Registry NPC trading as ZA Central Registry + "jot", // jot Amazon Registry Services, Inc. + "joy", // joy Amazon Registry Services, Inc. + "jpmorgan", // jpmorgan JPMorgan Chase & Co. + "jprs", // jprs Japan Registry Services Co., Ltd. + "juegos", // juegos Uniregistry, Corp. + "juniper", // juniper JUNIPER NETWORKS, INC. + "kaufen", // kaufen United TLD Holdco Ltd. + "kddi", // kddi KDDI CORPORATION + "kerryhotels", // kerryhotels Kerry Trading Co. Limited + "kerrylogistics", // kerrylogistics Kerry Trading Co. Limited + "kerryproperties", // kerryproperties Kerry Trading Co. Limited + "kfh", // kfh Kuwait Finance House + "kia", // kia KIA MOTORS CORPORATION + "kim", // kim Afilias Limited + "kinder", // kinder Ferrero Trading Lux S.A. + "kindle", // kindle Amazon Registry Services, Inc. + "kitchen", // kitchen Just Goodbye, LLC + "kiwi", // kiwi DOT KIWI LIMITED + "koeln", // koeln NetCologne Gesellschaft für Telekommunikation mbH + "komatsu", // komatsu Komatsu Ltd. + "kosher", // kosher Kosher Marketing Assets LLC + "kpmg", // kpmg KPMG International Cooperative (KPMG International Genossenschaft) + "kpn", // kpn Koninklijke KPN N.V. + "krd", // krd KRG Department of Information Technology + "kred", // kred KredTLD Pty Ltd + "kuokgroup", // kuokgroup Kerry Trading Co. Limited + "kyoto", // kyoto Academic Institution: Kyoto Jyoho Gakuen + "lacaixa", // lacaixa CAIXA D'ESTALVIS I PENSIONS DE BARCELONA + "ladbrokes", // ladbrokes LADBROKES INTERNATIONAL PLC + "lamborghini", // lamborghini Automobili Lamborghini S.p.A. + "lamer", // lamer The Estée Lauder Companies Inc. + "lancaster", // lancaster LANCASTER + "lancia", // lancia Fiat Chrysler Automobiles N.V. + "lancome", // lancome L'Oréal + "land", // land Pine Moon, LLC + "landrover", // landrover Jaguar Land Rover Ltd + "lanxess", // lanxess LANXESS Corporation + "lasalle", // lasalle Jones Lang LaSalle Incorporated + "lat", // lat ECOM-LAC Federación de Latinoamérica y el Caribe para Internet y el Comercio Electrónico + "latino", // latino Dish DBS Corporation + "latrobe", // latrobe La Trobe University + "law", // law Minds + Machines Group Limited + "lawyer", // lawyer United TLD Holdco, Ltd + "lds", // lds IRI Domain Management, LLC + "lease", // lease Victor Trail, LLC + "leclerc", // leclerc A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc + "lefrak", // lefrak LeFrak Organization, Inc. + "legal", // legal Blue Falls, LLC + "lego", // lego LEGO Juris A/S + "lexus", // lexus TOYOTA MOTOR CORPORATION + "lgbt", // lgbt Afilias Limited + "liaison", // liaison Liaison Technologies, Incorporated + "lidl", // lidl Schwarz Domains und Services GmbH & Co. KG + "life", // life Trixy Oaks, LLC + "lifeinsurance", // lifeinsurance American Council of Life Insurers + "lifestyle", // lifestyle Lifestyle Domain Holdings, Inc. + "lighting", // lighting John McCook, LLC + "like", // like Amazon Registry Services, Inc. + "lilly", // lilly Eli Lilly and Company + "limited", // limited Big Fest, LLC + "limo", // limo Hidden Frostbite, LLC + "lincoln", // lincoln Ford Motor Company + "linde", // linde Linde Aktiengesellschaft + "link", // link Uniregistry, Corp. + "lipsy", // lipsy Lipsy Ltd + "live", // live United TLD Holdco Ltd. + "living", // living Lifestyle Domain Holdings, Inc. + "lixil", // lixil LIXIL Group Corporation + "loan", // loan dot Loan Limited + "loans", // loans June Woods, LLC + "locker", // locker Dish DBS Corporation + "locus", // locus Locus Analytics LLC + "loft", // loft Annco, Inc. + "lol", // lol Uniregistry, Corp. + "london", // london Dot London Domains Limited + "lotte", // lotte Lotte Holdings Co., Ltd. + "lotto", // lotto Afilias Limited + "love", // love Merchant Law Group LLP + "lpl", // lpl LPL Holdings, Inc. + "lplfinancial", // lplfinancial LPL Holdings, Inc. + "ltd", // ltd Over Corner, LLC + "ltda", // ltda InterNetX Corp. + "lundbeck", // lundbeck H. Lundbeck A/S + "lupin", // lupin LUPIN LIMITED + "luxe", // luxe Top Level Domain Holdings Limited + "luxury", // luxury Luxury Partners LLC + "macys", // macys Macys, Inc. + "madrid", // madrid Comunidad de Madrid + "maif", // maif Mutuelle Assurance Instituteur France (MAIF) + "maison", // maison Victor Frostbite, LLC + "makeup", // makeup L'Oréal + "man", // man MAN SE + "management", // management John Goodbye, LLC + "mango", // mango PUNTO FA S.L. + "market", // market Unitied TLD Holdco, Ltd + "marketing", // marketing Fern Pass, LLC + "markets", // markets DOTMARKETS REGISTRY LTD + "marriott", // marriott Marriott Worldwide Corporation + "marshalls", // marshalls The TJX Companies, Inc. + "maserati", // maserati Fiat Chrysler Automobiles N.V. + "mattel", // mattel Mattel Sites, Inc. + "mba", // mba Lone Hollow, LLC + "mcd", // mcd McDonald’s Corporation + "mcdonalds", // mcdonalds McDonald’s Corporation + "mckinsey", // mckinsey McKinsey Holdings, Inc. + "med", // med Medistry LLC + "media", // media Grand Glen, LLC + "meet", // meet Afilias Limited + "melbourne", // melbourne The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation + "meme", // meme Charleston Road Registry Inc. + "memorial", // memorial Dog Beach, LLC + "men", // men Exclusive Registry Limited + "menu", // menu Wedding TLD2, LLC + "meo", // meo PT Comunicacoes S.A. + "metlife", // metlife MetLife Services and Solutions, LLC + "miami", // miami Top Level Domain Holdings Limited + "microsoft", // microsoft Microsoft Corporation + "mil", // mil DoD Network Information Center + "mini", // mini Bayerische Motoren Werke Aktiengesellschaft + "mint", // mint Intuit Administrative Services, Inc. + "mit", // mit Massachusetts Institute of Technology + "mitsubishi", // mitsubishi Mitsubishi Corporation + "mlb", // mlb MLB Advanced Media DH, LLC + "mls", // mls The Canadian Real Estate Association + "mma", // mma MMA IARD + "mobi", // mobi Afilias Technologies Limited dba dotMobi + "mobile", // mobile Dish DBS Corporation + "mobily", // mobily GreenTech Consultancy Company W.L.L. + "moda", // moda United TLD Holdco Ltd. + "moe", // moe Interlink Co., Ltd. + "moi", // moi Amazon Registry Services, Inc. + "mom", // mom Uniregistry, Corp. + "monash", // monash Monash University + "money", // money Outer McCook, LLC + "monster", // monster Monster Worldwide, Inc. + "montblanc", // montblanc Richemont DNS Inc. + "mopar", // mopar FCA US LLC. + "mormon", // mormon IRI Domain Management, LLC ("Applicant") + "mortgage", // mortgage United TLD Holdco, Ltd + "moscow", // moscow Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) + "moto", // moto Motorola Trademark Holdings, LLC + "motorcycles", // motorcycles DERMotorcycles, LLC + "mov", // mov Charleston Road Registry Inc. + "movie", // movie New Frostbite, LLC + "movistar", // movistar Telefónica S.A. + "msd", // msd MSD Registry Holdings, Inc. + "mtn", // mtn MTN Dubai Limited + "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation + "mtr", // mtr MTR Corporation Limited + "museum", // museum Museum Domain Management Association + "mutual", // mutual Northwestern Mutual MU TLD Registry, LLC +// "mutuelle", // mutuelle Fédération Nationale de la Mutualité Française (Retired) + "nab", // nab National Australia Bank Limited + "nadex", // nadex Nadex Domains, Inc + "nagoya", // nagoya GMO Registry, Inc. + "name", // name VeriSign Information Services, Inc. + "nationwide", // nationwide Nationwide Mutual Insurance Company + "natura", // natura NATURA COSMÉTICOS S.A. + "navy", // navy United TLD Holdco Ltd. + "nba", // nba NBA REGISTRY, LLC + "nec", // nec NEC Corporation + "net", // net VeriSign Global Registry Services + "netbank", // netbank COMMONWEALTH BANK OF AUSTRALIA + "netflix", // netflix Netflix, Inc. + "network", // network Trixy Manor, LLC + "neustar", // neustar NeuStar, Inc. + "new", // new Charleston Road Registry Inc. + "newholland", // newholland CNH Industrial N.V. + "news", // news United TLD Holdco Ltd. + "next", // next Next plc + "nextdirect", // nextdirect Next plc + "nexus", // nexus Charleston Road Registry Inc. + "nfl", // nfl NFL Reg Ops LLC + "ngo", // ngo Public Interest Registry + "nhk", // nhk Japan Broadcasting Corporation (NHK) + "nico", // nico DWANGO Co., Ltd. + "nike", // nike NIKE, Inc. + "nikon", // nikon NIKON CORPORATION + "ninja", // ninja United TLD Holdco Ltd. + "nissan", // nissan NISSAN MOTOR CO., LTD. + "nissay", // nissay Nippon Life Insurance Company + "nokia", // nokia Nokia Corporation + "northwesternmutual", // northwesternmutual Northwestern Mutual Registry, LLC + "norton", // norton Symantec Corporation + "now", // now Amazon Registry Services, Inc. + "nowruz", // nowruz Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "nowtv", // nowtv Starbucks (HK) Limited + "nra", // nra NRA Holdings Company, INC. + "nrw", // nrw Minds + Machines GmbH + "ntt", // ntt NIPPON TELEGRAPH AND TELEPHONE CORPORATION + "nyc", // nyc The City of New York by and through the New York City Department of Information Technology & Telecommunications + "obi", // obi OBI Group Holding SE & Co. KGaA + "observer", // observer Top Level Spectrum, Inc. + "off", // off Johnson Shareholdings, Inc. + "office", // office Microsoft Corporation + "okinawa", // okinawa BusinessRalliart inc. + "olayan", // olayan Crescent Holding GmbH + "olayangroup", // olayangroup Crescent Holding GmbH + "oldnavy", // oldnavy The Gap, Inc. + "ollo", // ollo Dish DBS Corporation + "omega", // omega The Swatch Group Ltd + "one", // one One.com A/S + "ong", // ong Public Interest Registry + "onl", // onl I-REGISTRY Ltd., Niederlassung Deutschland + "online", // online DotOnline Inc. + "onyourside", // onyourside Nationwide Mutual Insurance Company + "ooo", // ooo INFIBEAM INCORPORATION LIMITED + "open", // open American Express Travel Related Services Company, Inc. + "oracle", // oracle Oracle Corporation + "orange", // orange Orange Brand Services Limited + "org", // org Public Interest Registry (PIR) + "organic", // organic Afilias Limited + "orientexpress", // orientexpress Orient Express + "origins", // origins The Estée Lauder Companies Inc. + "osaka", // osaka Interlink Co., Ltd. + "otsuka", // otsuka Otsuka Holdings Co., Ltd. + "ott", // ott Dish DBS Corporation + "ovh", // ovh OVH SAS + "page", // page Charleston Road Registry Inc. + "pamperedchef", // pamperedchef The Pampered Chef, Ltd. + "panasonic", // panasonic Panasonic Corporation + "panerai", // panerai Richemont DNS Inc. + "paris", // paris City of Paris + "pars", // pars Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "partners", // partners Magic Glen, LLC + "parts", // parts Sea Goodbye, LLC + "party", // party Blue Sky Registry Limited + "passagens", // passagens Travel Reservations SRL + "pay", // pay Amazon Registry Services, Inc. + "pccw", // pccw PCCW Enterprises Limited + "pet", // pet Afilias plc + "pfizer", // pfizer Pfizer Inc. + "pharmacy", // pharmacy National Association of Boards of Pharmacy + "philips", // philips Koninklijke Philips N.V. + "phone", // phone Dish DBS Corporation + "photo", // photo Uniregistry, Corp. + "photography", // photography Sugar Glen, LLC + "photos", // photos Sea Corner, LLC + "physio", // physio PhysBiz Pty Ltd + "piaget", // piaget Richemont DNS Inc. + "pics", // pics Uniregistry, Corp. + "pictet", // pictet Pictet Europe S.A. + "pictures", // pictures Foggy Sky, LLC + "pid", // pid Top Level Spectrum, Inc. + "pin", // pin Amazon Registry Services, Inc. + "ping", // ping Ping Registry Provider, Inc. + "pink", // pink Afilias Limited + "pioneer", // pioneer Pioneer Corporation + "pizza", // pizza Foggy Moon, LLC + "place", // place Snow Galley, LLC + "play", // play Charleston Road Registry Inc. + "playstation", // playstation Sony Computer Entertainment Inc. + "plumbing", // plumbing Spring Tigers, LLC + "plus", // plus Sugar Mill, LLC + "pnc", // pnc PNC Domain Co., LLC + "pohl", // pohl Deutsche Vermögensberatung Aktiengesellschaft DVAG + "poker", // poker Afilias Domains No. 5 Limited + "politie", // politie Politie Nederland + "porn", // porn ICM Registry PN LLC + "post", // post Universal Postal Union + "pramerica", // pramerica Prudential Financial, Inc. + "praxi", // praxi Praxi S.p.A. + "press", // press DotPress Inc. + "prime", // prime Amazon Registry Services, Inc. + "pro", // pro Registry Services Corporation dba RegistryPro + "prod", // prod Charleston Road Registry Inc. + "productions", // productions Magic Birch, LLC + "prof", // prof Charleston Road Registry Inc. + "progressive", // progressive Progressive Casualty Insurance Company + "promo", // promo Afilias plc + "properties", // properties Big Pass, LLC + "property", // property Uniregistry, Corp. + "protection", // protection XYZ.COM LLC + "pru", // pru Prudential Financial, Inc. + "prudential", // prudential Prudential Financial, Inc. + "pub", // pub United TLD Holdco Ltd. + "pwc", // pwc PricewaterhouseCoopers LLP + "qpon", // qpon dotCOOL, Inc. + "quebec", // quebec PointQuébec Inc + "quest", // quest Quest ION Limited + "qvc", // qvc QVC, Inc. + "racing", // racing Premier Registry Limited + "radio", // radio European Broadcasting Union (EBU) + "raid", // raid Johnson Shareholdings, Inc. + "read", // read Amazon Registry Services, Inc. + "realestate", // realestate dotRealEstate LLC + "realtor", // realtor Real Estate Domains LLC + "realty", // realty Fegistry, LLC + "recipes", // recipes Grand Island, LLC + "red", // red Afilias Limited + "redstone", // redstone Redstone Haute Couture Co., Ltd. + "redumbrella", // redumbrella Travelers TLD, LLC + "rehab", // rehab United TLD Holdco Ltd. + "reise", // reise Foggy Way, LLC + "reisen", // reisen New Cypress, LLC + "reit", // reit National Association of Real Estate Investment Trusts, Inc. + "reliance", // reliance Reliance Industries Limited + "ren", // ren Beijing Qianxiang Wangjing Technology Development Co., Ltd. + "rent", // rent XYZ.COM LLC + "rentals", // rentals Big Hollow,LLC + "repair", // repair Lone Sunset, LLC + "report", // report Binky Glen, LLC + "republican", // republican United TLD Holdco Ltd. + "rest", // rest Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable + "restaurant", // restaurant Snow Avenue, LLC + "review", // review dot Review Limited + "reviews", // reviews United TLD Holdco, Ltd. + "rexroth", // rexroth Robert Bosch GMBH + "rich", // rich I-REGISTRY Ltd., Niederlassung Deutschland + "richardli", // richardli Pacific Century Asset Management (HK) Limited + "ricoh", // ricoh Ricoh Company, Ltd. + "rightathome", // rightathome Johnson Shareholdings, Inc. + "ril", // ril Reliance Industries Limited + "rio", // rio Empresa Municipal de Informática SA - IPLANRIO + "rip", // rip United TLD Holdco Ltd. + "rmit", // rmit Royal Melbourne Institute of Technology + "rocher", // rocher Ferrero Trading Lux S.A. + "rocks", // rocks United TLD Holdco, LTD. + "rodeo", // rodeo Top Level Domain Holdings Limited + "rogers", // rogers Rogers Communications Canada Inc. + "room", // room Amazon Registry Services, Inc. + "rsvp", // rsvp Charleston Road Registry Inc. + "ruhr", // ruhr regiodot GmbH & Co. KG + "run", // run Snow Park, LLC + "rwe", // rwe RWE AG + "ryukyu", // ryukyu BusinessRalliart inc. + "saarland", // saarland dotSaarland GmbH + "safe", // safe Amazon Registry Services, Inc. + "safety", // safety Safety Registry Services, LLC. + "sakura", // sakura SAKURA Internet Inc. + "sale", // sale United TLD Holdco, Ltd + "salon", // salon Outer Orchard, LLC + "samsclub", // samsclub Wal-Mart Stores, Inc. + "samsung", // samsung SAMSUNG SDS CO., LTD + "sandvik", // sandvik Sandvik AB + "sandvikcoromant", // sandvikcoromant Sandvik AB + "sanofi", // sanofi Sanofi + "sap", // sap SAP AG + "sapo", // sapo PT Comunicacoes S.A. + "sarl", // sarl Delta Orchard, LLC + "sas", // sas Research IP LLC + "save", // save Amazon Registry Services, Inc. + "saxo", // saxo Saxo Bank A/S + "sbi", // sbi STATE BANK OF INDIA + "sbs", // sbs SPECIAL BROADCASTING SERVICE CORPORATION + "sca", // sca SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) + "scb", // scb The Siam Commercial Bank Public Company Limited ("SCB") + "schaeffler", // schaeffler Schaeffler Technologies AG & Co. KG + "schmidt", // schmidt SALM S.A.S. + "scholarships", // scholarships Scholarships.com, LLC + "school", // school Little Galley, LLC + "schule", // schule Outer Moon, LLC + "schwarz", // schwarz Schwarz Domains und Services GmbH & Co. KG + "science", // science dot Science Limited + "scjohnson", // scjohnson Johnson Shareholdings, Inc. + "scor", // scor SCOR SE + "scot", // scot Dot Scot Registry Limited + "seat", // seat SEAT, S.A. (Sociedad Unipersonal) + "secure", // secure Amazon Registry Services, Inc. + "security", // security XYZ.COM LLC + "seek", // seek Seek Limited + "select", // select iSelect Ltd + "sener", // sener Sener Ingeniería y Sistemas, S.A. + "services", // services Fox Castle, LLC + "ses", // ses SES + "seven", // seven Seven West Media Ltd + "sew", // sew SEW-EURODRIVE GmbH & Co KG + "sex", // sex ICM Registry SX LLC + "sexy", // sexy Uniregistry, Corp. + "sfr", // sfr Societe Francaise du Radiotelephone - SFR + "shangrila", // shangrila Shangri‐La International Hotel Management Limited + "sharp", // sharp Sharp Corporation + "shaw", // shaw Shaw Cablesystems G.P. + "shell", // shell Shell Information Technology International Inc + "shia", // shia Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "shiksha", // shiksha Afilias Limited + "shoes", // shoes Binky Galley, LLC + "shop", // shop GMO Registry, Inc. + "shopping", // shopping Over Keep, LLC + "shouji", // shouji QIHOO 360 TECHNOLOGY CO. LTD. + "show", // show Snow Beach, LLC + "showtime", // showtime CBS Domains Inc. + "shriram", // shriram Shriram Capital Ltd. + "silk", // silk Amazon Registry Services, Inc. + "sina", // sina Sina Corporation + "singles", // singles Fern Madison, LLC + "site", // site DotSite Inc. + "ski", // ski STARTING DOT LIMITED + "skin", // skin L'Oréal + "sky", // sky Sky International AG + "skype", // skype Microsoft Corporation + "sling", // sling Hughes Satellite Systems Corporation + "smart", // smart Smart Communications, Inc. (SMART) + "smile", // smile Amazon Registry Services, Inc. + "sncf", // sncf SNCF (Société Nationale des Chemins de fer Francais) + "soccer", // soccer Foggy Shadow, LLC + "social", // social United TLD Holdco Ltd. + "softbank", // softbank SoftBank Group Corp. + "software", // software United TLD Holdco, Ltd + "sohu", // sohu Sohu.com Limited + "solar", // solar Ruby Town, LLC + "solutions", // solutions Silver Cover, LLC + "song", // song Amazon Registry Services, Inc. + "sony", // sony Sony Corporation + "soy", // soy Charleston Road Registry Inc. + "space", // space DotSpace Inc. + "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG + "spot", // spot Amazon Registry Services, Inc. + "spreadbetting", // spreadbetting DOTSPREADBETTING REGISTRY LTD + "srl", // srl InterNetX Corp. + "srt", // srt FCA US LLC. + "stada", // stada STADA Arzneimittel AG + "staples", // staples Staples, Inc. + "star", // star Star India Private Limited + "starhub", // starhub StarHub Limited + "statebank", // statebank STATE BANK OF INDIA + "statefarm", // statefarm State Farm Mutual Automobile Insurance Company + "statoil", // statoil Statoil ASA + "stc", // stc Saudi Telecom Company + "stcgroup", // stcgroup Saudi Telecom Company + "stockholm", // stockholm Stockholms kommun + "storage", // storage Self Storage Company LLC + "store", // store DotStore Inc. + "stream", // stream dot Stream Limited + "studio", // studio United TLD Holdco Ltd. + "study", // study OPEN UNIVERSITIES AUSTRALIA PTY LTD + "style", // style Binky Moon, LLC + "sucks", // sucks Vox Populi Registry Ltd. + "supplies", // supplies Atomic Fields, LLC + "supply", // supply Half Falls, LLC + "support", // support Grand Orchard, LLC + "surf", // surf Top Level Domain Holdings Limited + "surgery", // surgery Tin Avenue, LLC + "suzuki", // suzuki SUZUKI MOTOR CORPORATION + "swatch", // swatch The Swatch Group Ltd + "swiftcover", // swiftcover Swiftcover Insurance Services Limited + "swiss", // swiss Swiss Confederation + "sydney", // sydney State of New South Wales, Department of Premier and Cabinet + "symantec", // symantec Symantec Corporation + "systems", // systems Dash Cypress, LLC + "tab", // tab Tabcorp Holdings Limited + "taipei", // taipei Taipei City Government + "talk", // talk Amazon Registry Services, Inc. + "taobao", // taobao Alibaba Group Holding Limited + "target", // target Target Domain Holdings, LLC + "tatamotors", // tatamotors Tata Motors Ltd + "tatar", // tatar Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" + "tattoo", // tattoo Uniregistry, Corp. + "tax", // tax Storm Orchard, LLC + "taxi", // taxi Pine Falls, LLC + "tci", // tci Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "tdk", // tdk TDK Corporation + "team", // team Atomic Lake, LLC + "tech", // tech Dot Tech LLC + "technology", // technology Auburn Falls, LLC + "tel", // tel Telnic Ltd. + "telecity", // telecity TelecityGroup International Limited + "telefonica", // telefonica Telefónica S.A. + "temasek", // temasek Temasek Holdings (Private) Limited + "tennis", // tennis Cotton Bloom, LLC + "teva", // teva Teva Pharmaceutical Industries Limited + "thd", // thd Homer TLC, Inc. + "theater", // theater Blue Tigers, LLC + "theatre", // theatre XYZ.COM LLC + "tiaa", // tiaa Teachers Insurance and Annuity Association of America + "tickets", // tickets Accent Media Limited + "tienda", // tienda Victor Manor, LLC + "tiffany", // tiffany Tiffany and Company + "tips", // tips Corn Willow, LLC + "tires", // tires Dog Edge, LLC + "tirol", // tirol punkt Tirol GmbH + "tjmaxx", // tjmaxx The TJX Companies, Inc. + "tjx", // tjx The TJX Companies, Inc. + "tkmaxx", // tkmaxx The TJX Companies, Inc. + "tmall", // tmall Alibaba Group Holding Limited + "today", // today Pearl Woods, LLC + "tokyo", // tokyo GMO Registry, Inc. + "tools", // tools Pioneer North, LLC + "top", // top Jiangsu Bangning Science & Technology Co.,Ltd. + "toray", // toray Toray Industries, Inc. + "toshiba", // toshiba TOSHIBA Corporation + "total", // total Total SA + "tours", // tours Sugar Station, LLC + "town", // town Koko Moon, LLC + "toyota", // toyota TOYOTA MOTOR CORPORATION + "toys", // toys Pioneer Orchard, LLC + "trade", // trade Elite Registry Limited + "trading", // trading DOTTRADING REGISTRY LTD + "training", // training Wild Willow, LLC + "travel", // travel Tralliance Registry Management Company, LLC. + "travelchannel", // travelchannel Lifestyle Domain Holdings, Inc. + "travelers", // travelers Travelers TLD, LLC + "travelersinsurance", // travelersinsurance Travelers TLD, LLC + "trust", // trust Artemis Internet Inc + "trv", // trv Travelers TLD, LLC + "tube", // tube Latin American Telecom LLC + "tui", // tui TUI AG + "tunes", // tunes Amazon Registry Services, Inc. + "tushu", // tushu Amazon Registry Services, Inc. + "tvs", // tvs T V SUNDRAM IYENGAR & SONS PRIVATE LIMITED + "ubank", // ubank National Australia Bank Limited + "ubs", // ubs UBS AG + "uconnect", // uconnect FCA US LLC. + "unicom", // unicom China United Network Communications Corporation Limited + "university", // university Little Station, LLC + "uno", // uno Dot Latin LLC + "uol", // uol UBN INTERNET LTDA. + "ups", // ups UPS Market Driver, Inc. + "vacations", // vacations Atomic Tigers, LLC + "vana", // vana Lifestyle Domain Holdings, Inc. + "vanguard", // vanguard The Vanguard Group, Inc. + "vegas", // vegas Dot Vegas, Inc. + "ventures", // ventures Binky Lake, LLC + "verisign", // verisign VeriSign, Inc. + "versicherung", // versicherung dotversicherung-registry GmbH + "vet", // vet United TLD Holdco, Ltd + "viajes", // viajes Black Madison, LLC + "video", // video United TLD Holdco, Ltd + "vig", // vig VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe + "viking", // viking Viking River Cruises (Bermuda) Ltd. + "villas", // villas New Sky, LLC + "vin", // vin Holly Shadow, LLC + "vip", // vip Minds + Machines Group Limited + "virgin", // virgin Virgin Enterprises Limited + "visa", // visa Visa Worldwide Pte. Limited + "vision", // vision Koko Station, LLC + "vista", // vista Vistaprint Limited + "vistaprint", // vistaprint Vistaprint Limited + "viva", // viva Saudi Telecom Company + "vivo", // vivo Telefonica Brasil S.A. + "vlaanderen", // vlaanderen DNS.be vzw + "vodka", // vodka Top Level Domain Holdings Limited + "volkswagen", // volkswagen Volkswagen Group of America Inc. + "volvo", // volvo Volvo Holding Sverige Aktiebolag + "vote", // vote Monolith Registry LLC + "voting", // voting Valuetainment Corp. + "voto", // voto Monolith Registry LLC + "voyage", // voyage Ruby House, LLC + "vuelos", // vuelos Travel Reservations SRL + "wales", // wales Nominet UK + "walmart", // walmart Wal-Mart Stores, Inc. + "walter", // walter Sandvik AB + "wang", // wang Zodiac Registry Limited + "wanggou", // wanggou Amazon Registry Services, Inc. + "warman", // warman Weir Group IP Limited + "watch", // watch Sand Shadow, LLC + "watches", // watches Richemont DNS Inc. + "weather", // weather The Weather Channel, LLC + "weatherchannel", // weatherchannel The Weather Channel, LLC + "webcam", // webcam dot Webcam Limited + "weber", // weber Saint-Gobain Weber SA + "website", // website DotWebsite Inc. + "wed", // wed Atgron, Inc. + "wedding", // wedding Top Level Domain Holdings Limited + "weibo", // weibo Sina Corporation + "weir", // weir Weir Group IP Limited + "whoswho", // whoswho Who's Who Registry + "wien", // wien punkt.wien GmbH + "wiki", // wiki Top Level Design, LLC + "williamhill", // williamhill William Hill Organization Limited + "win", // win First Registry Limited + "windows", // windows Microsoft Corporation + "wine", // wine June Station, LLC + "winners", // winners The TJX Companies, Inc. + "wme", // wme William Morris Endeavor Entertainment, LLC + "wolterskluwer", // wolterskluwer Wolters Kluwer N.V. + "woodside", // woodside Woodside Petroleum Limited + "work", // work Top Level Domain Holdings Limited + "works", // works Little Dynamite, LLC + "world", // world Bitter Fields, LLC + "wow", // wow Amazon Registry Services, Inc. + "wtc", // wtc World Trade Centers Association, Inc. + "wtf", // wtf Hidden Way, LLC + "xbox", // xbox Microsoft Corporation + "xerox", // xerox Xerox DNHC LLC + "xfinity", // xfinity Comcast IP Holdings I, LLC + "xihuan", // xihuan QIHOO 360 TECHNOLOGY CO. LTD. + "xin", // xin Elegant Leader Limited + "xn--11b4c3d", // कॉम VeriSign Sarl + "xn--1ck2e1b", // セール Amazon Registry Services, Inc. + "xn--1qqw23a", // 佛山 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--30rr7y", // 慈善 Excellent First Limited + "xn--3bst00m", // 集团 Eagle Horizon Limited + "xn--3ds443g", // 在线 TLD REGISTRY LIMITED + "xn--3oq18vl8pn36a", // 大众汽车 Volkswagen (China) Investment Co., Ltd. + "xn--3pxu8k", // 点看 VeriSign Sarl + "xn--42c2d9a", // คอม VeriSign Sarl + "xn--45q11c", // 八卦 Zodiac Scorpio Limited + "xn--4gbrim", // موقع Suhub Electronic Establishment + "xn--55qw42g", // 公益 China Organizational Name Administration Center + "xn--55qx5d", // 公司 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) + "xn--5su34j936bgsg", // 香格里拉 Shangri‐La International Hotel Management Limited + "xn--5tzm5g", // 网站 Global Website TLD Asia Limited + "xn--6frz82g", // 移动 Afilias Limited + "xn--6qq986b3xl", // 我爱你 Tycoon Treasure Limited + "xn--80adxhks", // москва Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) + "xn--80aqecdr1a", // католик Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--80asehdb", // онлайн CORE Association + "xn--80aswg", // сайт CORE Association + "xn--8y0a063a", // 联通 China United Network Communications Corporation Limited + "xn--90ae", // бг Imena.BG Plc (NAMES.BG Plc) + "xn--9dbq2a", // קום VeriSign Sarl + "xn--9et52u", // 时尚 RISE VICTORY LIMITED + "xn--9krt00a", // 微博 Sina Corporation + "xn--b4w605ferd", // 淡马锡 Temasek Holdings (Private) Limited + "xn--bck1b9a5dre4c", // ファッション Amazon Registry Services, Inc. + "xn--c1avg", // орг Public Interest Registry + "xn--c2br7g", // नेट VeriSign Sarl + "xn--cck2b3b", // ストア Amazon Registry Services, Inc. + "xn--cg4bki", // 삼성 SAMSUNG SDS CO., LTD + "xn--czr694b", // 商标 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG LIMITED + "xn--czrs0t", // 商店 Wild Island, LLC + "xn--czru2d", // 商城 Zodiac Aquarius Limited + "xn--d1acj3b", // дети The Foundation for Network Initiatives “The Smart Internet” + "xn--eckvdtc9d", // ポイント Amazon Registry Services, Inc. + "xn--efvy88h", // 新闻 Xinhua News Agency Guangdong Branch 新华通讯社广东分社 + "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited + "xn--fct429k", // 家電 Amazon Registry Services, Inc. + "xn--fhbei", // كوم VeriSign Sarl + "xn--fiq228c5hs", // 中文网 TLD REGISTRY LIMITED + "xn--fiq64b", // 中信 CITIC Group Corporation + "xn--fjq720a", // 娱乐 Will Bloom, LLC + "xn--flw351e", // 谷歌 Charleston Road Registry Inc. + "xn--fzys8d69uvgm", // 電訊盈科 PCCW Enterprises Limited + "xn--g2xx48c", // 购物 Minds + Machines Group Limited + "xn--gckr3f0f", // クラウド Amazon Registry Services, Inc. + "xn--gk3at1e", // 通販 Amazon Registry Services, Inc. + "xn--hxt814e", // 网店 Zodiac Libra Limited + "xn--i1b6b1a6a2e", // संगठन Public Interest Registry + "xn--imr513n", // 餐厅 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED + "xn--io0a7i", // 网络 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) + "xn--j1aef", // ком VeriSign Sarl + "xn--jlq61u9w7b", // 诺基亚 Nokia Corporation + "xn--jvr189m", // 食品 Amazon Registry Services, Inc. + "xn--kcrx77d1x4a", // 飞利浦 Koninklijke Philips N.V. + "xn--kpu716f", // 手表 Richemont DNS Inc. + "xn--kput3i", // 手机 Beijing RITT-Net Technology Development Co., Ltd + "xn--mgba3a3ejt", // ارامكو Aramco Services Company + "xn--mgba7c0bbn0a", // العليان Crescent Holding GmbH + "xn--mgbab2bd", // بازار CORE Association + "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. + "xn--mgbca7dzdo", // ابوظبي Abu Dhabi Systems and Information Centre + "xn--mgbi4ecexp", // كاثوليك Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--mgbt3dhd", // همراه Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "xn--mk1bu44c", // 닷컴 VeriSign Sarl + "xn--mxtq1m", // 政府 Net-Chinese Co., Ltd. + "xn--ngbc5azd", // شبكة International Domain Registry Pty. Ltd. + "xn--ngbe9e0a", // بيتك Kuwait Finance House + "xn--nqv7f", // 机构 Public Interest Registry + "xn--nqv7fs00ema", // 组织机构 Public Interest Registry + "xn--nyqy26a", // 健康 Stable Tone Limited + "xn--p1acf", // рус Rusnames Limited + "xn--pbt977c", // 珠宝 Richemont DNS Inc. + "xn--pssy2u", // 大拿 VeriSign Sarl + "xn--q9jyb4c", // みんな Charleston Road Registry Inc. + "xn--qcka1pmc", // グーグル Charleston Road Registry Inc. + "xn--rhqv96g", // 世界 Stable Tone Limited + "xn--rovu88b", // 書籍 Amazon EU S.à r.l. + "xn--ses554g", // 网址 KNET Co., Ltd + "xn--t60b56a", // 닷넷 VeriSign Sarl + "xn--tckwe", // コム VeriSign Sarl + "xn--tiq49xqyj", // 天主教 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--unup4y", // 游戏 Spring Fields, LLC + "xn--vermgensberater-ctb", // VERMöGENSBERATER Deutsche Vermögensberatung Aktiengesellschaft DVAG + "xn--vermgensberatung-pwb", // VERMöGENSBERATUNG Deutsche Vermögensberatung Aktiengesellschaft DVAG + "xn--vhquv", // 企业 Dash McCook, LLC + "xn--vuq861b", // 信息 Beijing Tele-info Network Technology Co., Ltd. + "xn--w4r85el8fhu5dnra", // 嘉里大酒店 Kerry Trading Co. Limited + "xn--w4rs40l", // 嘉里 Kerry Trading Co. Limited + "xn--xhq521b", // 广东 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--zfr164b", // 政务 China Organizational Name Administration Center + "xperia", // xperia Sony Mobile Communications AB + "xxx", // xxx ICM Registry LLC + "xyz", // xyz XYZ.COM LLC + "yachts", // yachts DERYachts, LLC + "yahoo", // yahoo Yahoo! Domain Services Inc. + "yamaxun", // yamaxun Amazon Registry Services, Inc. + "yandex", // yandex YANDEX, LLC + "yodobashi", // yodobashi YODOBASHI CAMERA CO.,LTD. + "yoga", // yoga Top Level Domain Holdings Limited + "yokohama", // yokohama GMO Registry, Inc. + "you", // you Amazon Registry Services, Inc. + "youtube", // youtube Charleston Road Registry Inc. + "yun", // yun QIHOO 360 TECHNOLOGY CO. LTD. + "zappos", // zappos Amazon Registry Services, Inc. + "zara", // zara Industria de Diseño Textil, S.A. (INDITEX, S.A.) + "zero", // zero Amazon Registry Services, Inc. + "zip", // zip Charleston Road Registry Inc. + "zippo", // zippo Zadco Company + "zone", // zone Outer Falls, LLC + "zuerich", // zuerich Kanton Zürich (Canton of Zurich) + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] COUNTRY_CODE_TLDS = new String[] { + "ac", // Ascension Island + "ad", // Andorra + "ae", // United Arab Emirates + "af", // Afghanistan + "ag", // Antigua and Barbuda + "ai", // Anguilla + "al", // Albania + "am", // Armenia +// "an", // Netherlands Antilles (retired) + "ao", // Angola + "aq", // Antarctica + "ar", // Argentina + "as", // American Samoa + "at", // Austria + "au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands) + "aw", // Aruba + "ax", // Åland + "az", // Azerbaijan + "ba", // Bosnia and Herzegovina + "bb", // Barbados + "bd", // Bangladesh + "be", // Belgium + "bf", // Burkina Faso + "bg", // Bulgaria + "bh", // Bahrain + "bi", // Burundi + "bj", // Benin + "bm", // Bermuda + "bn", // Brunei Darussalam + "bo", // Bolivia + "br", // Brazil + "bs", // Bahamas + "bt", // Bhutan + "bv", // Bouvet Island + "bw", // Botswana + "by", // Belarus + "bz", // Belize + "ca", // Canada + "cc", // Cocos (Keeling) Islands + "cd", // Democratic Republic of the Congo (formerly Zaire) + "cf", // Central African Republic + "cg", // Republic of the Congo + "ch", // Switzerland + "ci", // Côte d'Ivoire + "ck", // Cook Islands + "cl", // Chile + "cm", // Cameroon + "cn", // China, mainland + "co", // Colombia + "cr", // Costa Rica + "cu", // Cuba + "cv", // Cape Verde + "cw", // Curaçao + "cx", // Christmas Island + "cy", // Cyprus + "cz", // Czech Republic + "de", // Germany + "dj", // Djibouti + "dk", // Denmark + "dm", // Dominica + "do", // Dominican Republic + "dz", // Algeria + "ec", // Ecuador + "ee", // Estonia + "eg", // Egypt + "er", // Eritrea + "es", // Spain + "et", // Ethiopia + "eu", // European Union + "fi", // Finland + "fj", // Fiji + "fk", // Falkland Islands + "fm", // Federated States of Micronesia + "fo", // Faroe Islands + "fr", // France + "ga", // Gabon + "gb", // Great Britain (United Kingdom) + "gd", // Grenada + "ge", // Georgia + "gf", // French Guiana + "gg", // Guernsey + "gh", // Ghana + "gi", // Gibraltar + "gl", // Greenland + "gm", // The Gambia + "gn", // Guinea + "gp", // Guadeloupe + "gq", // Equatorial Guinea + "gr", // Greece + "gs", // South Georgia and the South Sandwich Islands + "gt", // Guatemala + "gu", // Guam + "gw", // Guinea-Bissau + "gy", // Guyana + "hk", // Hong Kong + "hm", // Heard Island and McDonald Islands + "hn", // Honduras + "hr", // Croatia (Hrvatska) + "ht", // Haiti + "hu", // Hungary + "id", // Indonesia + "ie", // Ireland (Éire) + "il", // Israel + "im", // Isle of Man + "in", // India + "io", // British Indian Ocean Territory + "iq", // Iraq + "ir", // Iran + "is", // Iceland + "it", // Italy + "je", // Jersey + "jm", // Jamaica + "jo", // Jordan + "jp", // Japan + "ke", // Kenya + "kg", // Kyrgyzstan + "kh", // Cambodia (Khmer) + "ki", // Kiribati + "km", // Comoros + "kn", // Saint Kitts and Nevis + "kp", // North Korea + "kr", // South Korea + "kw", // Kuwait + "ky", // Cayman Islands + "kz", // Kazakhstan + "la", // Laos (currently being marketed as the official domain for Los Angeles) + "lb", // Lebanon + "lc", // Saint Lucia + "li", // Liechtenstein + "lk", // Sri Lanka + "lr", // Liberia + "ls", // Lesotho + "lt", // Lithuania + "lu", // Luxembourg + "lv", // Latvia + "ly", // Libya + "ma", // Morocco + "mc", // Monaco + "md", // Moldova + "me", // Montenegro + "mg", // Madagascar + "mh", // Marshall Islands + "mk", // Republic of Macedonia + "ml", // Mali + "mm", // Myanmar + "mn", // Mongolia + "mo", // Macau + "mp", // Northern Mariana Islands + "mq", // Martinique + "mr", // Mauritania + "ms", // Montserrat + "mt", // Malta + "mu", // Mauritius + "mv", // Maldives + "mw", // Malawi + "mx", // Mexico + "my", // Malaysia + "mz", // Mozambique + "na", // Namibia + "nc", // New Caledonia + "ne", // Niger + "nf", // Norfolk Island + "ng", // Nigeria + "ni", // Nicaragua + "nl", // Netherlands + "no", // Norway + "np", // Nepal + "nr", // Nauru + "nu", // Niue + "nz", // New Zealand + "om", // Oman + "pa", // Panama + "pe", // Peru + "pf", // French Polynesia With Clipperton Island + "pg", // Papua New Guinea + "ph", // Philippines + "pk", // Pakistan + "pl", // Poland + "pm", // Saint-Pierre and Miquelon + "pn", // Pitcairn Islands + "pr", // Puerto Rico + "ps", // Palestinian territories (PA-controlled West Bank and Gaza Strip) + "pt", // Portugal + "pw", // Palau + "py", // Paraguay + "qa", // Qatar + "re", // Réunion + "ro", // Romania + "rs", // Serbia + "ru", // Russia + "rw", // Rwanda + "sa", // Saudi Arabia + "sb", // Solomon Islands + "sc", // Seychelles + "sd", // Sudan + "se", // Sweden + "sg", // Singapore + "sh", // Saint Helena + "si", // Slovenia + "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no) + "sk", // Slovakia + "sl", // Sierra Leone + "sm", // San Marino + "sn", // Senegal + "so", // Somalia + "sr", // Suriname + "st", // São Tomé and Príncipe + "su", // Soviet Union (deprecated) + "sv", // El Salvador + "sx", // Sint Maarten + "sy", // Syria + "sz", // Swaziland + "tc", // Turks and Caicos Islands + "td", // Chad + "tf", // French Southern and Antarctic Lands + "tg", // Togo + "th", // Thailand + "tj", // Tajikistan + "tk", // Tokelau + "tl", // East Timor (deprecated old code) + "tm", // Turkmenistan + "tn", // Tunisia + "to", // Tonga +// "tp", // East Timor (Retired) + "tr", // Turkey + "tt", // Trinidad and Tobago + "tv", // Tuvalu + "tw", // Taiwan, Republic of China + "tz", // Tanzania + "ua", // Ukraine + "ug", // Uganda + "uk", // United Kingdom + "us", // United States of America + "uy", // Uruguay + "uz", // Uzbekistan + "va", // Vatican City State + "vc", // Saint Vincent and the Grenadines + "ve", // Venezuela + "vg", // British Virgin Islands + "vi", // U.S. Virgin Islands + "vn", // Vietnam + "vu", // Vanuatu + "wf", // Wallis and Futuna + "ws", // Samoa (formerly Western Samoa) + "xn--3e0b707e", // 한국 KISA (Korea Internet & Security Agency) + "xn--45brj9c", // ভারত National Internet Exchange of India + "xn--54b7fta0cc", // বাংলা Posts and Telecommunications Division + "xn--80ao21a", // қаз Association of IT Companies of Kazakhstan + "xn--90a3ac", // срб Serbian National Internet Domain Registry (RNIDS) + "xn--90ais", // ??? Reliable Software Inc. + "xn--clchc0ea0b2g2a9gcd", // சிங்கப்பூர் Singapore Network Information Centre (SGNIC) Pte Ltd + "xn--d1alf", // мкд Macedonian Academic Research Network Skopje + "xn--e1a4c", // ею EURid vzw/asbl + "xn--fiqs8s", // 中国 China Internet Network Information Center + "xn--fiqz9s", // 中國 China Internet Network Information Center + "xn--fpcrj9c3d", // భారత్ National Internet Exchange of India + "xn--fzc2c9e2c", // ලංකා LK Domain Registry + "xn--gecrj9c", // ભારત National Internet Exchange of India + "xn--h2brj9c", // भारत National Internet Exchange of India + "xn--j1amh", // укр Ukrainian Network Information Centre (UANIC), Inc. + "xn--j6w193g", // 香港 Hong Kong Internet Registration Corporation Ltd. + "xn--kprw13d", // 台湾 Taiwan Network Information Center (TWNIC) + "xn--kpry57d", // 台灣 Taiwan Network Information Center (TWNIC) + "xn--l1acc", // мон Datacom Co.,Ltd + "xn--lgbbat1ad8j", // الجزائر CERIST + "xn--mgb9awbf", // عمان Telecommunications Regulatory Authority (TRA) + "xn--mgba3a4f16a", // ایران Institute for Research in Fundamental Sciences (IPM) + "xn--mgbaam7a8h", // امارات Telecommunications Regulatory Authority (TRA) + "xn--mgbayh7gpa", // الاردن National Information Technology Center (NITC) + "xn--mgbbh1a71e", // بھارت National Internet Exchange of India + "xn--mgbc0a9azcg", // المغرب Agence Nationale de Réglementation des Télécommunications (ANRT) + "xn--mgberp4a5d4ar", // السعودية Communications and Information Technology Commission + "xn--mgbpl2fh", // ????? Sudan Internet Society + "xn--mgbtx2b", // عراق Communications and Media Commission (CMC) + "xn--mgbx4cd0ab", // مليسيا MYNIC Berhad + "xn--mix891f", // 澳門 Bureau of Telecommunications Regulation (DSRT) + "xn--node", // გე Information Technologies Development Center (ITDC) + "xn--o3cw4h", // ไทย Thai Network Information Center Foundation + "xn--ogbpf8fl", // سورية National Agency for Network Services (NANS) + "xn--p1ai", // рф Coordination Center for TLD RU + "xn--pgbs0dh", // تونس Agence Tunisienne d'Internet + "xn--qxam", // ελ ICS-FORTH GR + "xn--s9brj9c", // ਭਾਰਤ National Internet Exchange of India + "xn--wgbh1c", // مصر National Telecommunication Regulatory Authority - NTRA + "xn--wgbl6a", // قطر Communications Regulatory Authority + "xn--xkc2al3hye2a", // இலங்கை LK Domain Registry + "xn--xkc2dl3a5ee0h", // இந்தியா National Internet Exchange of India + "xn--y9a3aq", // ??? Internet Society + "xn--yfro4i67o", // 新加坡 Singapore Network Information Centre (SGNIC) Pte Ltd + "xn--ygbi2ammx", // فلسطين Ministry of Telecom & Information Technology (MTIT) + "ye", // Yemen + "yt", // Mayotte + "za", // South Africa + "zm", // Zambia + "zw", // Zimbabwe + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] LOCAL_TLDS = new String[] { + "localdomain", // Also widely used as localhost.localdomain + "localhost", // RFC2606 defined + }; + + // Additional arrays to supplement or override the built in ones. + // The PLUS arrays are valid keys, the MINUS arrays are invalid keys + + /* + * This field is used to detect whether the getInstance has been called. + * After this, the method updateTLDOverride is not allowed to be called. + * This field does not need to be volatile since it is only accessed from + * synchronized methods. + */ + private static boolean inUse = false; + + /* + * These arrays are mutable, but they don't need to be volatile. + * They can only be updated by the updateTLDOverride method, and any readers must get an instance + * using the getInstance methods which are all (now) synchronised. + */ + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] countryCodeTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] genericTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] countryCodeTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] genericTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + + /** + * enum used by {@link DomainValidator#updateTLDOverride(DomainValidator.ArrayType, String[])} + * to determine which override array to update / fetch + * @since 1.5.0 + * @since 1.5.1 made public and added read-only array references + */ + public enum ArrayType { + /** Update (or get a copy of) the GENERIC_TLDS_PLUS table containing additonal generic TLDs */ + GENERIC_PLUS, + /** Update (or get a copy of) the GENERIC_TLDS_MINUS table containing deleted generic TLDs */ + GENERIC_MINUS, + /** Update (or get a copy of) the COUNTRY_CODE_TLDS_PLUS table containing additonal country code TLDs */ + COUNTRY_CODE_PLUS, + /** Update (or get a copy of) the COUNTRY_CODE_TLDS_MINUS table containing deleted country code TLDs */ + COUNTRY_CODE_MINUS, + /** Get a copy of the generic TLDS table */ + GENERIC_RO, + /** Get a copy of the country code table */ + COUNTRY_CODE_RO, + /** Get a copy of the infrastructure table */ + INFRASTRUCTURE_RO, + /** Get a copy of the local table */ + LOCAL_RO + ; + }; + + // For use by unit test code only + static synchronized void clearTLDOverrides() { + inUse = false; + countryCodeTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + countryCodeTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + genericTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + genericTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + } + /** + * Update one of the TLD override arrays. + * This must only be done at program startup, before any instances are accessed using getInstance. + *

    + * For example: + *

    + * {@code DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"apache"})} + *

    + * To clear an override array, provide an empty array. + * + * @param table the table to update, see {@link DomainValidator.ArrayType} + * Must be one of the following + *

      + *
    • COUNTRY_CODE_MINUS
    • + *
    • COUNTRY_CODE_PLUS
    • + *
    • GENERIC_MINUS
    • + *
    • GENERIC_PLUS
    • + *
    + * @param tlds the array of TLDs, must not be null + * @throws IllegalStateException if the method is called after getInstance + * @throws IllegalArgumentException if one of the read-only tables is requested + * @since 1.5.0 + */ + public static synchronized void updateTLDOverride(DomainValidator.ArrayType table, String [] tlds) { + if (inUse) { + throw new IllegalStateException("Can only invoke this method before calling getInstance"); + } + String [] copy = new String[tlds.length]; + // Comparisons are always done with lower-case entries + for (int i = 0; i < tlds.length; i++) { + copy[i] = tlds[i].toLowerCase(Locale.ENGLISH); + } + Arrays.sort(copy); + switch(table) { + case COUNTRY_CODE_MINUS: + countryCodeTLDsMinus = copy; + break; + case COUNTRY_CODE_PLUS: + countryCodeTLDsPlus = copy; + break; + case GENERIC_MINUS: + genericTLDsMinus = copy; + break; + case GENERIC_PLUS: + genericTLDsPlus = copy; + break; + case COUNTRY_CODE_RO: + case GENERIC_RO: + case INFRASTRUCTURE_RO: + case LOCAL_RO: + throw new IllegalArgumentException("Cannot update the table: " + table); + default: + throw new IllegalArgumentException("Unexpected enum value: " + table); + } + } + + /** + * Get a copy of the internal array. + * @param table the array type (any of the enum values) + * @return a copy of the array + * @throws IllegalArgumentException if the table type is unexpected (should not happen) + * @since 1.5.1 + */ + public static String [] getTLDEntries(DomainValidator.ArrayType table) { + final String array[]; + switch(table) { + case COUNTRY_CODE_MINUS: + array = countryCodeTLDsMinus; + break; + case COUNTRY_CODE_PLUS: + array = countryCodeTLDsPlus; + break; + case GENERIC_MINUS: + array = genericTLDsMinus; + break; + case GENERIC_PLUS: + array = genericTLDsPlus; + break; + case GENERIC_RO: + array = GENERIC_TLDS; + break; + case COUNTRY_CODE_RO: + array = COUNTRY_CODE_TLDS; + break; + case INFRASTRUCTURE_RO: + array = INFRASTRUCTURE_TLDS; + break; + case LOCAL_RO: + array = LOCAL_TLDS; + break; + default: + throw new IllegalArgumentException("Unexpected enum value: " + table); + } + return Arrays.copyOf(array, array.length); // clone the array + } + + /** + * Converts potentially Unicode input to punycode. + * If conversion fails, returns the original input. + * + * @param input the string to convert, not null + * @return converted input, or original input if conversion fails + */ + // Needed by UrlValidator + //[PATCH] + public + // end of [PATCH] + static String unicodeToASCII(String input) { + if (isOnlyASCII(input)) { // skip possibly expensive processing + return input; + } + try { + final String ascii = IDN.toASCII(input); + if (DomainValidator.IDNBUGHOLDER.IDN_TOASCII_PRESERVES_TRAILING_DOTS) { + return ascii; + } + final int length = input.length(); + if (length == 0) {// check there is a last character + return input; + } + // RFC3490 3.1. 1) + // Whenever dots are used as label separators, the following + // characters MUST be recognized as dots: U+002E (full stop), U+3002 + // (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 + // (halfwidth ideographic full stop). + char lastChar = input.charAt(length-1);// fetch original last char + switch(lastChar) { + case '\u002E': // "." full stop + case '\u3002': // ideographic full stop + case '\uFF0E': // fullwidth full stop + case '\uFF61': // halfwidth ideographic full stop + return ascii + "."; // restore the missing stop + default: + return ascii; + } + } catch (IllegalArgumentException e) { // input is not valid + return input; + } + } + + private static class IDNBUGHOLDER { + private static boolean keepsTrailingDot() { + final String input = "a."; // must be a valid name + return input.equals(IDN.toASCII(input)); + } + private static final boolean IDN_TOASCII_PRESERVES_TRAILING_DOTS = keepsTrailingDot(); + } + + /* + * Check if input contains only ASCII + * Treats null as all ASCII + */ + private static boolean isOnlyASCII(String input) { + if (input == null) { + return true; + } + for(int i=0; i < input.length(); i++) { + if (input.charAt(i) > 0x7F) { // CHECKSTYLE IGNORE MagicNumber + return false; + } + } + return true; + } + + /** + * Check if a sorted array contains the specified key + * + * @param sortedArray the array to search + * @param key the key to find + * @return {@code true} if the array contains the key + */ + private static boolean arrayContains(String[] sortedArray, String key) { + return Arrays.binarySearch(sortedArray, key) >= 0; + } +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..59ac7ceb2b0ad55cf00e3a01ae4e5bb4633a0625 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *

    InetAddress validation and conversion routines (java.net.InetAddress).

    + * + *

    This class provides methods to validate a candidate IP address. + * + *

    + * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method. + *

    + * + * @version $Revision: 1783032 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class InetAddressValidator implements Serializable { + + private static final int IPV4_MAX_OCTET_VALUE = 255; + + private static final int MAX_UNSIGNED_SHORT = 0xffff; + + private static final int BASE_16 = 16; + + private static final long serialVersionUID = -919201640201914789L; + + private static final String IPV4_REGEX = + "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; + + // Max number of hex groups (separated by :) in an IPV6 address + private static final int IPV6_MAX_HEX_GROUPS = 8; + + // Max hex digits in each IPv6 group + private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; + + /** + * Singleton instance of this class. + */ + private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); + + /** IPv4 RegexValidator */ + private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); + + /** + * Returns the singleton instance of this validator. + * @return the singleton instance of this validator + */ + public static InetAddressValidator getInstance() { + return VALIDATOR; + } + + /** + * Checks if the specified string is a valid IP address. + * @param inetAddress the string to validate + * @return true if the string validates as an IP address + */ + public boolean isValid(String inetAddress) { + return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); + } + + /** + * Validates an IPv4 address. Returns true if valid. + * @param inet4Address the IPv4 address to validate + * @return true if the argument contains a valid IPv4 address + */ + public boolean isValidInet4Address(String inet4Address) { + // verify that address conforms to generic IPv4 format + String[] groups = ipv4Validator.match(inet4Address); + + if (groups == null) { + return false; + } + + // verify that address subgroups are legal + for (String ipSegment : groups) { + if (ipSegment == null || ipSegment.length() == 0) { + return false; + } + + int iIpSegment = 0; + + try { + iIpSegment = Integer.parseInt(ipSegment); + } catch(NumberFormatException e) { + return false; + } + + if (iIpSegment > IPV4_MAX_OCTET_VALUE) { + return false; + } + + if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { + return false; + } + + } + + return true; + } + + /** + * Validates an IPv6 address. Returns true if valid. + * @param inet6Address the IPv6 address to validate + * @return true if the argument contains a valid IPv6 address + * + * @since 1.4.1 + */ + public boolean isValidInet6Address(String inet6Address) { + boolean containsCompressedZeroes = inet6Address.contains("::"); + if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { + return false; + } + if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::")) + || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) { + return false; + } + String[] octets = inet6Address.split(":"); + if (containsCompressedZeroes) { + List octetList = new ArrayList(Arrays.asList(octets)); + if (inet6Address.endsWith("::")) { + // String.split() drops ending empty segments + octetList.add(""); + } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { + octetList.remove(0); + } + octets = octetList.toArray(new String[octetList.size()]); + } + if (octets.length > IPV6_MAX_HEX_GROUPS) { + return false; + } + int validOctets = 0; + int emptyOctets = 0; // consecutive empty chunks + for (int index = 0; index < octets.length; index++) { + String octet = octets[index]; + if (octet.length() == 0) { + emptyOctets++; + if (emptyOctets > 1) { + return false; + } + } else { + emptyOctets = 0; + // Is last chunk an IPv4 address? + if (index == octets.length - 1 && octet.contains(".")) { + if (!isValidInet4Address(octet)) { + return false; + } + validOctets += 2; + continue; + } + if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { + return false; + } + int octetInt = 0; + try { + octetInt = Integer.parseInt(octet, BASE_16); + } catch (NumberFormatException e) { + return false; + } + if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { + return false; + } + } + validOctets++; + } + if (validOctets > IPV6_MAX_HEX_GROUPS || (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes)) { + return false; + } + return true; + } +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..eebc3747d9ab4f85a0896b1da1d240d1f1068456 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Regular Expression validation (using JDK 1.4+ regex support). + *

    + * Construct the validator either for a single regular expression or a set (array) of + * regular expressions. By default validation is case sensitive but constructors + * are provided to allow case in-sensitive validation. For example to create + * a validator which does case in-sensitive validation for a set of regular + * expressions: + *

    + *
    + * 
    + * String[] regexs = new String[] {...};
    + * RegexValidator validator = new RegexValidator(regexs, false);
    + * 
    + * 
    + * + *
      + *
    • Validate true or false:
    • + *
    • + *
        + *
      • boolean valid = validator.isValidRootUrl(value);
      • + *
      + *
    • + *
    • Validate returning an aggregated String of the matched groups:
    • + *
    • + *
        + *
      • String result = validator.validate(value);
      • + *
      + *
    • + *
    • Validate returning the matched groups:
    • + *
    • + *
        + *
      • String[] result = validator.match(value);
      • + *
      + *
    • + *
    + * + * Note that patterns are matched against the entire input. + * + *

    + * Cached instances pre-compile and re-use {@link Pattern}(s) - which according + * to the {@link Pattern} API are safe to use in a multi-threaded environment. + *

    + * + * @version $Revision: 1739356 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class RegexValidator implements Serializable { + + private static final long serialVersionUID = -8832409930574867162L; + + private final Pattern[] patterns; + + /** + * Construct a case sensitive validator for a single + * regular expression. + * + * @param regex The regular expression this validator will + * validate against + */ + public RegexValidator(String regex) { + this(regex, true); + } + + /** + * Construct a validator for a single regular expression + * with the specified case sensitivity. + * + * @param regex The regular expression this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String regex, boolean caseSensitive) { + this(new String[] {regex}, caseSensitive); + } + + /** + * Construct a case sensitive validator that matches any one + * of the set of regular expressions. + * + * @param regexs The set of regular expressions this validator will + * validate against + */ + public RegexValidator(String[] regexs) { + this(regexs, true); + } + + /** + * Construct a validator that matches any one of the set of regular + * expressions with the specified case sensitivity. + * + * @param regexs The set of regular expressions this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String[] regexs, boolean caseSensitive) { + if (regexs == null || regexs.length == 0) { + throw new IllegalArgumentException("Regular expressions are missing"); + } + patterns = new Pattern[regexs.length]; + int flags = (caseSensitive ? 0: Pattern.CASE_INSENSITIVE); + for (int i = 0; i < regexs.length; i++) { + if (regexs[i] == null || regexs[i].length() == 0) { + throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); + } + patterns[i] = Pattern.compile(regexs[i], flags); + } + } + + /** + * Validate a value against the set of regular expressions. + * + * @param value The value to validate. + * @return true if the value is valid + * otherwise false. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].matcher(value).matches()) { + return true; + } + } + return false; + } + + /** + * Validate a value against the set of regular expressions + * returning the array of matched groups. + * + * @param value The value to validate. + * @return String array of the groups matched if + * valid or null if invalid + */ + public String[] match(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + String[] groups = new String[count]; + for (int j = 0; j < count; j++) { + groups[j] = matcher.group(j+1); + } + return groups; + } + } + return null; + } + + + /** + * Validate a value against the set of regular expressions + * returning a String value of the aggregated groups. + * + * @param value The value to validate. + * @return Aggregated String value comprised of the + * groups matched if valid or null if invalid + */ + public String validate(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + if (count == 1) { + return matcher.group(1); + } + StringBuilder buffer = new StringBuilder(); + for (int j = 0; j < count; j++) { + String component = matcher.group(j+1); + if (component != null) { + buffer.append(component); + } + } + return buffer.toString(); + } + } + return null; + } + + /** + * Provide a String representation of this validator. + * @return A String representation of this validator + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append("RegexValidator{"); + for (int i = 0; i < patterns.length; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(patterns[i].pattern()); + } + buffer.append("}"); + return buffer.toString(); + } + +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..f309b177e63653a46b491c15cbefa44ba432900c --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java @@ -0,0 +1,551 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    URL Validation routines.

    + * Behavior of validation is modified by passing in options: + *
      + *
    • ALLOW_2_SLASHES - [FALSE] Allows double '/' characters in the path + * component.
    • + *
    • NO_FRAGMENT- [FALSE] By default fragments are allowed, if this option is + * included then fragments are flagged as illegal.
    • + *
    • ALLOW_ALL_SCHEMES - [FALSE] By default only http, https, and ftp are + * considered valid schemes. Enabling this option will let any scheme pass validation.
    • + *
    + * + *

    Originally based in on php script by Debbie Dyer, validation.php v1.2b, Date: 03/07/02, + * http://javascript.internet.com. However, this validation now bears little resemblance + * to the php original.

    + *
    + *   Example of usage:
    + *   Construct a UrlValidator with valid schemes of "http", and "https".
    + *
    + *    String[] schemes = {"http","https"}.
    + *    UrlValidator urlValidator = new UrlValidator(schemes);
    + *    if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
    + *       System.out.println("url is valid");
    + *    } else {
    + *       System.out.println("url is invalid");
    + *    }
    + *
    + *    prints "url is invalid"
    + *   If instead the default constructor is used.
    + *
    + *    UrlValidator urlValidator = new UrlValidator();
    + *    if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
    + *       System.out.println("url is valid");
    + *    } else {
    + *       System.out.println("url is invalid");
    + *    }
    + *
    + *   prints out "url is valid"
    + *  
    + * + * @see + * + * Uniform Resource Identifiers (URI): Generic Syntax + * + * + * @version $Revision: 1783203 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class UrlValidator implements Serializable { + + private static final long serialVersionUID = 7557161713937335013L; + + private static final int MAX_UNSIGNED_16_BIT_INT = 0xFFFF; // port max + + /** + * Allows all validly formatted schemes to pass validation instead of + * supplying a set of valid schemes. + */ + public static final long ALLOW_ALL_SCHEMES = 1 << 0; + + /** + * Allow two slashes in the path component of the URL. + */ + public static final long ALLOW_2_SLASHES = 1 << 1; + + /** + * Enabling this options disallows any URL fragments. + */ + public static final long NO_FRAGMENTS = 1 << 2; + + /** + * Allow local URLs, such as http://localhost/ or http://machine/ . + * This enables a broad-brush check, for complex local machine name + * validation requirements you should create your validator with + * a {@link RegexValidator} instead ({@link #UrlValidator(RegexValidator, long)}) + */ + public static final long ALLOW_LOCAL_URLS = 1 << 3; // CHECKSTYLE IGNORE MagicNumber + + /** + * This expression derived/taken from the BNF for URI (RFC2396). + */ + private static final String URL_REGEX = + "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"; + // 12 3 4 5 6 7 8 9 + private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX); + + /** + * Schema/Protocol (ie. http:, ftp:, file:, etc). + */ + private static final int PARSE_URL_SCHEME = 2; + + /** + * Includes hostname/ip and port number. + */ + private static final int PARSE_URL_AUTHORITY = 4; + + private static final int PARSE_URL_PATH = 5; + + private static final int PARSE_URL_QUERY = 7; + + private static final int PARSE_URL_FRAGMENT = 9; + + /** + * Protocol scheme (e.g. http, ftp, https). + */ + private static final String SCHEME_REGEX = "^\\p{Alpha}[\\p{Alnum}\\+\\-\\.]*"; + private static final Pattern SCHEME_PATTERN = Pattern.compile(SCHEME_REGEX); + + // Drop numeric, and "+-." for now + // TODO does not allow for optional userinfo. + // Validation of character set is done by isValidAuthority + private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\."; // allows for IPV4 but not IPV6 + private static final String IPV6_REGEX = "[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix + + // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + // We assume that password has the same valid chars as user info + private static final String USERINFO_CHARS_REGEX = "[a-zA-Z0-9%-._~!$&'()*+,;=]"; + // since neither ':' nor '@' are allowed chars, we don't need to use non-greedy matching + private static final String USERINFO_FIELD_REGEX = + USERINFO_CHARS_REGEX + "+" + // At least one character for the name + "(?::" + USERINFO_CHARS_REGEX + "*)?@"; // colon and password may be absent + private static final String AUTHORITY_REGEX = + "(?:\\[("+IPV6_REGEX+")\\]|(?:(?:"+USERINFO_FIELD_REGEX+")?([" + AUTHORITY_CHARS_REGEX + "]*)))(?::(\\d*))?(.*)?"; + // 1 e.g. user:pass@ 2 3 4 + private static final Pattern AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEX); + + private static final int PARSE_AUTHORITY_IPV6 = 1; + + private static final int PARSE_AUTHORITY_HOST_IP = 2; // excludes userinfo, if present + + private static final int PARSE_AUTHORITY_PORT = 3; // excludes leading colon + + /** + * Should always be empty. The code currently allows spaces. + */ + private static final int PARSE_AUTHORITY_EXTRA = 4; + + private static final String PATH_REGEX = "^(/[-\\w:@&?=+,.!/~*'%$_;\\(\\)]*)?$"; + private static final Pattern PATH_PATTERN = Pattern.compile(PATH_REGEX); + + private static final String QUERY_REGEX = "^(\\S*)$"; + private static final Pattern QUERY_PATTERN = Pattern.compile(QUERY_REGEX); + + /** + * Holds the set of current validation options. + */ + private final long options; + + /** + * The set of schemes that are allowed to be in a URL. + */ + private final Set allowedSchemes; // Must be lower-case + + /** + * Regular expressions used to manually validate authorities if IANA + * domain name validation isn't desired. + */ + private final RegexValidator authorityValidator; + + /** + * If no schemes are provided, default to this set. + */ + private static final String[] DEFAULT_SCHEMES = {"http", "https", "ftp"}; // Must be lower-case + + /** + * Singleton instance of this class with default schemes and options. + */ + private static final UrlValidator DEFAULT_URL_VALIDATOR = new UrlValidator(); + + /** + * Returns the singleton instance of this class with default schemes and options. + * @return singleton instance with default schemes and options + */ + public static UrlValidator getInstance() { + return DEFAULT_URL_VALIDATOR; + } + + /** + * Create a UrlValidator with default properties. + */ + public UrlValidator() { + this(null); + } + + /** + * Behavior of validation is modified by passing in several strings options: + * @param schemes Pass in one or more url schemes to consider valid, passing in + * a null will default to "http,https,ftp" being valid. + * If a non-null schemes is specified then all valid schemes must + * be specified. Setting the ALLOW_ALL_SCHEMES option will + * ignore the contents of schemes. + */ + public UrlValidator(String[] schemes) { + this(schemes, 0L); + } + + /** + * Initialize a UrlValidator with the given validation options. + * @param options The options should be set using the public constants declared in + * this class. To set multiple options you simply add them together. For example, + * ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options. + */ + public UrlValidator(long options) { + this(null, null, options); + } + + /** + * Behavior of validation is modified by passing in options: + * @param schemes The set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set. + * @param options The options should be set using the public constants declared in + * this class. To set multiple options you simply add them together. For example, + * ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options. + */ + public UrlValidator(String[] schemes, long options) { + this(schemes, null, options); + } + + /** + * Initialize a UrlValidator with the given validation options. + * @param authorityValidator Regular expression validator used to validate the authority part + * This allows the user to override the standard set of domains. + * @param options Validation options. Set using the public constants of this class. + * To set multiple options, simply add them together: + *

    ALLOW_2_SLASHES + NO_FRAGMENTS

    + * enables both of those options. + */ + public UrlValidator(RegexValidator authorityValidator, long options) { + this(null, authorityValidator, options); + } + + /** + * Customizable constructor. Validation behavior is modifed by passing in options. + * @param schemes the set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set. + * @param authorityValidator Regular expression validator used to validate the authority part + * @param options Validation options. Set using the public constants of this class. + * To set multiple options, simply add them together: + *

    ALLOW_2_SLASHES + NO_FRAGMENTS

    + * enables both of those options. + */ + public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options) { + this.options = options; + + if (isOn(ALLOW_ALL_SCHEMES)) { + allowedSchemes = Collections.emptySet(); + } else { + if (schemes == null) { + schemes = DEFAULT_SCHEMES; + } + allowedSchemes = new HashSet(schemes.length); + for(int i=0; i < schemes.length; i++) { + allowedSchemes.add(schemes[i].toLowerCase(Locale.ENGLISH)); + } + } + + this.authorityValidator = authorityValidator; + } + + /** + *

    Checks if a field has a valid url address.

    + * + * Note that the method calls #isValidAuthority() + * which checks that the domain is valid. + * + * @param value The value validation is being performed on. A null + * value is considered invalid. + * @return true if the url is valid. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + + // Check the whole url address structure + Matcher urlMatcher = URL_PATTERN.matcher(value); + if (!urlMatcher.matches()) { + return false; + } + + String scheme = urlMatcher.group(PARSE_URL_SCHEME); + if (!isValidScheme(scheme)) { + return false; + } + + String authority = urlMatcher.group(PARSE_URL_AUTHORITY); + if ("file".equals(scheme)) {// Special case - file: allows an empty authority + if (authority != null) { + if (authority.contains(":")) { // but cannot allow trailing : + return false; + } + } + // drop through to continue validation + } else { // not file: + // Validate the authority + if (!isValidAuthority(authority)) { + return false; + } + } + + if (!isValidPath(urlMatcher.group(PARSE_URL_PATH))) { + return false; + } + + if (!isValidQuery(urlMatcher.group(PARSE_URL_QUERY))) { + return false; + } + + if (!isValidFragment(urlMatcher.group(PARSE_URL_FRAGMENT))) { + return false; + } + + return true; + } + + /** + * Validate scheme. If schemes[] was initialized to a non null, + * then only those schemes are allowed. + * Otherwise the default schemes are "http", "https", "ftp". + * Matching is case-blind. + * @param scheme The scheme to validate. A null value is considered + * invalid. + * @return true if valid. + */ + protected boolean isValidScheme(String scheme) { + if (scheme == null) { + return false; + } + + // TODO could be removed if external schemes were checked in the ctor before being stored + if (!SCHEME_PATTERN.matcher(scheme).matches()) { + return false; + } + + if (isOff(ALLOW_ALL_SCHEMES) && !allowedSchemes.contains(scheme.toLowerCase(Locale.ENGLISH))) { + return false; + } + + return true; + } + + /** + * Returns true if the authority is properly formatted. An authority is the combination + * of hostname and port. A null authority value is considered invalid. + * Note: this implementation validates the domain unless a RegexValidator was provided. + * If a RegexValidator was supplied and it matches, then the authority is regarded + * as valid with no further checks, otherwise the method checks against the + * AUTHORITY_PATTERN and the DomainValidator (ALLOW_LOCAL_URLS) + * @param authority Authority value to validate, alllows IDN + * @return true if authority (hostname and port) is valid. + */ + protected boolean isValidAuthority(String authority) { + if (authority == null) { + return false; + } + + // check manual authority validation if specified + if (authorityValidator != null && authorityValidator.isValid(authority)) { + return true; + } + // convert to ASCII if possible + final String authorityASCII = DomainValidator.unicodeToASCII(authority); + + Matcher authorityMatcher = AUTHORITY_PATTERN.matcher(authorityASCII); + if (!authorityMatcher.matches()) { + return false; + } + + // We have to process IPV6 separately because that is parsed in a different group + String ipv6 = authorityMatcher.group(PARSE_AUTHORITY_IPV6); + if (ipv6 != null) { + InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); + if (!inetAddressValidator.isValidInet6Address(ipv6)) { + return false; + } + } else { + String hostLocation = authorityMatcher.group(PARSE_AUTHORITY_HOST_IP); + // check if authority is hostname or IP address: + // try a hostname first since that's much more likely + DomainValidator domainValidator = DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS)); + if (!domainValidator.isValid(hostLocation)) { + // try an IPv4 address + InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); + if (!inetAddressValidator.isValidInet4Address(hostLocation)) { + // isn't IPv4, so the URL is invalid + return false; + } + } + String port = authorityMatcher.group(PARSE_AUTHORITY_PORT); + if (port != null && port.length() > 0) { + try { + int iPort = Integer.parseInt(port); + if (iPort < 0 || iPort > MAX_UNSIGNED_16_BIT_INT) { + return false; + } + } catch (NumberFormatException nfe) { + return false; // this can happen for big numbers + } + } + } + + String extra = authorityMatcher.group(PARSE_AUTHORITY_EXTRA); + if (extra != null && extra.trim().length() > 0){ + return false; + } + + return true; + } + + /** + * Returns true if the path is valid. A null value is considered invalid. + * @param path Path value to validate. + * @return true if path is valid. + */ + protected boolean isValidPath(String path) { + if (path == null) { + return false; + } + + if (!PATH_PATTERN.matcher(path).matches()) { + return false; + } + + try { + URI uri = new URI(null,null,path,null); + String norm = uri.normalize().getPath(); + if (norm.startsWith("/../") // Trying to go via the parent dir + || norm.equals("/..")) { // Trying to go to the parent dir + return false; + } + } catch (URISyntaxException e) { + return false; + } + + int slash2Count = countToken("//", path); + if (isOff(ALLOW_2_SLASHES) && (slash2Count > 0)) { + return false; + } + + return true; + } + + /** + * Returns true if the query is null or it's a properly formatted query string. + * @param query Query value to validate. + * @return true if query is valid. + */ + protected boolean isValidQuery(String query) { + if (query == null) { + return true; + } + + return QUERY_PATTERN.matcher(query).matches(); + } + + /** + * Returns true if the given fragment is null or fragments are allowed. + * @param fragment Fragment value to validate. + * @return true if fragment is valid. + */ + protected boolean isValidFragment(String fragment) { + if (fragment == null) { + return true; + } + + return isOff(NO_FRAGMENTS); + } + + /** + * Returns the number of times the token appears in the target. + * @param token Token value to be counted. + * @param target Target value to count tokens in. + * @return the number of tokens. + */ + protected int countToken(String token, String target) { + int tokenIndex = 0; + int count = 0; + while (tokenIndex != -1) { + tokenIndex = target.indexOf(token, tokenIndex); + if (tokenIndex > -1) { + tokenIndex++; + count++; + } + } + return count; + } + + /** + * Tests whether the given flag is on. If the flag is not a power of 2 + * (ie. 3) this tests whether the combination of flags is on. + * + * @param flag Flag value to check. + * + * @return whether the specified flag value is on. + */ + private boolean isOn(long flag) { + return (options & flag) > 0; + } + + /** + * Tests whether the given flag is off. If the flag is not a power of 2 + * (ie. 3) this tests whether the combination of flags is off. + * + * @param flag Flag value to check. + * + * @return whether the specified flag value is off. + */ + private boolean isOff(long flag) { + return (options & flag) == 0; + } + + // Unit test access to pattern matcher + Matcher matchURL(String value) { + return URL_PATTERN.matcher(value); + } +} diff --git a/core/src/main/java/jenkins/security/ApiTokenProperty.java b/core/src/main/java/jenkins/security/ApiTokenProperty.java index dd64d0e9d05862d842375c8c4ac4cc2e8fccd83b..1c2490052d0ee0e3bd7baa1214cecde16da96ab3 100644 --- a/core/src/main/java/jenkins/security/ApiTokenProperty.java +++ b/core/src/main/java/jenkins/security/ApiTokenProperty.java @@ -23,9 +23,13 @@ */ package jenkins.security; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; -import jenkins.util.SystemProperties; import hudson.Util; +import jenkins.security.apitoken.ApiTokenPropertyConfiguration; +import jenkins.security.apitoken.ApiTokenStats; +import jenkins.security.apitoken.ApiTokenStore; +import jenkins.util.SystemProperties; import hudson.model.Descriptor.FormException; import hudson.model.User; import hudson.model.UserProperty; @@ -34,19 +38,33 @@ import hudson.security.ACL; import hudson.util.HttpResponses; import hudson.util.Secret; import jenkins.model.Jenkins; +import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import java.io.IOException; -import java.nio.charset.Charset; -import java.security.MessageDigest; import java.security.SecureRandom; +import java.text.SimpleDateFormat; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -61,49 +79,108 @@ import org.kohsuke.stapler.interceptor.RequirePOST; * @since 1.426 */ public class ApiTokenProperty extends UserProperty { - private volatile Secret apiToken; + private static final Logger LOGGER = Logger.getLogger(ApiTokenProperty.class.getName()); /** - * If enabled, shows API tokens to users with {@link Jenkins#ADMINISTER) permissions. - * Disabled by default due to the security reasons. + * If enabled, the users with {@link Jenkins#ADMINISTER} permissions can view legacy tokens for + * other users.

    + * Disabled by default due to the security reasons.

    * If enabled, it restores the original Jenkins behavior (SECURITY-200). + * * @since 1.638 */ - private static final boolean SHOW_TOKEN_TO_ADMINS = + @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") + private static /* not final */ boolean SHOW_LEGACY_TOKEN_TO_ADMINS = SystemProperties.getBoolean(ApiTokenProperty.class.getName() + ".showTokenToAdmins"); + /** + * If enabled, the users with {@link Jenkins#ADMINISTER} permissions can generate new tokens for + * other users. Normally a user can only generate tokens for himself.

    + * Take care that only the creator of a token will have the plain value as it's only stored as an hash in the system.

    + * Disabled by default due to the security reasons. + * It's the version of {@link #SHOW_LEGACY_TOKEN_TO_ADMINS} for the new API Token system (SECURITY-200). + * + * @since 2.129 + */ + @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") + private static /* not final */ boolean ADMIN_CAN_GENERATE_NEW_TOKENS = + SystemProperties.getBoolean(ApiTokenProperty.class.getName() + ".adminCanGenerateNewTokens"); + + private volatile Secret apiToken; + private ApiTokenStore tokenStore; + + /** + * Store the usage information of the different token for this user + * The save operation can be toggled by using {@link ApiTokenPropertyConfiguration#usageStatisticsEnabled} + * The information are stored in a separate file to avoid problem with some configuration synchronization tools + */ + private transient ApiTokenStats tokenStats; @DataBoundConstructor public ApiTokenProperty() { - _changeApiToken(); } - + + @Override + protected void setUser(User u) { + super.setUser(u); + + if (this.tokenStore == null) { + this.tokenStore = new ApiTokenStore(); + } + if(this.tokenStats == null){ + this.tokenStats = ApiTokenStats.load(user.getUserFolder()); + } + if(this.apiToken != null){ + this.tokenStore.regenerateTokenFromLegacyIfRequired(this.apiToken); + } + } + /** * We don't let the external code set the API token, * but for the initial value of the token we need to compute the seed by ourselves. */ - /*package*/ ApiTokenProperty(String seed) { - apiToken = Secret.fromString(seed); + /*package*/ ApiTokenProperty(@CheckForNull String seed) { + if(seed != null){ + apiToken = Secret.fromString(seed); + } } /** * Gets the API token. * The method performs security checks since 1.638. Only the current user and SYSTEM may see it. - * Users with {@link Jenkins#ADMINISTER} may be allowed to do it using {@link #SHOW_TOKEN_TO_ADMINS}. + * Users with {@link Jenkins#ADMINISTER} may be allowed to do it using {@link #SHOW_LEGACY_TOKEN_TO_ADMINS}. * * @return API Token. Never null, but may be {@link Messages#ApiTokenProperty_ChangeToken_TokenIsHidden()} * if the user has no appropriate permissions. * @since 1.426, and since 1.638 the method performs security checks */ @Nonnull + @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION") public String getApiToken() { - return hasPermissionToSeeToken() ? getApiTokenInsecure() + LOGGER.log(Level.FINE, "Deprecated usage of getApiToken"); + if(LOGGER.isLoggable(Level.FINER)){ + LOGGER.log(Level.FINER, "Deprecated usage of getApiToken (trace)", new Exception()); + } + return hasPermissionToSeeToken() + ? getApiTokenInsecure() : Messages.ApiTokenProperty_ChangeToken_TokenIsHidden(); } + /** + * Determine if the legacy token is still present + */ + @Restricted(NoExternalUse.class) + public boolean hasLegacyToken(){ + return apiToken != null; + } + @Nonnull @Restricted(NoExternalUse.class) /*package*/ String getApiTokenInsecure() { + if(apiToken == null){ + return Messages.ApiTokenProperty_NoLegacyToken(); + } + String p = apiToken.getPlainText(); if (p.equals(Util.getDigestOf(Jenkins.getInstance().getSecretKey()+":"+user.getId()))) { // if the current token is the initial value created by pre SECURITY-49 Jenkins, we can't use that. @@ -112,24 +189,32 @@ public class ApiTokenProperty extends UserProperty { } return Util.getDigestOf(p); } - - public boolean matchesPassword(String password) { - String token = getApiTokenInsecure(); - // String.equals isn't constant time, but this is - return MessageDigest.isEqual(password.getBytes(Charset.forName("US-ASCII")), - token.getBytes(Charset.forName("US-ASCII"))); + + public boolean matchesPassword(String token) { + if(StringUtils.isBlank(token)){ + return false; + } + + ApiTokenStore.HashedToken matchingToken = tokenStore.findMatchingToken(token); + if(matchingToken == null){ + return false; + } + + tokenStats.updateUsageForId(matchingToken.getUuid()); + + return true; } + /** + * Only for legacy token + */ private boolean hasPermissionToSeeToken() { - final Jenkins jenkins = Jenkins.getInstance(); - // Administrators can do whatever they want - if (SHOW_TOKEN_TO_ADMINS && jenkins.hasPermission(Jenkins.ADMINISTER)) { + if (SHOW_LEGACY_TOKEN_TO_ADMINS && Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { return true; } - - final User current = User.current(); + User current = User.current(); if (current == null) { // Anonymous return false; } @@ -138,36 +223,158 @@ public class ApiTokenProperty extends UserProperty { if (Jenkins.getAuthentication() == ACL.SYSTEM) { return true; } - - //TODO: replace by IdStrategy in newer Jenkins versions - //return User.idStrategy().equals(user.getId(), current.getId()); - return StringUtils.equals(user.getId(), current.getId()); + + return User.idStrategy().equals(user.getId(), current.getId()); + } + + // only for Jelly + @Restricted(NoExternalUse.class) + public Collection getTokenList() { + return tokenStore.getTokenListSortedByName() + .stream() + .map(token -> { + ApiTokenStats.SingleTokenStats stats = tokenStats.findTokenStatsById(token.getUuid()); + return new TokenInfoAndStats(token, stats); + }) + .collect(Collectors.toList()); + } + + // only for Jelly + @Immutable + @Restricted(NoExternalUse.class) + public static class TokenInfoAndStats { + public final String uuid; + public final String name; + public final Date creationDate; + public final long numDaysCreation; + public final boolean isLegacy; + + public final int useCounter; + public final Date lastUseDate; + public final long numDaysUse; + + public TokenInfoAndStats(@Nonnull ApiTokenStore.HashedToken token, @Nonnull ApiTokenStats.SingleTokenStats stats) { + this.uuid = token.getUuid(); + this.name = token.getName(); + this.creationDate = token.getCreationDate(); + this.numDaysCreation = token.getNumDaysCreation(); + this.isLegacy = token.isLegacy(); + + this.useCounter = stats.getUseCounter(); + this.lastUseDate = stats.getLastUseDate(); + this.numDaysUse = stats.getNumDaysUse(); + } } + + /** + * Allow user to rename tokens + */ + @Override + public UserProperty reconfigure(StaplerRequest req, @CheckForNull JSONObject form) throws FormException { + if(form == null){ + return this; + } + Object tokenStoreData = form.get("tokenStore"); + Map tokenStoreTypedData = convertToTokenMap(tokenStoreData); + this.tokenStore.reconfigure(tokenStoreTypedData); + return this; + } + + private Map convertToTokenMap(Object tokenStoreData) { + if (tokenStoreData == null) { + // in case there are no token + return Collections.emptyMap(); + } else if (tokenStoreData instanceof JSONObject) { + // in case there is only one token + JSONObject singleTokenData = (JSONObject) tokenStoreData; + Map result = new HashMap<>(); + addJSONTokenIntoMap(result, singleTokenData); + return result; + } else if (tokenStoreData instanceof JSONArray) { + // in case there are multiple tokens + JSONArray tokenArray = ((JSONArray) tokenStoreData); + Map result = new HashMap<>(); + for (int i = 0; i < tokenArray.size(); i++) { + JSONObject tokenData = tokenArray.getJSONObject(i); + addJSONTokenIntoMap(result, tokenData); + } + return result; + } + + throw HttpResponses.error(400, "Unexpected class received for the token store information"); + } + + private void addJSONTokenIntoMap(Map tokenMap, JSONObject tokenData) { + String uuid = tokenData.getString("tokenUuid"); + tokenMap.put(uuid, tokenData); + } + + /** + * Only usable if the user still has the legacy API token. + * @deprecated Each token can be revoked now and new tokens can be requested without altering existing ones. + */ + @Deprecated public void changeApiToken() throws IOException { + // just to keep the same level of security user.checkPermission(Jenkins.ADMINISTER); + + LOGGER.log(Level.FINE, "Deprecated usage of changeApiToken"); + + ApiTokenStore.HashedToken existingLegacyToken = tokenStore.getLegacyToken(); _changeApiToken(); + tokenStore.regenerateTokenFromLegacy(apiToken); + + if(existingLegacyToken != null){ + tokenStats.removeId(existingLegacyToken.getUuid()); + } user.save(); } - - private void _changeApiToken() { + + @Deprecated + private void _changeApiToken(){ byte[] random = new byte[16]; // 16x8=128bit worth of randomness, since we use md5 digest as the API token RANDOM.nextBytes(random); apiToken = Secret.fromString(Util.toHexString(random)); } - - @Override - public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws FormException { - return this; + + /** + * Does not revoke the token stored in the store + */ + @Restricted(NoExternalUse.class) + public void deleteApiToken(){ + this.apiToken = null; + } + + @Restricted(NoExternalUse.class) + public ApiTokenStore getTokenStore() { + return tokenStore; + } + + @Restricted(NoExternalUse.class) + public ApiTokenStats getTokenStats() { + return tokenStats; } - @Extension @Symbol("apiToken") + @Extension + @Symbol("apiToken") public static final class DescriptorImpl extends UserPropertyDescriptor { public String getDisplayName() { return Messages.ApiTokenProperty_DisplayName(); } + @Restricted(NoExternalUse.class) // Jelly use + public String getNoLegacyToken(){ + return Messages.ApiTokenProperty_NoLegacyToken(); + } + /** + * New approach: + * API Token are generated only when a user request a new one. The value is randomly generated + * without any link to the user and only displayed to him the first time. + * We only store the hash for future comparisons. + * + * Legacy approach: * When we are creating a default {@link ApiTokenProperty} for User, * we need to make sure it yields the same value for the same user, * because there's no guarantee that the property is saved. @@ -176,29 +383,190 @@ public class ApiTokenProperty extends UserProperty { * the initial API token value. So we take the seed by hashing the secret + user ID. */ public ApiTokenProperty newInstance(User user) { - return new ApiTokenProperty(API_KEY_SEED.mac(user.getId())); + if (!ApiTokenPropertyConfiguration.get().isTokenGenerationOnCreationEnabled()) { + return forceNewInstance(user, false); + } + + return forceNewInstance(user, true); + } + + private ApiTokenProperty forceNewInstance(User user, boolean withLegacyToken) { + if(withLegacyToken){ + return new ApiTokenProperty(API_KEY_SEED.mac(user.getId())); + }else{ + return new ApiTokenProperty(null); + } + } + + // for Jelly view + @Restricted(NoExternalUse.class) + public boolean isStatisticsEnabled(){ + return ApiTokenPropertyConfiguration.get().isUsageStatisticsEnabled(); + } + + // for Jelly view + @Restricted(NoExternalUse.class) + public boolean mustDisplayLegacyApiToken(User propertyOwner) { + ApiTokenProperty property = propertyOwner.getProperty(ApiTokenProperty.class); + if(property != null && property.apiToken != null){ + return true; + } + return ApiTokenPropertyConfiguration.get().isCreationOfLegacyTokenEnabled(); + } + + // for Jelly view + @Restricted(NoExternalUse.class) + public boolean hasCurrentUserRightToGenerateNewToken(User propertyOwner){ + if (ADMIN_CAN_GENERATE_NEW_TOKENS && Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { + return true; + } + + User currentUser = User.current(); + if (currentUser == null) { + // Anonymous + return false; + } + + if (Jenkins.getAuthentication() == ACL.SYSTEM) { + // SYSTEM user is always eligible to see tokens + return true; + } + + return User.idStrategy().equals(propertyOwner.getId(), currentUser.getId()); } + /** + * @deprecated use {@link #doGenerateNewToken(User, String)} instead + */ + @Deprecated @RequirePOST public HttpResponse doChangeToken(@AncestorInPath User u, StaplerResponse rsp) throws IOException { + // you are the user or you have ADMINISTER permission + u.checkPermission(Jenkins.ADMINISTER); + + LOGGER.log(Level.FINE, "Deprecated action /changeToken used, consider using /generateNewToken instead"); + + if(!mustDisplayLegacyApiToken(u)){ + // user does not have legacy token and the capability to create one without an existing one is disabled + return HttpResponses.html(Messages.ApiTokenProperty_ChangeToken_CapabilityNotAllowed()); + } + ApiTokenProperty p = u.getProperty(ApiTokenProperty.class); - if (p==null) { - p = newInstance(u); + if (p == null) { + p = forceNewInstance(u, true); + p.setUser(u); u.addProperty(p); } else { + // even if the user does not have legacy token, this method let some legacy system to regenerate one p.changeApiToken(); } + rsp.setHeader("script","document.getElementById('apiToken').value='"+p.getApiToken()+"'"); - return HttpResponses.html(p.hasPermissionToSeeToken() - ? Messages.ApiTokenProperty_ChangeToken_Success() + return HttpResponses.html(p.hasPermissionToSeeToken() + ? Messages.ApiTokenProperty_ChangeToken_Success() : Messages.ApiTokenProperty_ChangeToken_SuccessHidden()); } - } + @RequirePOST + public HttpResponse doGenerateNewToken(@AncestorInPath User u, @QueryParameter String newTokenName) throws IOException { + if(!hasCurrentUserRightToGenerateNewToken(u)){ + return HttpResponses.forbidden(); + } + + final String tokenName; + if (StringUtils.isBlank(newTokenName)) { + tokenName = String.format("Token created on %s", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())); + }else{ + tokenName = newTokenName; + } + + ApiTokenProperty p = u.getProperty(ApiTokenProperty.class); + if (p == null) { + p = forceNewInstance(u, false); + u.addProperty(p); + } + + ApiTokenStore.TokenUuidAndPlainValue tokenUuidAndPlainValue = p.tokenStore.generateNewToken(tokenName); + u.save(); + + return HttpResponses.okJSON(new HashMap() {{ + put("tokenUuid", tokenUuidAndPlainValue.tokenUuid); + put("tokenName", tokenName); + put("tokenValue", tokenUuidAndPlainValue.plainValue); + }}); + } + + @RequirePOST + public HttpResponse doRename(@AncestorInPath User u, + @QueryParameter String tokenUuid, @QueryParameter String newName) throws IOException { + // only current user + administrator can rename token + u.checkPermission(Jenkins.ADMINISTER); + + if (StringUtils.isBlank(newName)) { + return HttpResponses.errorJSON("The name cannot be empty"); + } + if(StringUtils.isBlank(tokenUuid)){ + // using the web UI this should not occur + return HttpResponses.errorWithoutStack(400, "The tokenUuid cannot be empty"); + } + + ApiTokenProperty p = u.getProperty(ApiTokenProperty.class); + if (p == null) { + return HttpResponses.errorWithoutStack(400, "The user does not have any ApiToken yet, try generating one before."); + } + + boolean renameOk = p.tokenStore.renameToken(tokenUuid, newName); + if(!renameOk){ + // that could potentially happen if the token is removed from another page + // between your page loaded and your action + return HttpResponses.errorJSON("No token found, try refreshing the page"); + } + + u.save(); + + return HttpResponses.ok(); + } + + @RequirePOST + public HttpResponse doRevoke(@AncestorInPath User u, + @QueryParameter String tokenUuid) throws IOException { + // only current user + administrator can revoke token + u.checkPermission(Jenkins.ADMINISTER); + + if(StringUtils.isBlank(tokenUuid)){ + // using the web UI this should not occur + return HttpResponses.errorWithoutStack(400, "The tokenUuid cannot be empty"); + } + + ApiTokenProperty p = u.getProperty(ApiTokenProperty.class); + if (p == null) { + return HttpResponses.errorWithoutStack(400, "The user does not have any ApiToken yet, try generating one before."); + } + + ApiTokenStore.HashedToken revoked = p.tokenStore.revokeToken(tokenUuid); + if(revoked != null){ + if(revoked.isLegacy()){ + // if the user revoked the API Token, we can delete it + p.apiToken = null; + } + p.tokenStats.removeId(revoked.getUuid()); + } + u.save(); + + return HttpResponses.ok(); + } + } + + /** + * Only used for legacy API Token generation and change. After that token is revoked, it will be useless. + */ + @Deprecated private static final SecureRandom RANDOM = new SecureRandom(); /** * We don't want an API key that's too long, so cut the length to 16 (which produces 32-letter MAC code in hexdump) */ - private static final HMACConfidentialKey API_KEY_SEED = new HMACConfidentialKey(ApiTokenProperty.class,"seed",16); + @Deprecated + @Restricted(NoExternalUse.class) + public static final HMACConfidentialKey API_KEY_SEED = new HMACConfidentialKey(ApiTokenProperty.class,"seed",16); } diff --git a/core/src/main/java/jenkins/security/BasicApiTokenHelper.java b/core/src/main/java/jenkins/security/BasicApiTokenHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..ed45727b438273529174ac4632437dacf20f8fb0 --- /dev/null +++ b/core/src/main/java/jenkins/security/BasicApiTokenHelper.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security; + +import hudson.Util; +import hudson.model.User; +import jenkins.model.GlobalConfiguration; +import jenkins.security.apitoken.ApiTokenPropertyConfiguration; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.CheckForNull; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +@Restricted(NoExternalUse.class) +public class BasicApiTokenHelper { + public static @CheckForNull User isConnectingUsingApiToken(String username, String tokenValue){ + User user = User.getById(username, false); + if(user == null){ + ApiTokenPropertyConfiguration apiTokenConfiguration = GlobalConfiguration.all().getInstance(ApiTokenPropertyConfiguration.class); + if(apiTokenConfiguration.isTokenGenerationOnCreationEnabled()){ + String generatedTokenOnCreation = Util.getDigestOf(ApiTokenProperty.API_KEY_SEED.mac(username)); + boolean areTokenEqual = MessageDigest.isEqual( + generatedTokenOnCreation.getBytes(StandardCharsets.US_ASCII), + tokenValue.getBytes(StandardCharsets.US_ASCII) + ); + if(areTokenEqual){ + // directly return the user freshly created + // and no need to check its token as the generated token + // will be the same as the one we checked just above + return User.getById(username, true); + } + } + }else{ + ApiTokenProperty t = user.getProperty(ApiTokenProperty.class); + if (t!=null && t.matchesPassword(tokenValue)) { + return user; + } + } + + return null; + } +} diff --git a/core/src/main/java/jenkins/security/BasicHeaderApiTokenAuthenticator.java b/core/src/main/java/jenkins/security/BasicHeaderApiTokenAuthenticator.java index 345281fdce41c1f87899ced68bc755c3fc432bc7..2b0fb730ba2e7dbccc793d56574e8fd765b0f20f 100644 --- a/core/src/main/java/jenkins/security/BasicHeaderApiTokenAuthenticator.java +++ b/core/src/main/java/jenkins/security/BasicHeaderApiTokenAuthenticator.java @@ -28,10 +28,8 @@ public class BasicHeaderApiTokenAuthenticator extends BasicHeaderAuthenticator { */ @Override public Authentication authenticate(HttpServletRequest req, HttpServletResponse rsp, String username, String password) throws ServletException { - // attempt to authenticate as API token - User u = User.getById(username, true); - ApiTokenProperty t = u.getProperty(ApiTokenProperty.class); - if (t!=null && t.matchesPassword(password)) { + User u = BasicApiTokenHelper.isConnectingUsingApiToken(username, password); + if(u != null) { Authentication auth; try { UserDetails userDetails = u.getUserDetailsForImpersonation(); @@ -41,7 +39,7 @@ public class BasicHeaderApiTokenAuthenticator extends BasicHeaderAuthenticator { } catch (UsernameNotFoundException x) { // The token was valid, but the impersonation failed. This token is clearly not his real password, // so there's no point in continuing the request processing. Report this error and abort. - LOGGER.log(WARNING, "API token matched for user "+username+" but the impersonation failed",x); + LOGGER.log(WARNING, "API token matched for user " + username + " but the impersonation failed", x); throw new ServletException(x); } catch (DataAccessException x) { throw new ServletException(x); diff --git a/core/src/main/java/jenkins/security/BasicHeaderProcessor.java b/core/src/main/java/jenkins/security/BasicHeaderProcessor.java index df176b0a1b5a7dbbfc03bc9698cb84e9d2f7bd16..5e0986eca4b5afc1ee08e99d54d1b96a193124fe 100644 --- a/core/src/main/java/jenkins/security/BasicHeaderProcessor.java +++ b/core/src/main/java/jenkins/security/BasicHeaderProcessor.java @@ -29,7 +29,7 @@ import java.util.logging.Logger; import static java.util.logging.Level.*; /** - * Takes "username:password" given in the Authorization HTTP header and authenticates + * Takes "username:password" given in the {@code Authorization} HTTP header and authenticates * the request. * *

    diff --git a/core/src/main/java/jenkins/security/ClassFilterImpl.java b/core/src/main/java/jenkins/security/ClassFilterImpl.java index 2de71b01e334623fe526a0c16480e35dd147150c..98e2073c017c8b7fd8885971c4e01a50b6c28e9f 100644 --- a/core/src/main/java/jenkins/security/ClassFilterImpl.java +++ b/core/src/main/java/jenkins/security/ClassFilterImpl.java @@ -24,10 +24,12 @@ package jenkins.security; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import hudson.ExtensionList; import hudson.Main; import hudson.remoting.ClassFilter; +import hudson.remoting.Which; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -49,6 +51,8 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.apache.commons.io.IOUtils; @@ -71,11 +75,14 @@ public class ClassFilterImpl extends ClassFilter { private static /* not final */ boolean SUPPRESS_WHITELIST = SystemProperties.getBoolean("jenkins.security.ClassFilterImpl.SUPPRESS_WHITELIST"); private static /* not final */ boolean SUPPRESS_ALL = SystemProperties.getBoolean("jenkins.security.ClassFilterImpl.SUPPRESS_ALL"); + private static final String JENKINS_LOC = codeSource(Jenkins.class); + private static final String REMOTING_LOC = codeSource(ClassFilter.class); + /** * Register this implementation as the default in the system. */ public static void register() { - if (Main.isUnitTest && Jenkins.class.getProtectionDomain().getCodeSource().getLocation() == null) { + if (Main.isUnitTest && JENKINS_LOC == null) { mockOff(); return; } @@ -99,7 +106,8 @@ public class ClassFilterImpl extends ClassFilter { ClassFilter.setDefault(ClassFilter.NONE); // even Method on the standard blacklist is going to explode } - private ClassFilterImpl() {} + @VisibleForTesting + /*package*/ ClassFilterImpl() {} /** Whether a given class is blacklisted. */ private final Map, Boolean> cache = Collections.synchronizedMap(new WeakHashMap<>()); @@ -121,19 +129,24 @@ public class ClassFilterImpl extends ClassFilter { for (CustomClassFilter f : ExtensionList.lookup(CustomClassFilter.class)) { Boolean r = f.permits(_c); if (r != null) { - LOGGER.log(Level.FINER, "{0} specifies a policy for {1}: {2}", new Object[] {f, _c.getName(), r}); + if (r) { + LOGGER.log(Level.FINER, "{0} specifies a policy for {1}: {2}", new Object[] {f, _c.getName(), true}); + } else { + notifyRejected(_c, _c.getName(), String.format("%s specifies a policy for %s: %s ", f, _c.getName(), r)); + } return !r; } } return cache.computeIfAbsent(_c, c -> { - if (ClassFilter.STANDARD.isBlacklisted(c)) { // currently never true: only the name overload is overridden - return true; - } String name = c.getName(); if (Main.isUnitTest && (name.contains("$$EnhancerByMockitoWithCGLIB$$") || name.contains("$$FastClassByMockitoWithCGLIB$$") || name.startsWith("org.mockito."))) { mockOff(); return false; } + if (ClassFilter.STANDARD.isBlacklisted(c)) { // currently never true, but may issue diagnostics + notifyRejected(_c, _c.getName(), String.format("%s is not permitted ", _c.getName())); + return true; + } if (c.isArray()) { LOGGER.log(Level.FINE, "permitting {0} since it is an array", name); return false; @@ -146,10 +159,9 @@ public class ClassFilterImpl extends ClassFilter { LOGGER.log(Level.FINE, "permitting {0} since it is an enum", name); return false; } - CodeSource codeSource = c.getProtectionDomain().getCodeSource(); - URL location = codeSource != null ? codeSource.getLocation() : null; + String location = codeSource(c); if (location != null) { - if (isLocationWhitelisted(location.toString())) { + if (isLocationWhitelisted(location)) { LOGGER.log(Level.FINE, "permitting {0} due to its location in {1}", new Object[] {name, location}); return false; } @@ -165,10 +177,13 @@ public class ClassFilterImpl extends ClassFilter { return false; } if (SUPPRESS_WHITELIST || SUPPRESS_ALL) { - LOGGER.log(Level.WARNING, "{0} in {1} might be dangerous, so would normally be rejected; see https://jenkins.io/redirect/class-filter/", new Object[] {name, location != null ? location : "JRE"}); + notifyRejected(_c, null, + String.format("%s in %s might be dangerous, so would normally be rejected; see https://jenkins.io/redirect/class-filter/", name, location != null ?location : "JRE")); + return false; } - LOGGER.log(Level.WARNING, "{0} in {1} might be dangerous, so rejecting; see https://jenkins.io/redirect/class-filter/", new Object[] {name, location != null ? location : "JRE"}); + notifyRejected(_c, null, + String.format("%s in %s might be dangerous, so rejecting; see https://jenkins.io/redirect/class-filter/", name, location != null ?location : "JRE")); return true; }); } @@ -176,11 +191,11 @@ public class ClassFilterImpl extends ClassFilter { private static final Pattern CLASSES_JAR = Pattern.compile("(file:/.+/)WEB-INF/lib/classes[.]jar"); private boolean isLocationWhitelisted(String _loc) { return codeSourceCache.computeIfAbsent(_loc, loc -> { - if (loc.equals(Jenkins.class.getProtectionDomain().getCodeSource().getLocation().toString())) { + if (loc.equals(JENKINS_LOC)) { LOGGER.log(Level.FINE, "{0} seems to be the location of Jenkins core, OK", loc); return true; } - if (loc.equals(ClassFilter.class.getProtectionDomain().getCodeSource().getLocation().toString())) { + if (loc.equals(REMOTING_LOC)) { LOGGER.log(Level.FINE, "{0} seems to be the location of Remoting, OK", loc); return true; } @@ -222,12 +237,12 @@ public class ClassFilterImpl extends ClassFilter { LOGGER.log(Level.WARNING, "problem checking " + loc, x); } } - if (loc.endsWith("/target/classes/")) { + if (loc.endsWith("/target/classes/") || loc.matches(".+/build/classes/[^/]+/main/")) { LOGGER.log(Level.FINE, "{0} seems to be current plugin classes, OK", loc); return true; } if (Main.isUnitTest) { - if (loc.endsWith("/target/test-classes/") || loc.endsWith("-tests.jar")) { + if (loc.endsWith("/target/test-classes/") || loc.endsWith("-tests.jar") || loc.matches(".+/build/classes/[^/]+/test/")) { LOGGER.log(Level.FINE, "{0} seems to be test classes, OK", loc); return true; } @@ -241,6 +256,38 @@ public class ClassFilterImpl extends ClassFilter { }); } + /** + * Tries to determine what JAR file a given class was loaded from. + * The location is an opaque string suitable only for comparison to others. + * Similar to {@link Which#jarFile(Class)} but potentially faster, and more tolerant of unknown URL formats. + * @param c some class + * @return something typically like {@code file:/…/plugins/structs/WEB-INF/lib/structs-1.10.jar}; + * or null for classes in the Java Platform, some generated classes, etc. + */ + private static @CheckForNull String codeSource(@Nonnull Class c) { + CodeSource cs = c.getProtectionDomain().getCodeSource(); + if (cs == null) { + return null; + } + URL loc = cs.getLocation(); + if (loc == null) { + return null; + } + String r = loc.toString(); + if (r.endsWith(".class")) { + // JENKINS-49147: Tomcat bug. Now do the more expensive check… + String suffix = c.getName().replace('.', '/') + ".class"; + if (r.endsWith(suffix)) { + r = r.substring(0, r.length() - suffix.length()); + } + } + if (r.startsWith("jar:file:/") && r.endsWith(".jar!/")) { + // JENKINS-49543: also an old behavior of Tomcat. Legal enough, but unexpected by isLocationWhitelisted. + r = r.substring(4, r.length() - 2); + } + return r; + } + private static boolean isPluginManifest(Manifest mf) { Attributes attr = mf.getMainAttributes(); return attr.getValue("Short-Name") != null && (attr.getValue("Plugin-Version") != null || attr.getValue("Jenkins-Version") != null) || @@ -256,21 +303,39 @@ public class ClassFilterImpl extends ClassFilter { for (CustomClassFilter f : ExtensionList.lookup(CustomClassFilter.class)) { Boolean r = f.permits(name); if (r != null) { - LOGGER.log(Level.FINER, "{0} specifies a policy for {1}: {2}", new Object[] {f, name, r}); + if (r) { + LOGGER.log(Level.FINER, "{0} specifies a policy for {1}: {2}", new Object[] {f, name, true}); + } else { + notifyRejected(null, name, + String.format("%s specifies a policy for %s: %s", f, name, r)); + } + return !r; } } // could apply a cache if the pattern search turns out to be slow if (ClassFilter.STANDARD.isBlacklisted(name)) { if (SUPPRESS_ALL) { - LOGGER.log(Level.WARNING, "would normally reject {0} according to standard blacklist; see https://jenkins.io/redirect/class-filter/", name); + notifyRejected(null, name, + String.format("would normally reject %s according to standard blacklist; see https://jenkins.io/redirect/class-filter/", name)); return false; } - LOGGER.log(Level.WARNING, "rejecting {0} according to standard blacklist; see https://jenkins.io/redirect/class-filter/", name); + notifyRejected(null, name, + String.format("rejecting %s according to standard blacklist; see https://jenkins.io/redirect/class-filter/", name)); return true; } else { return false; } } + private void notifyRejected(@CheckForNull Class clazz, @CheckForNull String clazzName, String message) { + Throwable cause = null; + if (LOGGER.isLoggable(Level.FINE)) { + cause = new SecurityException("Class rejected by the class filter: " + + (clazz != null ? clazz.getName() : clazzName)); + } + LOGGER.log(Level.WARNING, message, cause); + + // TODO: add a Telemetry implementation (JEP-304) + } } diff --git a/core/src/main/java/jenkins/security/CustomClassFilter.java b/core/src/main/java/jenkins/security/CustomClassFilter.java index 9292e6cee9093ee7358f137616c9c3b6e7d3c016..f73068f097a60c2ac674cf3cedcd7e768e5378fc 100644 --- a/core/src/main/java/jenkins/security/CustomClassFilter.java +++ b/core/src/main/java/jenkins/security/CustomClassFilter.java @@ -44,7 +44,6 @@ import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.apache.commons.io.IOUtils; import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; /** @@ -83,7 +82,7 @@ public interface CustomClassFilter extends ExtensionPoint { * Entries may also be preceded by {@code !} to blacklist. * Example: {@code -Dhudson.remoting.ClassFilter=com.google.common.collect.LinkedListMultimap,!com.acme.illadvised.YoloReflectionFactory$Handle} */ - @Restricted(DoNotUse.class) + @Restricted(NoExternalUse.class) @Extension public class Static implements CustomClassFilter { diff --git a/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java b/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java index aaa71541764d1dc4b683c8094b675fbaa48236e5..24755588b027aab58b682429a39d57018e1d2a53 100644 --- a/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java +++ b/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java @@ -53,13 +53,13 @@ import java.util.logging.Logger; *

    * If an {@link AuthenticationException} is detected, the filter will launch the authenticationEntryPoint. * This allows common handling of authentication failures originating from any subclass of - * AbstractSecurityInterceptor. + * {@code AbstractSecurityInterceptor}. *

    *

    * If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous * user. If they are an anonymous user, the authenticationEntryPoint will be launched. If they are not - * an anonymous user, the filter will delegate to the AccessDeniedHandler. - * By default the filter will use AccessDeniedHandlerImpl. + * an anonymous user, the filter will delegate to the {@code AccessDeniedHandler}. + * By default the filter will use {@code AccessDeniedHandlerImpl}. *

    *

    * To use this filter, it is necessary to specify the following properties: @@ -74,7 +74,7 @@ import java.util.logging.Logger; *

*

* Do not use this class directly. Instead configure - * web.xml to use the FilterToBeanProxy. + * web.xml to use the {@code FilterToBeanProxy}. *

* * @author Ben Alex diff --git a/core/src/main/java/jenkins/security/HMACConfidentialKey.java b/core/src/main/java/jenkins/security/HMACConfidentialKey.java index 4a520cd82eb6c2581a9fc62dc2ddffd332c584b1..3a83d5e213bd3af5177ab5ccd5da79ea03c58756 100644 --- a/core/src/main/java/jenkins/security/HMACConfidentialKey.java +++ b/core/src/main/java/jenkins/security/HMACConfidentialKey.java @@ -14,7 +14,7 @@ import java.util.Arrays; /** * {@link ConfidentialKey} that's used for creating a token by hashing some information with secret - * (such as hash(msg|secret)). + * (such as {@code hash(msg|secret)}). * *

* This provides more secure version of it by using HMAC. diff --git a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java index 39be186fb0cf19582ecadb05e33d84deb6ce032f..d8ef8b09eed3b4f35dbb53ab9e95bf00d083f2ae 100644 --- a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java +++ b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java @@ -3,6 +3,7 @@ package jenkins.security; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.ChannelClosedException; +import jenkins.slaves.RemotingVersionInfo; import org.jenkinsci.remoting.RoleChecker; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -11,6 +12,9 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; /** * Convenient {@link Callable} meant to be run on agent. * + * Note that the logic within {@link #call()} should use API of a minimum supported Remoting version. + * See {@link RemotingVersionInfo#getMinimumSupportedVersion()}. + * * @author Kohsuke Kawaguchi * @since 1.587 / 1.580.1 * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist diff --git a/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java b/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java index 7a2ef81427ec953c91c25bd0f0452f91eec733c5..c6283401b258cc17df0108b60f9ca57d817bf7e2 100644 --- a/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java +++ b/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java @@ -1,6 +1,7 @@ package jenkins.security; import hudson.Extension; +import hudson.model.PersistentDescriptor; import hudson.model.queue.Tasks; import hudson.util.DescribableList; import jenkins.model.GlobalConfiguration; @@ -21,21 +22,17 @@ import java.util.List; * @since 1.520 */ @Extension @Symbol("queueItemAuthenticator") -public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration { +public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration implements PersistentDescriptor { private final DescribableList authenticators = new DescribableList(this); - public QueueItemAuthenticatorConfiguration() { - load(); - } - private Object readResolve() { authenticators.setOwner(this); return this; } @Override - public GlobalConfigurationCategory getCategory() { + public @Nonnull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } @@ -61,8 +58,8 @@ public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration { } } - public static QueueItemAuthenticatorConfiguration get() { - return Jenkins.getInstance().getInjector().getInstance(QueueItemAuthenticatorConfiguration.class); + public static @Nonnull QueueItemAuthenticatorConfiguration get() { + return GlobalConfiguration.all().getInstance(QueueItemAuthenticatorConfiguration.class); } @Extension(ordinal = 100) diff --git a/core/src/main/java/jenkins/security/RedactSecretJsonInErrorMessageSanitizer.java b/core/src/main/java/jenkins/security/RedactSecretJsonInErrorMessageSanitizer.java new file mode 100644 index 0000000000000000000000000000000000000000..e7b0e7f7dfa8836755db7746885380c52ce2c3ef --- /dev/null +++ b/core/src/main/java/jenkins/security/RedactSecretJsonInErrorMessageSanitizer.java @@ -0,0 +1,118 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security; + +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.JsonInErrorMessageSanitizer; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Restricted(NoExternalUse.class) +public class RedactSecretJsonInErrorMessageSanitizer implements JsonInErrorMessageSanitizer { + private static final Logger LOGGER = Logger.getLogger(RedactSecretJsonInErrorMessageSanitizer.class.getName()); + + // must be kept in sync with hudson-behavior.js in function buildFormTree, password case + public static final String REDACT_KEY = "$redact"; + public static final String REDACT_VALUE = "[value redacted]"; + + public static final RedactSecretJsonInErrorMessageSanitizer INSTANCE = new RedactSecretJsonInErrorMessageSanitizer(); + + private RedactSecretJsonInErrorMessageSanitizer() {} + + @Override + public JSONObject sanitize(JSONObject jsonObject) { + return copyAndSanitizeObject(jsonObject); + } + + /** + * Accept anything as value for the {@link #REDACT_KEY} but only process the first level of an array and the string value. + */ + private Set retrieveRedactedKeys(JSONObject jsonObject) { + Set redactedKeySet = new HashSet<>(); + if (jsonObject.has(REDACT_KEY)) { + Object value = jsonObject.get(REDACT_KEY); + if (value instanceof JSONArray) { + for (Object o : jsonObject.getJSONArray(REDACT_KEY)) { + if (o instanceof String) { + redactedKeySet.add((String) o); + } else { + // array, object, null, number, boolean + LOGGER.log(Level.WARNING, "Unsupported type " + o.getClass().getName() + " for " + REDACT_KEY + ", please use either a single String value or an Array"); + } + } + } else if (value instanceof String) { + redactedKeySet.add((String) value); + } else { + // object, null, number, boolean + LOGGER.log(Level.WARNING, "Unsupported type " + value.getClass().getName() + " for " + REDACT_KEY + ", please use either a single String value or an Array"); + } + } + return redactedKeySet; + } + + private Object copyAndSanitize(Object value) { + if (value instanceof JSONObject) { + return copyAndSanitizeObject((JSONObject) value); + } else if (value instanceof JSONArray) { + return copyAndSanitizeArray((JSONArray) value); + } else { + // string, null, number, boolean + return value; + } + } + + @SuppressWarnings("unchecked") + private JSONObject copyAndSanitizeObject(JSONObject jsonObject) { + Set redactedKeySet = retrieveRedactedKeys(jsonObject); + JSONObject result = new JSONObject(); + + jsonObject.keySet().forEach(keyObject -> { + String key = keyObject.toString(); + if (redactedKeySet.contains(key)) { + result.accumulate(key, REDACT_VALUE); + } else { + Object value = jsonObject.get(keyObject); + result.accumulate(key, copyAndSanitize(value)); + } + }); + + return result; + } + + private JSONArray copyAndSanitizeArray(JSONArray jsonArray) { + JSONArray result = new JSONArray(); + + jsonArray.forEach(value -> + result.add(copyAndSanitize(value)) + ); + + return result; + } +} diff --git a/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java b/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java index a8295d8b75ef187189e086b5f0eda5cb06ac0f51..a6237022582d9bf1d5b704baf64858238614a3ed 100644 --- a/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java +++ b/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java @@ -26,6 +26,7 @@ package jenkins.security; import hudson.Extension; import hudson.PluginWrapper; +import hudson.model.PersistentDescriptor; import hudson.model.UpdateSite; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; @@ -50,19 +51,15 @@ import java.util.Set; */ @Extension @Restricted(NoExternalUse.class) -public class UpdateSiteWarningsConfiguration extends GlobalConfiguration { +public class UpdateSiteWarningsConfiguration extends GlobalConfiguration implements PersistentDescriptor { private HashSet ignoredWarnings = new HashSet<>(); @Override - public GlobalConfigurationCategory getCategory() { + public @Nonnull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } - public UpdateSiteWarningsConfiguration() { - load(); - } - @Nonnull public Set getIgnoredWarnings() { return Collections.unmodifiableSet(ignoredWarnings); @@ -77,14 +74,14 @@ public class UpdateSiteWarningsConfiguration extends GlobalConfiguration { if (warning.type != UpdateSite.Warning.Type.PLUGIN) { return null; } - return Jenkins.getInstance().getPluginManager().getPlugin(warning.component); + return Jenkins.get().getPluginManager().getPlugin(warning.component); } @Nonnull public Set getAllWarnings() { HashSet allWarnings = new HashSet<>(); - for (UpdateSite site : Jenkins.getInstance().getUpdateCenter().getSites()) { + for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) { UpdateSite.Data data = site.getData(); if (data != null) { allWarnings.addAll(data.getWarnings()); diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyConfiguration.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..e032f2818d905564ccb572488853718fd6d90575 --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyConfiguration.java @@ -0,0 +1,97 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import hudson.Extension; +import hudson.model.PersistentDescriptor; +import jenkins.model.GlobalConfiguration; +import jenkins.model.GlobalConfigurationCategory; +import org.jenkinsci.Symbol; + +/** + * Configuration for the new token generation when a user is created + * + * @since 2.129 + */ +@Extension +@Symbol("apiToken") +public class ApiTokenPropertyConfiguration extends GlobalConfiguration implements PersistentDescriptor { + /** + * When a user is created, this property determines whether or not we create a legacy token for the user. + * For security reasons, we do not recommend you enable this but we left that open to ease upgrades. + */ + private boolean tokenGenerationOnCreationEnabled = false; + + /** + * When a user has a legacy token, this property determines whether or not the user can request a new legacy token. + * For security reasons, we do not recommend you enable this but we left that open to ease upgrades. + */ + private boolean creationOfLegacyTokenEnabled = false; + + /** + * Each time an API Token is used, its usage counter is incremented and the last used date is updated. + * You can disable this feature using this property. + */ + private boolean usageStatisticsEnabled = true; + + public static ApiTokenPropertyConfiguration get() { + return GlobalConfiguration.all().get(ApiTokenPropertyConfiguration.class); + } + + public boolean hasExistingConfigFile(){ + return getConfigFile().exists(); + } + + public boolean isTokenGenerationOnCreationEnabled() { + return tokenGenerationOnCreationEnabled; + } + + public void setTokenGenerationOnCreationEnabled(boolean tokenGenerationOnCreationEnabled) { + this.tokenGenerationOnCreationEnabled = tokenGenerationOnCreationEnabled; + save(); + } + + public boolean isCreationOfLegacyTokenEnabled() { + return creationOfLegacyTokenEnabled; + } + + public void setCreationOfLegacyTokenEnabled(boolean creationOfLegacyTokenEnabled) { + this.creationOfLegacyTokenEnabled = creationOfLegacyTokenEnabled; + save(); + } + + public boolean isUsageStatisticsEnabled() { + return usageStatisticsEnabled; + } + + public void setUsageStatisticsEnabled(boolean usageStatisticsEnabled) { + this.usageStatisticsEnabled = usageStatisticsEnabled; + save(); + } + + @Override + public GlobalConfigurationCategory getCategory() { + return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); + } +} diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyDisabledDefaultAdministrativeMonitor.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyDisabledDefaultAdministrativeMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..b5afdca805628739403c4dab810cc9082dd627df --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyDisabledDefaultAdministrativeMonitor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import hudson.util.HttpResponses; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import java.io.IOException; + +/** + * Monitor that the API Token are not generated by default without the user interaction. + */ +@Extension +@Symbol("apiTokenLegacyAutoGeneration") +@Restricted(NoExternalUse.class) +public class ApiTokenPropertyDisabledDefaultAdministrativeMonitor extends AdministrativeMonitor { + @Override + public String getDisplayName() { + return Messages.ApiTokenPropertyDisabledDefaultAdministrativeMonitor_displayName(); + } + + @Override + public boolean isActivated() { + return ApiTokenPropertyConfiguration.get().isTokenGenerationOnCreationEnabled(); + } + + @RequirePOST + public HttpResponse doAct(@QueryParameter String no) throws IOException { + if (no == null) { + ApiTokenPropertyConfiguration.get().setTokenGenerationOnCreationEnabled(false); + } else { + disable(true); + } + return HttpResponses.redirectViaContextPath("manage"); + } +} diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyEnabledNewLegacyAdministrativeMonitor.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyEnabledNewLegacyAdministrativeMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..2cae664d0878161def7bf9312a6564509af2da73 --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyEnabledNewLegacyAdministrativeMonitor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import hudson.util.HttpResponses; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import java.io.IOException; + +/** + * Monitor that the API Token cannot be created for a user without existing legacy token + */ +@Extension +@Symbol("apiTokenNewLegacyWithoutExisting") +@Restricted(NoExternalUse.class) +public class ApiTokenPropertyEnabledNewLegacyAdministrativeMonitor extends AdministrativeMonitor { + @Override + public String getDisplayName() { + return Messages.ApiTokenPropertyEnabledNewLegacyAdministrativeMonitor_displayName(); + } + + @Override + public boolean isActivated() { + return ApiTokenPropertyConfiguration.get().isCreationOfLegacyTokenEnabled(); + } + + @RequirePOST + public HttpResponse doAct(@QueryParameter String no) throws IOException { + if (no == null) { + ApiTokenPropertyConfiguration.get().setCreationOfLegacyTokenEnabled(false); + } else { + disable(true); + } + return HttpResponses.redirectViaContextPath("manage"); + } +} diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java new file mode 100644 index 0000000000000000000000000000000000000000..d481361f61d26044800fe5e636958c02e4771c03 --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java @@ -0,0 +1,256 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import hudson.BulkChange; +import hudson.Util; +import hudson.XmlFile; +import hudson.model.Saveable; +import hudson.model.listeners.SaveableListener; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Restricted(NoExternalUse.class) +public class ApiTokenStats implements Saveable { + private static final Logger LOGGER = Logger.getLogger(ApiTokenStats.class.getName()); + + /** + * Normally a user will not have more 2-3 tokens at a time, + * so there is no need to store a map here + */ + private List tokenStats; + + private transient File parent; + + public ApiTokenStats() { + this.init(); + } + + private Object readResolve() { + this.init(); + return this; + } + + private void init() { + if (this.tokenStats == null) { + this.tokenStats = new ArrayList<>(); + } else { + keepLastUpdatedUnique(); + } + } + + /** + * In case of duplicate entries, we keep only the last updated element + */ + private void keepLastUpdatedUnique() { + Map temp = new HashMap<>(); + this.tokenStats.forEach(candidate -> { + SingleTokenStats current = temp.get(candidate.tokenUuid); + if (current == null) { + temp.put(candidate.tokenUuid, candidate); + } else { + int comparison = SingleTokenStats.COMP_BY_LAST_USE_THEN_COUNTER.compare(current, candidate); + if (comparison < 0) { + // candidate was updated more recently (or has a bigger counter in case of perfectly equivalent dates) + temp.put(candidate.tokenUuid, candidate); + } + } + }); + + this.tokenStats = new ArrayList<>(temp.values()); + } + + void setParent(@Nonnull File parent) { + this.parent = parent; + } + + private boolean areStatsDisabled(){ + return !ApiTokenPropertyConfiguration.get().isUsageStatisticsEnabled(); + } + + /** + * Will trigger the save if there is some modification + */ + public synchronized void removeId(@Nonnull String tokenUuid) { + if(areStatsDisabled()){ + return; + } + + boolean tokenRemoved = tokenStats.removeIf(s -> s.tokenUuid.equals(tokenUuid)); + if (tokenRemoved) { + save(); + } + } + + /** + * Will trigger the save + */ + public synchronized @Nonnull SingleTokenStats updateUsageForId(@Nonnull String tokenUuid) { + if(areStatsDisabled()){ + return new SingleTokenStats(tokenUuid); + } + + SingleTokenStats stats = findById(tokenUuid) + .orElseGet(() -> { + SingleTokenStats result = new SingleTokenStats(tokenUuid); + tokenStats.add(result); + return result; + }); + + stats.notifyUse(); + save(); + + return stats; + } + + public synchronized @Nonnull SingleTokenStats findTokenStatsById(@Nonnull String tokenUuid) { + if(areStatsDisabled()){ + return new SingleTokenStats(tokenUuid); + } + + // if we create a new empty stats object, no need to add it to the list + return findById(tokenUuid) + .orElse(new SingleTokenStats(tokenUuid)); + } + + private @Nonnull Optional findById(@Nonnull String tokenUuid) { + return tokenStats.stream() + .filter(s -> s.tokenUuid.equals(tokenUuid)) + .findFirst(); + } + + /** + * Saves the configuration info to the disk. + */ + @Override + public synchronized void save() { + if(areStatsDisabled()){ + return; + } + + if (BulkChange.contains(this)) + return; + + XmlFile configFile = getConfigFile(parent); + try { + configFile.write(this); + SaveableListener.fireOnChange(this, configFile); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to save " + configFile, e); + } + } + + /** + * Loads the data from the disk into the new object. + *

+ * If the file is not present, a fresh new instance is created. + */ + public static @Nonnull ApiTokenStats load(@Nonnull File parent) { + // even if we are not using statistics, we load the existing one in case the configuration + // is enabled afterwards to avoid erasing data + + XmlFile file = getConfigFile(parent); + ApiTokenStats apiTokenStats; + if (file.exists()) { + try { + apiTokenStats = (ApiTokenStats) file.unmarshal(ApiTokenStats.class); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to load " + file, e); + apiTokenStats = new ApiTokenStats(); + } + } else { + apiTokenStats = new ApiTokenStats(); + } + + apiTokenStats.setParent(parent); + return apiTokenStats; + } + + protected static XmlFile getConfigFile(File parent) { + return new XmlFile(new File(parent, "apiTokenStats.xml")); + } + + public static class SingleTokenStats { + private static Comparator COMP_BY_LAST_USE_THEN_COUNTER = + Comparator.comparing(SingleTokenStats::getLastUseDate, Comparator.nullsFirst(Comparator.naturalOrder())) + .thenComparing(SingleTokenStats::getUseCounter); + + private final String tokenUuid; + private Date lastUseDate; + private Integer useCounter; + + private SingleTokenStats(String tokenUuid) { + this.tokenUuid = tokenUuid; + } + + private Object readResolve() { + if (this.useCounter != null) { + // to avoid negative numbers to be injected + this.useCounter = Math.max(0, this.useCounter); + } + return this; + } + + private void notifyUse() { + this.useCounter = useCounter == null ? 1 : useCounter + 1; + this.lastUseDate = new Date(); + } + + public String getTokenUuid() { + return tokenUuid; + } + + // used by Jelly view + public int getUseCounter() { + return useCounter == null ? 0 : useCounter; + } + + // used by Jelly view + public Date getLastUseDate() { + return lastUseDate; + } + + // used by Jelly view + /** + * Return the number of days since the last usage + * Relevant only if the lastUseDate is not null + */ + public long getNumDaysUse() { + return lastUseDate == null ? 0 : Util.daysElapsedSince(lastUseDate); + } + } +} diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java new file mode 100644 index 0000000000000000000000000000000000000000..4e4f4ca0ca229f28684706cfaf4cf97a72b2165e --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java @@ -0,0 +1,444 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.Util; +import hudson.util.Secret; +import jenkins.security.Messages; +import net.sf.json.JSONObject; +import org.apache.commons.lang.StringUtils; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +@Restricted(NoExternalUse.class) +public class ApiTokenStore { + private static final Logger LOGGER = Logger.getLogger(ApiTokenStore.class.getName()); + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final Comparator SORT_BY_LOWERCASED_NAME = + Comparator.comparing(hashedToken -> hashedToken.getName().toLowerCase(Locale.ENGLISH)); + + private static final int TOKEN_LENGTH_V2 = 34; + /** two hex characters, avoid starting with 0 to avoid troubles */ + private static final String LEGACY_VERSION = "10"; + private static final String HASH_VERSION = "11"; + + private static final String HASH_ALGORITHM = "SHA-256"; + + private List tokenList; + + public ApiTokenStore() { + this.init(); + } + + private Object readResolve() { + this.init(); + return this; + } + + private void init() { + if (this.tokenList == null) { + this.tokenList = new ArrayList<>(); + } + } + + @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION") + public synchronized @Nonnull Collection getTokenListSortedByName() { + return tokenList.stream() + .sorted(SORT_BY_LOWERCASED_NAME) + .collect(Collectors.toList()); + } + + private void addToken(HashedToken token) { + this.tokenList.add(token); + } + + /** + * Defensive approach to avoid involuntary change since the UUIDs are generated at startup only for UI + * and so between restart they change + */ + public synchronized void reconfigure(@Nonnull Map tokenStoreDataMap) { + tokenList.forEach(hashedToken -> { + JSONObject receivedTokenData = tokenStoreDataMap.get(hashedToken.uuid); + if (receivedTokenData == null) { + LOGGER.log(Level.INFO, "No token received for {0}", hashedToken.uuid); + return; + } + + String name = receivedTokenData.getString("tokenName"); + if (StringUtils.isBlank(name)) { + LOGGER.log(Level.INFO, "Empty name received for {0}, we do not care about it", hashedToken.uuid); + return; + } + + hashedToken.setName(name); + }); + } + + /** + * Remove the legacy token present and generate a new one using the given secret. + */ + public synchronized void regenerateTokenFromLegacy(@Nonnull Secret newLegacyApiToken) { + deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, false); + } + + /** + * Same as {@link #regenerateTokenFromLegacy(Secret)} but only applied if there is an existing legacy token. + *

+ * Otherwise, no effect. + */ + public synchronized void regenerateTokenFromLegacyIfRequired(@Nonnull Secret newLegacyApiToken) { + if(tokenList.stream().noneMatch(HashedToken::isLegacy)){ + deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, true); + } + } + + private void deleteAllLegacyAndGenerateNewOne(@Nonnull Secret newLegacyApiToken, boolean migrationFromExistingLegacy) { + deleteAllLegacyTokens(); + addLegacyToken(newLegacyApiToken, migrationFromExistingLegacy); + } + + private void deleteAllLegacyTokens() { + // normally there is only one, but just in case + tokenList.removeIf(HashedToken::isLegacy); + } + + private void addLegacyToken(@Nonnull Secret legacyToken, boolean migrationFromExistingLegacy) { + String tokenUserUseNormally = Util.getDigestOf(legacyToken.getPlainText()); + + String secretValueHashed = this.plainSecretToHashInHex(tokenUserUseNormally); + + HashValue hashValue = new HashValue(LEGACY_VERSION, secretValueHashed); + HashedToken token = HashedToken.buildNewFromLegacy(hashValue, migrationFromExistingLegacy); + + this.addToken(token); + } + + /** + * @return {@code null} iff there is no legacy token in the store, otherwise the legacy token is returned + */ + public synchronized @Nullable HashedToken getLegacyToken(){ + return tokenList.stream() + .filter(HashedToken::isLegacy) + .findFirst() + .orElse(null); + } + + /** + * Create a new token with the given name and return it id and secret value. + * Result meant to be sent / displayed and then discarded. + */ + public synchronized @Nonnull TokenUuidAndPlainValue generateNewToken(@Nonnull String name) { + // 16x8=128bit worth of randomness, using brute-force you need on average 2^127 tries (~10^37) + byte[] random = new byte[16]; + RANDOM.nextBytes(random); + String secretValue = Util.toHexString(random); + String tokenTheUserWillUse = HASH_VERSION + secretValue; + assert tokenTheUserWillUse.length() == 2 + 32; + + String secretValueHashed = this.plainSecretToHashInHex(secretValue); + + HashValue hashValue = new HashValue(HASH_VERSION, secretValueHashed); + HashedToken token = HashedToken.buildNew(name, hashValue); + + this.addToken(token); + + return new TokenUuidAndPlainValue(token.uuid, tokenTheUserWillUse); + } + + @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION") + private @Nonnull String plainSecretToHashInHex(@Nonnull String secretValueInPlainText) { + byte[] hashBytes = plainSecretToHashBytes(secretValueInPlainText); + return Util.toHexString(hashBytes); + } + + private @Nonnull byte[] plainSecretToHashBytes(@Nonnull String secretValueInPlainText) { + // ascii is sufficient for hex-format + return hashedBytes(secretValueInPlainText.getBytes(StandardCharsets.US_ASCII)); + } + + private @Nonnull byte[] hashedBytes(byte[] tokenBytes) { + MessageDigest digest; + try { + digest = MessageDigest.getInstance(HASH_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("There is no " + HASH_ALGORITHM + " available in this system"); + } + return digest.digest(tokenBytes); + } + + /** + * Search in the store if there is a token with the same secret as the one given + * @return {@code null} iff there is no matching token + */ + public synchronized @CheckForNull HashedToken findMatchingToken(@Nonnull String token) { + String plainToken; + if (isLegacyToken(token)) { + plainToken = token; + } else { + plainToken = getHashOfToken(token); + } + + return searchMatch(plainToken); + } + + /** + * Determine if the given token was generated by the legacy system or the new one + */ + private boolean isLegacyToken(@Nonnull String token) { + return token.length() != TOKEN_LENGTH_V2; + } + + /** + * Retrieve the hash part of the token + * @param token assumed the token is not a legacy one and represent the full token (version + hash) + * @return the hash part + */ + private @Nonnull String getHashOfToken(@Nonnull String token) { + /* + * Structure of the token: + * + * [2: version][32: real token] + * ------------^^^^^^^^^^^^^^^^ + */ + return token.substring(2); + } + + /** + * Search in the store if there is a matching token that has the same secret. + * @return {@code null} iff there is no matching token + */ + private @CheckForNull HashedToken searchMatch(@Nonnull String plainSecret) { + byte[] hashedBytes = plainSecretToHashBytes(plainSecret); + for (HashedToken token : tokenList) { + if (token.match(hashedBytes)) { + return token; + } + } + + return null; + } + + /** + * Remove a token given its identifier. Effectively make it unusable for future connection. + * + * @param tokenUuid The identifier of the token, could be retrieved directly from the {@link HashedToken#getUuid()} + * @return the revoked token corresponding to the given {@code tokenUuid} if one was found, otherwise {@code null} + */ + public synchronized @CheckForNull HashedToken revokeToken(@Nonnull String tokenUuid) { + for (Iterator iterator = tokenList.iterator(); iterator.hasNext(); ) { + HashedToken token = iterator.next(); + if (token.uuid.equals(tokenUuid)) { + iterator.remove(); + + return token; + } + } + + return null; + } + + /** + * Given a token identifier and a name, the system will try to find a corresponding token and rename it + * @return {@code true} iff the token was found and the rename was successful + */ + public synchronized boolean renameToken(@Nonnull String tokenUuid, @Nonnull String newName) { + for (HashedToken token : tokenList) { + if (token.uuid.equals(tokenUuid)) { + token.rename(newName); + return true; + } + } + + LOGGER.log(Level.FINER, "The target token for rename does not exist, for uuid = {0}, with desired name = {1}", new Object[]{tokenUuid, newName}); + return false; + } + + @Immutable + private static class HashValue implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Allow to distinguish tokens from different versions easily to adapt the logic + */ + private final String version; + /** + * Only confidential information in this class. It's a SHA-256 hash stored in hex format + */ + private final String hash; + + private HashValue(String version, String hash) { + this.version = version; + this.hash = hash; + } + } + + /** + * Contains information about the token and the secret value. + * It should not be stored as is, but just displayed once to the user and then forget about it. + */ + @Immutable + public static class TokenUuidAndPlainValue { + /** + * The token identifier to allow manipulation of the token + */ + public final String tokenUuid; + + /** + * Confidential information, must not be stored.

+ * It's meant to be send only one to the user and then only store the hash of this value. + */ + public final String plainValue; + + private TokenUuidAndPlainValue(String tokenUuid, String plainValue) { + this.tokenUuid = tokenUuid; + this.plainValue = plainValue; + } + } + + public static class HashedToken implements Serializable { + + private static final long serialVersionUID = 1L; + + // allow us to rename the token and link the statistics + private String uuid; + private String name; + private Date creationDate; + + private HashValue value; + + private HashedToken() { + this.init(); + } + + private Object readResolve() { + this.init(); + return this; + } + + private void init() { + if(this.uuid == null){ + this.uuid = UUID.randomUUID().toString(); + } + } + + public static @Nonnull HashedToken buildNew(@Nonnull String name, @Nonnull HashValue value) { + HashedToken result = new HashedToken(); + result.name = name; + result.creationDate = new Date(); + + result.value = value; + + return result; + } + + public static @Nonnull HashedToken buildNewFromLegacy(@Nonnull HashValue value, boolean migrationFromExistingLegacy) { + HashedToken result = new HashedToken(); + result.name = Messages.ApiTokenProperty_LegacyTokenName(); + if(migrationFromExistingLegacy){ + // we do not know when the legacy token was created + result.creationDate = null; + }else{ + // it comes from a manual action, so we set the creation date to now + result.creationDate = new Date(); + } + + result.value = value; + + return result; + } + + public void rename(String newName) { + this.name = newName; + } + + public boolean match(byte[] hashedBytes) { + byte[] hashFromHex; + try { + hashFromHex = Util.fromHexString(value.hash); + } catch (NumberFormatException e) { + LOGGER.log(Level.INFO, "The API token with name=[{0}] is not in hex-format and so cannot be used", name); + return false; + } + + // String.equals() is not constant-time but this method is. No link between correctness and time spent + return MessageDigest.isEqual(hashFromHex, hashedBytes); + } + + // used by Jelly view + public String getName() { + return name; + } + + // used by Jelly view + public Date getCreationDate() { + return creationDate; + } + + // used by Jelly view + /** + * Relevant only if the lastUseDate is not null + */ + public long getNumDaysCreation() { + return creationDate == null ? 0 : Util.daysElapsedSince(creationDate); + } + + // used by Jelly view + public String getUuid() { + return this.uuid; + } + + public boolean isLegacy() { + return this.value.version.equals(LEGACY_VERSION); + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java b/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..8e56ceb40b3ef1a1e3952645b67c6108a6840026 --- /dev/null +++ b/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java @@ -0,0 +1,200 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.security.apitoken; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import hudson.model.User; +import hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor; +import hudson.util.HttpResponses; +import jenkins.security.ApiTokenProperty; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.HttpRedirect; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.interceptor.RequirePOST; +import org.kohsuke.stapler.json.JsonBody; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * Monitor the list of users that still have legacy token + */ +@Extension +@Symbol("legacyApiTokenUsage") +@Restricted(NoExternalUse.class) +public class LegacyApiTokenAdministrativeMonitor extends AdministrativeMonitor { + private static final Logger LOGGER = Logger.getLogger(AbstractAsyncNodeMonitorDescriptor.class.getName()); + + public LegacyApiTokenAdministrativeMonitor() { + super("legacyApiToken"); + } + + @Override + public String getDisplayName() { + return Messages.LegacyApiTokenAdministrativeMonitor_displayName(); + } + + @Override + public boolean isActivated() { + return User.getAll().stream() + .anyMatch(user -> { + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + return (apiTokenProperty != null && apiTokenProperty.hasLegacyToken()); + }); + } + + public HttpResponse doIndex() throws IOException { + return new HttpRedirect("manage"); + } + + // used by Jelly view + @Restricted(NoExternalUse.class) + public List getImpactedUserList() { + return User.getAll().stream() + .filter(user -> { + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + return (apiTokenProperty != null && apiTokenProperty.hasLegacyToken()); + }) + .collect(Collectors.toList()); + } + + // used by Jelly view + @Restricted(NoExternalUse.class) + public @Nullable ApiTokenStore.HashedToken getLegacyTokenOf(@Nonnull User user) { + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + ApiTokenStore.HashedToken legacyToken = apiTokenProperty.getTokenStore().getLegacyToken(); + return legacyToken; + } + + // used by Jelly view + @Restricted(NoExternalUse.class) + public @Nullable ApiTokenProperty.TokenInfoAndStats getLegacyStatsOf(@Nonnull User user, @Nullable ApiTokenStore.HashedToken legacyToken) { + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + if (legacyToken != null) { + ApiTokenStats.SingleTokenStats legacyStats = apiTokenProperty.getTokenStats().findTokenStatsById(legacyToken.getUuid()); + ApiTokenProperty.TokenInfoAndStats tokenInfoAndStats = new ApiTokenProperty.TokenInfoAndStats(legacyToken, legacyStats); + return tokenInfoAndStats; + } + + // in case the legacy token was revoked during the request + return null; + } + + /** + * Determine if the user has at least one "new" token that was created after the last use of the legacy token + */ + // used by Jelly view + @Restricted(NoExternalUse.class) + public boolean hasFreshToken(@Nonnull User user, @Nullable ApiTokenProperty.TokenInfoAndStats legacyStats) { + if (legacyStats == null) { + return false; + } + + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + + return apiTokenProperty.getTokenList().stream() + .filter(token -> !token.isLegacy) + .anyMatch(token -> { + Date creationDate = token.creationDate; + Date lastUseDate = legacyStats.lastUseDate; + if (lastUseDate == null) { + lastUseDate = legacyStats.creationDate; + } + return creationDate != null && lastUseDate != null && creationDate.after(lastUseDate); + }); + } + + /** + * Determine if the user has at least one "new" token that was used after the last use of the legacy token + */ + // used by Jelly view + @Restricted(NoExternalUse.class) + public boolean hasMoreRecentlyUsedToken(@Nonnull User user, @Nullable ApiTokenProperty.TokenInfoAndStats legacyStats) { + if (legacyStats == null) { + return false; + } + + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + + return apiTokenProperty.getTokenList().stream() + .filter(token -> !token.isLegacy) + .anyMatch(token -> { + Date currentLastUseDate = token.lastUseDate; + Date legacyLastUseDate = legacyStats.lastUseDate; + if (legacyLastUseDate == null) { + legacyLastUseDate = legacyStats.creationDate; + } + return currentLastUseDate != null && legacyLastUseDate != null && currentLastUseDate.after(legacyLastUseDate); + }); + } + + @RequirePOST + public HttpResponse doRevokeAllSelected(@JsonBody RevokeAllSelectedModel content) throws IOException { + for (RevokeAllSelectedUserAndUuid value : content.values) { + if (value.userId == null) { + // special case not managed by JSONObject + value.userId = "null"; + } + User user = User.getById(value.userId, false); + if (user == null) { + LOGGER.log(Level.INFO, "User not found id={0}", value.userId); + } else { + ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); + if (apiTokenProperty == null) { + LOGGER.log(Level.INFO, "User without apiTokenProperty found id={0}", value.userId); + } else { + ApiTokenStore.HashedToken revokedToken = apiTokenProperty.getTokenStore().revokeToken(value.uuid); + if (revokedToken == null) { + LOGGER.log(Level.INFO, "User without selected token id={0}, tokenUuid={1}", new Object[]{value.userId, value.uuid}); + } else { + apiTokenProperty.deleteApiToken(); + user.save(); + LOGGER.log(Level.INFO, "Revocation success for user id={0}, tokenUuid={1}", new Object[]{value.userId, value.uuid}); + } + } + } + } + return HttpResponses.ok(); + } + + @Restricted(NoExternalUse.class) + public static final class RevokeAllSelectedModel { + public RevokeAllSelectedUserAndUuid[] values; + } + + @Restricted(NoExternalUse.class) + public static final class RevokeAllSelectedUserAndUuid { + public String userId; + public String uuid; + } +} diff --git a/core/src/main/java/jenkins/security/csrf/CSRFAdministrativeMonitor.java b/core/src/main/java/jenkins/security/csrf/CSRFAdministrativeMonitor.java index dbdb38e337cb309d161ba8602f58383dc41611ff..6ffc2422e42caca00832bddd486921344e86c2d1 100644 --- a/core/src/main/java/jenkins/security/csrf/CSRFAdministrativeMonitor.java +++ b/core/src/main/java/jenkins/security/csrf/CSRFAdministrativeMonitor.java @@ -46,6 +46,6 @@ public class CSRFAdministrativeMonitor extends AdministrativeMonitor { @Override public boolean isActivated() { - return Jenkins.getInstance().getCrumbIssuer() == null; + return Jenkins.get().getCrumbIssuer() == null; } } diff --git a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java index a05fd59e8ebc58c7ab1d112318f8a2f709144390..0a3eb62ffdd1f248c6981dfbdfa065635e766da2 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java +++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java @@ -16,6 +16,8 @@ import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.interceptor.RequirePOST; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -52,12 +54,10 @@ public class AdminWhitelistRule implements StaplerProxy { */ public final FilePathRuleConfig filePathRules; - private final Jenkins jenkins; - private boolean masterKillSwitch; public AdminWhitelistRule() throws IOException, InterruptedException { - this.jenkins = Jenkins.getInstance(); + final Jenkins jenkins = Jenkins.get(); // while this file is not a secret, write access to this file is dangerous, // so put this in the better-protected part of $JENKINS_HOME, which is in secrets/ @@ -79,17 +79,21 @@ public class AdminWhitelistRule implements StaplerProxy { whitelisted); this.filePathRules = new FilePathRuleConfig( new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-gui.conf")); - this.masterKillSwitch = loadMasterKillSwitchFile(); + + File f = getMasterKillSwitchFile(jenkins); + this.masterKillSwitch = loadMasterKillSwitchFile(f); } /** - * Reads the master kill switch. + * Reads the master kill switch from a file. * * Instead of {@link FileBoolean}, we use a text file so that the admin can prevent Jenkins from * writing this to file. + * @param f File to load + * @return {@code true} if the file was loaded, {@code false} otherwise */ - private boolean loadMasterKillSwitchFile() { - File f = getMasterKillSwitchFile(); + @CheckReturnValue + private boolean loadMasterKillSwitchFile(@Nonnull File f) { try { if (!f.exists()) return true; return Boolean.parseBoolean(FileUtils.readFileToString(f).trim()); @@ -99,7 +103,8 @@ public class AdminWhitelistRule implements StaplerProxy { } } - private File getMasterKillSwitchFile() { + @Nonnull + private File getMasterKillSwitchFile(@Nonnull Jenkins jenkins) { return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch"); } @@ -155,8 +160,6 @@ public class AdminWhitelistRule implements StaplerProxy { @RequirePOST public HttpResponse doSubmit(StaplerRequest req) throws IOException { - jenkins.checkPermission(Jenkins.RUN_SCRIPTS); - String whitelist = Util.fixNull(req.getParameter("whitelist")); if (!whitelist.endsWith("\n")) whitelist+="\n"; @@ -206,11 +209,13 @@ public class AdminWhitelistRule implements StaplerProxy { } public void setMasterKillSwitch(boolean state) { + final Jenkins jenkins = Jenkins.get(); try { jenkins.checkPermission(Jenkins.RUN_SCRIPTS); - FileUtils.writeStringToFile(getMasterKillSwitchFile(),Boolean.toString(state)); + File f = getMasterKillSwitchFile(jenkins); + FileUtils.writeStringToFile(f, Boolean.toString(state)); // treat the file as the canonical source of information in case write fails - masterKillSwitch = loadMasterKillSwitchFile(); + masterKillSwitch = loadMasterKillSwitchFile(f); } catch (IOException e) { LOGGER.log(WARNING, "Failed to write master kill switch", e); } @@ -221,7 +226,7 @@ public class AdminWhitelistRule implements StaplerProxy { */ @Override public Object getTarget() { - jenkins.checkPermission(Jenkins.RUN_SCRIPTS); + Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS); return this; } diff --git a/core/src/main/java/jenkins/security/s2m/ConfigFile.java b/core/src/main/java/jenkins/security/s2m/ConfigFile.java index 69d9358eed778ecc29ac6c9f71f548fad17c89f0..99b8c5f97516561a3ae5b22ec4d70e9e83c925fb 100644 --- a/core/src/main/java/jenkins/security/s2m/ConfigFile.java +++ b/core/src/main/java/jenkins/security/s2m/ConfigFile.java @@ -3,6 +3,7 @@ package jenkins.security.s2m; import hudson.CopyOnWrite; import hudson.util.TextFile; import jenkins.model.Jenkins; +import jenkins.util.io.LinesStream; import java.io.BufferedReader; import java.io.File; @@ -26,15 +27,42 @@ abstract class ConfigFile> extends TextFile { protected abstract COL create(); protected abstract COL readOnly(COL base); - public synchronized void load() { + /** + * Loads the configuration from the configuration file. + *

+ * This method is equivalent to {@link #load2()}, except that any + * {@link java.io.IOException} that occurs is wrapped as a + * {@link java.lang.RuntimeException}. + *

+ * This method exists for source compatibility. Users should call + * {@link #load2()} instead. + * @deprecated use {@link #load2()} instead. + */ + @Deprecated + public void load() { + try { + load2(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Loads the configuration from the configuration file. + * @throws IOException if the configuration file could not be read. + * @since 2.111 + */ + public synchronized void load2() throws IOException { COL result = create(); if (exists()) { - for (String line : lines()) { - if (line.startsWith("#")) continue; // comment - T r = parse(line); - if (r != null) - result.add(r); + try (LinesStream stream = linesStream()) { + for (String line : stream) { + if (line.startsWith("#")) continue; // comment + T r = parse(line); + if (r != null) + result.add(r); + } } } @@ -63,7 +91,7 @@ abstract class ConfigFile> extends TextFile { Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); write(newContent); - load(); + load2(); } public synchronized void append(String additional) throws IOException { @@ -79,8 +107,9 @@ abstract class ConfigFile> extends TextFile { // load upon the first use if (parsed==null) { synchronized (this) { - if (parsed==null) + if (parsed==null) { load(); + } } } return parsed; diff --git a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java index a35bfbe814ead7e3fdf6821c3a579ff0778fc783..356445b76c91e8f00412c53f25c4d4fdb60eea7b 100644 --- a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java @@ -30,7 +30,7 @@ import hudson.remoting.ChannelBuilder; import jenkins.ReflectiveFilePathFilter; import jenkins.security.ChannelConfigurator; import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; import java.io.File; import java.util.logging.Level; @@ -39,7 +39,7 @@ import java.util.logging.Logger; /** * Blocks agents from writing to files on the master by default (and also provide the kill switch.) */ -@Restricted(DoNotUse.class) // impl +@Restricted(NoExternalUse.class) // impl @Extension public class DefaultFilePathFilter extends ChannelConfigurator { /** diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java index 3a1e2b063d8a35e599ff36fb78445b1a7b717b1f..66e20fdd227fc9f39ceed04bf025e6e25c5940ca 100644 --- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java +++ b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java @@ -1,6 +1,8 @@ package jenkins.security.s2m; import hudson.Extension; + +import javax.annotation.Nonnull; import javax.inject.Inject; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; @@ -23,7 +25,7 @@ public class MasterKillSwitchConfiguration extends GlobalConfiguration { Jenkins jenkins; @Override - public GlobalConfigurationCategory getCategory() { + public @Nonnull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java index 6c339506904b58ce0f9231de8933733c17bf59b6..aca5fdb54a3bc4f21593287cec346698986a4f92 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java @@ -31,11 +31,11 @@ import org.jenkinsci.remoting.engine.JnlpProtocol1Handler; * *

* We do this by computing HMAC of the agent name. - * This code is sent to the agent inside the .jnlp file + * This code is sent to the agent inside the {@code .jnlp} file * (this file itself is protected by HTTP form-based authentication that * we use everywhere else in Jenkins), and the agent sends this * token back when it connects to the master. - * Unauthorized agents can't access the protected .jnlp file, + * Unauthorized agents can't access the protected {@code .jnlp} file, * so it can't impersonate a valid agent. * *

@@ -76,7 +76,7 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol { */ @Override public boolean isOptIn() { - return OPT_IN; + return true; } @Override @@ -104,14 +104,4 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol { ExtensionList.lookup(JnlpAgentReceiver.class)); } - - /** - * A/B test turning off this protocol by default. - */ - private static final boolean OPT_IN; - - static { - byte hash = Util.fromHexString(Jenkins.getInstance().getLegacyInstanceId())[0]; - OPT_IN = (hash % 10) == 0; - } } diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java index 24aca6696f62ed26021728c8ce3205326375c392..1c19f85a9471f3c0dd105891fb08f7a3bdde2928 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java @@ -50,7 +50,7 @@ public class JnlpSlaveAgentProtocol2 extends AgentProtocol { */ @Override public boolean isOptIn() { - return false; + return true; } @Override diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java index d216f628dd3bbd108692c0110beab02410f80f24..dac623046614292bcfd33401af879117f35f3b3c 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java @@ -48,15 +48,12 @@ public class JnlpSlaveAgentProtocol3 extends AgentProtocol { */ @Override public boolean isOptIn() { - return !ENABLED; + return true ; } @Override public String getName() { - // we only want to force the protocol off for users that have explicitly banned it via system property - // everyone on the A/B test will just have the opt-in flag toggled - // TODO strip all this out and hardcode OptIn==TRUE once JENKINS-36871 is merged - return forceEnabled != Boolean.FALSE ? handler.getName() : null; + return handler.isEnabled() ? handler.getName() : null; } /** @@ -79,26 +76,4 @@ public class JnlpSlaveAgentProtocol3 extends AgentProtocol { ExtensionList.lookup(JnlpAgentReceiver.class)); } - /** - * Flag to control the activation of JNLP3 protocol. - * - *

- * Once this will be on by default, the flag and this field will disappear. The system property is - * an escape hatch for those who hit any issues and those who are trying this out. - */ - @Restricted(NoExternalUse.class) - @SuppressFBWarnings(value = "MS_SHOULD_BE_REFACTORED_TO_BE_FINAL", - justification = "Part of the administrative API for System Groovy scripts.") - public static boolean ENABLED; - private static final Boolean forceEnabled; - - static { - forceEnabled = SystemProperties.optBoolean(JnlpSlaveAgentProtocol3.class.getName() + ".enabled"); - if (forceEnabled != null) { - ENABLED = forceEnabled; - } else { - byte hash = Util.fromHexString(Jenkins.getActiveInstance().getLegacyInstanceId())[0]; - ENABLED = (hash % 10) == 0; - } - } } diff --git a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java index e2c2211591b055f08d907c491ab605c08812b4db..d1e0c06e4b55a6980f54cb0b52e7785ad89278d9 100644 --- a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java +++ b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java @@ -24,10 +24,7 @@ package jenkins.slaves; import hudson.util.VersionNumber; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; @@ -35,21 +32,20 @@ import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; -// TODO: Make the API public (JENKINS-48766) /** * Provides information about Remoting versions used within the core. * @author Oleg Nenashev + * @since unrestricted since 2.104, initially added in 2.100. */ -@Restricted(NoExternalUse.class) public class RemotingVersionInfo { private static final Logger LOGGER = Logger.getLogger(RemotingVersionInfo.class.getName()); private static final String RESOURCE_NAME="remoting-info.properties"; - @CheckForNull + @Nonnull private static VersionNumber EMBEDDED_VERSION; - @CheckForNull + @Nonnull private static VersionNumber MINIMUM_SUPPORTED_VERSION; private RemotingVersionInfo() {} @@ -64,39 +60,53 @@ public class RemotingVersionInfo { LOGGER.log(Level.WARNING, "Failed to load Remoting Info from " + RESOURCE_NAME, e); } - EMBEDDED_VERSION = tryExtractVersion(props, "remoting.embedded.version"); - MINIMUM_SUPPORTED_VERSION = tryExtractVersion(props, "remoting.minimum.supported.version"); + EMBEDDED_VERSION = extractVersion(props, "remoting.embedded.version"); + MINIMUM_SUPPORTED_VERSION = extractVersion(props, "remoting.minimum.supported.version"); } - @CheckForNull - private static VersionNumber tryExtractVersion(@Nonnull Properties props, @Nonnull String propertyName) { + @Nonnull + private static VersionNumber extractVersion(@Nonnull Properties props, @Nonnull String propertyName) + throws ExceptionInInitializerError { String prop = props.getProperty(propertyName); if (prop == null) { - LOGGER.log(Level.FINE, "Property {0} is not defined in {1}", new Object[] {propertyName, RESOURCE_NAME}); - return null; + throw new ExceptionInInitializerError(String.format( + "Property %s is not defined in %s", propertyName, RESOURCE_NAME)); } if(prop.contains("${")) { // Due to whatever reason, Maven does not nullify them - LOGGER.log(Level.WARNING, "Property {0} in {1} has unresolved variable(s). Raw value: {2}", - new Object[] {propertyName, RESOURCE_NAME, prop}); - return null; + throw new ExceptionInInitializerError(String.format( + "Property %s in %s has unresolved variable(s). Raw value: %s", + propertyName, RESOURCE_NAME, prop)); } try { return new VersionNumber(prop); } catch (RuntimeException ex) { - LOGGER.log(Level.WARNING, String.format("Failed to parse version for for property %s in %s. Raw Value: %s", - propertyName, RESOURCE_NAME, prop), ex); - return null; + throw new ExceptionInInitializerError(new IOException( + String.format("Failed to parse version for for property %s in %s. Raw Value: %s", + propertyName, RESOURCE_NAME, prop), ex)); } } - @CheckForNull + /** + * Returns a version which is embedded into the Jenkins core. + * Note that this version may differ from one which is being really used in Jenkins. + * @return Remoting version + */ + @Nonnull public static VersionNumber getEmbeddedVersion() { return EMBEDDED_VERSION; } - @CheckForNull + /** + * Gets Remoting version which is supported by the core. + * Jenkins core and plugins make invoke operations on agents (e.g. {@link jenkins.security.MasterToSlaveCallable}) + * and use Remoting-internal API within them. + * In such case this API should be present on the remote side. + * This method defines a minimum expected version, so that all calls should use a compatible API. + * @return Minimal Remoting version for API calls. + */ + @Nonnull public static VersionNumber getMinimumSupportedVersion() { return MINIMUM_SUPPORTED_VERSION; } diff --git a/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java b/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java index 1eb68dcc6f6da09528e9e35f0e0c40f39be21954..3a8b9f6b4225d2fcebd863f63a7565cf8d1894cf 100644 --- a/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java +++ b/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java @@ -16,6 +16,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Callable; import java.util.logging.Logger; import static java.util.logging.Level.*; @@ -34,67 +35,80 @@ import jenkins.security.MasterToSlaveCallable; public class JnlpSlaveRestarterInstaller extends ComputerListener implements Serializable { @Override public void onOnline(final Computer c, final TaskListener listener) throws IOException, InterruptedException { - MasterComputer.threadPoolForRemoting.submit(new java.util.concurrent.Callable() { - @Override - public Void call() throws Exception { - install(c, listener); - return null; - } - }); + MasterComputer.threadPoolForRemoting.submit(new Install(c, listener)); + } + private static class Install implements Callable { + private final Computer c; + private final TaskListener listener; + Install(Computer c, TaskListener listener) { + this.c = c; + this.listener = listener; + } + @Override + public Void call() throws Exception { + install(c, listener); + return null; + } } - private void install(Computer c, TaskListener listener) { + private static void install(Computer c, TaskListener listener) { try { final List restarters = new ArrayList(SlaveRestarter.all()); VirtualChannel ch = c.getChannel(); if (ch==null) return; // defensive check - List effective = ch.call(new MasterToSlaveCallable, IOException>() { - public List call() throws IOException { - Engine e = Engine.current(); - if (e == null) return null; // not running under Engine + List effective = ch.call(new FindEffectiveRestarters(restarters)); - try { - Engine.class.getMethod("addListener", EngineListener.class); - } catch (NoSuchMethodException _) { - return null; // running with older version of remoting that doesn't support adding listener - } + LOGGER.log(FINE, "Effective SlaveRestarter on {0}: {1}", new Object[] {c.getName(), effective}); + } catch (Throwable e) { + Functions.printStackTrace(e, listener.error("Failed to install restarter")); + } + } + private static class FindEffectiveRestarters extends MasterToSlaveCallable, IOException> { + private final List restarters; + FindEffectiveRestarters(List restarters) { + this.restarters = restarters; + } + @Override + public List call() throws IOException { + Engine e = Engine.current(); + if (e == null) return null; // not running under Engine - // filter out ones that doesn't apply - for (Iterator itr = restarters.iterator(); itr.hasNext(); ) { - SlaveRestarter r = itr.next(); - if (!r.canWork()) - itr.remove(); - } + try { + Engine.class.getMethod("addListener", EngineListener.class); + } catch (NoSuchMethodException ignored) { + return null; // running with older version of remoting that doesn't support adding listener + } - e.addListener(new EngineListenerAdapter() { - @Override - public void onReconnect() { + // filter out ones that doesn't apply + for (Iterator itr = restarters.iterator(); itr.hasNext(); ) { + SlaveRestarter r = itr.next(); + if (!r.canWork()) + itr.remove(); + } + + e.addListener(new EngineListenerAdapter() { + @Override + public void onReconnect() { + try { + for (SlaveRestarter r : restarters) { try { - for (SlaveRestarter r : restarters) { - try { - LOGGER.info("Restarting agent via "+r); - r.restart(); - } catch (Exception x) { - LOGGER.log(SEVERE, "Failed to restart agent with "+r, x); - } - } - } finally { - // if we move on to the reconnection without restart, - // don't let the current implementations kick in when the agent loses connection again - restarters.clear(); + LOGGER.info("Restarting agent via "+r); + r.restart(); + } catch (Exception x) { + LOGGER.log(SEVERE, "Failed to restart agent with "+r, x); } } - }); - - return restarters; + } finally { + // if we move on to the reconnection without restart, + // don't let the current implementations kick in when the agent loses connection again + restarters.clear(); + } } }); - LOGGER.log(FINE, "Effective SlaveRestarter on {0}: {1}", new Object[] {c.getName(), effective}); - } catch (Throwable e) { - Functions.printStackTrace(e, listener.error("Failed to install restarter")); + return restarters; } } diff --git a/core/src/main/java/jenkins/slaves/systemInfo/SlaveSystemInfo.java b/core/src/main/java/jenkins/slaves/systemInfo/SlaveSystemInfo.java index 16a5352d09aad470496420168cc9d4e267a1b79e..8888ca426350c1a2e1b3e9e139dc9267244024df 100644 --- a/core/src/main/java/jenkins/slaves/systemInfo/SlaveSystemInfo.java +++ b/core/src/main/java/jenkins/slaves/systemInfo/SlaveSystemInfo.java @@ -8,7 +8,7 @@ import hudson.model.Computer; * Extension point that contributes to the system information page of {@link Computer}. * *

Views

- * Subtypes must have systemInfo.groovy/.jelly view. + * Subtypes must have {@code systemInfo.groovy/.jelly} view. * This view will have the "it" variable that refers to {@link Computer} object, and "instance" variable * that refers to {@link SlaveSystemInfo} object. * diff --git a/core/src/main/java/jenkins/tasks/SimpleBuildStep.java b/core/src/main/java/jenkins/tasks/SimpleBuildStep.java index 640ea01aa25b35dc754b0b8a6844fbcb128b2770..83a6c5a9ce2f89fab360422bcb3b4adffbc77138 100644 --- a/core/src/main/java/jenkins/tasks/SimpleBuildStep.java +++ b/core/src/main/java/jenkins/tasks/SimpleBuildStep.java @@ -52,7 +52,7 @@ import jenkins.model.DependencyDeclarer; import jenkins.model.RunAction2; import jenkins.model.TransientActionFactory; import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** * A build step (like a {@link Builder} or {@link Publisher}) which may be called at an arbitrary time during a build (or multiple times), run, and be done. @@ -103,7 +103,7 @@ public interface SimpleBuildStep extends BuildStep { } @SuppressWarnings("rawtypes") - @Restricted(DoNotUse.class) + @Restricted(NoExternalUse.class) @Extension public static final class LastBuildActionFactory extends TransientActionFactory { diff --git a/core/src/main/java/jenkins/telemetry/Correlator.java b/core/src/main/java/jenkins/telemetry/Correlator.java new file mode 100644 index 0000000000000000000000000000000000000000..9e9005934963c949c3bc5261adf29cbdd828ab98 --- /dev/null +++ b/core/src/main/java/jenkins/telemetry/Correlator.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.telemetry; + +import com.google.common.annotations.VisibleForTesting; +import hudson.Extension; +import hudson.model.Describable; +import hudson.model.Descriptor; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.util.UUID; + +/** + * This class stores a UUID identifying this instance for telemetry reporting to allow deduplication or merging of submitted records. + * + * We're not using anything derived from instance identity so we cannot connect an instance's public appearance with its submissions. + * + * This really only uses Descriptor/Describable to get a Saveable implementation for free. + */ +@Extension +@Restricted(NoExternalUse.class) +public class Correlator extends Descriptor implements Describable { + private String correlationId; + + public Correlator() { + super(Correlator.class); + load(); + if (correlationId == null) { + correlationId = UUID.randomUUID().toString(); + save(); + } + } + + public String getCorrelationId() { + return correlationId; + } + + @Restricted(NoExternalUse.class) + @VisibleForTesting + void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + @Override + public Descriptor getDescriptor() { + return this; + } +} \ No newline at end of file diff --git a/core/src/main/java/jenkins/telemetry/Telemetry.java b/core/src/main/java/jenkins/telemetry/Telemetry.java new file mode 100644 index 0000000000000000000000000000000000000000..d528f5e9ed53857ad358c6f792d0b90d0a61758a --- /dev/null +++ b/core/src/main/java/jenkins/telemetry/Telemetry.java @@ -0,0 +1,222 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.telemetry; + +import com.google.common.annotations.VisibleForTesting; +import hudson.Extension; +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import hudson.ProxyConfiguration; +import hudson.model.AsyncPeriodicWork; +import hudson.model.TaskListener; +import hudson.model.UsageStatistics; +import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; +import net.sf.json.JSONObject; +import org.apache.commons.codec.digest.DigestUtils; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Extension point for collecting JEP-214 telemetry. + * + * Implementations should provide a description.jelly file with additional details about their purpose and + * behavior which will be included in help-usageStatisticsCollected.jelly for {@link UsageStatistics}. + * + * @see JEP-214 + * + * @since 2.143 + */ +public abstract class Telemetry implements ExtensionPoint { + + // https://webhook.site is a nice stand-in for this during development; just needs to end in ? to submit the ID as query parameter + @Restricted(NoExternalUse.class) + @VisibleForTesting + static String ENDPOINT = SystemProperties.getString(Telemetry.class.getName() + ".endpoint", "https://uplink.jenkins.io/events"); + + private static final Logger LOGGER = Logger.getLogger(Telemetry.class.getName()); + + /** + * ID of this collector, typically an alphanumeric string (and punctuation). + * + * Good IDs are globally unique and human readable (i.e. no UUIDs). + * + * For a periodically updated list of all public implementations, see https://jenkins.io/doc/developer/extensions/jenkins-core/#telemetry + * + * @return ID of the collector, never null or empty + */ + @Nonnull + public String getId() { + return getClass().getName(); + } + + /** + * User friendly display name for this telemetry collector, ideally localized. + * + * @return display name, never null or empty + */ + @Nonnull + public abstract String getDisplayName(); + + /** + * Start date for the collection. + * Will be checked in Jenkins to not collect outside the defined time span. + * This does not have to be precise enough for time zones to be a consideration. + * + * @return collection start date + */ + @Nonnull + public abstract LocalDate getStart(); + + /** + * End date for the collection. + * Will be checked in Jenkins to not collect outside the defined time span. + * This does not have to be precise enough for time zones to be a consideration. + * + * @return collection end date + */ + @Nonnull + public abstract LocalDate getEnd(); + + /** + * Returns the content to be sent to the telemetry service. + * + * This method is called periodically, once per content submission. + * + * @return The JSON payload, or null if no content should be submitted. + */ + @CheckForNull + public abstract JSONObject createContent(); + + public static ExtensionList all() { + return ExtensionList.lookup(Telemetry.class); + } + + /** + * @since 2.147 + * @return whether to collect telemetry + */ + public static boolean isDisabled() { + if (UsageStatistics.DISABLED) { + return true; + } + Jenkins jenkins = Jenkins.getInstanceOrNull(); + + return jenkins == null || !jenkins.isUsageStatisticsCollected(); + } + + @Extension + public static class TelemetryReporter extends AsyncPeriodicWork { + + public TelemetryReporter() { + super("telemetry collection"); + } + + @Override + public long getRecurrencePeriod() { + return TimeUnit.HOURS.toMillis(24); + } + + @Override + protected void execute(TaskListener listener) throws IOException, InterruptedException { + if (isDisabled()) { + LOGGER.info("Collection of anonymous usage statistics is disabled, skipping telemetry collection and submission"); + return; + } + Telemetry.all().forEach(telemetry -> { + if (telemetry.getStart().isAfter(LocalDate.now())) { + LOGGER.config("Skipping telemetry for '" + telemetry.getId() + "' as it is configured to start later"); + return; + } + if (telemetry.getEnd().isBefore(LocalDate.now())) { + LOGGER.config("Skipping telemetry for '" + telemetry.getId() + "' as it is configured to end in the past"); + return; + } + + JSONObject data = new JSONObject(); + try { + data = telemetry.createContent(); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Failed to build telemetry content for: '" + telemetry.getId() + "'", e); + } + + if (data == null) { + LOGGER.log(Level.CONFIG, "Skipping telemetry for '" + telemetry.getId() + "' as it has no data"); + return; + } + + JSONObject wrappedData = new JSONObject(); + wrappedData.put("type", telemetry.getId()); + wrappedData.put("payload", data); + String correlationId = ExtensionList.lookupSingleton(Correlator.class).getCorrelationId(); + wrappedData.put("correlator", DigestUtils.sha256Hex(correlationId + telemetry.getId())); + + try { + URL url = new URL(ENDPOINT); + URLConnection conn = ProxyConfiguration.open(url); + if (!(conn instanceof HttpURLConnection)) { + LOGGER.config("URL did not result in an HttpURLConnection: " + ENDPOINT); + return; + } + HttpURLConnection http = (HttpURLConnection) conn; + http.setRequestProperty("Content-Type", "application/json; charset=utf-8"); + http.setDoOutput(true); + + String body = wrappedData.toString(); + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest("Submitting JSON: " + body); + } + + try (OutputStream out = http.getOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) { + writer.append(body); + } + + LOGGER.config("Telemetry submission received response '" + http.getResponseCode() + " " + http.getResponseMessage() + "' for: " + telemetry.getId()); + } catch (MalformedURLException e) { + LOGGER.config("Malformed endpoint URL: " + ENDPOINT + " for telemetry: " + telemetry.getId()); + } catch (IOException e) { + // deliberately low visibility, as temporary infra problems aren't a big deal and we'd + // rather have some unsuccessful submissions than admins opting out to clean up logs + LOGGER.log(Level.CONFIG, "Failed to submit telemetry: " + telemetry.getId() + " to: " + ENDPOINT, e); + } + }); + } + } +} diff --git a/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java b/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..1384946bed5dd3160ce7b49baefccb26376a0c58 --- /dev/null +++ b/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java @@ -0,0 +1,130 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.telemetry.impl; + +import hudson.Extension; +import jenkins.model.DownloadSettings; +import jenkins.model.Jenkins; +import jenkins.telemetry.Telemetry; +import jenkins.util.SystemProperties; +import net.sf.json.JSONObject; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.Nonnull; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; + +/** + * Telemetry implementation gathering information about system properties. + */ +@Extension +@Restricted(NoExternalUse.class) +public class SecuritySystemProperties extends Telemetry { + @Nonnull + @Override + public String getId() { + return "security-system-properties"; + } + + @Nonnull + @Override + public LocalDate getStart() { + return LocalDate.of(2018, 9, 1); + } + + @Nonnull + @Override + public LocalDate getEnd() { + return LocalDate.of(2018, 12, 1); + } + + @Nonnull + @Override + public String getDisplayName() { + return "Use of Security-related Java system properties"; + } + + @Nonnull + @Override + public JSONObject createContent() { + Map security = new TreeMap<>(); + putBoolean(security, "hudson.ConsoleNote.INSECURE", false); + putBoolean(security, "hudson.logging.LogRecorderManager.skipPermissionCheck", false); + putBoolean(security, "hudson.model.AbstractItem.skipPermissionCheck", false); + putBoolean(security, "hudson.model.ParametersAction.keepUndefinedParameters", false); + putBoolean(security, "hudson.model.Run.skipPermissionCheck", false); + putBoolean(security, "hudson.model.UpdateCenter.skipPermissionCheck", false); + putBoolean(security, "hudson.model.User.allowNonExistentUserToLogin", false); + putBoolean(security, "hudson.model.User.allowUserCreationViaUrl", false); + putBoolean(security, "hudson.model.User.SECURITY_243_FULL_DEFENSE", true); + putBoolean(security, "hudson.model.User.skipPermissionCheck", false); + putBoolean(security, "hudson.PluginManager.skipPermissionCheck", false); + putBoolean(security, "hudson.remoting.URLDeserializationHelper.avoidUrlWrapping", false); + putBoolean(security, "hudson.search.Search.skipPermissionCheck", false); + putBoolean(security, "jenkins.security.ClassFilterImpl.SUPPRESS_WHITELIST", false); + putBoolean(security, "jenkins.security.ClassFilterImpl.SUPPRESS_ALL", false); + putBoolean(security, "org.kohsuke.stapler.Facet.allowViewNamePathTraversal", false); + putBoolean(security, "org.kohsuke.stapler.jelly.CustomJellyContext.escapeByDefault", true); + + // not controlled by a system property for historical reasons only + security.put("jenkins.model.DownloadSettings.useBrowser", Boolean.toString(DownloadSettings.get().isUseBrowser())); + + putStringInfo(security, "hudson.model.ParametersAction.safeParameters"); + putStringInfo(security, "hudson.model.DirectoryBrowserSupport.CSP"); + putStringInfo(security, "hudson.security.HudsonPrivateSecurityRealm.ID_REGEX"); + + Map info = new TreeMap<>(); + info.put("core", Jenkins.getVersion().toString()); + info.put("clientDate", clientDateString()); + info.put("properties", security); + + return JSONObject.fromObject(info); + } + + private static String clientDateString() { + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); + df.setTimeZone(tz); // strip timezone + return df.format(new Date()); + } + + private static void putBoolean(Map propertiesMap, String systemProperty, boolean defaultValue) { + propertiesMap.put(systemProperty, Boolean.toString(SystemProperties.getBoolean(systemProperty, defaultValue))); + } + + private static void putStringInfo(Map propertiesMap, String systemProperty) { + String reportedValue = "null"; + String value = SystemProperties.getString(systemProperty); + if (value != null) { + reportedValue = Integer.toString(value.length()); + } + propertiesMap.put(systemProperty, reportedValue); + } +} diff --git a/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java b/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java new file mode 100644 index 0000000000000000000000000000000000000000..6e73a73e9528373fddad273cf48fd76a43aa5d0d --- /dev/null +++ b/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.telemetry.impl; + +import hudson.Extension; +import hudson.PluginWrapper; +import hudson.model.UsageStatistics; +import hudson.util.VersionNumber; +import jenkins.model.Jenkins; +import jenkins.telemetry.Telemetry; +import net.sf.json.JSONObject; +import org.kohsuke.MetaInfServices; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.EvaluationTrace; +import org.kohsuke.stapler.StaplerRequest; + +import javax.annotation.Nonnull; +import java.time.LocalDate; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentSkipListSet; + +/** + * Telemetry implementation gathering information about Stapler dispatch routes. + */ +@Extension +@Restricted(NoExternalUse.class) +public class StaplerDispatches extends Telemetry { + @Nonnull + @Override + public LocalDate getStart() { + return LocalDate.of(2018, 10, 10); + } + + @Nonnull + @Override + public LocalDate getEnd() { + return LocalDate.of(2019, 2, 1); + } + + @Nonnull + @Override + public String getDisplayName() { + return "Stapler request handling"; + } + + @Override + public JSONObject createContent() { + if (traces.size() == 0) { + return null; + } + Map info = new TreeMap<>(); + info.put("components", buildComponentInformation()); + info.put("dispatches", buildDispatches()); + + return JSONObject.fromObject(info); + } + + private Object buildDispatches() { + Set currentTraces = new TreeSet<>(traces); + traces.clear(); + return currentTraces; + } + + private Object buildComponentInformation() { + Map components = new TreeMap<>(); + VersionNumber core = Jenkins.getVersion(); + components.put("jenkins-core", core == null ? "" : core.toString()); + + for (PluginWrapper plugin : Jenkins.get().pluginManager.getPlugins()) { + if (plugin.isActive()) { + components.put(plugin.getShortName(), plugin.getVersion()); + } + } + return components; + } + + @MetaInfServices + public static class StaplerTrace extends EvaluationTrace.ApplicationTracer { + + @Override + protected void record(StaplerRequest staplerRequest, String s) { + if (Telemetry.isDisabled()) { + // do not collect traces while usage statistics are disabled + return; + } + traces.add(s); + } + } + + private static final Set traces = new ConcurrentSkipListSet<>(); +} diff --git a/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java b/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java new file mode 100644 index 0000000000000000000000000000000000000000..3856cea6344bff7069c0c20dc9f779a209fc2e1f --- /dev/null +++ b/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java @@ -0,0 +1,147 @@ +/* + * The MIT License + * + * Copyright (c) 2018, Daniel Beck + * + * 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.telemetry.impl; + +import hudson.Extension; +import hudson.init.Initializer; +import hudson.util.PluginServletFilter; +import jenkins.telemetry.Telemetry; +import net.sf.json.JSONObject; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.annotation.Nonnull; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.time.LocalDate; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Extension +@Restricted(NoExternalUse.class) +public class UserLanguages extends Telemetry { + + private static final Map requestsByLanguage = new ConcurrentSkipListMap<>(); + private static Logger LOGGER = Logger.getLogger(UserLanguages.class.getName()); + + @Nonnull + @Override + public String getId() { + return UserLanguages.class.getName(); + } + + @Nonnull + @Override + public String getDisplayName() { + return "Browser languages"; + } + + @Nonnull + @Override + public LocalDate getStart() { + return LocalDate.of(2018, 10, 1); + } + + @Nonnull + @Override + public LocalDate getEnd() { + return LocalDate.of(2019, 1, 1); + } + + @Override + public JSONObject createContent() { + if (requestsByLanguage.size() == 0) { + return null; + } + Map currentRequests = new TreeMap<>(requestsByLanguage); + requestsByLanguage.clear(); + + JSONObject payload = new JSONObject(); + for (Map.Entry entry : currentRequests.entrySet()) { + payload.put(entry.getKey(), entry.getValue().longValue()); + } + return payload; + } + + @Initializer + public static void setUpFilter() { + Filter filter = new AcceptLanguageFilter(); + if (!PluginServletFilter.hasFilter(filter)) { + try { + PluginServletFilter.addFilter(filter); + } catch (ServletException ex) { + LOGGER.log(Level.WARNING, "Failed to set up languages servlet filter", ex); + } + } + } + + public static final class AcceptLanguageFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (request instanceof HttpServletRequest && !Telemetry.isDisabled()) { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String language = httpServletRequest.getHeader("Accept-Language"); + if (language != null) { + if (!requestsByLanguage.containsKey(language)) { + requestsByLanguage.put(language, new AtomicLong(0)); + } + requestsByLanguage.get(language).incrementAndGet(); + } + } + chain.doFilter(request, response); + } + + @Override + public void destroy() { + + } + + @Override + public boolean equals(Object obj) { // support PluginServletFilter#hasFilter + return obj != null && obj.getClass() == AcceptLanguageFilter.class; + } + + // findbugs + @Override + public int hashCode() { + return 42; + } + } +} diff --git a/core/src/main/java/jenkins/tools/ToolConfigurationCategory.java b/core/src/main/java/jenkins/tools/ToolConfigurationCategory.java index e785369556d1ec36ea0eaee5e30e080500c4f8d7..5c6d6ce695c0db70749f8f2fdf78b82d70f210e5 100644 --- a/core/src/main/java/jenkins/tools/ToolConfigurationCategory.java +++ b/core/src/main/java/jenkins/tools/ToolConfigurationCategory.java @@ -2,6 +2,7 @@ package jenkins.tools; import hudson.Extension; import jenkins.model.GlobalConfigurationCategory; +import jenkins.management.Messages; /** * Global configuration of tool locations and installers. @@ -12,10 +13,10 @@ import jenkins.model.GlobalConfigurationCategory; public class ToolConfigurationCategory extends GlobalConfigurationCategory { @Override public String getShortDescription() { - return jenkins.management.Messages.ConfigureTools_Description(); + return Messages.ConfigureTools_Description(); } public String getDisplayName() { - return jenkins.management.Messages.ConfigureTools_DisplayName(); + return Messages.ConfigureTools_DisplayName(); } } diff --git a/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java b/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java index 1c341bbd1777b19f9a3d4cf5436116d4dde6a062..c601cebc2629486ec81d69faaaf24d1914fc406c 100644 --- a/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java +++ b/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java @@ -241,7 +241,7 @@ public final class ReverseBuildTrigger extends Trigger implements Dependenc @Extension public static final class RunListenerImpl extends RunListener { static RunListenerImpl get() { - return ExtensionList.lookup(RunListener.class).get(RunListenerImpl.class); + return ExtensionList.lookupSingleton(RunListenerImpl.class); } private Map> upstream2Trigger; @@ -251,7 +251,7 @@ public final class ReverseBuildTrigger extends Trigger implements Dependenc } private Map> calculateCache() { - try (ACLContext _ = ACL.as(ACL.SYSTEM)) { + try (ACLContext acl = ACL.as(ACL.SYSTEM)) { final Map> result = new WeakHashMap<>(); for (Job downstream : Jenkins.getInstance().allItems(Job.class)) { ReverseBuildTrigger trigger = @@ -312,7 +312,7 @@ public final class ReverseBuildTrigger extends Trigger implements Dependenc public static class ItemListenerImpl extends ItemListener { @Override public void onLocationChanged(Item item, final String oldFullName, final String newFullName) { - try (ACLContext _ = ACL.as(ACL.SYSTEM)) { + try (ACLContext acl = ACL.as(ACL.SYSTEM)) { for (Job p : Jenkins.getInstance().allItems(Job.class)) { ReverseBuildTrigger t = ParameterizedJobMixIn.getTrigger(p, ReverseBuildTrigger.class); if (t != null) { diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index 1d23b709847c6cdbaa6f006ee5f2c56d73910e8c..915a5580344904adda3d77edbdff43a58a8c4297 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -2,10 +2,15 @@ package jenkins.util; import com.trilead.ssh2.crypto.Base64; import hudson.util.FormValidation; + +import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.InvalidPathException; import jenkins.model.Jenkins; import net.sf.json.JSONObject; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.Charsets; import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.TeeOutputStream; import org.jvnet.hudson.crypto.CertificateUtil; @@ -20,7 +25,9 @@ import java.io.OutputStreamWriter; import java.security.DigestOutputStream; import java.security.GeneralSecurityException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.Signature; +import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; @@ -78,64 +85,158 @@ public class JSONSignatureValidator { CertificateUtil.validatePath(certs, loadTrustAnchors(cf)); } - // this is for computing a digest to check sanity - MessageDigest sha1 = MessageDigest.getInstance("SHA1"); - DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(),sha1); - - // this is for computing a signature - Signature sig = Signature.getInstance("SHA1withRSA"); if (certs.isEmpty()) { return FormValidation.error("No certificate found in %s. Cannot verify the signature", name); - } else { - sig.initVerify(certs.get(0)); - } - SignatureOutputStream sos = new SignatureOutputStream(sig); - - // until JENKINS-11110 fix, UC used to serve invalid digest (and therefore unverifiable signature) - // that only covers the earlier portion of the file. This was caused by the lack of close() call - // in the canonical writing, which apparently leave some bytes somewhere that's not flushed to - // the digest output stream. This affects Jenkins [1.424,1,431]. - // Jenkins 1.432 shipped with the "fix" (1eb0c64abb3794edce29cbb1de50c93fa03a8229) that made it - // compute the correct digest, but it breaks all the existing UC json metadata out there. We then - // quickly discovered ourselves in the catch-22 situation. If we generate UC with the correct signature, - // it'll cut off [1.424,1.431] from the UC. But if we don't, we'll cut off [1.432,*). - // - // In 1.433, we revisited 1eb0c64abb3794edce29cbb1de50c93fa03a8229 so that the original "digest"/"signature" - // pair continues to be generated in a buggy form, while "correct_digest"/"correct_signature" are generated - // correctly. - // - // Jenkins should ignore "digest"/"signature" pair. Accepting it creates a vulnerability that allows - // the attacker to inject a fragment at the end of the json. - o.writeCanonical(new OutputStreamWriter(new TeeOutputStream(dos,sos),"UTF-8")).close(); - - // did the digest match? this is not a part of the signature validation, but if we have a bug in the c14n - // (which is more likely than someone tampering with update center), we can tell - String computedDigest = new String(Base64.encode(sha1.digest())); - String providedDigest = signature.optString("correct_digest"); - if (providedDigest==null) { - return FormValidation.error("No correct_digest parameter in "+name+". This metadata appears to be old."); } - if (!computedDigest.equalsIgnoreCase(providedDigest)) { - String msg = "Digest mismatch: computed=" + computedDigest + " vs expected=" + providedDigest + " in " + name; - if (LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.severe(msg); - LOGGER.severe(o.toString(2)); + + // check the better digest first + FormValidation resultSha512 = null; + try { + MessageDigest digest = MessageDigest.getInstance("SHA-512"); + Signature sig = Signature.getInstance("SHA512withRSA"); + sig.initVerify(certs.get(0)); + resultSha512 = checkSpecificSignature(o, signature, digest, "correct_digest512", sig, "correct_signature512", "SHA-512"); + switch (resultSha512.kind) { + case ERROR: + return resultSha512; + case WARNING: + LOGGER.log(Level.INFO, "JSON data source '" + name + "' does not provide a SHA-512 content checksum or signature. Looking for SHA-1."); + break; + case OK: + // fall through } - return FormValidation.error(msg); + } catch (NoSuchAlgorithmException nsa) { + LOGGER.log(Level.WARNING, "Failed to verify potential SHA-512 digest/signature, falling back to SHA-1", nsa); } - String providedSignature = signature.getString("correct_signature"); - if (!sig.verify(Base64.decode(providedSignature.toCharArray()))) { - return FormValidation.error("Signature in the update center doesn't match with the certificate in "+name); + // if we get here, SHA-512 passed, wasn't provided, or the JRE is terrible. + + MessageDigest digest = MessageDigest.getInstance("SHA1"); + Signature sig = Signature.getInstance("SHA1withRSA"); + sig.initVerify(certs.get(0)); + FormValidation resultSha1 = checkSpecificSignature(o, signature, digest, "correct_digest", sig, "correct_signature", "SHA-1"); + + switch (resultSha1.kind) { + case ERROR: + return resultSha1; + case WARNING: + if (resultSha512.kind == FormValidation.Kind.WARNING) { + // neither signature provided + return FormValidation.error("No correct_signature or correct_signature512 entry found in '" + name + "'."); + } + case OK: + // fall through } if (warning!=null) return warning; return FormValidation.ok(); } catch (GeneralSecurityException e) { - return FormValidation.error(e,"Signature verification failed in "+name); + return FormValidation.error(e, "Signature verification failed in "+name); } } + + /** + * Computes the specified {@code digest} and {@code signature} for the provided {@code json} object and checks whether they match {@code digestEntry} and {@signatureEntry} in the provided {@code signatureJson} object. + * + * @param json the full update-center.json content + * @param signatureJson signature block from update-center.json + * @param digest digest to compute + * @param digestEntry key of the digest entry in {@code signatureJson} to check + * @param signature signature to compute + * @param signatureEntry key of the signature entry in {@code signatureJson} to check + * @param digestName name of the digest used for log/error messages + * @return {@link FormValidation.Kind#WARNING} if digest or signature are not provided, {@link FormValidation.Kind#OK} if check is successful, {@link FormValidation.Kind#ERROR} otherwise. + * @throws IOException if this somehow fails to write the canonical JSON representation to an in-memory stream. + */ + private FormValidation checkSpecificSignature(JSONObject json, JSONObject signatureJson, MessageDigest digest, String digestEntry, Signature signature, String signatureEntry, String digestName) throws IOException { + // this is for computing a digest to check sanity + DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(), digest); + SignatureOutputStream sos = new SignatureOutputStream(signature); + + String providedDigest = signatureJson.optString(digestEntry, null); + if (providedDigest == null) { + return FormValidation.warning("No '" + digestEntry + "' found"); + } + + String providedSignature = signatureJson.optString(signatureEntry, null); + if (providedSignature == null) { + return FormValidation.warning("No '" + signatureEntry + "' found"); + } + + // until JENKINS-11110 fix, UC used to serve invalid digest (and therefore unverifiable signature) + // that only covers the earlier portion of the file. This was caused by the lack of close() call + // in the canonical writing, which apparently leave some bytes somewhere that's not flushed to + // the digest output stream. This affects Jenkins [1.424,1,431]. + // Jenkins 1.432 shipped with the "fix" (1eb0c64abb3794edce29cbb1de50c93fa03a8229) that made it + // compute the correct digest, but it breaks all the existing UC json metadata out there. We then + // quickly discovered ourselves in the catch-22 situation. If we generate UC with the correct signature, + // it'll cut off [1.424,1.431] from the UC. But if we don't, we'll cut off [1.432,*). + // + // In 1.433, we revisited 1eb0c64abb3794edce29cbb1de50c93fa03a8229 so that the original "digest"/"signature" + // pair continues to be generated in a buggy form, while "correct_digest"/"correct_signature" are generated + // correctly. + // + // Jenkins should ignore "digest"/"signature" pair. Accepting it creates a vulnerability that allows + // the attacker to inject a fragment at the end of the json. + json.writeCanonical(new OutputStreamWriter(new TeeOutputStream(dos,sos), Charsets.UTF_8)).close(); + + // did the digest match? this is not a part of the signature validation, but if we have a bug in the c14n + // (which is more likely than someone tampering with update center), we can tell + + if (!digestMatches(digest.digest(), providedDigest)) { + String msg = digestName + " digest mismatch: expected=" + providedDigest + " in '" + name + "'"; + if (LOGGER.isLoggable(Level.SEVERE)) { + LOGGER.severe(msg); + LOGGER.severe(json.toString(2)); + } + return FormValidation.error(msg); + } + + if (!verifySignature(signature, providedSignature)) { + return FormValidation.error(digestName + " based signature in the update center doesn't match with the certificate in '"+name + "'"); + } + + return FormValidation.ok(); + } + + /** + * Utility method supporting both possible signature formats: Base64 and Hex + */ + private boolean verifySignature(Signature signature, String providedSignature) { + // We can only make one call to Signature#verify here. + // Since we need to potentially check two values (one decoded from hex, the other decoded from base64), + // try hex first: It's almost certainly going to fail decoding if a base64 string was passed. + // It is extremely unlikely for base64 strings to be a valid hex string. + // This way, if it's base64, the #verify call will be skipped, and we continue with the #verify for decoded base64. + // This approach might look unnecessarily clever, but short of having redundant Signature instances, + // there doesn't seem to be a better approach for this. + try { + if (signature.verify(Hex.decodeHex(providedSignature.toCharArray()))) { + return true; + } + } catch (SignatureException|DecoderException ignore) { + // ignore + } + + try { + if (signature.verify(Base64.decode(providedSignature.toCharArray()))) { + return true; + } + } catch (SignatureException|IOException ignore) { + // ignore + } + return false; + } + + /** + * Utility method supporting both possible digest formats: Base64 and Hex + */ + private boolean digestMatches(byte[] digest, String providedDigest) { + return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(new String(Base64.encode(digest))); + } + + protected Set loadTrustAnchors(CertificateFactory cf) throws IOException { // if we trust default root CAs, we end up trusting anyone who has a valid certificate, // which isn't useful at all diff --git a/core/src/main/java/jenkins/util/MemoryReductionUtil.java b/core/src/main/java/jenkins/util/MemoryReductionUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..7d7c84a030e6bda00a7f5b62e3246306c2fd72f0 --- /dev/null +++ b/core/src/main/java/jenkins/util/MemoryReductionUtil.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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.util; + +import hudson.Util; +import java.util.HashMap; +import java.util.Map; + +/** + * Utilities to reduce memory footprint + * @author Sam Van Oort + */ +public class MemoryReductionUtil { + /** Returns the capacity we need to allocate for a HashMap so it will hold all elements without needing to resize. */ + public static int preallocatedHashmapCapacity(int elementsToHold) { + if (elementsToHold <= 0) { + return 0; + } else if (elementsToHold < 3) { + return elementsToHold+1; + } else { + return elementsToHold+elementsToHold/3; // Default load factor is 0.75, so we want to fill that much. + } + } + + /** Returns a mutable HashMap presized to hold the given number of elements without needing to resize. */ + public static Map getPresizedMutableMap(int elementCount) { + return new HashMap(preallocatedHashmapCapacity(elementCount)); + } + + /** Empty string array, exactly what it says on the tin. Avoids repeatedly created empty array when calling "toArray." */ + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + + /** Returns the input strings, but with all values interned. */ + public static String[] internInPlace(String[] input) { + if (input == null) { + return null; + } else if (input.length == 0) { + return EMPTY_STRING_ARRAY; + } + for (int i=0; idomain + *

+ * Avoid {@code -} to be first or last, and {@code .} to be first (but can be last) + *

+ * + * Lenient version of:

    + *
  1. RFC-952 GRAMMATICAL HOST TABLE SPECIFICATION
  2. + *
  3. RFC-1034 3.5
  4. + *
  5. RFC-17383.1, host
  6. + *
  7. RFC-1123 2.1
  8. + *
+ *

+ * + * Deliberately allow:

    + *
  1. short domain name (often there are rules like minimum of 3 characters)
  2. + *
  3. long domain name (normally limit on whole domain of 255 and for each subdomain/label of 63)
  4. + *
  5. starting by numbers (disallowed by RFC-952 and RFC-1034, but nowadays it's supported by RFC-1123)
  6. + *
  7. use of underscore (not explicitly allowed in RFC but could occur in internal network, we do not speak about path here, just domain)
  8. + *
  9. custom TLD like "intern" that is not standard but could be registered locally in a network
  10. + *
+ */ + private static String DOMAIN_REGEX = System.getProperty( + UrlHelper.class.getName() + ".DOMAIN_REGEX", + "^" + + "\\w" + // must start with letter / number / underscore + "(-*(\\.|\\w))*" +// dashes are allowed but not as last character + "\\.*" + // can end with zero (most common), one or multiple dots + "(:\\d{1,5})?" + // and potentially the port specification + "$" + ); + + public static boolean isValidRootUrl(String url) { + UrlValidator validator = new CustomUrlValidator(); + return validator.isValid(url); + } + + private static class CustomUrlValidator extends UrlValidator { + private CustomUrlValidator() { + super(new String[]{"http", "https"}, UrlValidator.ALLOW_LOCAL_URLS + UrlValidator.NO_FRAGMENTS); + } + + @Override + protected boolean isValidAuthority(String authority) { + boolean superResult = super.isValidAuthority(authority); + if(superResult && authority.contains("[")){ + // to support ipv6 + return true; + } + if(!superResult && authority == null){ + return false; + } + String authorityASCII = DomainValidator.unicodeToASCII(authority); + return authorityASCII.matches(DOMAIN_REGEX); + } + + @Override + protected boolean isValidQuery(String query) { + // does not accept query + return query == null; + } + } +} diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index e2d27d75ec6a050555d6e7cc8f677ac92d803b50..fcb7ec2d1acc9fa923466ae0e6753c87b91be240 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -25,29 +25,46 @@ package jenkins.util; import hudson.FilePath; +import hudson.Util; import hudson.model.DirectoryBrowserSupport; +import hudson.os.PosixException; import hudson.remoting.Callable; import hudson.remoting.Channel; +import hudson.remoting.RemoteInputStream; import hudson.remoting.VirtualChannel; import hudson.util.DirScanner; import hudson.util.FileVisitor; +import hudson.util.IOUtils; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.URI; +import java.net.URL; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.LinkOption; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import jenkins.MasterToSlaveFileCallable; +import jenkins.model.ArtifactManager; +import jenkins.security.MasterToSlaveCallable; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.types.AbstractFileSet; +import org.apache.tools.ant.types.selectors.SelectorUtils; +import org.apache.tools.ant.types.selectors.TokenizedPath; +import org.apache.tools.ant.types.selectors.TokenizedPattern; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; /** * Abstraction over {@link File}, {@link FilePath}, or other items such as network resources or ZIP entries. @@ -62,6 +79,25 @@ import jenkins.MasterToSlaveFileCallable; * {@link VirtualFile} makes no assumption about where the actual files are, or whether there really exists * {@link File}s somewhere. This makes VirtualFile more abstract. * + *

Opening files from other machines

+ * + * While {@link VirtualFile} is marked {@link Serializable}, + * it is not safe in general to transfer over a Remoting channel. + * (For example, an implementation from {@link #forFilePath} could be sent on the same channel, + * but an implementation from {@link #forFile} will not.) + * Thus callers should assume that methods such as {@link #open} will work + * only on the node on which the object was created. + * + *

Since some implementations may in fact use external file storage, + * callers may request optional APIs to access those services more efficiently. + * Otherwise, for example, a plugin copying a file + * previously saved by {@link ArtifactManager} to an external storage service + * which tunneled a stream from {@link #open} using {@link RemoteInputStream} + * would wind up transferring the file from the service to the Jenkins master and then on to an agent. + * Similarly, if {@link DirectoryBrowserSupport} rendered a link to an in-Jenkins URL, + * a large file could be transferred from the service to the Jenkins master and then on to the browser. + * To avoid this overhead, callers may check whether an implementation supports {@link #toExternalURL}. + * * @see DirectoryBrowserSupport * @see FilePath * @since 1.532 @@ -80,9 +116,11 @@ public abstract class VirtualFile implements Comparable, Serializab /** * Gets a URI. * Should at least uniquely identify this virtual file within its root, but not necessarily globally. + *

When {@link #toExternalURL} is implemented, that same value could be used here, + * unless some sort of authentication is also embedded. * @return a URI (need not be absolute) */ - public abstract URI toURI(); + public abstract @Nonnull URI toURI(); /** * Gets the parent file. @@ -105,8 +143,22 @@ public abstract class VirtualFile implements Comparable, Serializab */ public abstract boolean isFile() throws IOException; + /** + * If this file is a symlink, returns the link target. + *

The default implementation always returns null. + * Some implementations may not support symlinks under any conditions. + * @return a target (typically a relative path in some format), or null if this is not a link + * @throws IOException if reading the link, or even determining whether this file is a link, failed + * @since 2.118 + */ + @Restricted(Beta.class) + public @CheckForNull String readLink() throws IOException { + return null; + } + /** * Checks whether this file exists. + * The behavior is undefined for symlinks; if in doubt, check {@link #readLink} first. * @return true if it is a plain file or directory, false if nonexistent * @throws IOException in case checking status failed */ @@ -119,13 +171,75 @@ public abstract class VirtualFile implements Comparable, Serializab */ public abstract @Nonnull VirtualFile[] list() throws IOException; + /** + * @deprecated use {@link #list(String, String, boolean)} instead + */ + @Deprecated + public @Nonnull String[] list(String glob) throws IOException { + return list(glob.replace('\\', '/'), null, true).toArray(MemoryReductionUtil.EMPTY_STRING_ARRAY); + } + /** * Lists recursive files of this directory with pattern matching. - * @param glob an Ant-style glob - * @return a list of relative names of children (files directly inside or in subdirectories) + *

The default implementation calls {@link #list()} recursively inside {@link #run} and applies filtering to the result. + * Implementations may wish to override this more efficiently. + * @param includes comma-separated Ant-style globs as per {@link Util#createFileSet(File, String, String)} using {@code /} as a path separator; + * the empty string means no matches (use {@link SelectorUtils#DEEP_TREE_MATCH} if you want to match everything except some excludes) + * @param excludes optional excludes in similar format to {@code includes} + * @param useDefaultExcludes as per {@link AbstractFileSet#setDefaultexcludes} + * @return a list of {@code /}-separated relative names of children (files directly inside or in subdirectories) * @throws IOException if this is not a directory, or listing was not possible for some other reason + * @since 2.118 */ - public abstract @Nonnull String[] list(String glob) throws IOException; + @Restricted(Beta.class) + public @Nonnull Collection list(@Nonnull String includes, @CheckForNull String excludes, boolean useDefaultExcludes) throws IOException { + Collection r = run(new CollectFiles(this)); + List includePatterns = patterns(includes); + List excludePatterns = patterns(excludes); + if (useDefaultExcludes) { + for (String patt : DirectoryScanner.getDefaultExcludes()) { + excludePatterns.add(new TokenizedPattern(patt.replace('/', File.separatorChar))); + } + } + return r.stream().filter(p -> { + TokenizedPath path = new TokenizedPath(p.replace('/', File.separatorChar)); + return includePatterns.stream().anyMatch(patt -> patt.matchPath(path, true)) && !excludePatterns.stream().anyMatch(patt -> patt.matchPath(path, true)); + }).collect(Collectors.toSet()); + } + private static final class CollectFiles extends MasterToSlaveCallable, IOException> { + private static final long serialVersionUID = 1; + private final VirtualFile root; + CollectFiles(VirtualFile root) { + this.root = root; + } + @Override + public Collection call() throws IOException { + List r = new ArrayList<>(); + collectFiles(root, r, ""); + return r; + } + private static void collectFiles(VirtualFile d, Collection names, String prefix) throws IOException { + for (VirtualFile child : d.list()) { + if (child.isFile()) { + names.add(prefix + child.getName()); + } else if (child.isDirectory()) { + collectFiles(child, names, prefix + child.getName() + "/"); + } + } + } + } + private List patterns(String patts) { + List r = new ArrayList<>(); + if (patts != null) { + for (String patt : patts.split(",")) { + if (patt.endsWith("/")) { + patt += SelectorUtils.DEEP_TREE_MATCH; + } + r.add(new TokenizedPattern(patt.replace('/', File.separatorChar))); + } + } + return r; + } /** * Obtains a child file. @@ -148,6 +262,18 @@ public abstract class VirtualFile implements Comparable, Serializab */ public abstract long lastModified() throws IOException; + /** + * Gets the file’s Unix mode, if meaningful. + * If the file is symlink (see {@link #readLink}), the mode is that of the link target, not the link itself. + * @return for example, 0644 ~ {@code rw-r--r--}; -1 by default, meaning unknown or inapplicable + * @throws IOException if checking the mode failed + * @since 2.118 + */ + @Restricted(Beta.class) + public int mode() throws IOException { + return -1; + } + /** * Checks whether this file can be read. * @return true normally @@ -208,6 +334,28 @@ public abstract class VirtualFile implements Comparable, Serializab return callable.call(); } + /** + * Optionally obtains a URL which may be used to retrieve file contents from any process on any node. + * For example, given cloud storage this might produce a permalink to the file. + *

Only {@code http} and {@code https} protocols are permitted. + * It is recommended to use {@code RobustHTTPClient.downloadFile} to work with these URLs. + *

This is only meaningful for {@link #isFile}: + * no ZIP etc. archiving protocol is defined to allow bulk access to directory trees. + *

Any necessary authentication must be encoded somehow into the URL itself; + * do not include any tokens or other authentication which might allow access to unrelated files + * (for example {@link ArtifactManager} builds from a different job). + * Authentication should be limited to download, not upload or any other modifications. + *

The URL might be valid for only a limited amount of time or even only a single use; + * this method should be called anew every time an external URL is required. + * @return an externally usable URL like {@code https://gist.githubusercontent.com/ACCT/GISTID/raw/COMMITHASH/FILE}, or null if there is no such support + * @since 2.118 + * @see #toURI + */ + @Restricted(Beta.class) + public @CheckForNull URL toExternalURL() throws IOException { + return null; + } + /** * Creates a virtual file wrapper for a local file. * @param f a disk file (need not exist) @@ -250,6 +398,12 @@ public abstract class VirtualFile implements Comparable, Serializab } return f.exists(); } + @Override public String readLink() throws IOException { + if (isIllegalSymlink()) { + return null; // best to just ignore link -> ../whatever + } + return Util.resolveSymlink(f); + } @Override public VirtualFile[] list() throws IOException { if (isIllegalSymlink()) { return new VirtualFile[0]; @@ -264,11 +418,12 @@ public abstract class VirtualFile implements Comparable, Serializab } return vfs; } - @Override public String[] list(String glob) throws IOException { + @Override + public Collection list(String includes, String excludes, boolean useDefaultExcludes) throws IOException { if (isIllegalSymlink()) { - return new String[0]; + return Collections.emptySet(); } - return new Scanner(glob).invoke(f, null); + return new Scanner(includes, excludes, useDefaultExcludes).invoke(f, null); } @Override public VirtualFile child(String name) { return new FileVF(new File(f, name), root); @@ -279,6 +434,12 @@ public abstract class VirtualFile implements Comparable, Serializab } return f.length(); } + @Override public int mode() throws IOException { + if (isIllegalSymlink()) { + return -1; + } + return IOUtils.mode(f); + } @Override public long lastModified() throws IOException { if (isIllegalSymlink()) { return 0; @@ -348,7 +509,7 @@ public abstract class VirtualFile implements Comparable, Serializab try { return f.isDirectory(); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } @Override public boolean isFile() throws IOException { @@ -359,7 +520,14 @@ public abstract class VirtualFile implements Comparable, Serializab try { return f.exists(); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); + } + } + @Override public String readLink() throws IOException { + try { + return f.readLink(); + } catch (InterruptedException x) { + throw new IOException(x); } } @Override public VirtualFile[] list() throws IOException { @@ -371,14 +539,14 @@ public abstract class VirtualFile implements Comparable, Serializab } return vfs; } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } - @Override public String[] list(String glob) throws IOException { + @Override public Collection list(String includes, String excludes, boolean useDefaultExcludes) throws IOException { try { - return f.act(new Scanner(glob)); + return f.act(new Scanner(includes, excludes, useDefaultExcludes)); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } @Override public VirtualFile child(String name) { @@ -388,52 +556,65 @@ public abstract class VirtualFile implements Comparable, Serializab try { return f.length(); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); + } + } + @Override public int mode() throws IOException { + try { + return f.mode(); + } catch (InterruptedException | PosixException x) { + throw new IOException(x); } } @Override public long lastModified() throws IOException { try { return f.lastModified(); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } @Override public boolean canRead() throws IOException { try { return f.act(new Readable()); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } @Override public InputStream open() throws IOException { try { return f.read(); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } @Override public V run(Callable callable) throws IOException { try { return f.act(callable); } catch (InterruptedException x) { - throw (IOException) new IOException(x.toString()).initCause(x); + throw new IOException(x); } } } - private static final class Scanner extends MasterToSlaveFileCallable { - private final String glob; - Scanner(String glob) { - this.glob = glob; + private static final class Scanner extends MasterToSlaveFileCallable> { + private final String includes, excludes; + private final boolean useDefaultExcludes; + Scanner(String includes, String excludes, boolean useDefaultExcludes) { + this.includes = includes; + this.excludes = excludes; + this.useDefaultExcludes = useDefaultExcludes; } - @Override public String[] invoke(File f, VirtualChannel channel) throws IOException { + @Override public List invoke(File f, VirtualChannel channel) throws IOException { + if (includes.isEmpty()) { // see Glob class Javadoc, and list(String, String, boolean) note + return Collections.emptyList(); + } final List paths = new ArrayList(); - new DirScanner.Glob(glob, null).scan(f, new FileVisitor() { + new DirScanner.Glob(includes, excludes, useDefaultExcludes).scan(f, new FileVisitor() { @Override public void visit(File f, String relativePath) throws IOException { - paths.add(relativePath); + paths.add(relativePath.replace('\\', '/')); } }); - return paths.toArray(new String[paths.size()]); + return paths; } } diff --git a/core/src/main/java/jenkins/util/groovy/AbstractGroovyViewModule.java b/core/src/main/java/jenkins/util/groovy/AbstractGroovyViewModule.java index ccf47202931d7febe20dcbd0ea6694675dadd5ff..9bc12c6313d07753ab13789a356630ed255d05c3 100644 --- a/core/src/main/java/jenkins/util/groovy/AbstractGroovyViewModule.java +++ b/core/src/main/java/jenkins/util/groovy/AbstractGroovyViewModule.java @@ -12,9 +12,9 @@ import lib.JenkinsTagLib; *

* Usage from script of a subclass, say ViewHelper: *

- * new ViewHelper(delegate).method(); + * {@code new ViewHelper(delegate).method();} *

- * see ModularizeViewScript in ui-samples for an example how to use + * see {@code ModularizeViewScript} in ui-samples for an example how to use * this class. */ public abstract class AbstractGroovyViewModule extends GroovyObjectSupport { diff --git a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java index 3c01ebf827da233145b29d1e80e2a03c706cd573..4f15ed49658e1d3549109dc61c4df529d5a842c0 100644 --- a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java +++ b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java @@ -31,8 +31,8 @@ import jenkins.model.Jenkins; * * *

- * Scripts inside /WEB-INF is meant for OEM distributions of Jenkins. Files inside - * $JENKINS_HOME are for installation local settings. Use of HOOK.groovy.d + * Scripts inside {@code /WEB-INF} is meant for OEM distributions of Jenkins. Files inside + * {@code $JENKINS_HOME} are for installation local settings. Use of {@code HOOK.groovy.d} * allows configuration management tools to control scripts easily. * * @author Kohsuke Kawaguchi diff --git a/core/src/main/java/jenkins/util/io/LinesStream.java b/core/src/main/java/jenkins/util/io/LinesStream.java new file mode 100644 index 0000000000000000000000000000000000000000..92931c4ccd1e025df1e08d4c8b591d82453f2205 --- /dev/null +++ b/core/src/main/java/jenkins/util/io/LinesStream.java @@ -0,0 +1,114 @@ +/* + * The MIT License + * + * Copyright 2018 Daniel Trebbien. + * + * 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.util.io; + +import com.google.common.collect.AbstractIterator; + +import edu.umd.cs.findbugs.annotations.CleanupObligation; +import edu.umd.cs.findbugs.annotations.DischargesObligation; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents a stream over the lines of a text file. + *

+ * Although LinesStream implements {@link java.lang.Iterable}, it + * is intended to be first used to initialize a resource in a try-with-resources + * statement and then iterated, as in: + *

+ *  try (LinesStream stream = new LinesStream(...)) {
+ *      for (String line : stream) {
+ *          ...
+ *      }
+ *  }
+ * 
+ * This pattern ensures that the underlying file handle is closed properly. + *

+ * Like {@link java.nio.file.DirectoryStream}, LinesStream supports + * creating at most one Iterator. Invoking {@link #iterator()} to + * obtain a second or subsequent Iterator throws + * IllegalStateException. + * + * @since 2.111 + */ +@CleanupObligation +public class LinesStream implements Closeable, Iterable { + + private final @Nonnull BufferedReader in; + private transient @Nullable Iterator iterator; + + /** + * Opens the text file at path for reading using charset + * {@link java.nio.charset.StandardCharsets#UTF_8}. + * @param path Path to the file to open for reading. + * @throws IOException if the file at path cannot be opened for + * reading. + */ + public LinesStream(@Nonnull Path path) throws IOException { + in = Files.newBufferedReader(path); // uses UTF-8 by default + } + + @DischargesObligation + @Override + public void close() throws IOException { + in.close(); + } + + @Override + public Iterator iterator() { + if (iterator!=null) + throw new IllegalStateException("Only one Iterator can be created."); + + iterator = new AbstractIterator() { + @Override + protected String computeNext() { + try { + String r = in.readLine(); + if (r==null) { + // Calling close() here helps ensure that the file + // handle is closed even when LinesStream is being used + // incorrectly, where it is iterated over without being + // used to initialize a resource of a try-with-resources + // statement. + in.close(); + return endOfData(); + } + return r; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + return iterator; + } +} diff --git a/core/src/main/java/jenkins/util/xstream/SafeURLConverter.java b/core/src/main/java/jenkins/util/xstream/SafeURLConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..0de0a10074a26f0e9cd62daf9b8243cca44e6d48 --- /dev/null +++ b/core/src/main/java/jenkins/util/xstream/SafeURLConverter.java @@ -0,0 +1,55 @@ +/* + * The MIT License + * + * Copyright (c) 2018 CloudBees, Inc. + * + * 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.util.xstream; + +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.basic.URLConverter; +import hudson.remoting.URLDeserializationHelper; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.IOException; +import java.net.URL; +import java.net.URLStreamHandler; + +/** + * Wrap the URL handler during deserialization into a specific one that does not generate DNS query on the hostname + * for {@link URLStreamHandler#equals(URL, URL)} or {@link URLStreamHandler#hashCode(URL)}. + * Required to protect against SECURITY-637 + * + * @since TODO + */ +@Restricted(NoExternalUse.class) +public class SafeURLConverter extends URLConverter { + + @Override + public Object fromString(String str) { + URL url = (URL) super.fromString(str); + try { + return URLDeserializationHelper.wrapIfRequired(url); + } catch (IOException e) { + throw new ConversionException(e); + } + } +} diff --git a/core/src/main/java/jenkins/util/xstream/XStreamDOM.java b/core/src/main/java/jenkins/util/xstream/XStreamDOM.java index d98f34654006178149dcdc90bb92068ce96116b2..2b2ed1621bd19511039532afd825b8327383e7ba 100644 --- a/core/src/main/java/jenkins/util/xstream/XStreamDOM.java +++ b/core/src/main/java/jenkins/util/xstream/XStreamDOM.java @@ -35,10 +35,10 @@ import com.thoughtworks.xstream.io.xml.AbstractXmlReader; import com.thoughtworks.xstream.io.xml.AbstractXmlWriter; import com.thoughtworks.xstream.io.xml.DocumentReader; import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer; -import com.thoughtworks.xstream.io.xml.Xpp3Driver; import hudson.Util; import hudson.util.VariableResolver; +import hudson.util.XStream2; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -241,11 +241,11 @@ public class XStreamDOM { * Writes this {@link XStreamDOM} into {@link OutputStream}. */ public void writeTo(OutputStream os) { - writeTo(new Xpp3Driver().createWriter(os)); + writeTo(XStream2.getDefaultDriver().createWriter(os)); } public void writeTo(Writer w) { - writeTo(new Xpp3Driver().createWriter(w)); + writeTo(XStream2.getDefaultDriver().createWriter(w)); } public void writeTo(HierarchicalStreamWriter w) { @@ -262,11 +262,11 @@ public class XStreamDOM { } public static XStreamDOM from(InputStream in) { - return from(new Xpp3Driver().createReader(in)); + return from(XStream2.getDefaultDriver().createReader(in)); } public static XStreamDOM from(Reader in) { - return from(new Xpp3Driver().createReader(in)); + return from(XStream2.getDefaultDriver().createReader(in)); } public static XStreamDOM from(HierarchicalStreamReader in) { diff --git a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java index 269f5e44fc45ba5c3c718a1d9825ba9054adc19e..521ace8b791ecb09387fda38a6909cb4331c02c5 100644 --- a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java +++ b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java @@ -24,7 +24,6 @@ package jenkins.widgets; import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; import hudson.model.AbstractBuild; import hudson.model.Job; import hudson.model.ParameterValue; @@ -32,6 +31,7 @@ import hudson.model.ParametersAction; import hudson.model.Queue; import hudson.model.Run; import hudson.search.UserSearchProperty; +import hudson.util.Iterators; import hudson.widgets.HistoryWidget; import javax.annotation.Nonnull; diff --git a/core/src/main/resources/hudson/AboutJenkins/index.jelly b/core/src/main/resources/hudson/AboutJenkins/index.jelly index e2b539719367aba0f005a3cbb8b5ccf1367d2740..51d7b090038a3adf34dd08de3b861b79e585095e 100644 --- a/core/src/main/resources/hudson/AboutJenkins/index.jelly +++ b/core/src/main/resources/hudson/AboutJenkins/index.jelly @@ -26,7 +26,7 @@ THE SOFTWARE. - +

diff --git a/core/src/main/resources/hudson/AboutJenkins/index_zh_CN.properties b/core/src/main/resources/hudson/AboutJenkins/index_zh_CN.properties deleted file mode 100644 index 2b459d4f22f35a8305685efc4ece8cd48850519a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/AboutJenkins/index_zh_CN.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -about=\u5173\u4E8EJenkins{0} -blurb=Jenkins\u662F\u4E00\u4E2A\u57FA\u4E8E\u793E\u533A\u5F00\u53D1\u7684\u5F00\u6E90\u6301\u7EED\u96C6\u6210\u670D\u52A1\u5668 - -dependencies=Jenkins\u4F9D\u8D56\u4E8E\u4EE5\u4E0B\u7B2C\u4E09\u65B9\u5E93 -maven.dependencies=Maven\u76F8\u5173\u4F9D\u8D56 -plugin.dependencies=\u63D2\u4EF6\u7684\u8BB8\u53EF\u548C\u4F9D\u8D56 -static.dependencies=\u9759\u6001\u8D44\u6E90 diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties index 80ecb8db835f27d6f2e181080c3d696a8e15b1f5..57fa82605f27ca9823b11364ff4df115df70b4db 100644 --- a/core/src/main/resources/hudson/Messages.properties +++ b/core/src/main/resources/hudson/Messages.properties @@ -83,6 +83,10 @@ PluginWrapper.disabled={0} is disabled. To fix, enable it. PluginWrapper.obsolete={0} v{1} is older than required. To fix, install v{2} or later. PluginWrapper.obsoleteCore=You must update Jenkins from v{0} to v{1} or later to run this plugin. PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Plugins Failed To Load - +PluginWrapper.Already.Disabled=The plugin ''{0}'' was already disabled +PluginWrapper.Plugin.Has.Dependant=The plugin ''{0}'' has, at least, one dependant plugin ({1}) and the disable strategy is {2}, so it cannot be disabled +PluginWrapper.Plugin.Disabled=Plugin ''{0}'' disabled +PluginWrapper.NoSuchPlugin=No such plugin found with the name ''{0}'' +PluginWrapper.Error.Disabling=There was an error disabling the ''{0}'' plugin. Error: ''{1}'' TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping protocol diff --git a/core/src/main/resources/hudson/Messages_es.properties b/core/src/main/resources/hudson/Messages_es.properties index 9ad1d7eeb3e00d25f1d4358510d148a6bc985d08..c787fed9c31a89bbf5ba5b472d59e0a678300fa0 100644 --- a/core/src/main/resources/hudson/Messages_es.properties +++ b/core/src/main/resources/hudson/Messages_es.properties @@ -55,3 +55,7 @@ PluginManager.PluginDoesntSupportDynamicLoad.RestartRequired=El plugin {0} no so PluginManager.PortNotANumber=El puerto no es un nmero AboutJenkins.Description=Eche un vistazo a la informacin sobre la versin y la licencia. PluginManager.PortNotInRange=El puerto no est en el rango de {0} hasta {1} +PluginWrapper.Already.Disabled=El plugin {0} ya estaba deshabilitado +PluginWrapper.Plugin.Has.Dependant=El plugin {0} tiene, al menos, un plugin dependiente ({1}) y la estrategia de deshabilitacin es {2}, as que no puede ser deshabilitado +PluginWrapper.Plugin.Disabled=Plugin {0} deshabilitado +PluginWrapper.NoSuchPlugin=No se encuentra un plugin con el nombre {0} diff --git a/core/src/main/resources/hudson/Messages_zh_CN.properties b/core/src/main/resources/hudson/Messages_zh_CN.properties index 949abfd603bfa1acb16c41e3ad7fd1fe9e63aac1..eac233818c9fa6aed913b7fdedc6c484b9c639b4 100644 --- a/core/src/main/resources/hudson/Messages_zh_CN.properties +++ b/core/src/main/resources/hudson/Messages_zh_CN.properties @@ -30,7 +30,7 @@ Util.millisecond={0} \u6BEB\u79D2 Util.second={0} \u79D2 Util.minute={0} \u5206 Util.hour ={0} \u5C0F\u65F6 -Util.day ={0} {0,choice,0#days|1#day|1 -# -# 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. -RequiredPluginUpdates=\u4E0B\u9762\u7684\u63D2\u4EF6\u9700\u8981\u66F4\u65B0\u3002 diff --git a/core/src/main/resources/hudson/PluginManager/advanced.jelly b/core/src/main/resources/hudson/PluginManager/advanced.jelly index 362d28e9ca87abe5ca7e6a8642f23e302231ee18..9c06363f035a2cbb8f2e58e04c86905ae0e32f1a 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced.jelly +++ b/core/src/main/resources/hudson/PluginManager/advanced.jelly @@ -44,7 +44,7 @@ THE SOFTWARE. - + @@ -73,7 +73,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/PluginManager/advanced_bg.properties b/core/src/main/resources/hudson/PluginManager/advanced_bg.properties index 3fc652c4632b8f03cbcde07003a6e6107845e51b..0fb4aa2df67dbb390cd9f3461de653f8163de9a7 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_bg.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_bg.properties @@ -21,23 +21,21 @@ # THE SOFTWARE. File=\ - \u0424\u0430\u0439\u043b + \u0424\u0430\u0439\u043B HTTP\ Proxy\ Configuration=\ - \u0421\u044a\u0440\u0432\u044a\u0440-\u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0437\u0430 HTTP -Submit=\ - \u041f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0430\u0432\u0430\u043d\u0435 + \u0421\u044A\u0440\u0432\u044A\u0440-\u043F\u043E\u0441\u0440\u0435\u0434\u043D\u0438\u043A \u0437\u0430 HTTP Update\ Site=\ - \u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0441\u0430\u0439\u0442\u0430 + \u041E\u0431\u043D\u043E\u0432\u044F\u0432\u0430\u043D\u0435 \u043D\u0430 \u0441\u0430\u0439\u0442\u0430 Upload=\ - \u041a\u0430\u0447\u0432\u0430\u043d\u0435 + \u041A\u0430\u0447\u0432\u0430\u043D\u0435 Upload\ Plugin=\ - \u041a\u0430\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430 + \u041A\u0430\u0447\u0432\u0430\u043D\u0435 \u043D\u0430 \u043F\u0440\u0438\u0441\u0442\u0430\u0432\u043A\u0430 uploadtext=\ - \u041c\u043e\u0436\u0435 \u0434\u0430 \u043a\u0430\u0447\u0438\u0442\u0435 \u0444\u0430\u0439\u043b \u0432\u044a\u0432 \u0444\u043e\u0440\u043c\u0430\u0442 \u201e.hpi\u201c, \u0437\u0430 \u0434\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u0442\u0435 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430 \u0438\u0437\u0432\u044a\u043d\ - \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u043d\u043e\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. + \u041C\u043E\u0436\u0435 \u0434\u0430 \u043A\u0430\u0447\u0438\u0442\u0435 \u0444\u0430\u0439\u043B \u0432\u044A\u0432 \u0444\u043E\u0440\u043C\u0430\u0442 \u201E.hpi\u201C, \u0437\u0430 \u0434\u0430 \u0438\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u0442\u0435 \u043F\u0440\u0438\u0441\u0442\u0430\u0432\u043A\u0430 \u0438\u0437\u0432\u044A\u043D\ + \u043E\u0444\u0438\u0446\u0438\u0430\u043B\u043D\u043E\u0442\u043E \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435. Other\ Sites=\ - \u0414\u0440\u0443\u0433\u0438 \u0441\u0430\u0439\u0442\u043e\u0432\u0435 + \u0414\u0440\u0443\u0433\u0438 \u0441\u0430\u0439\u0442\u043E\u0432\u0435 Update\ Center=\ - \u0421\u0430\u0439\u0442 \u0437\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f + \u0421\u0430\u0439\u0442 \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F URL=\ \u0410\u0434\u0440\u0435\u0441 diff --git a/core/src/main/resources/hudson/PluginManager/advanced_cs.properties b/core/src/main/resources/hudson/PluginManager/advanced_cs.properties index 4551fc765436b07c2e6aa60b370ef2dfd19e9f35..1d0183d7438feb2052b41e448cb506eac6be7297 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_cs.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_cs.properties @@ -2,7 +2,6 @@ File=Soubor HTTP\ Proxy\ Configuration=Nastaven\u00ED HTTP Proxy -Submit=Odeslat Update\ Site=\u00DAlo\u017Ei\u0161t\u011B aktualizac\u00ED Upload=Nahr\u00E1t Upload\ Plugin=Nahr\u00E1t Plugin diff --git a/core/src/main/resources/hudson/PluginManager/advanced_da.properties b/core/src/main/resources/hudson/PluginManager/advanced_da.properties index cea2ce412ded480e5ccc630d9e2a1ff3cee6a603..6d149c87fcf9bc7c6b13d0cb707c7fca3b7d4ce4 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_da.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_da.properties @@ -22,14 +22,13 @@ Update\ Site=Opdateringssite Password=Adgangskode -Upload=L\u00e6g op +Upload=L\u00E6g op User\ name=Brugernavn File=Fil URL=URL lastUpdated=Opdateringsinformation hentet: {0} dage siden -uploadtext=Du kan l\u00e6gge en .hpi fil op for at installere en plugin fra udenfor det centrale plugin lager. +uploadtext=Du kan l\u00E6gge en .hpi fil op for at installere en plugin fra udenfor det centrale plugin lager. Port=Port HTTP\ Proxy\ Configuration=HTTP proxykonfiguration Server=Server -Upload\ Plugin=L\u00e6g plugin op -Submit=Gem +Upload\ Plugin=L\u00E6g plugin op diff --git a/core/src/main/resources/hudson/PluginManager/advanced_de.properties b/core/src/main/resources/hudson/PluginManager/advanced_de.properties index d5b7ed5f20bfb5e6477167d17bb09723c19fe9f7..d38b9767fed6db662fc5f7f31dc9b06bd7498f25 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_de.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_de.properties @@ -21,7 +21,6 @@ # THE SOFTWARE. HTTP\ Proxy\ Configuration=HTTP-Proxy Konfiguration -Submit=bernehmen Upload\ Plugin=Plugin hochladen File=Datei Upload=Hochladen diff --git a/core/src/main/resources/hudson/PluginManager/advanced_es.properties b/core/src/main/resources/hudson/PluginManager/advanced_es.properties index 1a5ebcf151b3e6748deb957f44fd5e3ca60986f5..881f9874cd088fad10f928e7b54d89d3bd28564c 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_es.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_es.properties @@ -20,14 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -lastUpdated=Informacion de actualizacin es de hace {0}. +lastUpdated=Informacion de actualizaci\uFFFDn es de hace {0}. uploadtext=\ Puedes subir un fichero .hpi para instalar un plugin que no este en el repositorio central. -Submit=Enviar File=Archivo Upload\ Plugin=Subir un plugin Upload=Subir -HTTP\ Proxy\ Configuration=Configuracin de proxy -Update\ Site=Direccin para la actualizacin +HTTP\ Proxy\ Configuration=Configuraci\uFFFDn de proxy +Update\ Site=Direcci\uFFFDn para la actualizaci\uFFFDn URL=Url Update\ Center=Centro de actualizaciones diff --git a/core/src/main/resources/hudson/PluginManager/advanced_fi.properties b/core/src/main/resources/hudson/PluginManager/advanced_fi.properties index e406d3c570ddd905b3432e2bc388896c158df316..6604dfe06cdfc92b3c3a83cee6b204d9766a8f0f 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_fi.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_fi.properties @@ -25,7 +25,6 @@ HTTP\ Proxy\ Configuration=HTTP-v\u00E4lipalvelinasetukset Password=Salasana Port=Portti Server=Palvelin -Submit=L\u00E4het\u00E4 URL=URL Update\ Site=P\u00E4ivityssivusto Upload=Lataa diff --git a/core/src/main/resources/hudson/PluginManager/advanced_fr.properties b/core/src/main/resources/hudson/PluginManager/advanced_fr.properties index 85763f7c7b395ce67524abcb104bcb609e775ff9..7e9c382c2872030abafc6abe384a4e37cb12646d 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_fr.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_fr.properties @@ -21,7 +21,6 @@ # THE SOFTWARE. HTTP\ Proxy\ Configuration=Configuration du proxy HTTP -Submit=Soumettre Upload\ Plugin=Soumettre un plugin File=Fichier Update\ Site=Site de mise \u00E0 jour diff --git a/core/src/main/resources/hudson/PluginManager/advanced_hu.properties b/core/src/main/resources/hudson/PluginManager/advanced_hu.properties index 01e7d77e9e2976d4773d64bcd43cc799bd857c2e..23f4e734192dfe55c6c601fee16266b12bb12e13 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_hu.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_hu.properties @@ -2,7 +2,6 @@ File=\u00C1llom\u00E1ny HTTP\ Proxy\ Configuration=HTTP Proxy Be\u00E1ll\u00EDt\u00E1sok -Submit=Elk\u00FCld Update\ Site=Friss\u00EDt\u00E9si Oldal Upload=Felt\u00F6lt Upload\ Plugin=Be\u00E9p\u00FCl\u0151 Felt\u00F6lt\u00E9se diff --git a/core/src/main/resources/hudson/PluginManager/advanced_it.properties b/core/src/main/resources/hudson/PluginManager/advanced_it.properties index 560add8ba32376f147827a40111156b7bb325466..31a9263d9cfe36274401b19cce3bff1deeda87ed 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_it.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_it.properties @@ -21,12 +21,11 @@ # THE SOFTWARE. HTTP\ Proxy\ Configuration=Configurazione proxy HTTP -Submit=Invia URL=URL Update\ Site=Aggiorna sito Upload=Carica Upload\ Plugin=Carica plugin -uploadtext= possibile caricare un file .hpi per installare un plugin da una fonte esterna al repository plugin centrale. +uploadtext=\uFFFD possibile caricare un file .hpi per installare un plugin da una fonte esterna al repository plugin centrale. Update\ Center=Centro aggiornamenti File=File Other\ Sites=Altri siti diff --git a/core/src/main/resources/hudson/PluginManager/advanced_ja.properties b/core/src/main/resources/hudson/PluginManager/advanced_ja.properties index 149795c96008d2076a5051e1ff3d4d8d1238e2d1..2f2ea0932c7feecad1317cb2f6176aa0e0d7cbe4 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_ja.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_ja.properties @@ -20,14 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -HTTP\ Proxy\ Configuration=HTTP Proxy\u306e\u8a2d\u5b9a -Submit=\u4fdd\u5b58 -Upload\ Plugin=\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9 -Upload=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9 -File=\u30d5\u30a1\u30a4\u30eb +HTTP\ Proxy\ Configuration=HTTP Proxy\u306E\u8A2D\u5B9A +Upload\ Plugin=\u30D7\u30E9\u30B0\u30A4\u30F3\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9 +Upload=\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9 +File=\u30D5\u30A1\u30A4\u30EB uploadtext=\ - .hpi\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3057\u3066\u3001\u30d7\u30e9\u30b0\u30a4\u30f3\u30ea\u30dd\u30b8\u30c8\u30ea\u4ee5\u5916\u304b\u3089\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u304d\u307e\u3059\u3002 -Update\ Site=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30b5\u30a4\u30c8 + .hpi\u30D5\u30A1\u30A4\u30EB\u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u3066\u3001\u30D7\u30E9\u30B0\u30A4\u30F3\u30EA\u30DD\u30B8\u30C8\u30EA\u4EE5\u5916\u304B\u3089\u30D7\u30E9\u30B0\u30A4\u30F3\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3067\u304D\u307E\u3059\u3002 +Update\ Site=\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u30B5\u30A4\u30C8 URL=URL -Update\ Center=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30bb\u30f3\u30bf\u30fc -Other\ Sites=\u4ed6\u30b5\u30a4\u30c8 +Update\ Center=\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u30BB\u30F3\u30BF\u30FC +Other\ Sites=\u4ED6\u30B5\u30A4\u30C8 diff --git a/core/src/main/resources/hudson/PluginManager/advanced_ko.properties b/core/src/main/resources/hudson/PluginManager/advanced_ko.properties index 18b829d7bdeae58b2272d17ae0a6dc42a3b154df..d1146c49661a03fda97b74f76c71f7945770b074 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_ko.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_ko.properties @@ -25,7 +25,6 @@ HTTP\ Proxy\ Configuration=HTTP \uD504\uB85D\uC2DC \uC124\uC815 Password=\uC554\uD638 Port=\uD3EC\uD2B8 Server=\uC11C\uBC84 -Submit=\uC800\uC7A5 URL=\uC0AC\uC774\uD2B8\uACBD\uB85C Update\ Site=\uC5C5\uB370\uC774\uD2B8 \uC0AC\uC774\uD2B8 Upload=\uC62C\uB9AC\uAE30 diff --git a/core/src/main/resources/hudson/PluginManager/advanced_lv.properties b/core/src/main/resources/hudson/PluginManager/advanced_lv.properties index 3e103e2a01c141ee0988a7633386abfd8b774e87..c5e1bac0032fe9a14dbd03957a75eb7ce9727155 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_lv.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_lv.properties @@ -2,7 +2,6 @@ File=Fails HTTP\ Proxy\ Configuration=HTTP Starpniekservera konfigur\u0101cija -Submit=Nos\u016Bt\u012Bt Update\ Site=Aug\u0161upl\u0101des vietne Upload=Aug\u0161upl\u0101d\u0113t Upload\ Plugin=Aug\u0161upl\u0101d\u0113t spraudni diff --git a/core/src/main/resources/hudson/PluginManager/advanced_nb_NO.properties b/core/src/main/resources/hudson/PluginManager/advanced_nb_NO.properties index 5cec6ec57d6638dd012f69973dc516c5caf58695..2ac5d17c50d1124e88fa03252beb233fe3220440 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_nb_NO.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_nb_NO.properties @@ -25,7 +25,6 @@ HTTP\ Proxy\ Configuration=HTTP mellomtjener konfigurasjon Password=Passord Port=Port Server=Server -Submit=Lagre Upload=Last opp Upload\ Plugin=Last opp programtillegg User\ name=Brukernavn diff --git a/core/src/main/resources/hudson/PluginManager/advanced_nl.properties b/core/src/main/resources/hudson/PluginManager/advanced_nl.properties index eddf5de9983b729c25567f112c4179e17e5d7409..f082f09c983dc186ac6218fd6b499fd12f717b84 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_nl.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_nl.properties @@ -29,6 +29,5 @@ User\ name=Gebruikersnaam URL=URL Upload=Uploaden HTTP\ Proxy\ Configuration=HTTP-proxyconfiguratie -Submit=Versturen Upload\ Plugin=Plugin uploaden File=Bestand diff --git a/core/src/main/resources/hudson/PluginManager/advanced_pl.properties b/core/src/main/resources/hudson/PluginManager/advanced_pl.properties index d930415d3111339612c82b1e4cc7fd80758c5e70..2bd68cd86105ca5dbc889dfc5b4b87eb109c1e63 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_pl.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_pl.properties @@ -22,7 +22,6 @@ File=Plik HTTP\ Proxy\ Configuration=Konfiguracja HTTP Proxy -Submit=Prze\u015Blij URL=Adres URL Update\ Site=Strona z aktualizacjami Upload=Prze\u015Blij diff --git a/core/src/main/resources/hudson/PluginManager/advanced_pt_BR.properties b/core/src/main/resources/hudson/PluginManager/advanced_pt_BR.properties index 6de0e5b3beaf545c65ebc05d0928b37226063585..27f20b4e17a5101d61d40e84c3dc5a558f89ea95 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_pt_BR.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_pt_BR.properties @@ -20,14 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -lastUpdated=Informa\u00e7\u00e3o de atualiza\u00e7\u00e3o obtida: {0} atr\u00e1s +lastUpdated=Informa\u00E7\u00E3o de atualiza\u00E7\u00E3o obtida: {0} atr\u00E1s uploadtext=Voc\u00EA pode fazer o upload de um arquivo .hpi para instalar um plugin fora do reposit\u00F3rio central. -Update\ Site=Site de atualiza\u00e7\u00e3o +Update\ Site=Site de atualiza\u00E7\u00E3o File=Arquivo Upload\ Plugin=Atualizar plugin -HTTP\ Proxy\ Configuration=Configura\u00e7\u00e3o de Proxy/HTTP +HTTP\ Proxy\ Configuration=Configura\u00E7\u00E3o de Proxy/HTTP URL=URL Upload=Upload -Submit=Enviar -Update\ Center=Central de atualiza\u00e7\u00f5es +Update\ Center=Central de atualiza\u00E7\u00F5es Other\ Sites=Outros sites diff --git a/core/src/main/resources/hudson/PluginManager/advanced_pt_PT.properties b/core/src/main/resources/hudson/PluginManager/advanced_pt_PT.properties index 5f2a622c91561abba8a4ec39542aaed39da1acfd..e3be3112eb1f143c1f02d8d1abbbb9a1e4bc110a 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_pt_PT.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_pt_PT.properties @@ -1,7 +1,6 @@ # This file is under the MIT License by authors File=Ficheiro -Submit=Enviar Update\ Site=Atualizar Site Upload=Enviar Upload\ Plugin=Enviar Plugin diff --git a/core/src/main/resources/hudson/PluginManager/advanced_ru.properties b/core/src/main/resources/hudson/PluginManager/advanced_ru.properties index 98ef9a67d9c0477a39df396e461ec66aa4be75db..c04a6fe862f4287f62d4705669afeebb66ee4502 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_ru.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_ru.properties @@ -25,7 +25,6 @@ HTTP\ Proxy\ Configuration=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u043 Password=\u041F\u0430\u0440\u043E\u043B\u044C Port=\u041F\u043E\u0440\u0442 Server=\u0421\u0435\u0440\u0432\u0435\u0440 -Submit=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C URL=\u0410\u0434\u0440\u0435\u0441 URL Update\ Site=\u0421\u0430\u0439\u0442 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439 Upload=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C diff --git a/core/src/main/resources/hudson/PluginManager/advanced_sk.properties b/core/src/main/resources/hudson/PluginManager/advanced_sk.properties index 2b35c4933a515d7e1a8b049ad9dc59b0dd75f574..5fda294bb9a2fcd39178cb44bc6b69d087d9b1eb 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_sk.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_sk.properties @@ -2,5 +2,4 @@ File=S\u00FAbor HTTP\ Proxy\ Configuration=Konfigur\u00E1cia HTTP proxy -Submit=Po\u0161li lastUpdated=Inform\u00E1cia o aktualiz\u00E1ci\u00E1ch z\u00EDskan\u00E1 pred: {0} diff --git a/core/src/main/resources/hudson/PluginManager/advanced_sr.properties b/core/src/main/resources/hudson/PluginManager/advanced_sr.properties index 99f4d1a1e12f373202f7ccda4f029501ddd896d6..ec453b66ce17bfd52c66544f2a6877dc1b7b43bf 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_sr.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_sr.properties @@ -2,7 +2,6 @@ Update\ Center=\u0426\u0435\u043D\u0442\u0430\u0440 \u0437\u0430 \u0430\u0436\u0443\u0440\u0438\u0440\u0430\u045A\u0435 HTTP\ Proxy\ Configuration=\u041F\u043E\u0441\u0442\u0430\u0432\u0459\u0430\u045A\u0435 HTTP Proxy -Submit=\u041F\u043E\u0442\u0432\u0440\u0434\u0438 Upload\ Plugin=\u041E\u0442\u043F\u0440\u0435\u043C\u0438 \u043C\u043E\u0434\u0443\u043B\u0443 uploadtext=\u041C\u043E\u0436\u0435\u0442\u0435 \u043E\u0442\u043F\u0440\u0435\u043C\u0438\u0442\u0438 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0443 \u0443 \u0444\u043E\u0440\u043C\u0430\u0442\u0443 ".hpi", \u0434\u0430 \u0431\u0438\u0441\u0442\u0435 \u0438\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u043B\u0438 \u043C\u043E\u0434\u0443\u043B\u0443 \u0432\u0430\u043D\ \u0446\u0435\u043D\u0442\u0440\u0430\u043B\u043D\u043E\u0433 \u0438\u0437\u0432\u043E\u0440\u0430 \u0437\u0430 \u043C\u043E\u0434\u0443\u043B\u0435. diff --git a/core/src/main/resources/hudson/PluginManager/advanced_sv_SE.properties b/core/src/main/resources/hudson/PluginManager/advanced_sv_SE.properties index 10c631c4425c4fc8be151e753f48e622baa37b1f..7c6bae6e81a76fe5e1a603d08f365bf8196dbf97 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_sv_SE.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_sv_SE.properties @@ -25,7 +25,6 @@ HTTP\ Proxy\ Configuration=HTTP-proxykonfiguration Password=L\u00F6senord Port=Port Server=Server -Submit=Skicka URL=URL Update\ Site=Updateringssajt Upload=Ladda upp diff --git a/core/src/main/resources/hudson/PluginManager/advanced_tr.properties b/core/src/main/resources/hudson/PluginManager/advanced_tr.properties index daf9bed295c36d36e3065814568f7c2be3606f62..34bd395fc3b11367a80fa1391e2c8d919d7427e6 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_tr.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_tr.properties @@ -20,12 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -HTTP\ Proxy\ Configuration=HTTP Proxy Konfig\u00fcrasyonu -Submit=G\u00f6nder -Upload\ Plugin=Eklenti Y\u00fckle +HTTP\ Proxy\ Configuration=HTTP Proxy Konfig\u00FCrasyonu +Upload\ Plugin=Eklenti Y\u00FCkle uploadtext=\ -Merkezi eklenti repository''si d\u0131\u015f\u0131nda bir eklenti eklemek i\u00e7in .hpi dosyas\u0131n\u0131 y\u00fcklemeniz yeterli olacakt\u0131r. +Merkezi eklenti repository''si d\u0131\u015F\u0131nda bir eklenti eklemek i\u00E7in .hpi dosyas\u0131n\u0131 y\u00FCklemeniz yeterli olacakt\u0131r. File=Dosya Update\ Site=G\u00FCncelleme sitesi -Upload=Y\u00fckle -lastUpdated=Al\u0131nan son g\u00fcncelleme bilgisi : {0} \u00f6nce +Upload=Y\u00FCkle +lastUpdated=Al\u0131nan son g\u00FCncelleme bilgisi : {0} \u00F6nce diff --git a/core/src/main/resources/hudson/PluginManager/advanced_zh_TW.properties b/core/src/main/resources/hudson/PluginManager/advanced_zh_TW.properties index 299d2013bc0a59afa01bc775deaec8a0c73d3ff9..5a6a9c1420a9a3e4c19b72924ab5b4a5acd9e65a 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced_zh_TW.properties +++ b/core/src/main/resources/hudson/PluginManager/advanced_zh_TW.properties @@ -21,18 +21,17 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Update\ Center=\u66f4\u65b0\u4e2d\u5fc3 +Update\ Center=\u66F4\u65B0\u4E2D\u5FC3 HTTP\ Proxy\ Configuration=HTTP Proxy\u8A2D\u5B9A -Submit=\u9001\u51fa -Upload\ Plugin=\u4e0a\u50b3\u5916\u639b\u7a0b\u5f0f -uploadtext=\u60a8\u53ef\u4ee5\u624b\u52d5\u4e0a\u50b3 .hpi \u6a94\u6848\u4f86\u5b89\u88dd\u4e0d\u5728\u4e2d\u592e\u5132\u5b58\u5eab\u4e0a\u7684\u5916\u639b\u7a0b\u5f0f\u3002 -File=\u6a94\u6848 -Upload=\u4e0a\u50b3 +Upload\ Plugin=\u4E0A\u50B3\u5916\u639B\u7A0B\u5F0F +uploadtext=\u60A8\u53EF\u4EE5\u624B\u52D5\u4E0A\u50B3 .hpi \u6A94\u6848\u4F86\u5B89\u88DD\u4E0D\u5728\u4E2D\u592E\u5132\u5B58\u5EAB\u4E0A\u7684\u5916\u639B\u7A0B\u5F0F\u3002 +File=\u6A94\u6848 +Upload=\u4E0A\u50B3 Update\ Site=\u66F4\u65B0\u7DB2\u5740 URL=URL -Other\ Sites=\u5176\u4ed6\u7db2\u7ad9 -lastUpdated=\u66f4\u65b0\u8cc7\u8a0a\u53d6\u5f97\u6642\u9593: {0}\u4ee5\u524d +Other\ Sites=\u5176\u4ED6\u7DB2\u7AD9 +lastUpdated=\u66F4\u65B0\u8CC7\u8A0A\u53D6\u5F97\u6642\u9593: {0}\u4EE5\u524D diff --git a/core/src/main/resources/hudson/PluginManager/available_nl.properties b/core/src/main/resources/hudson/PluginManager/available_nl.properties index d40785c36550313ba4a6d1b8381efb78793965ee..5951113a3c49252579ad53cf757ac1be0c067620 100644 --- a/core/src/main/resources/hudson/PluginManager/available_nl.properties +++ b/core/src/main/resources/hudson/PluginManager/available_nl.properties @@ -20,6 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Updates=Nieuwere versies +Updates=Updates Available=Beschikbaar Installed=Ge\u00EFnstalleerd diff --git a/core/src/main/resources/hudson/PluginManager/installed_nl.properties b/core/src/main/resources/hudson/PluginManager/installed_nl.properties index aa7cc818944895680c9fafd0b01dbf47cf58fb2d..2133f18ddc80460d606648171a860e4ed079bc1d 100644 --- a/core/src/main/resources/hudson/PluginManager/installed_nl.properties +++ b/core/src/main/resources/hudson/PluginManager/installed_nl.properties @@ -21,10 +21,10 @@ # THE SOFTWARE. No\ plugins\ installed.=Er werd nog geen enkele plugin ge\u00EFnstalleerd. -New\ plugins\ will\ take\ effect\ once\ you\ restart\ Jenkins=Neuw geregistreerde plugins worden pas actie na het herstarten van Jenkins. +New\ plugins\ will\ take\ effect\ once\ you\ restart\ Jenkins=Nieuw geregistreerde plugins worden pas actief na het herstarten van Jenkins. Changes\ will\ take\ effect\ when\ you\ restart\ Jenkins=Uw wijzigingen zullen actief worden na het herstarten van Jenkins. Restart\ Once\ No\ Jobs\ Are\ Running=Opnieuw starten -Uncheck\ to\ disable\ the\ plugin=Vink aan om de plugin te de-activeren. +Uncheck\ to\ disable\ the\ plugin=Vink aan om de plugin te deactiveren. Enabled=Actief Name=Naam Version=Versie diff --git a/core/src/main/resources/hudson/PluginManager/installed_zh_CN.properties b/core/src/main/resources/hudson/PluginManager/installed_zh_CN.properties deleted file mode 100644 index 3ef1b371c792341317b2a226f596a927283bd428..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/PluginManager/installed_zh_CN.properties +++ /dev/null @@ -1,36 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Changes\ will\ take\ effect\ when\ you\ restart\ Jenkins=\u6240\u6709\u6539\u53D8\u4F1A\u5728\u91CD\u65B0\u542F\u52A8Jenkins\u4EE5\u540E\u751F\u6548\u3002 -Enabled=\u542F\u7528 -Name=\u540D\u79F0 -Previously\ installed\ version=\u4E0A\u4E00\u4E2A\u5B89\u88C5\u7684\u7248\u672C -Restart\ Once\ No\ Jobs\ Are\ Running=\u5F53\u6CA1\u6709\u4EFB\u52A1\u65F6\u91CD\u542F -Uncheck\ to\ disable\ the\ plugin=\u53D6\u6D88\u9009\u62E9\u4EE5\u7981\u7528\u63D2\u4EF6 -Uninstall=\u5378\u8F7D -Version=\u7248\u672C -downgradeTo=\u964D\u5230 -No\ plugins\ installed.=\u6CA1\u6709\u5B89\u88C5\u4EFB\u52A1\u63D2\u4EF6\u3002 -Update\ Center=\u66F4\u65B0\u4E2D\u5FC3 -Warning=\u8B66\u544A -requires.restart=Jenkins\u9700\u8981\u91CD\u542F\u3002\u8FD9\u65F6\u5019\u4E0D\u5EFA\u8BAE\u4FEE\u6539\u63D2\u4EF6\u7684\u72B6\u6001\u3002\u5728\u64CD\u4F5C\u4E4B\u524D\u5148\u91CD\u542FJenkins\u3002 -Filter=\u8FC7\u6EE4 diff --git a/core/src/main/resources/hudson/PluginManager/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/PluginManager/sidepanel_zh_CN.properties deleted file mode 100644 index 3a25d8fbda4b454f5f1a63ef9a0fa92ad56d0758..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/PluginManager/sidepanel_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2011, Sun Microsystems, Inc. -# -# 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. -Back\ to\ Dashboard=\u8FD4\u56DE\u5230\u5DE5\u4F5C\u53F0 -Manage\ Jenkins=\u7CFB\u7EDF\u7BA1\u7406 -Update\ Center=\u66F4\u65B0\u4E2D\u5FC3 diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index 5e2ab64709d3a4defa8537099582f58d016c166c..0fe9b6933e1d920ed71e8d751d683e0734612ec4 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -72,6 +72,7 @@ THE SOFTWARE. + @@ -105,10 +106,10 @@ THE SOFTWARE.
${%coreWarning(p.requiredCore)}
- +
${%depCompatWarning}
- +
${%depCoreWarning(p.getNeededDependenciesRequiredCore().toString())}
diff --git a/core/src/main/resources/hudson/PluginManager/table_zh_CN.properties b/core/src/main/resources/hudson/PluginManager/table_zh_CN.properties deleted file mode 100644 index ce14c758546698dd7469008656887eae628a8504..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/PluginManager/table_zh_CN.properties +++ /dev/null @@ -1,39 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Check\ to\ install\ the\ plugin=\u8BF7\u52FE\u9009\u8981\u5B89\u88C5\u7684\u63D2\u4EF6 -Click\ this\ heading\ to\ sort\ by\ category=\u70B9\u51FB\u6807\u9898\u6309\u5206\u7C7B\u6392\u5E8F -Download\ now\ and\ install\ after\ restart=\u4E0B\u8F7D\u5F85\u91CD\u542F\u540E\u5B89\u88C5 -Filter=\u8FC7\u6EE4 -Inactive=\u672A\u6FC0\u6D3B -Install=\u5B89\u88C5 -Install\ without\ restart=\u76F4\u63A5\u5B89\u88C5 -Installed=\u5DF2\u5B89\u88C5 -Name=\u540D\u79F0 -No\ updates=\u6CA1\u6709\u66F4\u65B0 -Version=\u7248\u672C -coreWarning=\u8B66\u544A\uFF1A\u8BE5\u63D2\u4EF6\u53EA\u9002\u7528\u4E8EJenkins{0}\u6216\u66F4\u65B0\u7248\u672C\u3002\u5B83\u53EF\u80FD\u4E0D\u80FD\u6B63\u5E38\u8FD0\u884C\u4E8E\u4F60\u7684Jenkins -compatWarning=\ - \u8B66\u544A\uFF1A \u65B0\u7248\u672C\u548C\u5DF2\u7ECF\u5B89\u88C5\u7684\u63D2\u4EF6\u4F7F\u7528\u4E86\u4E0D\u540C\u683C\u5F0F\u7684\u8BBE\u7F6E\u3002 \ - \u4F7F\u7528\u4E86\u8FD9\u4E2A\u63D2\u4EF6\u7684\u4EFB\u52A1\u53EF\u80FD\u9700\u8981\u91CD\u65B0\u8BBE\u7F6E\uFF0C\u800C\u4E14\u53EF\u80FD\u9700\u8981\u624B\u52A8\u8BBE\u7F6E\u624D\u80FD\u56DE\u9000\u5230\u4E0A\u4E2A\u7248\u672C\u7684\u8BBE\u7F6E\u3002 \ - \u60A8\u53EF\u4EE5\u901A\u8FC7\u67E5\u770B\u8BE5\u63D2\u4EF6\u7684\u53D1\u5E03\u4FE1\u606F\u6765\u83B7\u53D6\u8BE6\u7EC6\u4FE1\u606F\u3002 -Update\ Center=\u66F4\u65B0\u4E2D\u5FC3 diff --git a/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.jelly b/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.jelly index a98627489a40343fc73714bc6d2f5bc3658407ee..067f0c1c973bfd0fa9712950c7ddd78113d9334f 100644 --- a/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.jelly +++ b/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.jelly @@ -4,14 +4,31 @@
- ${%Dependency errors} +

${%Dependency errors}:

+

${%blurbOriginal}

-
-
${plugin.longName} v${plugin.version}
- -
${d}
-
-
+ +
+
${plugin.longName} version ${plugin.version}
+ +
${d}
+
+
+
+ +

${%Downstream dependency errors}:

+

${%blurbDerived}

+
+ + +
${plugin.displayName} version ${plugin.version}
+ +
${d}
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.properties b/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.properties index 555e651d66468bbc56d6a70acd76b4bbfa9d51f7..9313dac39c7199e67b9bb587246e18fc0d464cf1 100644 --- a/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.properties +++ b/core/src/main/resources/hudson/PluginWrapper/PluginWrapperAdministrativeMonitor/message.properties @@ -19,4 +19,5 @@ # 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. -Dependency\ errors=There are dependency errors loading some plugins. +blurbOriginal=Some plugins could not be loaded due to unsatisfied dependencies. Fix these issues and restart Jenkins to restore the functionality provided by these plugins. +blurbDerived=These plugins failed to load because of one or more of the errors above. Fix those and these plugins will load again. diff --git a/core/src/main/resources/hudson/PluginWrapper/uninstall_zh_CN.properties b/core/src/main/resources/hudson/PluginWrapper/uninstall_zh_CN.properties deleted file mode 100644 index d9f59658a8b93b94cb4f596235683825f0b6005a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/PluginWrapper/uninstall_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. -title=\u5378\u8F7D {0} \u63D2\u4EF6 -msg=\u4F60\u6B63\u5728\u5378\u8F7D {0} \u63D2\u4EF6\u3002\u8FD9\u5C06\u4F1A\u4ECE$JENKINS_HOME\u4E2D\u5220\u9664\uFF0C\u4F46\u662F\u914D\u7F6E\u6587\u4EF6\u4F1A\u88AB\u4FDD\u7559\u3002 -Yes=\u786E\u5B9A diff --git a/core/src/main/resources/hudson/ProxyConfiguration/config_zh_CN.properties b/core/src/main/resources/hudson/ProxyConfiguration/config_zh_CN.properties deleted file mode 100644 index c4a753039147a4e67082f78bbdd7713bd297a57b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/ProxyConfiguration/config_zh_CN.properties +++ /dev/null @@ -1,39 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Check\ now=\u7ACB\u5373\u83B7\u53D6 -File=\u6587\u4EF6 -HTTP\ Proxy\ Configuration=\u4EE3\u7406\u8BBE\u7F6E -Password=\u5BC6\u7801 -Port=\u7AEF\u53E3 -Server=\u670D\u52A1\u5668 -Submit=\u63D0\u4EA4 -URL=URL -Update\ Site=\u5347\u7EA7\u7AD9\u70B9 -Upload=\u4E0A\u4F20 -Upload\ Plugin=\u4E0A\u4F20\u63D2\u4EF6 -User\ name=\u7528\u6237\u540D -lastUpdated=\u66F4\u65B0\u4FE1\u606F\u83B7\u53D6\u4E8E{0}\u524D -uploadtext=\u60A8\u53EF\u4EE5\u901A\u8FC7\u4E0A\u4F20\u4E00\u4E2A.hpi\u6587\u4EF6\u6765\u5B89\u88C5\u63D2\u4EF6\u3002 -No\ Proxy\ Host=\u4E0D\u901A\u8FC7\u4EE3\u7406\u7684\u4E3B\u673A -Validate\ Proxy=\u9A8C\u8BC1\u4EE3\u7406 -Test\ URL=\u6D4B\u8BD5 URL diff --git a/core/src/main/resources/hudson/cli/CLIAction/index.properties b/core/src/main/resources/hudson/cli/CLIAction/index.properties index e4eebf4895f7df8a40581f8f41ff9fdf39f88903..4808085a7a38638e949881ce3bbb82b8431c003c 100644 --- a/core/src/main/resources/hudson/cli/CLIAction/index.properties +++ b/core/src/main/resources/hudson/cli/CLIAction/index.properties @@ -1,4 +1,4 @@ Jenkins\ CLI=Jenkins CLI blurb=You can access various features in Jenkins through a command-line tool. See \ - the documentation for more details of this feature.\ + the documentation for more details of this feature. \ To get started, download jenkins-cli.jar, and run it as follows: diff --git a/core/src/main/resources/hudson/cli/CLIAction/index_zh_CN.properties b/core/src/main/resources/hudson/cli/CLIAction/index_zh_CN.properties deleted file mode 100644 index bd6ca6d779ec5cf93bfab86cf1639fa22112c033..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/cli/CLIAction/index_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Available\ Commands=\u53EF\u7528\u7684\u547D\u4EE4 -Jenkins\ CLI=Jenkins \u547d\u4ee4\u884c -blurb=\u4F60\u53EF\u4EE5\u901A\u8FC7\u547D\u4EE4\u884C\u5DE5\u5177\u64CD\u4F5CJenkins\u7684\u8BB8\u591A\u7279\u6027\u3002\u4F60\u53EF\u4EE5\u901A\u8FC7 Wiki\u83B7\u5F97\u66F4\u591A\u4FE1\u606F\u3002\u4F5C\u4E3A\u5F00\u59CB\uFF0C\u4F60\u53EF\u4EE5\u4E0B\u8F7Djenkins-cli.jar\uFF0C\u7136\u540E\u8FD0\u884C\u4E0B\u5217\u547D\u4EE4\uFF1A diff --git a/core/src/main/resources/hudson/cli/Messages.properties b/core/src/main/resources/hudson/cli/Messages.properties index 8b9c933e6dc90092f473b1a0cfbf71842e02acba..85bbf56b6dad9e9ffa74e5b2ec88d778170bfb6b 100644 --- a/core/src/main/resources/hudson/cli/Messages.properties +++ b/core/src/main/resources/hudson/cli/Messages.properties @@ -7,6 +7,9 @@ InstallPluginCommand.NoUpdateCenterDefined=Note that no update center is defined InstallPluginCommand.NoUpdateDataRetrieved=No update center data is retrieved yet from: {0} InstallPluginCommand.NotAValidSourceName={0} is neither a valid file, URL, nor a plugin artifact name in the update center +EnablePluginCommand.NoSuchPlugin=No such plugin found with the name {0} +EnablePluginCommand.MissingDependencies=Cannot enable plugin {0} as it is missing the dependency {1} + AddJobToViewCommand.ShortDescription=\ Adds jobs to view. BuildCommand.ShortDescription=\ @@ -25,6 +28,8 @@ DeleteViewCommand.ShortDescription=\ Deletes view(s). DeleteJobCommand.ShortDescription=\ Deletes job(s). +EnablePluginCommand.ShortDescription=\ + Enables one or more installed plugins transitively. GroovyCommand.ShortDescription=\ Executes the specified Groovy script. GroovyshCommand.ShortDescription=\ @@ -108,3 +113,10 @@ WaitNodeOfflineCommand.ShortDescription=Wait for a node to become offline. CliProtocol.displayName=Jenkins CLI Protocol/1 (unencrypted) CliProtocol2.displayName=Jenkins CLI Protocol/2 (transport encryption) + +DisablePluginCommand.ShortDescription=\ +Disable one or more installed plugins. +DisablePluginCommand.NoSuchStrategy=This strategy ({0}) does not exist. Allowed strategies are {1} +DisablePluginCommand.PrintUsageSummary=\ +Disable the plugins with the given short names. You can define how to proceed with the dependant plugins and if a restart after should be done. You can also set the quiet mode to avoid extra info in the console. +DisablePluginCommand.StatusMessage=Disabling ''{0}'': {1} ({2}) diff --git a/core/src/main/resources/hudson/cli/Messages_es.properties b/core/src/main/resources/hudson/cli/Messages_es.properties index 11525579ca6435b6433a4acfac31c4feecf6cd02..d8f7b4d7cc40eab48e10a877762da1843209f8d1 100644 --- a/core/src/main/resources/hudson/cli/Messages_es.properties +++ b/core/src/main/resources/hudson/cli/Messages_es.properties @@ -59,4 +59,9 @@ CancelQuietDownCommand.ShortDescription=Cancelar el efecto del comando "quiet-do OfflineNodeCommand.ShortDescription=Dejar de utilizar un nodo temporalmente hasta que se ejecute el comando "online-node". WaitNodeOnlineCommand.ShortDescription=Esperando hasta que el nodo est activado WaitNodeOfflineCommand.ShortDescription=Esperando a que el nodo est desactivado - +DisablePluginCommand.ShortDescription=\ +Deshabilita uno o ms plugins instalados. +DisablePluginCommand.NoSuchStrategy=Esta estrategia ({0}) no existe. Las estrategias permitidas son {1} +DisablePluginCommand.PrintUsageSummary=\ +Deshabilita los plugins con los nombre cortos dados. Puede definir cmo proceder con los plugins dependientes y si se realiza un reinicio posterior. +DisablePluginCommand.StatusMessage=Deshabilitando {0}: {1} ({2}) diff --git a/core/src/main/resources/hudson/diagnosis/HudsonHomeDiskUsageMonitor/message_zh_CN.properties b/core/src/main/resources/hudson/diagnosis/HudsonHomeDiskUsageMonitor/message_zh_CN.properties deleted file mode 100644 index 81263a6e4ebfac8b2074ef20712279c83313558c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/diagnosis/HudsonHomeDiskUsageMonitor/message_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Dismiss=\u4E0D\u518D\u663E\u793A -Tell\ me\ more=\u66F4\u591A\u4FE1\u606F -blurb=\u4F60\u7684 Jenkins \u6570\u636E\u76EE\u5F55 "{0}" (AKA JENKINS_HOME) \u5C31\u5FEB\u8981\u7A7A\u95F4\u4E0D\u8DB3\u4E86\u3002\u4F60\u5E94\u8BE5\u5728\u5B83\u88AB\u5B8C\u5168\u6491\u6EE1\u4E4B\u524D\u5C31\u6709\u6240\u884C\u52A8\u3002 diff --git a/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/diagnosis/Messages_zh_CN.properties similarity index 64% rename from core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties rename to core/src/main/resources/hudson/diagnosis/Messages_zh_CN.properties index 0861540bb9d90ade7d7878db991e853dcf03ffcb..a35f0e0c6712d88121e94274612bed6fde2a3f60 100644 --- a/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties +++ b/core/src/main/resources/hudson/diagnosis/Messages_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2018, suren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Back\ to\ List=\u8fd4\u56de\u5217\u8868 -Build\ History=\u6784\u5efa\u5386\u53f2 -Configure=\u914D\u7F6E\u4ECE\u8282\u70B9 -Load\ Statistics=\u8d1f\u8f7d\u7edf\u8ba1 -Script\ Console=\u811a\u672c\u547d\u4ee4\u884c -Status=\u72b6\u6001 +MemoryUsageMonitor.USED=\u5DF2\u7528 +MemoryUsageMonitor.TOTAL=\u603B\u5171 +OldDataMonitor.Description=\u4ECE\u65E7\u7684\u3001\u65E9\u671F\u7248\u672C\u7684\u63D2\u4EF6\u4E2D\u6E05\u7406\u914D\u7F6E\u6587\u4EF6\u3002 +OldDataMonitor.DisplayName=\u7BA1\u7406\u65E7\u6570\u636E +HudsonHomeDiskUsageMonitor.DisplayName=\u78C1\u76D8\u4F7F\u7528\u76D1\u63A7 + +NullIdDescriptorMonitor.DisplayName=\u63CF\u8FF0ID\u4E3A\u7A7A +ReverseProxySetupMonitor.DisplayName=\u53CD\u5411\u4EE3\u7406\u8BBE\u7F6E +TooManyJobsButNoView.DisplayName=\u4EFB\u52A1\u8FC7\u591A\u800C\u6CA1\u6709\u7EC4\u7EC7\u5230\u89C6\u56FE\u4E2D diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly index 0f73ec6d0fcba592507f0ae191b646d8e79d6b02..56587c3092ecad6a9c7c4b110869e8db9e19dd02 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly @@ -34,15 +34,22 @@ THE SOFTWARE. ${%Type}${%Name}${%Version} - - - ${range} + ${obj.class.name} ${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name} - ${range} + + + + ${item.value} + + + ${item.value} + + + ${item.value.extra} diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_CN.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_CN.properties deleted file mode 100644 index 1ebe0c9ff05879e9e55892613ddc8fa28f288eaa..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_CN.properties +++ /dev/null @@ -1,7 +0,0 @@ -# This file is under the MIT License by authors - -Manage\ Old\ Data=\u7BA1\u7406\u65E7\u6570\u636E -Name=\u540D\u79F0 -No\ old\ data\ was\ found.=\u672A\u627E\u5230\u65E7\u6570\u636E -Type=\u7C7B\u578B -Version=\u7248\u672C diff --git a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_fr.properties b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_fr.properties index 232c302baf801229313def194b4d41e427c2cfd5..c79851bbb6f12479b801433a0ed8b8f2224c2119 100644 --- a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_fr.properties +++ b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_fr.properties @@ -22,4 +22,4 @@ Dismiss=Annuler More\ Info=Plus d\u2019informations -blurb=La configuration de votre proxy inverse n''est pas bonne +blurb=La configuration de votre proxy inverse est incorrecte. diff --git a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_zh_CN.properties b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_zh_CN.properties deleted file mode 100644 index a0d2ca74c0534723214b89acccd5c1fd7e3dbf14..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Dismiss=\u653E\u5F03 -More\ Info=\u66F4\u591A\u4FE1\u606F -blurb=\u53CD\u5411\u4EE3\u7406\u8BBE\u7F6E\u6709\u8BEF diff --git a/core/src/main/resources/hudson/lifecycle/WindowsInstallerLink/index_zh_CN.properties b/core/src/main/resources/hudson/lifecycle/WindowsInstallerLink/index_zh_CN.properties deleted file mode 100644 index 2b41052d8ffc0dd4a931a780dd301b06b201ffce..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/lifecycle/WindowsInstallerLink/index_zh_CN.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -Install=\u5B89\u88C5 -Install\ as\ Windows\ Service=\u5B89\u88C5\u4E3AWindows\u670D\u52A1 -Installation\ Directory=\u5B89\u88C5\u76EE\u5F55 -installBlurb=\u5C06Jenkins \u5B89\u88C5\u4E3AWindows\u7CFB\u7EDF\u670D\u52A1\u4EE5\u4FBFJenkins\u80FD\u5F00\u673A\u81EA\u52A8\u542F\u52A8\u3002 diff --git a/core/src/main/resources/hudson/logging/LogRecorder/delete_nl.properties b/core/src/main/resources/hudson/logging/LogRecorder/delete_nl.properties index fcb6492f484bb3dd219bd994ac2b6cd91ccf17fa..5063bc68c320aa6b6b664b084781439357a0abfc 100644 --- a/core/src/main/resources/hudson/logging/LogRecorder/delete_nl.properties +++ b/core/src/main/resources/hudson/logging/LogRecorder/delete_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Are\ you\ sure\ about\ deleting\ this\ log\ recorder?=Ben U zeker dat U deze logger wenst te verwijderen? +Are\ you\ sure\ about\ deleting\ this\ log\ recorder?=Weet U zeker dat U deze logger wenst te verwijderen? Yes=Ja diff --git a/core/src/main/resources/hudson/logging/LogRecorder/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/logging/LogRecorder/sidepanel_zh_CN.properties deleted file mode 100644 index 87bed4cdb74536d3c1dc8b6c4ad05997c6771b41..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/logging/LogRecorder/sidepanel_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Loggers=\u8FD4\u56DE -Configure=\u8BBE\u7F6E -Delete=\u5220\u9664 -Log\ records=\u65E5\u5FD7\u8BB0\u5F55 diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/all_zh_CN.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/all_zh_CN.properties deleted file mode 100644 index 938db1442bc715d63293af4a08282dc86107beae..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/all_zh_CN.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Jenkins\ Log=\u7CFB\u7EDF\u65E5\u5FD7 -Level=\u7B49\u7EA7 -Logger\ Configuration=\u65E5\u5FD7\u8BBE\u7F6E -Name=\u540D\u79F0 -Submit=\u63D0\u4EA4 diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/index_zh_CN.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/index_zh_CN.properties deleted file mode 100644 index b9d7e37c3112a1fd38bcf10a8bc397c7922c632b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/index_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Add\ new\ log\ recorder=\u6DFB\u52A0\u65B0\u7684\u65E5\u5FD7\u8BB0\u5F55\u5668 -All\ Jenkins\ Logs=\u6240\u6709\u7CFB\u7EDF\u65E5\u5FD7 -Log\ Recorders=\u65E5\u5FD7\u8BB0\u5F55\u5668 -Name=\u540D\u79F0 diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels.jelly b/core/src/main/resources/hudson/logging/LogRecorderManager/levels.jelly index a115216e81520d32786d4ca40f9248c16f5641a1..4a4de33506f1df9d2fc74fc7e8a1dbc5caca6411 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels.jelly +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels.jelly @@ -68,7 +68,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_bg.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_bg.properties index 1372aaaa67540a7e0e89a4a3ae425503b444cc8e..14bc4ff268489429822fd3ff828dd7cd8ea94165 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_bg.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_bg.properties @@ -23,18 +23,16 @@ # Logger with no name is the default logger. \ # This level will be inherited by all loggers without a configured level. defaultLoggerMsg=\ - \u0416\u0443\u0440\u043d\u0430\u043b\u044a\u0442 \u0431\u0435\u0437 \u0438\u043c\u0435 \u0435 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435. \u041d\u0435\u0433\u043e\u0432\u043e\u0442\u043e \u043d\u0438\u0432\u043e \u0441\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u044f\u0432\u0430 \u043e\u0442 \u0432\u0441\u0438\u0447\u043a\u0438\ - \u0436\u0443\u0440\u043d\u0430\u043b\u0438 \u0431\u0435\u0437 \u0438\u0437\u0440\u0438\u0447\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u043d\u0438\u0432\u043e. + \u0416\u0443\u0440\u043D\u0430\u043B\u044A\u0442 \u0431\u0435\u0437 \u0438\u043C\u0435 \u0435 \u043F\u043E \u043F\u043E\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043D\u0435. \u041D\u0435\u0433\u043E\u0432\u043E\u0442\u043E \u043D\u0438\u0432\u043E \u0441\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u044F\u0432\u0430 \u043E\u0442 \u0432\u0441\u0438\u0447\u043A\u0438\ + \u0436\u0443\u0440\u043D\u0430\u043B\u0438 \u0431\u0435\u0437 \u0438\u0437\u0440\u0438\u0447\u043D\u043E \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D\u043E \u043D\u0438\u0432\u043E. Level=\ - \u041d\u0438\u0432\u043e -Submit=\ - \u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435 + \u041D\u0438\u0432\u043E Logger\ Configuration=\ - \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 + \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043D\u0430 \u0436\u0443\u0440\u043D\u0430\u043B\u0430 Adjust\ Levels=\ - \u041f\u0440\u043e\u043c\u044f\u043d\u0430 \u043d\u0430 \u043d\u0438\u0432\u0430\u0442\u0430 + \u041F\u0440\u043E\u043C\u044F\u043D\u0430 \u043D\u0430 \u043D\u0438\u0432\u0430\u0442\u0430 Name=\ - \u0418\u043c\u0435 + \u0418\u043C\u0435 # https://jenkins.io/redirect/log-levels url=\ https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_da.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_da.properties index 4b029252569aa6365544a8d55e760b80a3fde67d..382913ca6e36b486b8b8758e8c83d8be825cf563 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_da.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_da.properties @@ -26,5 +26,4 @@ defaultLoggerMsg=Unavngiven logger er standardlogger. \ Dette niveau vil nedarve til alle loggere uden et konfigurationsniveau. Name=Navn Adjust\ Levels=Juster niveau -Submit=Gem Logger\ Configuration=Logger konfiguration diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties index 1a8a389aec05c724e118f35f244b223fb491def5..6917971793e3feb8180aa1d762847c4e7a6cd2f0 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties @@ -23,8 +23,7 @@ Logger\ Configuration=Logger-Konfiguration url=https://jenkins.io/redirect/log-levels Name=Name -Level=Prioritt -Submit=bernehmen -Adjust\ Levels=Prioritten (levels) anpassen -defaultLoggerMsg=Der Logger ohne Namen ist der Default-Logger. Seine Prioritt \ - wird von allen Loggern bernommen, fr die keine Prioritt explizit angegeben wurde. \ No newline at end of file +Level=Priorit\u00e4t +Adjust\ Levels=Priorit\u00e4ten (levels) anpassen +defaultLoggerMsg=Der Logger ohne Namen ist der Default-Logger. Seine Priorit\u00e4t \ + wird von allen Loggern \u00fcbernommen, f\u00fcr die keine Priorit\u00e4t explizit angegeben wurde. \ No newline at end of file diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties index aa803dda357c43799c6f11aed81b95de469ce9b4..b0619b2434fbc307643552ab195f188e0ba4eedf 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties @@ -22,9 +22,8 @@ url=https://jenkins.io/redirect/log-levels Level=Nivel de log -Logger\ Configuration=Configuracin del logger -Submit=Enviar +Logger\ Configuration=Configuraci\u00f3n del logger Name=Nombre defaultLoggerMsg=Por defecto se usa un Logger sin mombre. \ - Este nivel ser heredado por todos los loggers que no estn configurados con un nivel de log. + Este nivel ser\u00e0 heredado por todos los loggers que no est\u00e9n configurados con un nivel de log. Adjust\ Levels=Ajustar niveles de log. diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_fr.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_fr.properties index c30bb5735afa49fac158cc1b037de24fd7010ef1..04402c73d163001b7952d5ae5bc51175c76aeff0 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_fr.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_fr.properties @@ -23,5 +23,4 @@ Logger\ Configuration=Configuration du logger Name=Nom Level=Niveau -Submit=Envoyer url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties index 8f15d87798bc45563167432ecaf7c56e85025d5e..e34d443d08f0e58578523e1ce25891d6e42a182f 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties @@ -4,6 +4,5 @@ Adjust\ Levels=Regola livelli Level=Livello Logger\ Configuration=Configurazione registro Name=Nome -Submit=Invia -defaultLoggerMsg=Il registro senza nome quello predefinito. Questo livello sar ereditato da tutti i registri senza un livello configurato. +defaultLoggerMsg=Il registro senza nome \u00e8 quello predefinito. Questo livello sar\u00e0 ereditato da tutti i registri senza un livello configurato. url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ja.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ja.properties index 2f8444273b1bb023e72c369fe5146437133ff500..c2b79891c3147953e768b8460b2d03121578078e 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ja.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ja.properties @@ -23,7 +23,6 @@ Logger\ Configuration=\u30ED\u30AC\u30FC\u306E\u8A2D\u5B9A Name=\u540D\u524D Level=\u30EC\u30D9\u30EB -Submit=\u767B\u9332 url=https://jenkins.io/redirect/log-levels defaultLoggerMsg=\u540D\u524D\u304C\u306A\u3044\u30ED\u30AC\u30FC\u306F\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30ED\u30AC\u30FC\u3067\u3059\u3002\ \u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30ED\u30AC\u30FC\u306E\u30EC\u30D9\u30EB\u306F\u3001\u8A2D\u5B9A\u3057\u306A\u304F\u3066\u3082\u5168\u3066\u306E\u30ED\u30AC\u30FC\u306B\u5F15\u304D\u7D99\u304C\u308C\u307E\u3059\u3002 diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ko.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ko.properties index bc7562a4935bca8af66d185e4513aa028e391988..10c5c84266db5d939ef834d1c47d2a6c27279077 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ko.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ko.properties @@ -4,6 +4,5 @@ Adjust\ Levels=\uB808\uBCA8 \uC870\uC815 Level=\uB808\uBCA8 Logger\ Configuration=\uB85C\uAC70 \uC124\uC815 Name=\uC774\uB984 -Submit=\uC81C\uCD9C defaultLoggerMsg=\uC774\uB984\uC774 \uC5C6\uB294 Logger\uB294 \uAE30\uBCF8 Logger\uC785\uB2C8\uB2E4. \uB808\uBCA8\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uBAA8\uB4E0 Logger\uB4E4\uC740 \uC774 \uB808\uBCA8\uC744 \uC0C1\uC18D\uD569\uB2C8\uB2E4. url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pl.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pl.properties index b8451f3f6fa56b168f4b87d5b75f264e41060292..f8a899292007dbeb6b8d8ffe029a1f4410c19ef1 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pl.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pl.properties @@ -4,5 +4,4 @@ Adjust\ Levels=Dopasuj poziomy Level=Poziom Logger\ Configuration=Konfiguracja rejestratora Name=Nazwa -Submit=Zg\u0142o\u015B defaultLoggerMsg=Rejestrator bez nazwy jest domy\u015Blnym rejestratorem. Ten poziom b\u0119dzie dziedziczony przez wszystkie rejestratory bez skonfigurowanego poziomu. diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt.properties index 9863626db1fc5850003322e43e9ff356ff82a8cd..18bb8983ed0e508722274bc952a43f62bad5dad5 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt.properties @@ -20,11 +20,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Adjust\ Levels=Ajustar n\u00edveis +Adjust\ Levels=Ajustar n\u00EDveis url=https://jenkins.io/redirect/log-levels -Submit=Enviar -Logger\ Configuration=Configura\u00e7\u00e3o de logger -defaultLoggerMsg=Um logger sem nome ser\u00e1 o logger padr\u00e3o. Esse n\u00edvel ser\u00e1 herdado por todos os loggers sem um n\u00edvel configurado. -Level=N\u00edvel +Logger\ Configuration=Configura\u00E7\u00E3o de logger +defaultLoggerMsg=Um logger sem nome ser\u00E1 o logger padr\u00E3o. Esse n\u00EDvel ser\u00E1 herdado por todos os loggers sem um n\u00EDvel configurado. +Level=N\u00EDvel Name=Nome diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt_BR.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt_BR.properties index 6be55205954c3eab7b770730bbd3a4c5e2bcec1b..10df0f6b4c25070a86ec3160884057960396b0d1 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt_BR.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_pt_BR.properties @@ -23,10 +23,9 @@ Level=N\u00EDvel # Logger with no name is the default logger. \ # This level will be inherited by all loggers without a configured level. -defaultLoggerMsg=Logger padr\u00e3o \u00e9 o logger sem nome. +defaultLoggerMsg=Logger padr\u00E3o \u00E9 o logger sem nome. Name=Nome -Adjust\ Levels=Ajustar os n\u00edveis -Submit=Enviar -Logger\ Configuration=Configura\u00e7\u00e3o do Logger +Adjust\ Levels=Ajustar os n\u00EDveis +Logger\ Configuration=Configura\u00E7\u00E3o do Logger # https://jenkins.io/redirect/log-levels url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ru.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ru.properties index b156024d81b9618b66799474c3699d63255d8bbc..057b15c304388fcae591e8eb08af2c42ed2ca660 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ru.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_ru.properties @@ -24,6 +24,5 @@ Adjust\ Levels=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u0443\u04 Level=\u0422\u0438\u043F\u044B \u0441\u043E\u0431\u044B\u0442\u0438\u0439 Logger\ Configuration=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F \u0436\u0443\u0440\u043D\u0430\u043B\u0430 Name=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 -Submit=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C defaultLoggerMsg=\u0416\u0443\u0440\u043D\u0430\u043B \u0431\u0435\u0437 \u0438\u043C\u0435\u043D\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043F\u043E-\u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E. \u0423\u0440\u043E\u0432\u0435\u043D\u044C \u0436\u0443\u0440\u043D\u0430\u043B\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u043D\u043D\u044B\u0439 \u0434\u043B\u044F \u043D\u0435\u0433\u043E \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F \u0432\u0441\u0435\u043C\u0438 \u0436\u0443\u0440\u043D\u0430\u043B\u0430\u043C\u0438, \u0434\u043B\u044F \u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u0436\u0443\u0440\u043D\u0430\u043B\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043D\u0435 \u0437\u0430\u0434\u0430\u043D. url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_sr.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_sr.properties index 059a02ebf24d513b15ff6e8caf07cb9fe2b0ddb2..a775c74c34d90c8288a429859a2134e2d253e71e 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_sr.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_sr.properties @@ -5,5 +5,4 @@ url=https://jenkins.io/redirect/log-levels Name=\u0418\u043C\u0435 Level=\u041D\u0438\u0432\u043E defaultLoggerMsg=\u041F\u0440\u0435\u043F\u0438\u0441\u0438\u0432\u0430\u0447 \u0431\u0435\u0437 \u0438\u043C\u0435\u043D\u0430 \u0458\u0435 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0434\u043D\u0438 \u043F\u0440\u0435\u043F\u0438\u0441\u0438\u0432\u0430\u0447. \u0422\u0430\u0458 \u043D\u0438\u0432\u043E \u045B\u0435 \u0432\u0438\u0442\u0438 \u043D\u0430\u0441\u043B\u0435\u0452\u0435\u043D -Adjust\ Levels=\u041F\u043E\u0434\u0435\u0441\u0438 \u043D\u0438\u0432\u043E\u0435 -Submit=\u041F\u043E\u0434\u043D\u0435\u0441\u0438 \ No newline at end of file +Adjust\ Levels=\u041F\u043E\u0434\u0435\u0441\u0438 \u043D\u0438\u0432\u043E\u0435 \ No newline at end of file diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_CN.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_CN.properties deleted file mode 100644 index a74842b77be172e3cfa3d6d9ce84f4bc56133f1a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Adjust\ Levels=\u914D\u7F6E\u7EA7\u522B -Level=\u7EA7\u522B -Logger\ Configuration=\u65E5\u5FD7\u914D\u7F6E -Name=\u540D\u79F0 -Submit=\u63D0\u4EA4 -defaultLoggerMsg=\u6CA1\u6709\u540D\u79F0\u7684\u662F\u9ED8\u8BA4\u65E5\u5FD7\u3002\u6240\u6709\u6CA1\u914D\u7F6E\u7EA7\u522B\u7684\u65E5\u5FD7\u5C06\u7EE7\u627F\u9ED8\u8BA4\u65E5\u5FD7 diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_TW.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_TW.properties index 1ac8df73eb3018ab6fa5e81c16244609e9989119..2d47cd3533ac4cc1518e4a0d266ab3e467d94370 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_TW.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_zh_TW.properties @@ -20,10 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Adjust\ Levels=\u8abf\u6574\u7b49\u7d1a -Level=\u7b49\u7d1a -Logger\ Configuration=\u8a18\u9304\u5668\u8a2d\u5b9a -Name=\u540d\u7a31 -Submit=\u9001\u51fa -defaultLoggerMsg=\u6c92\u6709\u540d\u7a31\u7684\u8a18\u9304\u5668\u5c31\u662f\u9810\u8a2d\u8a18\u9304\u5668\u3002\u5b83\u7684\u7b49\u7d1a\u6703\u88ab\u6240\u6709\u6c92\u6709\u6307\u5b9a\u7b49\u7d1a\u7684\u8a18\u9304\u5668\u7e7c\u627f\u3002 +Adjust\ Levels=\u8ABF\u6574\u7B49\u7D1A +Level=\u7B49\u7D1A +Logger\ Configuration=\u8A18\u9304\u5668\u8A2D\u5B9A +Name=\u540D\u7A31 +defaultLoggerMsg=\u6C92\u6709\u540D\u7A31\u7684\u8A18\u9304\u5668\u5C31\u662F\u9810\u8A2D\u8A18\u9304\u5668\u3002\u5B83\u7684\u7B49\u7D1A\u6703\u88AB\u6240\u6709\u6C92\u6709\u6307\u5B9A\u7B49\u7D1A\u7684\u8A18\u9304\u5668\u7E7C\u627F\u3002 url=https://jenkins.io/redirect/log-levels diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/sidepanel_zh_CN.properties deleted file mode 100644 index 9585c4d98b9b9865a081a8c3731ec96f0883a8a6..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/sidepanel_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -All\ Logs=\u6240\u6709\u65e5\u5fd7 -Back\ to\ Dashboard=\u8fd4\u56de -Manage\ Jenkins=\u7cfb\u7edf\u7ba1\u7406 -Log\ Levels=\u65E5\u5FD7\u7EA7\u522B -Logger\ List=\u65e5\u5fd7\u5217\u8868 -New\ Log\ Recorder=\u65b0\u5efa\u65e5\u5fd7\u8bb0\u5f55\u5668 diff --git a/core/src/main/resources/hudson/model/AbstractBuild/index_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractBuild/index_zh_CN.properties deleted file mode 100644 index 201e0fbb78c55489c420f576532b0b69f25b996c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractBuild/index_zh_CN.properties +++ /dev/null @@ -1,34 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Build=\u6784\u5efa -Build\ Artifacts=\u6784\u5EFA\u4EA7\u751F\u6587\u4EF6 -Build\ number=\u6784\u5efa\u53f7 -Failed\ to\ determine=\u786E\u8BA4\u5931\u8D25 -Not\ yet\ determined=\u8FD8\u672A\u786E\u5B9A -Permalinks=\u6c38\u4e45\u8fde\u63a5 -Took=\u6784\u5EFA\u7528\u65F6\uFF1A -beingExecuted=\u6784\u5EFA\u5DF2\u7ECF\u8FDB\u884C\u4E86{0} -detail=\u8BE6\u7EC6\u5185\u5BB9 -log=\u65E5\u5FD7 -on=\u8FD0\u884C\u4E8E -startedAgo=\u542F\u52A8\u65F6\u95F4\uFF1A{0} diff --git a/core/src/main/resources/hudson/model/AbstractBuild/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractBuild/sidepanel_zh_CN.properties deleted file mode 100644 index 7d66b4b014e2e775695c8586f39ff3012547ea1f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractBuild/sidepanel_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Next\ Build=\u540E\u4E00\u6B21\u751F\u6210 -Previous\ Build=\u524D\u4E00\u6B21\u6784\u5EFA diff --git a/core/src/main/resources/hudson/model/AbstractBuild/tasks_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractBuild/tasks_zh_CN.properties deleted file mode 100644 index 1747ae85816d94a2b969fd83176ccecee7055238..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractBuild/tasks_zh_CN.properties +++ /dev/null @@ -1,30 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Project=\u8FD4\u56DE\u5230\u5DE5\u7A0B -Changes=\u53D8\u66F4\u8BB0\u5F55 -Console\ Output=\u63A7\u5236\u53F0\u8F93\u51FA -View\ as\ plain\ text=\u4EE5\u7EAF\u6587\u672C\u65B9\u5F0F\u67E5\u770B -Edit\ Build\ Information=\u7F16\u8F91\u7F16\u8BD1\u4FE1\u606F -Status=\u72B6\u6001\u96C6 -View\ Build\ Information=\u67E5\u770B\u751F\u6210\u4FE1\u606F -raw=\u539F\u6587\u4EF6 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.jelly b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename.jelly similarity index 66% rename from core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.jelly rename to core/src/main/resources/hudson/model/AbstractItem/confirm-rename.jelly index 0bd42bc9a2fb53dd9ac579b1f7f291f2b363b8e8..86daf0d31b3434c9710a8648090059e813c273e2 100644 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.jelly +++ b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename.jelly @@ -1,7 +1,7 @@ + - - + + + - -

- - ${%Enter Your Oracle Account} -

-

- ${%description} -

- - - - - +

${%DescribeRename(it.pronoun, it.name)}

+ + + - - - - - - - + + + +
diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_da.properties b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename.properties similarity index 91% rename from core/src/main/resources/hudson/model/AbstractModelObject/editDescription_da.properties rename to core/src/main/resources/hudson/model/AbstractItem/confirm-rename.properties index 3bdeaf1bbf93fee0efd4e63aa48a2d70cd723c99..bcdc4a32d764bb95e8d91d2ffb8b5cb8b44c9d41 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_da.properties +++ b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. +# Copyright (c) 2018 CloudBees, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Submit=Gem +DescribeRename=Rename {0} {1} +NewName=New Name +Rename=Rename diff --git a/core/src/main/resources/hudson/model/AbstractItem/confirm-rename_de.properties b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..8fd89ed115e24ab3d43df64413b1b2786d2e755d --- /dev/null +++ b/core/src/main/resources/hudson/model/AbstractItem/confirm-rename_de.properties @@ -0,0 +1,3 @@ +DescribeRename={0} {1} umbenennen +NewName=Neuer Name +Rename=Umbenennen diff --git a/core/src/main/resources/hudson/model/AbstractItem/delete_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractItem/delete_zh_CN.properties deleted file mode 100644 index adef472d906b39b898da935903a0cab96935d703..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractItem/delete_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Are\ you\ sure\ about\ deleting\ the\ job?=\u786E\u5B9A\u8981\u5220\u9664\u5F53\u524D\u4EFB\u52A1\u5417\uFF1F -Yes=\u662F -blurb=\u786E\u5B9A\u8981\u5220\u9664\u9879\u76EE"{1}"? diff --git a/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_nl.properties b/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_nl.properties index 2e441f20407920f6ca523661a5090080378745a4..0e2a65ed222562a495bf30d18a1f6a5f041f5457 100644 --- a/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_nl.properties +++ b/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_nl.properties @@ -21,7 +21,7 @@ # THE SOFTWARE. Error\:\ no\ workspace=Fout : geen werkplaats beschikbaar -A\ project\ won't\ have\ any\ workspace\ until\ at\ least\ one\ build\ is\ performed.=Een project zal geen werkplaats hebben tot er op zijn mins \u00E9\u00E9n bouwpoging plaatsgevonden heeft. +A\ project\ won't\ have\ any\ workspace\ until\ at\ least\ one\ build\ is\ performed.=Een project zal geen werkplaats hebben tot er op zijn minst \u00E9\u00E9n bouwpoging plaatsgevonden heeft. There's\ no\ workspace\ for\ this\ project.\ Possible\ reasons\ are\:=Er is geen werkplaats beschikbaar voor dit project. Mogelijke redenen zijn : The\ project\ was\ renamed\ recently\ and\ no\ build\ was\ done\ under\ the\ new\ name.=Dit project werd hernoemd en heeft nog bouwpoging plaats gevonden onder de nieuwe naam. li3=Het pad naar de werkplaats ({0}) werd buiten Jenkins verwijderd. diff --git a/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_zh_CN.properties deleted file mode 100644 index cb9247d61736b630d2714fc91dbb75d55098f28f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractItem/noWorkspace_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Error\:\ no\ workspace=\u9519\u8BEF\uFF1A\u6CA1\u6709\u5DE5\u4F5C\u7A7A\u95F4 -There's\ no\ workspace\ for\ this\ project.\ Possible\ reasons\ are\:=\u6B64\u9879\u76EE\u6CA1\u6709\u5DE5\u4F5C\u7A7A\u95F4\u3002\u53EF\u80FD\u7684\u539F\u56E0\u5982\u4E0B\uFF1A -li3=\u6B64\u5DE5\u4F5C\u7A7A\u95F4\u7684\u76EE\u5F55({0})\u5728Jenkins\u4E4B\u5916\u88AB\u79FB\u9664 -text=\u6267\u884C\u6784\u5EFA\u4F7FJenkins\u521B\u5EFA\u5DE5\u4F5C\u533A diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly b/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly index 9891f09a181b02eb1126fa77405528db31069471..8bb30e90ce3427b984ec283e476d83ce5fb74498 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly +++ b/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly @@ -39,7 +39,7 @@ THE SOFTWARE.
- +
diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly index 91940efa8d4091a42d10887dea83310bc360ccbd..54cf6494ad09c33e2c8b2a6dd6b7b734da5cccbc 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly +++ b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly @@ -39,7 +39,7 @@ THE SOFTWARE. codemirror-mode="${app.markupFormatter.codeMirrorMode}" codemirror-config="${app.markupFormatter.codeMirrorConfig}" previewEndpoint="/markupFormatter/previewDescription"/> - +
diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_bg.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_bg.properties deleted file mode 100644 index ce43fb6569eea3c2acd240ef64ac449ff8e66ff5..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_bg.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2015, 2016, Alexander Shopov -# -# 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. - -Submit=\ - \u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435 diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_de.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_de.properties deleted file mode 100644 index abc47f4038cc558cb033acc0dc73e174a6ceaaf7..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_de.properties +++ /dev/null @@ -1 +0,0 @@ -Submit=bernehmen diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_it.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_it.properties deleted file mode 100644 index e86f33e771bf844aaecb1592fccee54fd07cccf3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_it.properties +++ /dev/null @@ -1 +0,0 @@ -Submit=Invia diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pl.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pl.properties deleted file mode 100644 index 18411cab6f566123942f421984b97bc6990d5151..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pl.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Submit=Zapisz diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pt_BR.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pt_BR.properties deleted file mode 100644 index bedff77246aa35456362ab405a43bd890bd07c3e..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_pt_BR.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva -# -# 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. - -Submit=Enviar diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ru.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ru.properties deleted file mode 100644 index e421a4569987e560c6ff73639b440532a268edce..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ru.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Submit=\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_sr.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_sr.properties deleted file mode 100644 index c8e10fc8ec69e893059e2f2cc4ec087c5fc78321..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_sr.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Submit=\u041F\u043E\u0434\u043D\u0435\u0441\u0438 diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_CN.properties deleted file mode 100644 index 4da5732b2a29c516321a966a16480a21a10dc5a7..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Submit=\u63D0\u4EA4 diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_TW.properties b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_TW.properties deleted file mode 100644 index b93603b37ce905f87935a4c06375599efd0b3fc9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_zh_TW.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -Submit=\u9001\u51fa diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/error_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractModelObject/error_zh_CN.properties deleted file mode 100644 index 49aab8bb5515397f1393859c26d1b732cd5bfee2..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractModelObject/error_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Error=\u51FA\u9519\u5566 diff --git a/core/src/main/resources/hudson/model/AbstractProject/changes_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractProject/changes_zh_CN.properties deleted file mode 100644 index 18857cd640d676258951a5ac93b76366744d953a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractProject/changes_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Changes=\u4FEE\u6539\u8BB0\u5F55 diff --git a/core/src/main/resources/hudson/model/AbstractProject/configure-common_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractProject/configure-common_zh_CN.properties deleted file mode 100644 index 6cf6e9d5d48b94444abbe982208fc9d9df315440..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractProject/configure-common_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Advanced\ Project\ Options=\u9ad8\u7ea7\u9879\u76ee\u9009\u9879 -Display\ Name=\u663e\u793a\u540d\u79f0 -JDK\ to\ be\ used\ for\ this\ project=\u9879\u76ee\u4f7f\u7528\u7684JDK -Restrict\ where\ this\ project\ can\ be\ run=\u6307\u5b9a\u4ece\u8282\u70b9\u6807\u7b7e -default.value=(\u9ed8\u8ba4) -Keep\ the\ build\ logs\ of\ dependencies=\u4fdd\u7559\u6784\u5efa\u7684\u4f9d\u8d56\u65e5\u5fd7 diff --git a/core/src/main/resources/hudson/model/AbstractProject/main_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractProject/main_zh_CN.properties deleted file mode 100644 index 3ad95d17a1a5c2b5ceedb5a276790b8642fd1bf9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractProject/main_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Last\ Successful\ Artifacts=\u6700\u540E\u4E00\u6B21\u6210\u529F\u7684\u6784\u5EFA\u7ED3\u679C -Recent\ Changes=\u6700\u65B0\u4FEE\u6539\u8BB0\u5F55 -Workspace=\u5DE5\u4F5C\u533A diff --git a/core/src/main/resources/hudson/model/AbstractProject/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/AbstractProject/sidepanel_zh_CN.properties deleted file mode 100644 index eb009ec9254ef57a19d042be76e206b2ab80e06f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AbstractProject/sidepanel_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Dashboard=\u8fd4\u56de\u9762\u677f -Changes=\u4fee\u6539\u8bb0\u5f55 -Status=\u72b6\u6001 -Wipe\ Out\ Workspace=\u6e05\u7406\u5de5\u4f5c\u7a7a\u95f4 -Workspace=\u5de5\u4f5c\u7a7a\u95f4 -wipe.out.confirm=\u786e\u5b9a\u60f3\u8981\u6e05\u7a7a\u5de5\u4f5c\u7a7a\u95f4\u5417\uff1f diff --git a/core/src/main/resources/hudson/model/AllView/newViewDetail_zh_CN.properties b/core/src/main/resources/hudson/model/AllView/newViewDetail_zh_CN.properties deleted file mode 100644 index 38dc4d8bc8ee4319c6e19c86ee3be7498f3068df..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AllView/newViewDetail_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -blurb=\u8BE5\u89C6\u56FE\u663E\u793AJenkins\u4E2D\u6240\u6709\u7684\u4EFB\u52A1\u3002 diff --git a/core/src/main/resources/hudson/model/AllView/noJob_zh_CN.properties b/core/src/main/resources/hudson/model/AllView/noJob_zh_CN.properties deleted file mode 100644 index be8a0d93bc03578e891f84985545123b78839066..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/AllView/noJob_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Welcome\ to\ Jenkins!=\u6B22\u8FCE\u4F7F\u7528Jenkins! -login=\u767B\u5F55\u4EE5\u521B\u5EFA jobs. -newJob=\u5F00\u59CB\u521B\u5EFA\u4E00\u4E2A\u65B0\u4EFB\u52A1. -signup=\u5982\u679C\u60A8\u8FD8\u6CA1\u6709\u5E10\u6237\uFF0C\u60A8\u73B0\u5728\u53EF\u4EE5\u6CE8\u518C\u3002 diff --git a/core/src/main/resources/hudson/model/Api/index.jelly b/core/src/main/resources/hudson/model/Api/index.jelly index 6985eda72d88226d66064a823726ef25449904e5..082c66400e93b89ed6a816c6ab15c064e8a8e7a1 100644 --- a/core/src/main/resources/hudson/model/Api/index.jelly +++ b/core/src/main/resources/hudson/model/Api/index.jelly @@ -137,7 +137,7 @@ THE SOFTWARE.

- Another way to retrieve more data is to use the depth=N query parameter . + Another way to retrieve more data is to use the depth=N query parameter. This retrieves all the data up to the specified depth. Compare depth=0 and depth=1 and see what the difference is for yourself. Also note that data created by a smaller depth value is always a subset of diff --git a/core/src/main/resources/hudson/model/BooleanParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/BooleanParameterDefinition/config_zh_CN.properties deleted file mode 100644 index ce517528d3070c05e4eb9545f77d2f9181e71858..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/BooleanParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. -Name=\u540D\u79F0 -Default\ Value=\u9ED8\u8BA4\u503C -Description=\u63CF\u8FF0 diff --git a/core/src/main/resources/hudson/model/BuildAuthorizationToken/config_zh_CN.properties b/core/src/main/resources/hudson/model/BuildAuthorizationToken/config_zh_CN.properties deleted file mode 100644 index ae7ebca70efcc67a98464b47257e9369736bab6a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/BuildAuthorizationToken/config_zh_CN.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Authentication\ Token=\u8EAB\u4EFD\u9A8C\u8BC1\u4EE4\u724C -Trigger\ builds\ remotely=\u89E6\u53D1\u8FDC\u7A0B\u6784\u5EFA -Use\ the\ following\ URL\ to\ trigger\ build\ remotely:=\u4F7F\u7528\u4E0B\u5217URL\u6765\u89E6\u53D1\u8FDC\u7A0B\u6784\u5EFA -e.g.,\ from\ scripts=\u4F8B\u5982,\u4F7F\u7528\u811A\u672C -or=\u6216\u8005 diff --git a/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly b/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly index a9efb2525b46622f9c4f34dbf445122b062ac070..71d6cc22f75f8537662af805c70611e12f9d16fb 100644 --- a/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly +++ b/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly @@ -56,7 +56,7 @@ THE SOFTWARE. onSuccess: function(t) { if (t.status != 0) { try { - eventSource1.loadJSON(eval('('+t.responseText+')'),'.'); + eventSource1.loadJSON(JSON.parse(t.responseText),'.'); getData(eventSource1, current-1, min, max); } catch (e) { alert(e); diff --git a/core/src/main/resources/hudson/model/Cause/UpstreamCause/description_zh_CN.properties b/core/src/main/resources/hudson/model/Cause/UpstreamCause/description_zh_CN.properties deleted file mode 100644 index 3c316130de3a4d0b5d2cf0258abcbb4db1f890f1..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Cause/UpstreamCause/description_zh_CN.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -caused_by=\u89E6\u53D1\u6784\u5EFA -started_by_project=\u7531\u4E0A\u6E38\u9879\u76EE {0} \u6784\u5EFA\u53F7 {1} -started_by_project_with_deleted_build=\u7531\u4E0A\u6E38\u9879\u76EE {0} \u6784\u5EFA\u53F7 {1} diff --git a/core/src/main/resources/hudson/model/Cause/UserCause/description_zh_CN.properties b/core/src/main/resources/hudson/model/Cause/UserCause/description_zh_CN.properties deleted file mode 100644 index 8e4afee84e481f5dd16acd42fccf25c965869fb5..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Cause/UserCause/description_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -started_by_user=\u542F\u52A8\u7528\u6237{0} diff --git a/core/src/main/resources/hudson/model/Cause/UserIdCause/description_zh_CN.properties b/core/src/main/resources/hudson/model/Cause/UserIdCause/description_zh_CN.properties deleted file mode 100644 index a053ff7b958e5aa00b2dcdc310bfb1f86f14cd26..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Cause/UserIdCause/description_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2011, Sun Microsystems, Inc. -# -# 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. - -started_by_anonymous=\u7531\u533F\u540D\u7528\u6237\u89E6\u53D1 -started_by_user=\u542f\u52a8\u7528\u6237{1} diff --git a/core/src/main/resources/hudson/model/ChoiceParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/ChoiceParameterDefinition/config_zh_CN.properties deleted file mode 100644 index 628e80119f79152e76c6bb29b3f7f033d6fec648..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ChoiceParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Name=\u540D\u79F0 -Choices=\u9009\u9879 -Description=\u63CF\u8FF0 diff --git a/core/src/main/resources/hudson/model/Computer/builds_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/builds_zh_CN.properties deleted file mode 100644 index fd2c54e0e525fabbe3893a02caf7daaab24a7cf0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Computer/builds_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -title={0}\u4E0A\u7684\u6784\u5EFA\u5386\u53F2 diff --git a/core/src/main/resources/hudson/model/Computer/configure_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/configure_zh_CN.properties deleted file mode 100644 index b76bbc325a2af6bfcb609db4f22a2b641a735cea..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Computer/configure_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -title={0} \u8BBE\u7F6E -Name=\u540D\u79F0 -Save=\u4FDD\u5B58 diff --git a/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties deleted file mode 100644 index ecb166e08c9a7671ec3f0ba25685fa61f69ddc3f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Labels:=\u6807\u7B7E -None=\u65E0 -submit.not.temporarilyOffline=\u4E34\u65F6\u65AD\u5F00\u6B64\u8282\u70B9 -title.projects_tied_on=\u5173\u8054\u5230{0}\u7684\u9879\u76EE diff --git a/core/src/main/resources/hudson/model/Computer/sidepanel_nl.properties b/core/src/main/resources/hudson/model/Computer/sidepanel_nl.properties index 654c2904d9ab56415bda3d602df072bfde242315..85b25eb54eb7b13d79497fb5b223fb65d414ce3d 100644 --- a/core/src/main/resources/hudson/model/Computer/sidepanel_nl.properties +++ b/core/src/main/resources/hudson/model/Computer/sidepanel_nl.properties @@ -23,6 +23,6 @@ Back\ to\ List=Terug naar overzicht Build\ History=Bouwhistorie Configure=Configureren -Load\ Statistics=Belastings statistieken +Load\ Statistics=Belastingsstatistieken Script\ Console=Scriptconsole Status=Status diff --git a/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties deleted file mode 100644 index 79d34d46af750581d3ef27d4754d720bab017fb5..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Name=\u540D\u5B57 -Name\ is\ mandatory=\u540D\u79F0\uFF08\u5FC5\u586B\uFF09 diff --git a/core/src/main/resources/hudson/model/ComputerSet/configure_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/configure_zh_CN.properties deleted file mode 100644 index ba65c4e62446a7cf35680fb9c445034509f1466c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ComputerSet/configure_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -OK=\u786E\u8BA4 -Preventive\ Node\ Monitoring=\u9632\u5FA1\u6027\u8282\u70B9\u68C0\u6D4B diff --git a/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties deleted file mode 100644 index 25d63958ffcefef682328e4e827d272ae5341333..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Configure=\u8BBE\u7F6E -Data\ obtained=\u83B7\u53D6\u5230\u7684\u6570\u636E -Name=\u540D\u79F0 -Refresh\ status=\u5237\u65B0\u72B6\u6001 diff --git a/core/src/main/resources/hudson/model/ComputerSet/new_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/new_zh_CN.properties deleted file mode 100644 index 9db496c9eda70717d537b627c3966cbfd44013a4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ComputerSet/new_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Copy\ Existing\ Node=\u590D\u5236\u73B0\u6709\u8282\u70B9 -Node\ name=\u8282\u70B9\u540D\u79F0 diff --git a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties deleted file mode 100644 index c823d04e2c5f62db6d4aaa3d259b89cb407f8023..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Dashboard=\u8fd4\u56de -Manage\ Jenkins=\u7cfb\u7edf\u7ba1\u7406 -Configure=\u914D\u7F6E -New\ Node=\u65b0\u5efa\u8282\u70b9 diff --git a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir.jelly b/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir.jelly index 16a08537edb363cdfc578dc15e92e959d7611b4d..0b12450d528417ae78094c0788aaec5fead3a4e6 100644 --- a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir.jelly +++ b/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir.jelly @@ -70,6 +70,14 @@ THE SOFTWARE. + + + + + + ${%N/A} + + ${h.humanReadableByteSize(x.getSize())} diff --git a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_nl.properties b/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_nl.properties index 21503956239437d006db1c8cbdb6db443e1261af..a6ce4da665b318a6e047247a167d7ca8c9eb2540 100644 --- a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_nl.properties +++ b/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_nl.properties @@ -20,6 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -No\ files\ in\ directory=Geen bestanden in de directorie +No\ files\ in\ directory=Geen bestanden in de map all\ files\ in\ zip=alle bestanden in een zip-archief view=weergeven diff --git a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_zh_CN.properties b/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_zh_CN.properties deleted file mode 100644 index 3210f74de78b4c4cb366ef412e0893a4cafbf274..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/DirectoryBrowserSupport/dir_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -No\ files\ in\ directory=\u7A7A\u76EE\u5F55 -all\ files\ in\ zip=\u6253\u5305\u4E0B\u8F7D\u5168\u90E8\u6587\u4EF6 -view=\u67E5\u770B diff --git a/core/src/main/resources/hudson/model/FileParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/FileParameterDefinition/config_zh_CN.properties deleted file mode 100644 index 15c668fdb43b99d86f203f4a0c61a65c7f7fecdd..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/FileParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -File\ location=\u6587\u4EF6\u8DEF\u5F84 -Description=\u63CF\u8FF0 diff --git a/core/src/main/resources/hudson/model/Fingerprint/index_nl.properties b/core/src/main/resources/hudson/model/Fingerprint/index_nl.properties index 05475942792366b74a69a2831078d38dc59ac69c..5f55c763e5ff6afd9672233f632a35fbe4b9323c 100644 --- a/core/src/main/resources/hudson/model/Fingerprint/index_nl.properties +++ b/core/src/main/resources/hudson/model/Fingerprint/index_nl.properties @@ -22,5 +22,5 @@ introduced={0} geleden ge\u00EFntroduceerd outside\ Jenkins=buiten Jenkins -This\ file\ has\ not\ been\ used\ anywhere\ else.=Dit bestand werd nog nergens anders gebruikt. -This\ file\ has\ been\ used\ in\ the\ following\ places=Dit bestand werd op de volgende plaatsen gebruikt. +This\ file\ has\ not\ been\ used\ anywhere\ else.=Dit bestand is nergens anders gebruikt. +This\ file\ has\ been\ used\ in\ the\ following\ places=Dit bestand is op de volgende plaatsen gebruikt. diff --git a/core/src/main/resources/hudson/model/JDK/config_zh_CN.properties b/core/src/main/resources/hudson/model/JDK/config_zh_CN.properties deleted file mode 100644 index 3259f3501578f656d94be94154deb240ffe2c189..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/JDK/config_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Name=\u522b\u540d diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly b/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly index ec951a785d62ecf273b76c75e7912c40bc9aae98..d88ebc1094926f1e05a0a27b52e242ab443a364a 100644 --- a/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly +++ b/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly @@ -35,7 +35,7 @@ THE SOFTWARE.

${%Build Time Trend}

- [Build time graph] + [${%Build time graph}]
diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend_nl.properties b/core/src/main/resources/hudson/model/Job/buildTimeTrend_nl.properties index bac54ff829de3fdf9353b921588bc9fbdd82a7a3..32beabb317a71300e40ca261b0c4a5772307e065 100644 --- a/core/src/main/resources/hudson/model/Job/buildTimeTrend_nl.properties +++ b/core/src/main/resources/hudson/model/Job/buildTimeTrend_nl.properties @@ -25,4 +25,4 @@ title={0} Duurtrend van een bouwpoging Build=Bouwpoging Build\ Time\ Trend=Bouw tijd trend Duration=Duur -More\ than\ 1\ builds\ are\ needed\ for\ the\ trend\ report.=Er dienen minsten 2 bouwpogingen plaatsgevonden te hebben, vooralleer een trendrapport kan opgesteld worden. +More\ than\ 1\ builds\ are\ needed\ for\ the\ trend\ report.=Er dienen minstens 2 bouwpogingen plaats te hebben gevonden voordat een trendrapport kan opgesteld worden. diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend_zh_CN.properties b/core/src/main/resources/hudson/model/Job/buildTimeTrend_zh_CN.properties deleted file mode 100644 index 89dd54c9e54afa608571f716c523895b4754ac15..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/buildTimeTrend_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Build=\u6784\u5efa -Build\ Time\ Trend=\u6784\u5EFA\u65F6\u95F4\u8D8B\u52BF -Duration=\u6301\u7eed\u65f6\u95f4 -Timeline=\u65F6\u95F4\u8F74 diff --git a/core/src/main/resources/hudson/model/Job/configure.jelly b/core/src/main/resources/hudson/model/Job/configure.jelly index ddf5467f8d092f54324356f616e56ffca36648ec..d875e4cced8bd32dafc6e10f9ff48d8047eb7340 100644 --- a/core/src/main/resources/hudson/model/Job/configure.jelly +++ b/core/src/main/resources/hudson/model/Job/configure.jelly @@ -44,11 +44,6 @@ THE SOFTWARE. - - - - - diff --git a/core/src/main/resources/hudson/model/Job/configure_zh_CN.properties b/core/src/main/resources/hudson/model/Job/configure_zh_CN.properties deleted file mode 100644 index 18ccf96a6a95c280e8264a5cf401d81378bfb2ae..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/configure_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Description=\u63CF\u8FF0 -LOADING=\u52A0\u8F7D\u4E2D -Apply=\u5E94\u7528 -Save=\u4FDD\u5B58 -Strategy=\u7B56\u7565 -name=\u9879\u76EE\u540D\u79F0 diff --git a/core/src/main/resources/hudson/model/Job/index_zh_CN.properties b/core/src/main/resources/hudson/model/Job/index_zh_CN.properties deleted file mode 100644 index 76e27e45d57c758fbf4c2f21611a381af5df6db4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/index_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Disable\ Project=\u7981\u7528\u9879\u76EE -Project\ name=\u9879\u76EE\u540D\u79F0 diff --git a/core/src/main/resources/hudson/model/Job/permalinks_zh_CN.properties b/core/src/main/resources/hudson/model/Job/permalinks_zh_CN.properties deleted file mode 100644 index f7ce595cdfffef75fdef03ecd66cb77ff58da76c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/permalinks_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Permalinks=\u76F8\u5173\u8FDE\u63A5 -Restrict\ where\ this\ project\ can\ be\ run=\u9650\u5236\u9879\u76EE\u7684\u8FD0\u884C\u8282\u70B9 -Label\ Expression=\u6807\u7B7E\u8868\u8FBE\u5F0F diff --git a/core/src/main/resources/hudson/model/Job/rename.jelly b/core/src/main/resources/hudson/model/Job/rename.jelly deleted file mode 100644 index 39c88032a4fea2004bfddcedbc68009b671d60f5..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename.jelly +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - ${%noRenameWhileBuilding} - -
${%configWasSaved} -
-
- - - - ${%newNameInUse(newName)} - - - ${%newNameNotValid(newName)} - - -
${%configWasSaved} -
-
- - -
- ${%description(it.name, newName)} - - - -
-
-
-
-
diff --git a/core/src/main/resources/hudson/model/Job/rename.properties b/core/src/main/resources/hudson/model/Job/rename.properties deleted file mode 100644 index 6ba2a2187508f6ca3996caefcb50956f76aab882..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -noRenameWhileBuilding=Unable to rename a job while it is building. -newNameInUse=The name {0} is already in use. -newNameNotValid=The name {0} is not valid. -configWasSaved=All other configuration options were saved. -description=Are you sure about renaming {0} to {1}? -Yes=Yes diff --git a/core/src/main/resources/hudson/model/Job/rename_bg.properties b/core/src/main/resources/hudson/model/Job/rename_bg.properties deleted file mode 100644 index cdc08c0d849d4f017d2e42f583fd53156df983ed..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_bg.properties +++ /dev/null @@ -1,37 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2015, 2016, Alexander Shopov -# -# 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. - -# The name {0} is already in use. -newNameInUse=\ - \u0418\u043c\u0435\u0442\u043e \u201e{0}\u201c \u0435 \u0437\u0430\u0435\u0442\u043e. -# Yes -Yes=\ - \u0414\u0430 -# Unable to rename a job while it is building. -noRenameWhileBuilding=\ - \u041d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043f\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u043f\u043e \u0432\u0440\u0435\u043c\u0435 \u043d\u0430 \u043d\u0435\u0439\u043d\u043e\u0442\u043e \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435 . -# All other configuration options were saved. -configWasSaved=\ - \u0412\u0441\u0438\u0447\u043a\u0438 \u0434\u0440\u0443\u0433\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0430 \u0437\u0430\u043f\u0430\u0437\u0435\u043d\u0438. -# Are you sure about renaming {0} to {1}? -description=\ - \u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043f\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u0442\u0435 \u201e{0}\u201c \u043d\u0430 \u201e{1}\u201c? diff --git a/core/src/main/resources/hudson/model/Job/rename_da.properties b/core/src/main/resources/hudson/model/Job/rename_da.properties deleted file mode 100644 index c9e21f910d737b9dbc3e871fb52557bdf18be399..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_da.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. -# -# 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. - -noRenameWhileBuilding=Kan ikke omd\u00f8be et job imens det bygger -Yes=Ja -No=Nej -configWasSaved=Alle andre konfigurationsindstillinger blev gemt. -description=Er du sikker p\u00e5 at du vil omd\u00f8be {0} til {1}? -newNameInUse=Navnet {0} er allerede i brug diff --git a/core/src/main/resources/hudson/model/Job/rename_de.properties b/core/src/main/resources/hudson/model/Job/rename_de.properties deleted file mode 100644 index 424d1ab66c42f129d6e73e9d5796a66deaada8df..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_de.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe, Simon Wiest -# -# 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. - -description=Mchten Sie wirklich {0} in {1} umbenennen? -Yes=Ja -noRenameWhileBuilding=Ein Job kann nicht umbenannt werden, whrend er gebaut wird. -configWasSaved=Alle anderen Konfigurationseinstellungen wurden bernommen. -newNameInUse=Der Name {0} wird bereits verwendet. \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/Job/rename_es.properties b/core/src/main/resources/hudson/model/Job/rename_es.properties deleted file mode 100644 index 58c3c21f77d256e4d50b3a335da2375acdb47eb2..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_es.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -noRenameWhileBuilding=No es posible renombrar un proyecto mientras se est ejecutando. -configWasSaved=Las dems opciones de configuracin s se han guardado. -description=Ests seguro de querer renombrar {0} a {1}? -Yes=S -No=No -newNameInUse=El nombre {0} ya existe. diff --git a/core/src/main/resources/hudson/model/Job/rename_fr.properties b/core/src/main/resources/hudson/model/Job/rename_fr.properties deleted file mode 100644 index 29757ddd9b876aec0761fa26eb06abe1bcd3684c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_fr.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant -# -# 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. - -description=Voulez-vous vraiment renommer {0} en {1}? -Yes=Oui -No=Non diff --git a/core/src/main/resources/hudson/model/Job/rename_it.properties b/core/src/main/resources/hudson/model/Job/rename_it.properties deleted file mode 100644 index 1fddaaf9055f6f877b5cbffd313c4520add031fb..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_it.properties +++ /dev/null @@ -1,6 +0,0 @@ -configWasSaved=Tutte le altre opzioni di configurazione sono state salvate. -newNameNotValid=Il nome {0} non valido. -description=Rinominare {0} in {1}? -noRenameWhileBuilding=Impossibile rinominare un processo finch la sua compilazione in corso. -newNameInUse=Il nome {0} gi utilizzato. -Yes=S diff --git a/core/src/main/resources/hudson/model/Job/rename_ja.properties b/core/src/main/resources/hudson/model/Job/rename_ja.properties deleted file mode 100644 index 968dcd1ee688c5f7040ee5a8883cf3e39c4de776..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_ja.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -noRenameWhileBuilding=\u30d3\u30eb\u30c9\u4e2d\u306b\u30b8\u30e7\u30d6\u540d\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\u3002 -newNameInUse=\u30b8\u30e7\u30d6\u540d {0} \u306f\u3059\u3067\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002 -configWasSaved=\u4ed6\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u3059\u3079\u3066\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f\u3002 -description={0}\u304b\u3089{1}\u306b\u5909\u66f4\u3057\u3066\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -Yes=\u306f\u3044 \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/Job/rename_pt_BR.properties b/core/src/main/resources/hudson/model/Job/rename_pt_BR.properties deleted file mode 100644 index 25b67038d32e9fac8cc1638df8971adced730a99..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_pt_BR.properties +++ /dev/null @@ -1,33 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva, Fernando Boaglio -# -# 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. - -# Unable to rename a job while it is building. -noRenameWhileBuilding=N\u00e3o \u00e9 poss\u00edvel renomear enquanto o job est\u00e1 em constru\u00e7\u00e3o -# Yes -Yes=Sim -# No -# All other configuration options were saved. -configWasSaved=Todas as outras op\u00e7\u00f5es de configura\u00e7\u00e3o foram salvas -# Are you sure about renaming {0} to {1}? -description=Tem certeza que quer renomear {0} para {1}? -# The name {0} is already in use. -newNameInUse=O nome {0} j\u00e1 esta em uso. diff --git a/core/src/main/resources/hudson/model/Job/rename_ru.properties b/core/src/main/resources/hudson/model/Job/rename_ru.properties deleted file mode 100644 index 1a9631708c3d4d64d16e8d7465b94c636010f551..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_ru.properties +++ /dev/null @@ -1,8 +0,0 @@ -# This file is under the MIT License by authors - -No=\u041d\u0435\u0442 -Yes=\u0414\u0430 -description=\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u0442\u044c {0} \u0432 {1}? -noRenameWhileBuilding=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u0440\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043a\u0430\u0445 -newNameInUse=\u0417\u0430\u0434\u0430\u0447\u0430 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c {0} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. -configWasSaved=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b diff --git a/core/src/main/resources/hudson/model/Job/rename_sr.properties b/core/src/main/resources/hudson/model/Job/rename_sr.properties deleted file mode 100644 index 531c0a42259c88d77b6499db587c656fefe01946..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_sr.properties +++ /dev/null @@ -1,8 +0,0 @@ -# This file is under the MIT License by authors - -noRenameWhileBuilding=\u041D\u0438\u0458\u0435 \u043C\u043E\u0433\u0443\u045B\u0435 \u043F\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u0442\u0438 \u0437\u0430\u0434\u0430\u0442\u0430\u043A \u0434\u043E\u043A \u0441\u0435 \u0433\u0440\u0430\u0434\u0438. -configWasSaved=\u0421\u0432\u0430 \u043F\u043E\u0434\u0435\u0448\u0430\u0432\u0430\u045A\u0430 \u0441\u0443 \u0441\u0430\u0447\u0443\u0432\u0430\u043D\u0430. -newNameInUse=\u0418\u043C\u0435 {0} \u0458\u0435 \u0437\u0430\u0443\u0437\u0435\u0442\u043E. -description=\u0414\u0430 \u043B\u0438 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043D\u0438 \u0434\u0430 \u0436\u0435\u043B\u0438\u0442\u0435 \u0434\u0430 \u043F\u0440\u0435\u0438\u043C\u0435\u043D\u0443\u0458\u0435\u0442\u0435 "{0}" \u0443 "{1}"? -Yes=\u0414\u0430 -No=\u041D\u0435 diff --git a/core/src/main/resources/hudson/model/Job/rename_sv_SE.properties b/core/src/main/resources/hudson/model/Job/rename_sv_SE.properties deleted file mode 100644 index fbadf7bcfee4880610cf4ef668d469718f38da3f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_sv_SE.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -No=Nej -Yes=Ja -description=\u00C4r du s\u00E4ker att du vill \u00E4ndra namn ifr\u00E5n {0} till {1}? diff --git a/core/src/main/resources/hudson/model/Job/rename_zh_CN.properties b/core/src/main/resources/hudson/model/Job/rename_zh_CN.properties deleted file mode 100644 index 0ddc3baefde79d9db13e499bc3f88f82dc22cd45..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -noRenameWhileBuilding=\u4E0D\u5141\u8BB8\u91CD\u547D\u540D\u6B63\u5728\u6784\u5EFA\u4E2D\u7684\u4EFB\u52A1\u3002 -newNameInUse=\u8BE5\u547D\u540D {0} \u5DF2\u7ECF\u88AB\u5360\u7528\u3002 -newNameNotValid=\u8BE5\u547D\u540D {0} \u4E0D\u5408\u6CD5\u3002 -configWasSaved=All other configuration options were saved. -description=\u4F60\u786E\u5B9A\u628A {0} \u91CD\u547D\u540D\u4E3A {1}\uFF1F -Yes=\u786E\u5B9A diff --git a/core/src/main/resources/hudson/model/Job/rename_zh_TW.properties b/core/src/main/resources/hudson/model/Job/rename_zh_TW.properties deleted file mode 100644 index efe780eb01452fd4e15c68da2ca8778cac718305..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Job/rename_zh_TW.properties +++ /dev/null @@ -1,30 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -configWasSaved=\u5176\u4ed6\u8a2d\u5b9a\u9078\u9805\u5df2\u88ab\u5132\u5b58\u3002 - -noRenameWhileBuilding=\u4f5c\u696d\u5efa\u7f6e\u4e2d\uff0c\u7121\u6cd5\u6539\u540d\u3002 -newNameInUse={0} \u9019\u500b\u540d\u5b57\u5df2\u7d93\u88ab\u4f7f\u7528\u4e86\u3002 - -description=\u4F60\u78BA\u5B9A\u8981\u5C07 {0} \u6539\u540D\u6210 {1}? -Yes=\u662F -No=\u5426 diff --git a/core/src/main/resources/hudson/model/Label/index.jelly b/core/src/main/resources/hudson/model/Label/index.jelly index 10b239fd63cb4ad07a3edf8b70724107ef034151..a5b4ea43ad9256da5d4c34388ff89b3dcd42d894 100644 --- a/core/src/main/resources/hudson/model/Label/index.jelly +++ b/core/src/main/resources/hudson/model/Label/index.jelly @@ -42,7 +42,7 @@ THE SOFTWARE.

${%Nodes}

- + diff --git a/core/src/main/resources/hudson/model/ListView/configure-entries_nl.properties b/core/src/main/resources/hudson/model/ListView/configure-entries_nl.properties index 32ca1bc849eefd1199c2cd52c5ce387f74cfde68..f0a231f7a8f8f9c9b00bc38a5e6125d0ddbf7f34 100644 --- a/core/src/main/resources/hudson/model/ListView/configure-entries_nl.properties +++ b/core/src/main/resources/hudson/model/ListView/configure-entries_nl.properties @@ -29,4 +29,4 @@ Job\ Filters=Bouwpoging Filters Jobs=Jobs Status\ Filter=Status Filter Use\ a\ regular\ expression\ to\ include\ jobs\ into\ the\ view=Gebruik een reguliere uitdrukking om jobs te selecteren voor het overzichtsscherm -Regular\ expression=Reguliere uitdrukking +Regular\ expression=Reguliere expressie diff --git a/core/src/main/resources/hudson/model/ListView/configure-entries_zh_CN.properties b/core/src/main/resources/hudson/model/ListView/configure-entries_zh_CN.properties deleted file mode 100644 index 4e571856e26d26e1202e2281fc73704aaabc55a5..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ListView/configure-entries_zh_CN.properties +++ /dev/null @@ -1,34 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Add\ column=\u6DFB\u52A0\u5217 -All\ selected\ jobs=\u6240\u6709\u9009\u62E9\u7684Jobs -Columns=\u5217 -Disabled\ jobs\ only=\u53EA\u663E\u793A\u7981\u7528\u7684\u4EFB\u52A1 -Enabled\ jobs\ only=\u53EA\u663E\u793A\u542F\u7528\u7684\u4EFB\u52A1 -Job\ Filters=\u4EFB\u52A1\u8FC7\u6EE4\u5668 -Regular\ expression=\u6B63\u5219\u8868\u8FBE\u5F0F -Status\ Filter=\u72B6\u6001\u8FC7\u6EE4 -Recurse\ in\ subfolders=\u904D\u5386\u5B50\u6587\u4EF6\u5939 -Jobs=\u4EFB\u52A1\u5217\u8868 -Use\ a\ regular\ expression\ to\ include\ jobs\ into\ the\ view=\u4F7F\u7528\u6B63\u5219\u8868\u8FBE\u5F0F\u5728\u89C6\u56FE\u4E2D\u663E\u793AJobs -Add\ Job\ Filter=\u589E\u52A0\u4EFB\u52A1\u8FC7\u6EE4\u5668 diff --git a/core/src/main/resources/hudson/model/ListView/newJobButtonBar_zh_CN.properties b/core/src/main/resources/hudson/model/ListView/newJobButtonBar_zh_CN.properties deleted file mode 100644 index d5345a7bad14ee8885ca74539e744b294e6a7011..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ListView/newJobButtonBar_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Add\ to\ current\ view=\u6DFB\u52A0\u5230\u5F53\u524D\u89C6\u56FE diff --git a/core/src/main/resources/hudson/model/ListView/newViewDetail_nl.properties b/core/src/main/resources/hudson/model/ListView/newViewDetail_nl.properties index f554888906bca82c8e774fdec683cf9d87be7459..cf16ead11369fd675789e9d0f030fd6fcd378cd6 100644 --- a/core/src/main/resources/hudson/model/ListView/newViewDetail_nl.properties +++ b/core/src/main/resources/hudson/model/ListView/newViewDetail_nl.properties @@ -1,3 +1,3 @@ # This file is under the MIT License by authors -blurb=Toont items in een eenvoudig lijst formaat. Je kan kiezen welke bouw opdrachten te tonen zijn in welk overzicht. +blurb=Toont items in een eenvoudig lijst formaat. Je kan kiezen welke bouwopdrachten te tonen zijn in welk overzicht. diff --git a/core/src/main/resources/hudson/model/ListView/newViewDetail_zh_CN.properties b/core/src/main/resources/hudson/model/ListView/newViewDetail_zh_CN.properties deleted file mode 100644 index 2b9cfff40ec88a6649de0d4f11faad9ce20f5330..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ListView/newViewDetail_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -blurb=\u663E\u793A\u7B80\u5355\u5217\u8868\u3002\u4F60\u53EF\u4EE5\u4ECE\u4E2D\u9009\u62E9\u4EFB\u52A1\u6765\u663E\u793A\u5728\u67D0\u4E2A\u89C6\u56FE\u4E2D diff --git a/core/src/main/resources/hudson/model/LoadStatistics/main.properties b/core/src/main/resources/hudson/model/LoadStatistics/main.properties index 62d0348c77ab2df5e32b38888fbf115ff2508d88..23ffe45ae62c09098f734dfda6532e6d173dfa1f 100644 --- a/core/src/main/resources/hudson/model/LoadStatistics/main.properties +++ b/core/src/main/resources/hudson/model/LoadStatistics/main.properties @@ -50,7 +50,7 @@ blurb=\
Queue length
\
\ This is the number of jobs that are in the build queue, waiting for an \ - available executor (of this computer, of this label, or in this Jenkins, respectively.) \ + available executor (of this computer, of this label, or in this Jenkins, respectively). \ This doesn''t include jobs that are in the quiet period, nor does it include \ jobs that are in the queue because earlier builds are still in progress. \ If this line ever goes above 0, that means your Jenkins will run more builds by \ diff --git a/core/src/main/resources/hudson/model/LoadStatistics/main_zh_CN.properties b/core/src/main/resources/hudson/model/LoadStatistics/main_zh_CN.properties deleted file mode 100644 index 672dd289393233b97afe22f762a555e8c6e8c97f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/LoadStatistics/main_zh_CN.properties +++ /dev/null @@ -1,31 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Load\ statistics\ graph=\u52A0\u8F7D\u7EDF\u8BA1\u56FE -Long=\u957F -Medium=\u4E2D -Short=\u77ED -Timespan=\u65F6\u95F4\u95F4\u9694 -blurb=\u8D1F\u8F7D\u7EDF\u8BA1\u6301\u7EED\u8DDF\u8E2A\u8D44\u6E90\u6682\u7528\u7684\u4E09\u4E2A\u6838\u5FC3\u6570\u636E\uFF1A -\u6267\u884C\u603B\u6570\uFF1A - \u8FD9\u4E2A\u6570\u503C\u4EE3\u8868\u4E00\u53F0\u8BA1\u7B97\u673A\u7684\u6267\u884C\u5355\u5143\u6570\u503C -title=\u8D1F\u8F7D\u7EDF\u8BA1:{0} diff --git a/core/src/main/resources/hudson/model/Messages.properties b/core/src/main/resources/hudson/model/Messages.properties index 35d5c33cc8b7b336a5c18ebf3c7a2f22ac0c51e4..191592641fd84c552dfa8466f28534ae7b71a2a7 100644 --- a/core/src/main/resources/hudson/model/Messages.properties +++ b/core/src/main/resources/hudson/model/Messages.properties @@ -35,14 +35,16 @@ AbstractItem.TaskNoun=Build AbstractItem.BeingDeleted={0} is currently being deleted AbstractItem.FailureToStopBuilds=Failed to interrupt and stop {0,choice,1#{0,number,integer} build|1<{0,number,integer} \ builds} of {1} +AbstractItem.NewNameInUse=The name \u201c{0}\u201d is already in use. +AbstractItem.NewNameUnchanged=The new name is the same as the current name. AbstractProject.AssignedLabelString_NoMatch_DidYouMean=There\u2019s no agent/cloud that matches this assignment. Did you mean \u2018{1}\u2019 instead of \u2018{0}\u2019? AbstractProject.NewBuildForWorkspace=Scheduling a new build to get a workspace. AbstractProject.AwaitingBuildForWorkspace=Awaiting build to get a workspace. AbstractProject.AwaitingWorkspaceToComeOnline=We need to schedule a new build to get a workspace, but deferring {0}ms in the hope that one will become available soon AbstractProject.Pronoun=Project AbstractProject.Aborted=Aborted -AbstractProject.UpstreamBuildInProgress=Upstream project {0} is already building. -AbstractProject.DownstreamBuildInProgress=Downstream project {0} is already building. +AbstractProject.UpstreamBuildInProgress=Upstream project \u2018{0}\u2019 is already building +AbstractProject.DownstreamBuildInProgress=Downstream project \u2018{0}\u2019 is already building AbstractProject.Disabled=Build disabled AbstractProject.NoBuilds=No existing build. Scheduling a new one. AbstractProject.NoSCM=No SCM @@ -85,6 +87,7 @@ AbstractProject.LabelLink=Label {1} is serviced by {3,choic Api.MultipleMatch=XPath "{0}" matched {1} nodes. \ Create XPath that only matches one, or use the "wrapper" query parameter to wrap them all under a root element. Api.NoXPathMatch=XPath {0} didn\u2019t match +Api.WrapperParamInvalid=The wrapper parameter can only contain alphanumeric characters or dash/dot/underscore. The first character has to be a letter or underscore. BallColor.Aborted=Aborted BallColor.Disabled=Disabled @@ -149,6 +152,8 @@ Hudson.NotANumber=Not a number Hudson.NotAPositiveNumber=Not a positive number Hudson.NotANonNegativeNumber=Number may not be negative Hudson.NotANegativeNumber=Not a negative number +Hudson.MustBeAtLeast=Must be {0} or greater +Hudson.MustBeAtMost=Must be {0} or less Hudson.NotUsesUTF8ToDecodeURL=\ Your container doesn\u2019t use UTF-8 to decode URLs. If you use non-ASCII characters as a job name etc, \ this will cause problems. \ @@ -172,6 +177,7 @@ Item.CREATE.description=Create a new job. Item.DELETE.description=Delete a job. Item.CONFIGURE.description=Change the configuration of a job. Item.READ.description=See a job. (You may deny this permission but allow Discover to force an anonymous user to log in to see the job.) +Item.RENAME.description=Rename a job. ItemGroupMixIn.may_not_copy_as_it_contains_secrets_and_=May not copy {0} as it contains secrets and {1} has {2}/{3} but not /{4} Job.AllRecentBuildFailed=All recent builds failed. Job.BuildStability=Build stability: {0} @@ -179,7 +185,7 @@ Job.NOfMFailed={0} out of the last {1} builds failed. Job.NoRecentBuildFailed=No recent builds failed. Job.Pronoun=Project Job.minutes=mins - +Job.NoRenameWhileBuilding=Unable to rename a job while it is building. Job.you_must_use_the_save_button_if_you_wish=You must use the Save button if you wish to rename a job. Label.GroupOf=group of {0} Label.InvalidLabel=invalid label @@ -188,14 +194,14 @@ ManageJenkinsAction.DisplayName=Manage Jenkins MultiStageTimeSeries.EMPTY_STRING= Queue.AllNodesOffline=All nodes of label \u2018{0}\u2019 are offline Queue.LabelHasNoNodes=There are no nodes with the label \u2018{0}\u2019 -Queue.BlockedBy=Blocked by {0} +Queue.BlockedBy=Blocked by \u2018{0}\u2019 Queue.HudsonIsAboutToShutDown=Jenkins is about to shut down Queue.InProgress=A build is already in progress Queue.InQuietPeriod=In the quiet period. Expires in {0} -Queue.NodeOffline={0} is offline +Queue.NodeOffline=\u2018{0}\u2019 is offline Queue.Unknown=??? Queue.WaitingForNextAvailableExecutor=Waiting for next available executor -Queue.WaitingForNextAvailableExecutorOn=Waiting for next available executor on {0} +Queue.WaitingForNextAvailableExecutorOn=Waiting for next available executor on \u2018{0}\u2019 Queue.init=Restoring the build queue Queue.node_has_been_removed_from_configuration={0} has been removed from configuration Queue.executor_slot_already_in_use=Executor slot already in use @@ -336,10 +342,10 @@ ChoiceParameterDefinition.MissingChoices=Requires Choices. RunParameterDefinition.DisplayName=Run Parameter PasswordParameterDefinition.DisplayName=Password Parameter -Node.BecauseNodeIsReserved={0} is reserved for jobs with matching label expression -Node.BecauseNodeIsNotAcceptingTasks={0} is not accepting tasks -Node.LabelMissing={0} doesn\u2019t have label {1} -Node.LackingBuildPermission={0} lacks permission to run on {1} +Node.BecauseNodeIsReserved=\u2018{0}\u2019 is reserved for jobs with matching label expression +Node.BecauseNodeIsNotAcceptingTasks=\u2018{0}\u2019 is not accepting tasks +Node.LabelMissing=\u2018{0}\u2019 doesn\u2019t have label \u2018{1}\u2019 +Node.LackingBuildPermission=\u2018{0}\u2019 lacks permission to run on \u2018{1}\u2019 Node.Mode.NORMAL=Use this node as much as possible Node.Mode.EXCLUSIVE=Only build jobs with label expressions matching this node @@ -381,7 +387,7 @@ BuildAuthorizationToken.InvalidTokenProvided=Invalid token provided. Jenkins.CheckDisplayName.NameNotUniqueWarning=The display name, "{0}", is used as a name by a job and could cause confusing search results. Jenkins.CheckDisplayName.DisplayNameNotUniqueWarning=The display name, "{0}", is already in use by another job and could cause confusion and delay. -Jenkins.NotAllowedName="{0}" is not allowed name +Jenkins.NotAllowedName=\u201c{0}\u201d is not an allowed name Jenkins.IsRestarting=Jenkins is restarting User.IllegalUsername="{0}" is prohibited as a username for security reasons. diff --git a/core/src/main/resources/hudson/model/Messages_bg.properties b/core/src/main/resources/hudson/model/Messages_bg.properties index 550cbf603414d50f1a33a7fda681ff0b4d9d90f8..d6bb585efd9c3414dd68ffb613d4a864cccd92b0 100644 --- a/core/src/main/resources/hudson/model/Messages_bg.properties +++ b/core/src/main/resources/hudson/model/Messages_bg.properties @@ -37,6 +37,8 @@ AbstractItem.NoSuchJobExistsWithoutSuggestion=\ \u041d\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u043d\u0430 \u0438\u043c\u0435 \u201e{0}\u201c. AbstractItem.Pronoun=\ \u0415\u043b\u0435\u043c\u0435\u043d\u0442 +AbstractItem.NewNameInUse=\ + \u0418\u043c\u0435\u0442\u043e \u201e{0}\u201c \u0435 \u0437\u0430\u0435\u0442\u043e. AbstractProject.NewBuildForWorkspace=\ \u041d\u0430\u0441\u0440\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u043d\u043e\u0432\u043e \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435, \u0437\u0430 \u0434\u0430 \u0441\u0435 \u043f\u043e\u043b\u0443\u0447\u0438 \u0440\u0430\u0431\u043e\u0442\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e. AbstractProject.AwaitingBuildForWorkspace=\ @@ -260,7 +262,8 @@ Job.Pronoun=\ \u041f\u0440\u043e\u0435\u043a\u0442 Job.minutes=\ \u043c\u0438\u043d. - +Job.NoRenameWhileBuilding=\ + \u041d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043f\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u043f\u043e \u0432\u0440\u0435\u043c\u0435 \u043d\u0430 \u043d\u0435\u0439\u043d\u043e\u0442\u043e \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435 . Job.you_must_use_the_save_button_if_you_wish=\ \u0417\u0430 \u0434\u0430 \u043f\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0430, \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \u0431\u0443\u0442\u043e\u043d\u0430 \u201e\u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435\u201c. Label.GroupOf=\ diff --git a/core/src/main/resources/hudson/model/Messages_da.properties b/core/src/main/resources/hudson/model/Messages_da.properties index 32e1ecbe2c4e4a3d083e2a733f46306d9520a00d..4140fe97227ecc119a585a13d6d1d30228603df1 100644 --- a/core/src/main/resources/hudson/model/Messages_da.properties +++ b/core/src/main/resources/hudson/model/Messages_da.properties @@ -216,3 +216,5 @@ HealthReport.EmptyString= MultiStageTimeSeries.EMPTY_STRING= ManageJenkinsAction.DisplayName=Bestyr Jenkins ParametersDefinitionProperty.DisplayName=Dette byg er parameteriseret +AbstractItem.NewNameInUse=Navnet {0} er allerede i brug +Job.NoRenameWhileBuilding=Kan ikke omd\u00f8be et job imens det bygger \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/Messages_de.properties b/core/src/main/resources/hudson/model/Messages_de.properties index 11523a03f38b69bbb9e03d77cafee8943ea572fc..70cd593b8bdf3d5df2626893ee1bf56176b3c1cb 100644 --- a/core/src/main/resources/hudson/model/Messages_de.properties +++ b/core/src/main/resources/hudson/model/Messages_de.properties @@ -29,6 +29,7 @@ AbstractBuild.KeptBecause=Aufbewahrt wegen {0} AbstractItem.NoSuchJobExists=Element \u201E{0}\u201C existiert nicht. Meinten Sie vielleicht \u201E{1}\u201C? AbstractItem.NoSuchJobExistsWithoutSuggestion=Es gibt kein Element \u201E{0}\u201C. AbstractItem.Pronoun=Element +AbstractItem.NewNameInUse=Der Name {0} wird bereits verwendet. AbstractProject.AssignedLabelString.InvalidBooleanExpression=Ung\u00FCltiger boolscher Ausdruck: \u201E{0}\u201C AbstractProject.AssignedLabelString.NoMatch=Es gibt keine Agenten oder Clouds, die diesen Label-Ausdruck bedienen. AbstractProject.AssignedLabelString_NoMatch_DidYouMean=Es gibt keine Knoten oder Clouds f\u00FCr diesen Ausdruck. Meinten Sie \u201E{1}\u201C statt \u201E{0}\u201C? @@ -181,6 +182,7 @@ Job.NOfMFailed={0} der letzten {1} Builds schlug fehl. Job.NoRecentBuildFailed=In letzter Zeit schlug kein Build fehl. Job.Pronoun=Projekt Job.minutes=Minuten +Job.NoRenameWhileBuilding=Ein Job kann nicht umbenannt werden, während er gebaut wird. Job.you_must_use_the_save_button_if_you_wish=Um das Projekt umzubennen, m\u00FCssen Sie die Schaltfl\u00E4che \u201ESpeichern\u201C verwenden. Label.GroupOf={0} Gruppe diff --git a/core/src/main/resources/hudson/model/Messages_es.properties b/core/src/main/resources/hudson/model/Messages_es.properties index 0d9c1357272e0638a66405bfe6a20984f2b0bd9e..b61a753a64cf274f4477554c9a0a6f5a6799a7b5 100644 --- a/core/src/main/resources/hudson/model/Messages_es.properties +++ b/core/src/main/resources/hudson/model/Messages_es.properties @@ -25,6 +25,7 @@ AbstractBuild.BuildingOnMaster=Ejecutando en el nodo principal AbstractBuild.KeptBecause=Conservar porque {0} AbstractItem.NoSuchJobExists=La tarea ''{0}'' no existe. \u00bfQuiz\u00e1s quieras decir ''{1}''? +AbstractItem.NewNameInUse=El nombre {0} ya existe. AbstractProject.NewBuildForWorkspace=Lanzando una nueva ejecuci\u00f3n para crear el espacio de trabajo. AbstractProject.Pronoun=Proyecto AbstractProject.Aborted=Cancelado @@ -47,6 +48,7 @@ AbstractProject.ExtendedReadPermission.Description=\ Api.MultipleMatch=XPath "{0}" encontr\u00f3 coincidencias en {1} nodos. \ Crea una expresi\u00f3n XPath que s\u00f3lo encuentre uno, o utiliza el par\u00e1metro "wraper" para que todos los nodos se agrupen bajo un elemento. Api.NoXPathMatch=XPath {0} no encontr\u00f3 nada +Api.WrapperParamInvalid=El par\u00E1metro wrapper s\u00F3lo puede contener caracteres alfanum\u00E9ricos o guiones/puntos/subrayado. El primer car\u00E1cter debe ser una letra o subrayado. BallColor.Aborted=Abortado BallColor.Disabled=Desactivado @@ -118,6 +120,7 @@ Job.NOfMFailed={0} de las {1} \u00faltimas ejecuciones fallaron. Job.NoRecentBuildFailed=No hay ejecuciones recientes con fallos. Job.Pronoun=Proyecto Job.minutes=Min +Job.NoRenameWhileBuilding=No es posible renombrar un proyecto mientras se est ejecutando. Label.GroupOf=grupo de {0} Label.InvalidLabel=etiqueta inv\u00e1lida diff --git a/core/src/main/resources/hudson/model/Messages_fr.properties b/core/src/main/resources/hudson/model/Messages_fr.properties index d1ded2fc0fd08985109baf983d6ec936d29b844e..7b65394ff055201e506e362abb25db8320511253 100644 --- a/core/src/main/resources/hudson/model/Messages_fr.properties +++ b/core/src/main/resources/hudson/model/Messages_fr.properties @@ -41,6 +41,7 @@ AbstractProject.WorkspacePermission.Description=\ Api.MultipleMatch=Le XPath "{0}" correspond \u00e0 {1} noeud(s). \ Merci de fournir un XPath qui ne correspond qu''\u00e0 un seul noeud, ou utilisez le param\u00e8tre de requ\u00e8te "wrapper" pour les encapsuler tous dans un \u00e9l\u00e9ment racine. Api.NoXPathMatch=Pas de correspondance avec le XPath {0} +Api.WrapperParamInvalid=Le param\u00E8tre wrapper ne doit contenir que des caract\u00E8res alphanum\u00E9riques ainsi que des points / tirets bas / traits d''union, et finalement le premier caract\u00E8re doit \u00EAtre une lettre ou un trait d''union. BallColor.Aborted=Annul\u00e9 BallColor.Disabled=D\u00e9sactiv\u00e9 @@ -178,3 +179,4 @@ MyViewsProperty.GlobalAction.DisplayName=Mes vues ManageJenkinsAction.DisplayName=Administrer Jenkins ParametersDefinitionProperty.DisplayName=Ce build a des paramtres + diff --git a/core/src/main/resources/hudson/model/Messages_it.properties b/core/src/main/resources/hudson/model/Messages_it.properties index 939e4558b8c2752d87e2462421e9bd562e40ffc0..c009c0f3444107c8f3cc829e5683de09ea954752 100644 --- a/core/src/main/resources/hudson/model/Messages_it.properties +++ b/core/src/main/resources/hudson/model/Messages_it.properties @@ -34,6 +34,7 @@ AbstractItem.TaskNoun=Compilazione AbstractItem.BeingDeleted=Eliminazione di {0} in corso AbstractItem.FailureToStopBuilds=Interruzione e arresto {0,choice,1#{0,number,integer} \ della compilazione|1<{0,number,integer} delle compilazioni} di {1} non riusciti +AbstractItem.NewNameInUse=Il nome {0} gi utilizzato. AbstractProject.AssignedLabelString_NoMatch_DidYouMean=Non esiste alcun agente/cloud corrispondente a questo assegnamento. Forse si intendeva "{1}" anzich "{0}"? AbstractProject.NewBuildForWorkspace=Pianificazione di una nuova compilazione per ottenere uno spazio di lavoro in corso. AbstractProject.AwaitingBuildForWorkspace=In attesa della compilazione per ottenere uno spazio di lavoro. @@ -181,7 +182,7 @@ Job.NOfMFailed={0} delle ultime {1} compilazioni non sono riuscite. Job.NoRecentBuildFailed=Nessuna compilazione recente non riuscita. Job.Pronoun=Progetto Job.minutes=min - +Job.NoRenameWhileBuilding=Impossibile rinominare un processo finch la sua compilazione in corso. Job.you_must_use_the_save_button_if_you_wish= necessario utilizzare il pulsante Salva se si desidera rinominare un processo. Label.GroupOf=gruppo di {0} Label.InvalidLabel=etichetta non valida diff --git a/core/src/main/resources/hudson/model/Messages_ja.properties b/core/src/main/resources/hudson/model/Messages_ja.properties index 5f98501187eb0d9bb2a23fd3e5235f918c15e2d7..5bb5a55d678b7292ce722f955118838ad24156dc 100644 --- a/core/src/main/resources/hudson/model/Messages_ja.properties +++ b/core/src/main/resources/hudson/model/Messages_ja.properties @@ -28,6 +28,7 @@ AbstractBuild.KeptBecause={0}\u306e\u305f\u3081\u3001\u4fdd\u5b58\u3057\u307e\u3 AbstractItem.NoSuchJobExists=\u30b8\u30e7\u30d6''{0}''\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002''{1}''\u3067\u3059\u304b? AbstractItem.Pronoun=\u30b8\u30e7\u30d6 +AbstractItem.NewNameInUse=\u30b8\u30e7\u30d6\u540d {0} \u306f\u3059\u3067\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002 AbstractProject.NewBuildForWorkspace=\u65b0\u898f\u306e\u30d3\u30eb\u30c9\u3092\u5b9f\u884c\u3057\u3066\u3001\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002 AbstractProject.AwaitingBuildForWorkspace=\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u304c\u7528\u610f\u3067\u304d\u308b\u307e\u3067\u30d3\u30eb\u30c9\u3092\u4fdd\u7559\u3057\u307e\u3059\u3002 AbstractProject.Pronoun=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8 @@ -140,6 +141,7 @@ Job.NOfMFailed=\u6700\u8fd1\u306e{1}\u500b\u4e2d\u3001{0}\u500b\u30d3\u30eb\u30c Job.NoRecentBuildFailed=\u6700\u8fd1\u306e\u30d3\u30eb\u30c9\u306f\u5931\u6557\u3057\u3066\u307e\u305b\u3093\u3002 Job.Pronoun=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8 Job.minutes=\u5206 +Job.NoRenameWhileBuilding=\u30d3\u30eb\u30c9\u4e2d\u306b\u30b8\u30e7\u30d6\u540d\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\u3002 Label.GroupOf={0} Label.InvalidLabel=\u4e0d\u6b63\u306a\u30e9\u30d9\u30eb diff --git a/core/src/main/resources/hudson/model/Messages_pt_BR.properties b/core/src/main/resources/hudson/model/Messages_pt_BR.properties index 4dbe5bf0862ec823ae657a8175301024e69882aa..ebdc7739418ff2ea224a3537b25db4473c4b43c2 100644 --- a/core/src/main/resources/hudson/model/Messages_pt_BR.properties +++ b/core/src/main/resources/hudson/model/Messages_pt_BR.properties @@ -23,6 +23,8 @@ AbstractBuild.BuildingRemotely=Construindo remotamente em {0} AbstractBuild.KeptBecause=mantido por causa de {0} +AbstractItem.NewNameInUse=O nome {0} j\u00e1 esta em uso. + AbstractProject.Pronoun=Projeto AbstractProject.Aborted=Abortado AbstractProject.Disabled=builds desabilitada @@ -74,6 +76,7 @@ Job.NOfMFailed={0} das ultimas {1} builds falharam. Job.NoRecentBuildFailed=Nenhuma builds recente falhou. Job.Pronoun=Projeto Job.minutes=minutos +Job.NoRenameWhileBuilding=N\u00e3o \u00e9 poss\u00edvel renomear enquanto o job est\u00e1 em constru\u00e7\u00e3o Queue.BlockedBy=Bloqueado por {0} Queue.InProgress=Uma builds j\u00e1 esta em progresso diff --git a/core/src/main/resources/hudson/model/Messages_ru.properties b/core/src/main/resources/hudson/model/Messages_ru.properties index 2b0130c8fd51be5fe0757d2681c901643a9e9713..bb551b4c914da3014af49106b9d9048ff851e8da 100644 --- a/core/src/main/resources/hudson/model/Messages_ru.properties +++ b/core/src/main/resources/hudson/model/Messages_ru.properties @@ -23,6 +23,8 @@ AbstractBuild.BuildingRemotely=\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e \u043d\u0430 {0} AbstractBuild.KeptBecause=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e {0} +AbstractItem.NewNameInUse=\u0417\u0430\u0434\u0430\u0447\u0430 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c {0} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. + AbstractProject.Pronoun=\u041f\u0440\u043e\u0435\u043a\u0442 AbstractProject.Aborted=\u041f\u0440\u0435\u0440\u0432\u0430\u043d\u043e AbstractProject.Disabled=\u0421\u0431\u043e\u0440\u043a\u0430 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 @@ -78,6 +80,7 @@ Job.NOfMFailed={0} \u0438\u0437 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438 Job.NoRecentBuildFailed=\u0421\u0440\u0435\u0434\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0441\u0431\u043e\u0440\u043e\u043a \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u043d\u0435\u0442. Job.Pronoun=\u041f\u0440\u043e\u0435\u043a\u0442 Job.minutes=\u043c\u0438\u043d +Job.NoRenameWhileBuilding=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u0440\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043a\u0430\u0445 Queue.BlockedBy=\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e {0} Queue.InProgress=\u0421\u0431\u043e\u0440\u043a\u0430 \u0443\u0436\u0435 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 diff --git a/core/src/main/resources/hudson/model/Messages_sr.properties b/core/src/main/resources/hudson/model/Messages_sr.properties index 6a1b7ed6c310184db78b3c7355f9e0fa3a0e08bb..3d6a308916802d363b6e9e958d714c1b9f2c4c95 100644 --- a/core/src/main/resources/hudson/model/Messages_sr.properties +++ b/core/src/main/resources/hudson/model/Messages_sr.properties @@ -8,6 +8,7 @@ AbstractBuild.KeptBecause=\u041E\u0432\u043E \u0438\u0437\u0433\u0440\u0430\u043 AbstractItem.NoSuchJobExists=\u041D\u0435 \u043F\u043E\u0441\u0442\u043E\u0458\u0438 \u0437\u0430\u0434\u0430\u0442\u0430\u043A "{0}". \u0414\u0430 \u043B\u0438 \u0441\u0442\u0435 \u0438\u043C\u0430\u043B\u0438 "{1}"? AbstractItem.NoSuchJobExistsWithoutSuggestion=\u041D\u0435 \u043F\u043E\u0441\u0442\u043E\u0458\u0438 \u0437\u0430\u0434\u0430\u0442\u0430\u043A \u0441\u0430 \u0438\u043C\u0435\u043D\u043E\u043C "{0}". AbstractItem.Pronoun=\u043E\u0431\u0458\u0435\u043A\u0430\u0442 +AbstractItem.NewNameInUse=\u0418\u043C\u0435 {0} \u0458\u0435 \u0437\u0430\u0443\u0437\u0435\u0442\u043E. AbstractProject.AssignedLabelString_NoMatch_DidYouMean=\u041D\u0435\u043C\u0430 \u0442\u0430\u043A\u0432\u043E\u0433 \u0430\u0433\u0435\u043D\u0442\u0430/cloud. \u0414\u0430 \u043B\u0438 \u0441\u0442\u0435 \u043C\u0438\u0441\u043B\u0438\u043B\u0438 "{1}" \u043D\u0430 \u0443\u043C\u0443 \u0443\u043C\u0435\u0441\u0442\u043E "{0}"? AbstractProject.NewBuildForWorkspace=\u0417\u0430\u043A\u0430\u0437\u0438\u0432\u0430\u045A\u0435 \u043D\u043E\u0432\u0435 \u0438\u0437\u0433\u0440\u0430\u0434\u045A\u0435 \u0434\u0430 \u0431\u0438 \u0441\u0435 \u0434\u043E\u0431\u0438\u043E \u043D\u043E\u0432\u0438 \u0440\u0430\u0434\u043D\u0438 \u043F\u0440\u043E\u0441\u0442\u043E\u0440. AbstractProject.Pronoun=\u041F\u0440\u043E\u0458\u0435\u043A\u0430\u0442 @@ -132,6 +133,7 @@ Job.NOfMFailed={0} \u043E\u0434 \u043F\u043E\u0441\u043B\u0435\u0434\u045A\u0438 Job.NoRecentBuildFailed=\u0418\u0437\u043C\u0435\u045B\u0443 \u043D\u0430\u0458\u043D\u043E\u0432\u0438\u0458\u0438\u0445 \u0438\u0437\u0433\u0440\u0430\u0434\u045A\u0430 \u043D\u0435\u043C\u0430 \u043D\u0435\u0443\u0441\u043F\u0435\u0448\u043D\u0438\u0445. Job.Pronoun=\u041F\u0440\u043E\u0458\u0435\u043A\u0430\u0442 Job.minutes=\u043C\u0438\u043D. +Job.NoRenameWhileBuilding=\u041D\u0438\u0458\u0435 \u043C\u043E\u0433\u0443\u045B\u0435 \u043F\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u0442\u0438 \u0437\u0430\u0434\u0430\u0442\u0430\u043A \u0434\u043E\u043A \u0441\u0435 \u0433\u0440\u0430\u0434\u0438. Job.you_must_use_the_save_button_if_you_wish=\u0414\u0430 \u043F\u0440\u0435\u0438\u043C\u0435\u043D\u0443\u0458\u0442\u0435 \u0437\u0430\u0434\u0430\u0442\u0430\u043A, \u043A\u043B\u0438\u043A\u043D\u0438\u0442\u0435 \u043D\u0430 \u0434\u0443\u0433\u043C\u0435 "\u0421\u0430\u0447\u0443\u0432\u0430\u0458". Label.GroupOf=\u0433\u0440\u0443\u043F\u0430 {0} Label.InvalidLabel=\u043D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u043D\u0430 \u043B\u0430\u0431\u0435\u043B\u0430 diff --git a/core/src/main/resources/hudson/model/Messages_zh_CN.properties b/core/src/main/resources/hudson/model/Messages_zh_CN.properties index 15f139a58b810b26c947df4e0f54b971a7da5690..a1e7da75862f4b651eb4737c5f882a6346a555b9 100644 --- a/core/src/main/resources/hudson/model/Messages_zh_CN.properties +++ b/core/src/main/resources/hudson/model/Messages_zh_CN.properties @@ -1,7 +1,7 @@ # The MIT License # # Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, -# Eric Lefevre-Ardant, Erik Ramfelt, Seiji Sogabe, id:cactusman +# Eric Lefevre-Ardant, Erik Ramfelt, Seiji Sogabe, id:cactusman, suren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -21,10 +21,57 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -FreeStyleProject.DisplayName=\u6784\u5EFA\u4E00\u4E2A\u81EA\u7531\u98CE\u683C\u7684\u8F6F\u4EF6\u9879\u76EE -FreeStyleProject.Description=\u8FD9\u662FJenkins\u7684\u4E3B\u8981\u529F\u80FD.Jenkins\u5C06\u4F1A\u7ED3\u5408\u4EFB\u4F55SCM\u548C\u4EFB\u4F55\u6784\u5EFA\u7CFB\u7EDF\u6765\u6784\u5EFA\u4F60\u7684\u9879\u76EE, \u751A\u81F3\u53EF\u4EE5\u6784\u5EFA\u8F6F\u4EF6\u4EE5\u5916\u7684\u7CFB\u7EDF. +AbstractBuild.BuildingRemotely=\u5728\u8fdc\u7a0b\u8282\u70b9 {0} \u4e0a\u6784\u5efa +AbstractBuild.BuildingOnMaster=\u5728 master \u4e0a\u6784\u5efa +AbstractBuild_Building=\u6784\u5efa\u4e2d +AbstractBuild.BuildingInWorkspace=\ \u5728\u5de5\u4f5c\u7a7a\u95f4 {0} \u4e2d +AbstractBuild.KeptBecause=\u672c\u6b21\u6784\u5efa\u7531\u4e8e {0} \u88ab\u4fdd\u7559\u3002 -HealthReport.EmptyString= +AbstractItem.NoSuchJobExists=\u4efb\u52a1 \u2018{0}\u2019 \u4e0d\u5b58\u5728\u3002\u53ef\u80fd\u662f \u2018{1}\u2019? +AbstractItem.NoSuchJobExistsWithoutSuggestion=\u4efb\u52a1 \u2018{0}\u2019 \u4e0d\u5b58\u5728\u3002 +AbstractItem.Pronoun=\u4EFB\u52A1 +AbstractItem.TaskNoun=\u6784\u5efa +AbstractItem.BeingDeleted={0} \u5df2\u7ecf\u88ab\u5220\u9664 +AbstractItem.NewNameInUse=\u8BE5\u547D\u540D {0} \u5DF2\u7ECF\u88AB\u5360\u7528\u3002 +AbstractItem.NewNameUnchanged=\u65b0\u540d\u79f0\u548c\u5f53\u524d\u7684\u4e00\u6837\u3002 +AbstractProject.AssignedLabelString_NoMatch_DidYouMean=\u6ca1\u6709\u5339\u914d\u7684\u4ee3\u7406\u6216\u8005\u4e91\u670d\u52a1\u3002\u4f60\u53ef\u80fd\u60f3\u8981\u7684\u662f \u2018{1}\u2019 \u800c\u4e0d\u662f \u2018{0}\u2019? +AbstractProject.NewBuildForWorkspace=\u8ba1\u5212\u4e00\u4e2a\u65b0\u7684\u6784\u5efa\u6765\u83b7\u53d6\u5de5\u4f5c\u7a7a\u95f4\u3002 +AbstractProject.AwaitingBuildForWorkspace=\u7b49\u5f85\u6784\u5efa\u6765\u83b7\u53d6\u5de5\u4f5c\u7a7a\u95f4\u3002 +AbstractProject.AwaitingWorkspaceToComeOnline=\u6211\u4eec\u9700\u8981\u8ba1\u5212\u4e00\u4e2a\u65b0\u7684\u6784\u5efa\u6765\u83b7\u53d6\u5de5\u4f5c\u7a7a\u95f4\uff0c\u4f46\u5ef6\u8fdf\u4e86 {0}\u6beb\u79d2\uff0c\u5f88\u5feb\u5c31\u4f1a\u53d8\u6210\u53ef\u7528\u7684\u3002 +AbstractProject.Pronoun=\u5de5\u7a0b +AbstractProject.Aborted=\u4e2d\u6b62 +AbstractProject.UpstreamBuildInProgress=\u4e0a\u6e38\u5de5\u7a0b {0} \u6b63\u5728\u6784\u5efa\u4e2d\u3002 +AbstractProject.DownstreamBuildInProgress=\u4e0b\u6e38\u5de5\u7a0b {0} \u6b63\u5728\u6784\u5efa\u4e2d\u3002 +AbstractProject.Disabled=\u7981\u6b62\u6784\u5efa +AbstractProject.NoBuilds=\u6ca1\u6709\u5df2\u7ecf\u5b58\u5728\u7684\u6784\u5efa\uff0c\u8ba1\u5212\u4e00\u4e2a\u65b0\u7684\u3002 +AbstractProject.NoSCM=\u65e0 SCM +AbstractProject.NoWorkspace=\u6ca1\u6709\u53ef\u7528\u7684\u5de5\u4f5c\u7a7a\u95f4\uff0c\u56e0\u6b64\u65e0\u6cd5\u68c0\u67e5\u66f4\u65b0\u3002 +AbstractProject.WorkspaceTitle=\u5de5\u4f5c\u7a7a\u95f4 {0} +AbstractProject.WorkspaceTitleOnComputer=\u8282\u70b9 {1} \u4e0a\u7684\u5de5\u4f5c\u7a7a\u95f4 {0} +AbstractProject.PollingABorted=SCM \u8f6e\u8be2\u88ab\u4e2d\u6b62 + +AbstractProject.PollingVetoed=SCM\u8f6e\u5bfb\u88ab{0}\u4e2d\u65ad +AbstractProject.ScmAborted=SCM \u68c0\u51fa\u88ab\u4e2d\u6b62 +AbstractProject.WorkspaceOffline=\u5de5\u4f5c\u7a7a\u95f4\u79bb\u7ebf\u3002 + +AbstractProject.BuildPermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u542f\u52a8\u4e00\u4e2a\u65b0\u7684\u6784\u5efa\u4efb\u52a1\u3002 +AbstractProject.WorkspacePermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u83b7\u53d6 Jenkins \u6267\u884c\u6784\u5efa\u4efb\u52a1\u65f6\u68c0\u51fa\u7684\u5de5\u4f5c\u7a7a\u95f4\u5185\u5bb9\u3002\u5982\u679c\u4f60\u4e0d\u5e0c\u671b\u7528\u6237\u901a\u8fc7\u5de5\u4f5c\u7a7a\u95f4\u6d4f\u89c8\u5668\u8bbf\u95ee\u5de5\u4f5c\u7a7a\u95f4\u4e2d\u7684\u6587\u4ef6\uff08\u4f8b\u5982\uff1a SCM \u68c0\u51fa\u7684\u6e90\u7801\u6216\u8005\u6784\u5efa\u7684\u4e2d\u95f4\u4ea7\u7269\uff09\uff0c\u4f60\u53ef\u4ee5\u53d6\u6d88\u8be5\u6743\u9650\u3002\u000d\u000a +AbstractProject.ExtendedReadPermission.Description=\ + \u8be5\u6743\u9650\u6388\u4e88\u9879\u76ee\u914d\u7f6e\u7684\u53ea\u8bfb\u8bbf\u95ee\u3002\u8bf7\u6ce8\u610f\uff0c\u4f60\u6784\u5efa\u8fc7\u7a0b\u4e2d\u7684\u654f\u611f\u4fe1\u606f\uff0c\u4f8b\u5982\u5bc6\u7801\uff0c\u5c31\u4f1a\u88ab\u66b4\u9732\u3002 +AbstractProject.DiscoverPermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u67e5\u627e\u4efb\u52a1\u3002\u6bd4\u8bfb\u53d6\u7684\u6743\u9650\u4f4e\uff0c\u5f53\u7528\u6237\u8bd5\u56fe\u8bbf\u95ee\u4efb\u52a1\u65f6\u4f1a\u91cd\u5b9a\u5411\u5230\u767b\u5f55\u9875\u9762\u3002\u5982\u679c\u6ca1\u6709\u8be5\u6743\u9650\uff0c\u5c31\u4e0d\u80fd\u67e5\u627e\u5de5\u7a0b\u540d\u79f0\uff0c\u5e76\u4f1a\u5f97\u5230404\u9519\u8bef\u3002 +AbstractProject.WipeOutPermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u6e05\u7a7a\u5de5\u4f5c\u7a7a\u95f4 +AbstractProject.CancelPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u53d6\u6d88\u8ba1\u5212\u6216\u7ec8\u6b62\u8fd0\u884c\u4e2d\u7684\u6784\u5efa\u4efb\u52a1\u3002 +AbstractProject.AssignedLabelString.InvalidBooleanExpression=\ + \u975e\u6cd5\u7684\u5e03\u5c14\u503c\u8868\u8fbe\u5f0f: {0} +AbstractProject.AssignedLabelString.NoMatch=\u81ea\u5b9a\u4e49\u5de5\u4f5c\u7a7a\u95f4\u4e3a\u7a7a\u3002\u6ca1\u6709\u627e\u5230\u5339\u914d\u7684\u4ee3\u7406\u6216\u8005\u4e91\u63d0\u4f9b\u5546\u3002 +AbstractProject.CustomWorkspaceEmpty=\u81ea\u5b9a\u4e49\u5de5\u4f5c\u7a7a\u95f4\u4e3a\u7a7a\u3002 + +Api.MultipleMatch=XPath "{0}" \u5339\u914d\u5230\u8282\u70b9 {1} \u3002XPath\u53ef\u4ee5\u5339\u914d\u5230\u4e00\u4e2a\uff0c\u6216\u8005\u4f7f\u7528\u53c2\u6570"wrapper"\u5728\u8ddf\u5143\u7d20\u4e0b\u5305\u88f9\u7740\u3002 +Api.NoXPathMatch=XPath {0} \u4e0d\u5339\u914d BallColor.Aborted=\u5DF2\u7EC8\u6B62 BallColor.Disabled=\u7981\u7528 @@ -35,10 +82,113 @@ BallColor.Pending=\u7B49\u5F85 BallColor.Success=\u6210\u529F BallColor.Unstable=\u4E0D\u7A33\u5B9A +Build.post_build_steps_failed=\u6784\u5efa\u540e\u7684\u6b65\u9aa4\u5931\u8d25 + +BuildAuthorizationToken.InvalidTokenProvided=\u63d0\u4f9b\u7684token\u975e\u6cd5\u3002 + +CLI.clear-queue.shortDescription=\u6e05\u9664\u6784\u5efa\u961f\u5217\u3002 +CLI.online-node.shortDescription=\u7ee7\u7eed\u4f7f\u7528\u8282\u70b9\u6267\u884c\u6784\u5efa\u4efb\u52a1\uff0c\u66ff\u4ee3\u65e9\u671f\u7684\u547d\u4ee4"offline-node"\u3002 + +Computer.Caption=\u4ee3\u7406 {0} +Computer.NoSuchSlaveExists=\u4ee3\u7406 "{0}" \u4e0d\u5b58\u5728\u3002\u53ef\u80fd\u662f "{1}"? +Computer.NoSuchSlaveExistsWithoutAdvice=\u4ee3\u7406 "{0}" \u4e0d\u5b58\u5728\u3002 +Computer.Permissions.Title=\u4ee3\u7406 +Computer.ExtendedReadPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u8bfb\u53d6\u4ee3\u7406\u914d\u7f6e\u3002 +Computer.ConfigurePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u914d\u7f6e\u4ee3\u7406\u3002 +Computer.DeletePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u5220\u9664\u5df2\u5b58\u5728\u7684\u4ee3\u7406\u3002 +Computer.CreatePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u521b\u5efa\u4ee3\u7406\u3002 +Computer.ConnectPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u8fde\u63a5\u4ee3\u7406\u6216\u8005\u8ba9\u4ee3\u7406\u4e0a\u7ebf\u3002 +Computer.DisconnectPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u65ad\u5f00\u6216\u8005\u4e34\u65f6\u4e0b\u7ebf\u4ee3\u7406\u3002 +Computer.BuildPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u5728\u4ee3\u7406\u4e0a\u5141\u8bb8\u4efb\u52a1\u3002 +Computer.BadChannel=\u4ee3\u7406\u8282\u70b9\u79bb\u7ebf\u6216\u8005\u4e0d\u662f\u4e00\u4e2a\u8fdc\u7a0b\u901a\u9053\uff08\u4f8b\u5982\uff1amaster\u8282\u70b9\uff09\u3002 + +ComputerSet.NoSuchSlave=\u6ca1\u6709\u8fd9\u6837\u7684\u4ee3\u7406: {0} +ComputerSet.SlaveAlreadyExists=\u4ee3\u7406 \u2018{0}\u2019 \u5df2\u7ecf\u5b58\u5728 +ComputerSet.SpecifySlaveToCopy=\u6307\u5b9a\u8981\u62f7\u8d1d\u7684\u4ee3\u7406 +ComputerSet.DisplayName=\u8282\u70b9\u5217\u8868 + +Descriptor.From=(\u4ece {0}) + +Executor.NotAvailable=\u4e0d\u53ef\u7528 + +FreeStyleProject.DisplayName=\u6784\u5EFA\u4E00\u4E2A\u81EA\u7531\u98CE\u683C\u7684\u8F6F\u4EF6\u9879\u76EE +FreeStyleProject.Description=\u8FD9\u662FJenkins\u7684\u4E3B\u8981\u529F\u80FD.Jenkins\u5C06\u4F1A\u7ED3\u5408\u4EFB\u4F55SCM\u548C\u4EFB\u4F55\u6784\u5EFA\u7CFB\u7EDF\u6765\u6784\u5EFA\u4F60\u7684\u9879\u76EE, \u751A\u81F3\u53EF\u4EE5\u6784\u5EFA\u8F6F\u4EF6\u4EE5\u5916\u7684\u7CFB\u7EDF. + +HealthReport.EmptyString= + +Hudson.BadPortNumber=\u7aef\u53e3\u53f7\u9519\u8bef {0} +Hudson.Computer.Caption=Master +Hudson.Computer.DisplayName=master +Hudson.ControlCodeNotAllowed=\u4e0d\u5141\u8bb8\u63a7\u5236\u7801: {0} +Hudson.DisplayName=Jenkins +Hudson.JobAlreadyExists=\u4efb\u52a1\u540d \u2018{0}\u2019 \u5df2\u5b58\u5728 +Hudson.NoJavaInPath=java \u4e0d\u5728\u4f60\u7684\u73af\u5883\u53d8\u91cf PATH \u4e2d\u3002\u4f60\u53ef\u80fd\u9700\u8981 \u914d\u7f6e JDKs? +Hudson.NoName=\u6ca1\u6709\u6307\u5b9a\u540d\u79f0 +Hudson.NoSuchDirectory=\u76ee\u5f55\u4e0d\u5b58\u5728: {0} +Hudson.NodeBeingRemoved=\u8282\u70b9\u5df2\u7ecf\u88ab\u79fb\u9664 +Hudson.NotAPlugin={0} \u4e0d\u662f Jenkins \u63d2\u4ef6 +Hudson.NotJDKDir={0} \u4e0d\u662f JDK \u76ee\u5f55 +Hudson.Permissions.Title=\u5168\u90e8 +Hudson.USER_CONTENT_README=\u8be5\u76ee\u5f55\u4e2d\u7684\u6587\u4ef6\u53ef\u4ee5\u901a\u8fc7 http://yourjenkins/userContent/ \u8bbf\u95ee +Hudson.UnsafeChar=\u2018{0}\u2019 \u662f\u4e00\u4e2a\u4e0d\u5b89\u5168\u7684\u5b57\u7b26 +Hudson.ViewAlreadyExists=\u89c6\u56fe\u540d "{0}" \u5df2\u5b58\u5728 +Hudson.ViewName=\u6240\u6709 +Hudson.NotANumber=\u4e0d\u662f\u4e00\u4e2a\u6570\u5b57 +Hudson.NotAPositiveNumber=\u4e0d\u662f\u4e00\u4e2a\u6b63\u6570 +Hudson.NotANonNegativeNumber=\u4e0d\u662f\u4e00\u4e2a\u8d1f\u6570 +Hudson.NotANegativeNumber=\u4e0d\u662f\u4e00\u4e2a\u8d1f\u6570 +Hudson.MustBeAtLeast=\u5fc5\u987b\u5927\u4e8e\u7b49\u4e8e {0} +Hudson.MustBeAtMost=\u5fc5\u987b\u5c0f\u4e8e\u7b49\u4e8e {0} +Hudson.NotUsesUTF8ToDecodeURL=\ + \u4f60\u7684\u5bb9\u5668\u6ca1\u6709\u4f7f\u7528UTF-8\u89e3\u7801URL\u5730\u5740\u3002\u5982\u679c\u4f60\u4f7f\u7528\u4e86\u975eASCII\u5b57\u7b26\u4f5c\u4e3a\u4efb\u52a1\u540d\u79f0\u7b49\uff0c\u53ef\u80fd\u4f1a\u6709\u95ee\u9898\u3002 \ + \u67e5\u770b \u5bb9\u5668 \u548c \ + Tomcat i18n \u83b7\u53d6\u66f4\u591a\u8be6\u60c5\u3002 +Hudson.AdministerPermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u4fee\u6539\u7cfb\u7edf\u7ea7\u522b\u7684\u914d\u7f6e\uff0c\u4e5f\u5c31\u662f\u6267\u884c\u9ad8\u5ea6\u654f\u611f\u7684\u64cd\u4f5c\uff0c\u4f8b\u5982\u6302\u8f7d\u672c\u5730\u7cfb\u7edf\u8bbf\u95ee\uff08\u8fd9\u5c31\u8d4b\u4e88\u4e86\u57fa\u672c\u7684\u64cd\u4f5c\u7cfb\u7edf\u6743\u9650\uff09 +Hudson.ReadPermission.Description=\ + \u8bfb\u6743\u9650\u5bf9\u4e8e\u67e5\u770bJenkins\u7684\u5927\u90e8\u5206\u9875\u9762\u662f\u5fc5\u9700\u7684\u3002\u5f53\u4f60\u4e0d\u5e0c\u671b\u672a\u8ba4\u8bc1\u7684\u7528\u6237\u770b\u5230Jenkins\u9875\u9762\u65f6\uff0c\u8be5\u6743\u9650\u5c31\u5f88\u6709\u7528\uff1a\u4ece\u533f\u540d\u7528\u6237\u4e2d\u53d6\u6d88\u8be5\u6743\u9650\uff0c \ + \u7136\u540e\u7ed9\u53d7\u8ba4\u8bc1\u7684\u7528\u6237\u8d4b\u4e88\u8be5\u6743\u9650\u3002 +Hudson.RunScriptsPermission.Description=\ + \u5728Jenkins\u8fdb\u7a0b\u4e2d\u8981\u8fd0\u884c\u811a\u672c\u65f6\u9700\u8981\u8be5\u6743\u9650\uff0c\u4f8b\u5982\uff1a\u901a\u8fc7Groovy\u63a7\u5236\u53f0\u83b7\u53d6\u547d\u4ee4\u884c\u3002 +Hudson.NodeDescription=Jenkins\u7684master\u8282\u70b9 + Item.Permissions.Title=\u4EFB\u52A1 Item.CREATE.description=\u521B\u5EFA\u65B0\u7684\u4EFB\u52A1\u3002 Item.DELETE.description=\u5220\u9664\u4EFB\u52A1\u3002 Item.CONFIGURE.description=\u4FEE\u6539\u4EFB\u52A1\u7684\u914D\u7F6E\u3002 +Item.READ.description=\u67e5\u770b\u4efb\u52a1\u3002\uff08\u53ef\u4ee5\u62d2\u7edd\u8be5\u6743\u9650\uff0c\u4f46\u5141\u8bb8\u53d1\u73b0\uff0c\u8feb\u4f7f\u533f\u540d\u7528\u6237\u767b\u5f55\u5230\u770b\u5230\u4efb\u52a1\uff09 +Item.RENAME.description=\u91cd\u547d\u540d\u4efb\u52a1\u3002 +ItemGroupMixIn.may_not_copy_as_it_contains_secrets_and_=\u53ef\u80fd\u4e0d\u62f7\u8d1d {0} \u4f5c\u4e3a\u5b83\u7684\u5bb9\u5668\u5bc6\u94a5\uff0c\u5e76\u4e14 {1} \u6709 {2}/{3} \u4f46\u6ca1\u6709 /{4} + +Job.AllRecentBuildFailed=\u6700\u8fd1\u6240\u6709\u7684\u6784\u5efa\u90fd\u5931\u8d25\u4e86\u3002 +Job.BuildStability=\u6784\u5efa\u7a33\u5b9a\u6027: {0} +Job.NOfMFailed=\u6700\u8fd1 {1} \u6b21\u6784\u5efa\u4e2d\u6709 {0} \u6b21\u5931\u8d25\u3002 +Job.NoRecentBuildFailed=\u6700\u8fd1\u6ca1\u6709\u5931\u8d25\u7684\u6784\u5efa\u3002 +Job.Pronoun=\u5de5\u7a0b +Job.minutes=\u5206 +Job.NoRenameWhileBuilding=\u4E0D\u5141\u8BB8\u91CD\u547D\u540D\u6B63\u5728\u6784\u5EFA\u4E2D\u7684\u4EFB\u52A1\u3002 +Job.you_must_use_the_save_button_if_you_wish=\u5982\u679c\u60f3\u8981\u91cd\u547d\u540d\u4efb\u52a1\u4f60\u5fc5\u987b\u70b9\u51fb\u4fdd\u5b58\u6309\u94ae\u3002 + +Label.GroupOf=\u4e00\u7ec4 {0} +Label.InvalidLabel=\u975e\u6cd5\u7684\u6807\u7b7e +Label.ProvisionedFrom={0} \u7684\u4f9b\u5e94 + +ManageJenkinsAction.DisplayName=\u7CFB\u7EDF\u7BA1\u7406 +MultiStageTimeSeries.EMPTY_STRING= + +Queue.AllNodesOffline=\u5e26\u6709\u6807\u7b7e \u2018{0}\u2019 \u7684\u6240\u6709\u8282\u70b9\u90fd\u79bb\u7ebf +Queue.LabelHasNoNodes=\u6ca1\u6709\u5e26\u6709\u6807\u7b7e \u2018{0}\u2019 \u7684\u8282\u70b9 +Queue.BlockedBy=\u88ab {0} \u963b\u65ad +Queue.HudsonIsAboutToShutDown=Jenkins \u51c6\u5907\u5173\u95ed +Queue.InProgress=\u6784\u5efa\u6b63\u5728\u8fdb\u884c\u4e2d +Queue.InQuietPeriod=\u5904\u4e8e\u9759\u9ed8\u671f\u3002\u622a\u6b62 {0} +Queue.NodeOffline={0} \u5df2\u7ecf\u79bb\u7ebf +Queue.Unknown=\u672a\u77e5 +Queue.WaitingForNextAvailableExecutor=\u7b49\u5f85\u4e0b\u4e00\u4e2a\u53ef\u7528\u7684\u6267\u884c\u5668 +Queue.WaitingForNextAvailableExecutorOn=\u5728 {0} \u4e0a\u7b49\u5f85\u4e0b\u4e00\u4e2a\u53ef\u7528\u7684\u6267\u884c\u5668 +Queue.init=\u6062\u590d\u6784\u5efa\u961f\u5217 +Queue.node_has_been_removed_from_configuration={0} \u5df2\u7ecf\u4ece\u914d\u7f6e\u4e2d\u79fb\u9664 +Queue.executor_slot_already_in_use=\u6267\u884c\u5668\u5df2\u7ecf\u88ab\u4f7f\u7528 ResultTrend.Aborted=\u5DF2\u7EC8\u6B62 ResultTrend.Failure=\u5931\u8D25 @@ -50,21 +200,113 @@ ResultTrend.StillUnstable=\u4ECD\u7136\u4E0D\u7A33\u5B9A ResultTrend.Success=\u6210\u529F ResultTrend.Unstable=\u4E0D\u7A33\u5B9A -Node.Mode.NORMAL=\u5C3D\u53EF\u80FD\u7684\u4F7F\u7528\u8FD9\u4E2A\u8282\u70B9 -Node.Mode.EXCLUSIVE=\u53EA\u5141\u8BB8\u8FD0\u884C\u7ED1\u5B9A\u5230\u8FD9\u53F0\u673A\u5668\u7684Job +Run._is_waiting_for_a_checkpoint_on_={0} \u5728\u7b49\u5f85\u68c0\u67e5\u70b9 {1} +Run.BuildAborted=\u6784\u5efa\u88ab\u4e2d\u65ad +Run.MarkedExplicitly=\u8be5\u8bb0\u5f55\u660e\u786e\u6807\u8bb0\u4e3a\u4fdd\u7559\u3002 +Run.Permissions.Title=\u8fd0\u884c +Run.running_as_=\u4f5c\u4e3a {0} \u8fd0\u884c +Run.UnableToDelete=\u65e0\u6cd5\u5220\u9664 {0}: {1} +Run.DeletePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u4ece\u6784\u5efa\u5386\u53f2\u4e2d\u624b\u52a8\u5220\u9664\u6307\u5b9a\u7684\u8bb0\u5f55 +Run.UpdatePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u4fee\u6539\u4e00\u6b21\u6784\u5efa\u7684\u63cf\u8ff0\u6216\u8005\u5176\u4ed6\u5c5e\u6027\uff0c\u4f8b\u5982\uff1a\u63cf\u8ff0\u6784\u5efa\u5931\u8d25\u7684\u539f\u56e0\u3002 +Run.ArtifactsPermission.Description=\u8be5\u6743\u9650\u6388\u4e88\u83b7\u53d6\u6784\u5efa\u6210\u54c1\u7684\u80fd\u529b\u3002\u5982\u679c\u4f60\u4e0d\u60f3\u8ba9\u7528\u6237\u8bbf\u95ee\u6210\u54c1\uff0c\u53ef\u4ee5\u53d6\u6d88\u8be5\u6743\u9650\u3002 +Run.InProgressDuration={0} \u5e76\u8ba1\u6570\u4e2d +Run.NotStartedYet=\u6ca1\u5f00\u59cb +Run.ArtifactsBrowserTitle={0} {1} \u7684\u6210\u54c1 -MyView.DisplayName=\u6211\u7684\u89C6\u56FE +Run.Summary.Stable=\u7a33\u5b9a +Run.Summary.Unstable=\u4e0d\u7a33\u5b9a +Run.Summary.Aborted=\u4e2d\u65ad +Run.Summary.NotBuilt=\u672a\u6784\u5efa +Run.Summary.BackToNormal=\u8fd4\u56de\u5230\u6b63\u5e38 +Run.Summary.BrokenForALongTime=\u65ad\u5f00\u5f88\u957f\u65f6\u95f4 +Run.Summary.BrokenSinceThisBuild=\u81ea\u4ece\u8fd9\u6b21\u6784\u5efa\u540e\u65ad\u5f00 +Run.Summary.BrokenSince=\u81ea\u4ece\u6784\u5efa {0} \u540e\u65ad\u5f00 +Run.Summary.Unknown=\u672a\u77e5 -AbstractItem.Pronoun=\u4EFB\u52A1 +Slave.InvalidConfig.Executors=\u4ee3\u7406\u914d\u7f6e\u4e0d\u5408\u6cd5 {0} \u3002\u6267\u884c\u5668\u6570\u4e0d\u5408\u6cd5\u3002 +Slave.InvalidConfig.NoName=\u4ee3\u7406\u914d\u7f6e\u4e0d\u5408\u6cd5\uff0c\u540d\u79f0\u4e3a\u7a7a +Slave.Network.Mounted.File.System.Warning=\u4f60\u786e\u5b9a\u8981\u4f7f\u7528\u7f51\u7edc\u6302\u8f7d\u6587\u4ef6\u7cfb\u7edf\u4f5c\u4e3a\u6587\u4ef6\u7cfb\u7edf\u7684\u6839\u5417\uff1f\u6ce8\u610f\u8be5\u76ee\u5f55\u65e0\u9700\u5bf9master\u53ef\u89c1\u3002 +Slave.Remote.Director.Mandatory=\u8fdc\u7a0b\u76ee\u5f55\u662f\u5fc5\u586b\u9879 +Slave.Terminated={0} \u4ee3\u7406\u88ab\u7ec8\u6b62 +Slave.Remote.Relative.Path.Warning=\u4f60\u786e\u5b9a\u8981\u4f7f\u7528\u76f8\u5bf9\u8def\u5f84\u4f5c\u4e3a\u6587\u4ef6\u7cfb\u7edf\u7684\u6839\u5417\uff1f\u6ce8\u610f\uff0c\u8981\u786e\u4fdd\u60a8\u6240\u9009\u62e9\u7684\u542f\u52a8\u5668\u63d0\u4f9b\u4e86\u4e00\u81f4\u7684\u5f53\u524d\u5de5\u4f5c\u76ee\u5f55\u3002\u5f3a\u70c8\u5efa\u8bae\u4f7f\u7528\u7edd\u5bf9\u8def\u5f84\u3002 +Slave.UnixSlave=\u8fd9\u662f\u4e00\u4e2aUnix\u4ee3\u7406 +Slave.WindowsSlave=\u8fd9\u662f\u4e00\u4e2aWindows\u4ee3\u7406 -MyViewsProperty.DisplayName=\u6211\u7684\u89C6\u56FE -MyViewsProperty.GlobalAction.DisplayName=\u6211\u7684\u89C6\u56FE +TopLevelItemDescriptor.NotApplicableIn=\u9009\u9879 {0} \u5728 {1} \u4e2d\u4e0d\u5408\u9002 -ManageJenkinsAction.DisplayName=\u7CFB\u7EDF\u7BA1\u7406 -ParametersDefinitionProperty.DisplayName=\u53C2\u6570\u5316\u6784\u5EFA\u8FC7\u7A0B -ListView.DisplayName=\u7B80\u5355\u89C6\u56FE +UpdateCenter.DownloadButNotActivated=\u4e0b\u8f7d\u6210\u529f\uff0c\u4e0b\u6b21\u542f\u52a8\u65f6\u751f\u6548 +UpdateCenter.n_a=\u4e0d\u53ef\u7528 + +View.Permissions.Title=\u89c6\u56fe +View.CreatePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u521b\u5efa\u65b0\u7684\u89c6\u56fe\u3002 +View.DeletePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u5220\u9664\u5df2\u6709\u7684\u89c6\u56fe\u3002 +View.ConfigurePermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u6539\u53d8\u89c6\u56fe\u7684\u914d\u7f6e\u3002 +View.ReadPermission.Description=\u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u67e5\u770b\u89c6\u56fe\uff08\u5141\u8bb8\u8bfb\u53d6\u8bbf\u95ee\uff09\u3002 +View.MissingMode=\u6ca1\u6709\u6307\u5b9a\u89c6\u56fe\u7c7b\u578b +View.DisplayNameNotUniqueWarning=\u663e\u793a\u540d\u79f0, "{0}", \u5df2\u7ecf\u88ab\u5176\u4ed6\u89c6\u56fe\u4f7f\u7528\uff0c\u5e76\u53ef\u80fd\u5f15\u8d77\u6df7\u4e71\u3002 + +UpdateCenter.DisplayName=\u66f4\u65b0\u4e2d\u5fc3 +UpdateCenter.Status.CheckingInternet=\u68c0\u67e5\u7f51\u7edc\u8fde\u63a5 +UpdateCenter.Status.CheckingJavaNet=\u68c0\u67e5\u66f4\u65b0\u4e2d\u5fc3\u8fde\u63a5 +UpdateCenter.Status.Success=\u6210\u529f +UpdateCenter.Status.UnknownHostException=\ + \u89e3\u6790\u4e3b\u673a\u540d {0}. \ + \u5931\u8d25\u3002\u4f60\u53ef\u80fd\u9700\u8981 \u914d\u7f6eHTTP\u4ee3\u7406\uff1f +UpdateCenter.Status.ConnectionFailed=\ + \u65e0\u6cd5\u8fde\u63a5 {0}. \ + \u3002\u4f60\u53ef\u80fd\u9700\u8981 \u914d\u7f6eHTTP\u4ee3\u7406\uff1f +UpdateCenter.init=\u521d\u59cb\u5316\u66f4\u65b0\u4e2d\u5fc3 +UpdateCenter.CoreUpdateMonitor.DisplayName=Jenkins\u66f4\u65b0\u901a\u77e5 + +UpdateCenter.PluginCategory.android=\u5b89\u5353\u5f00\u53d1 +UpdateCenter.PluginCategory.builder=\u6784\u5efa\u5de5\u5177 +UpdateCenter.PluginCategory.buildwrapper=\u6784\u5efa\u5305\u88c5 +UpdateCenter.PluginCategory.cli=\u547d\u4ee4\u884c\u63a5\u53e3 +UpdateCenter.PluginCategory.cloud=\u4e91\u63d0\u4f9b\u5546 +UpdateCenter.PluginCategory.cluster=\u96c6\u7fa4\u7ba1\u7406\u548c\u5206\u5e03\u5f0f\u6784\u5efa +UpdateCenter.PluginCategory.database=\u6570\u636e\u5e93 +UpdateCenter.PluginCategory.deployment=\u5f00\u53d1 +UpdateCenter.PluginCategory.devops=DevOps +UpdateCenter.PluginCategory.dotnet=.NET \u5f00\u53d1 +UpdateCenter.PluginCategory.external=\u5916\u90e8\u5de5\u5177\u96c6\u6210 +UpdateCenter.PluginCategory.groovy-related=Groovy\u76f8\u5173 +UpdateCenter.PluginCategory.ios=iOS \u5f00\u53d1 +UpdateCenter.PluginCategory.library=\u63d2\u4ef6\u5e93\uff08\u88ab\u5176\u4ed6\u63d2\u4ef6\u4f7f\u7528\uff09 +UpdateCenter.PluginCategory.listview-column=\u89c6\u56fe\u5217 +UpdateCenter.PluginCategory.maven=Maven +UpdateCenter.PluginCategory.misc=\u6742\u9879 +UpdateCenter.PluginCategory.notifier=\u6784\u5efa\u901a\u77e5 +UpdateCenter.PluginCategory.page-decorator=\u9875\u9762\u88c5\u9970\u5668 +UpdateCenter.PluginCategory.parameter=\u6784\u5efa\u53c2\u6570 +UpdateCenter.PluginCategory.post-build=\u5176\u4ed6\u6784\u5efa\u540e\u52a8\u4f5c +UpdateCenter.PluginCategory.python=Python \u5f00\u53d1 +UpdateCenter.PluginCategory.report=\u6784\u5efa\u62a5\u544a +UpdateCenter.PluginCategory.ruby=Ruby \u5f00\u53d1 +UpdateCenter.PluginCategory.runcondition=\u7528\u4e8e\u8fd0\u884c\u6761\u4ef6\u7684\u63d2\u4ef6 +UpdateCenter.PluginCategory.scala=Scala \u5f00\u53d1 +UpdateCenter.PluginCategory.scm=\u6e90\u7801\u7ba1\u7406 +UpdateCenter.PluginCategory.scm-related=\u6e90\u7801\u7ba1\u7406\u76f8\u5173 +UpdateCenter.PluginCategory.security=\u5b89\u5168 +UpdateCenter.PluginCategory.slaves=\u4ee3\u7406\u542f\u52a8\u5668\u548c\u63a7\u5236\u5668 +UpdateCenter.PluginCategory.test=\u6d4b\u8bd5 +UpdateCenter.PluginCategory.trigger=\u6784\u5efa\u89e6\u53d1\u5668 +UpdateCenter.PluginCategory.ui=\u7528\u6237\u754c\u9762 +UpdateCenter.PluginCategory.upload=\u6210\u54c1\u4e0a\u4f20 +UpdateCenter.PluginCategory.user=\u8ba4\u8bc1\u548c\u7528\u6237\u7ba1\u7406 +UpdateCenter.PluginCategory.view=\u89c6\u56fe +UpdateCenter.PluginCategory.must-be-labeled=\u672a\u5206\u7c7b +UpdateCenter.PluginCategory.unrecognized=Misc ({0}) + +Permalink.LastBuild=\u6700\u8fd1\u4e00\u6b21\u6784\u5efa +Permalink.LastStableBuild=\u6700\u8fd1\u7a33\u5b9a\u6784\u5efa +Permalink.LastUnstableBuild=\u6700\u8fd1\u4e0d\u7a33\u5b9a\u7684\u6784\u5efa +Permalink.LastUnsuccessfulBuild=\u6700\u8fd1\u672a\u6210\u529f\u7684\u6784\u5efa +Permalink.LastSuccessfulBuild=\u6700\u8fd1\u6210\u529f\u7684\u6784\u5efa +Permalink.LastFailedBuild=\u6700\u8fd1\u5931\u8d25\u7684\u6784\u5efa +Permalink.LastCompletedBuild=\u6700\u8fd1\u5b8c\u6210\u7684\u6784\u5efa ParameterAction.DisplayName=\u53C2\u6570 +ParametersDefinitionProperty.DisplayName=\u53C2\u6570\u5316\u6784\u5EFA\u8FC7\u7A0B StringParameterDefinition.DisplayName=\u5B57\u7B26\u53C2\u6570 TextParameterDefinition.DisplayName=\u6587\u672C\u53C2\u6570 FileParameterDefinition.DisplayName=\u6587\u4EF6\u53C2\u6570 @@ -73,3 +315,52 @@ ChoiceParameterDefinition.DisplayName=\u9009\u9879\u53C2\u6570 ChoiceParameterDefinition.MissingChoices=\u9700\u8981\u9009\u9879\u3002 RunParameterDefinition.DisplayName=\u8FD0\u884C\u65F6\u53C2\u6570 PasswordParameterDefinition.DisplayName=\u5BC6\u7801\u53C2\u6570 + +Node.BecauseNodeIsReserved={0} \u662f\u5bf9\u4efb\u52a1\u4fdd\u7559\u7684\uff0c\u7528\u4e8e\u5339\u914d\u6807\u7b7e\u8868\u8fbe\u5f0f +Node.BecauseNodeIsNotAcceptingTasks={0} \u4e0d\u63a5\u53d7\u4efb\u52a1 +Node.LabelMissing={0} \u6ca1\u6709\u6807\u7b7e {1} +Node.LackingBuildPermission={0} \u5728 {1} \u4e0a\u8fd0\u884c\u7f3a\u5c11\u6743\u9650 +Node.Mode.NORMAL=\u5C3D\u53EF\u80FD\u7684\u4F7F\u7528\u8FD9\u4E2A\u8282\u70B9 +Node.Mode.EXCLUSIVE=\u53EA\u5141\u8BB8\u8FD0\u884C\u7ED1\u5B9A\u5230\u8FD9\u53F0\u673A\u5668\u7684Job + +ListView.DisplayName=\u5217\u8868\u89c6\u56fe + +MyView.DisplayName=\u6211\u7684\u89C6\u56FE + +MyViewsProperty.DisplayName=\u6211\u7684\u89C6\u56FE +MyViewsProperty.GlobalAction.DisplayName=\u6211\u7684\u89C6\u56FE +MyViewsProperty.ViewExistsCheck.NotExist=\u4ee5 {0} \u547d\u540d\u7684\u89c6\u56fe\u4e0d\u5b58\u5728 +MyViewsProperty.ViewExistsCheck.AlreadyExists=\u4ee5 {0} \u547d\u540d\u7684\u89c6\u56fe\u5df2\u7ecf\u5b58\u5728 + +LoadStatistics.Legends.DefinedExecutors=\u660e\u786e\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.OnlineExecutors=\u5728\u7ebf\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.ConnectingExecutors=\u8fde\u63a5\u4e2d\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.TotalExecutors=\u6240\u6709\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.BusyExecutors=\u5fd9\u788c\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.IdleExecutors=\u7a7a\u95f2\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.AvailableExecutors=\u53ef\u7528\u7684\u6267\u884c\u5668 +LoadStatistics.Legends.QueueLength=\u961f\u5217\u957f\u5ea6 + +Cause.LegacyCodeCause.ShortDescription=\u7531\u9057\u7559\u529f\u80fd\u542f\u52a8\uff0c\u6ca1\u6709\u76f8\u5173\u4fe1\u606f +Cause.UpstreamCause.ShortDescription=\u7531\u4e0a\u6e38\u5de5\u7a0b "{0}" \u542f\u52a8\uff0c\u6784\u5efa\u53f7\u4e3a {1} +Cause.UpstreamCause.CausedBy=\u6700\u521d\u5f15\u8d77\u7684: +Cause.UserCause.ShortDescription=\u7531\u7528\u6237 {0} \u542f\u52a8 +Cause.UserIdCause.ShortDescription=\u7531\u7528\u6237 {0} \u542f\u52a8 +Cause.RemoteCause.ShortDescription=\u7531\u8fdc\u7a0b\u4e3b\u673a {0} \u542f\u52a8 +Cause.RemoteCause.ShortDescriptionWithNote=\u7531\u8fdc\u7a0b\u4e3b\u673a {0} \u542f\u52a8 \u5907\u6ce8: {1} + +ProxyView.NoSuchViewExists=\u5168\u5c40\u89c6\u56fe {0} \u4e0d\u5b58\u5728 +ProxyView.DisplayName=\u5305\u62ec\u5168\u5c40\u89c6\u56fe + +CLI.restart.shortDescription=\u91cd\u542fJenkins +CLI.safe-restart.shortDescription=\u5b89\u5168\u5730\u91cd\u542fJenkins +CLI.keep-build.shortDescription=\u6c38\u4e45\u4fdd\u7559\u8fd9\u6b21\u6784\u5efa\u3002 + +Jenkins.CheckDisplayName.NameNotUniqueWarning=\u663e\u793a\u7684\u540d\u79f0, "{0}", \u4f5c\u4e3a\u4efb\u52a1\u7684\u540d\u79f0\uff0c\u53ef\u80fd\u4f1a\u5728\u641c\u7d22\u7ed3\u679c\u4e2d\u5f15\u8d77\u56f0\u60d1\u3002 +Jenkins.CheckDisplayName.DisplayNameNotUniqueWarning=\u663e\u793a\u7684\u540d\u79f0, "{0}", \u5df2\u7ecf\u88ab\u5176\u4ed6\u4efb\u52a1\u4f7f\u7528\uff0c\u5e76\u4e14\u53ef\u80fd\u5f15\u8d77\u56f0\u60d1\u3001\u5ef6\u8fdf\u3002 + +Jenkins.NotAllowedName=\u201c{0}\u201d \u4e3a\u4e0d\u5141\u8bb8\u7684\u540d\u79f0 +Jenkins.IsRestarting=Jenkins\u6b63\u5728\u91cd\u542f + +User.IllegalUsername="{0}" \u51fa\u4e8e\u5b89\u5168\u8003\u8651\u662f\u4e0d\u88ab\u5141\u8bb8\u7684\u7528\u6237\u540d\u3002 +User.IllegalFullname="{0}" \u51fa\u4e8e\u5b89\u5168\u8003\u8651\u662f\u4e0d\u88ab\u5141\u8bb8\u7684\u5168\u540d\u3002 diff --git a/core/src/main/resources/hudson/model/Messages_zh_TW.properties b/core/src/main/resources/hudson/model/Messages_zh_TW.properties index c2b09dfa8f2c5e2443ba522a456b7fc897bcd9c7..dbf2b55745529fdd4a5204897fafb4e81a876379 100644 --- a/core/src/main/resources/hudson/model/Messages_zh_TW.properties +++ b/core/src/main/resources/hudson/model/Messages_zh_TW.properties @@ -29,6 +29,7 @@ AbstractBuild.KeptBecause=\u56e0\u70ba {0} \u800c\u4fdd\u7559 AbstractItem.NoSuchJobExists=\u6c92\u6709 ''{0}'' \u4f5c\u696d\u3002\u60a8\u6307\u7684\u662f ''{1}'' \u55ce? AbstractItem.Pronoun=\u4f5c\u696d +AbstractItem.NewNameInUse={0} \u9019\u500b\u540d\u5b57\u5df2\u7d93\u88ab\u4f7f\u7528\u4e86\u3002 AbstractProject.NewBuildForWorkspace=\u6311\u500b\u6642\u9593\u5efa\u7f6e\uff0c\u5c31\u80fd\u7522\u751f\u5de5\u4f5c\u5340\u3002 AbstractProject.AwaitingBuildForWorkspace=\u7b49\u5019\u5efa\u7f6e\u7522\u51fa\u5de5\u4f5c\u5340\u3002 AbstractProject.Pronoun=\u5c08\u6848 @@ -141,6 +142,7 @@ Job.NOfMFailed=\u6700\u8fd1 {1} \u6b21\u5efa\u7f6e\u4e2d\u6709 {0} \u6b21\u5931\ Job.NoRecentBuildFailed=\u6700\u8fd1\u5e7e\u6b21\u5efa\u7f6e\u90fd\u6c92\u6709\u5931\u6557\u3002 Job.Pronoun=\u5c08\u6848 Job.minutes=\u5206 +Job.NoRenameWhileBuilding=\u4f5c\u696d\u5efa\u7f6e\u4e2d\uff0c\u7121\u6cd5\u6539\u540d\u3002 Label.GroupOf={0} \u7fa4\u7d44 Label.InvalidLabel=\u6a19\u7c64\u7121\u6548 diff --git a/core/src/main/resources/hudson/model/MyView/newViewDetail_nl.properties b/core/src/main/resources/hudson/model/MyView/newViewDetail_nl.properties index 24567966a7a973d18c3f3128bbbb8c70f389d66c..a714269114fd823ab342cbe7556be5242d620548 100644 --- a/core/src/main/resources/hudson/model/MyView/newViewDetail_nl.properties +++ b/core/src/main/resources/hudson/model/MyView/newViewDetail_nl.properties @@ -1,3 +1,3 @@ # This file is under the MIT License by authors -blurb=Dit overzicht toont automatisch alle bouw opdrachten de huidige gebruiker toegang toe heeft. +blurb=Dit overzicht toont automatisch alle bouwopdrachten waar de huidige gebruiker toegang tot heeft. diff --git a/core/src/main/resources/hudson/model/MyView/newViewDetail_zh_CN.properties b/core/src/main/resources/hudson/model/MyView/newViewDetail_zh_CN.properties deleted file mode 100644 index fba0db8a81d627cc0da52967c284325b90f47159..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/MyView/newViewDetail_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -blurb=\ - \u8BE5\u89C6\u56FE\u81EA\u52A8\u663E\u793A\u5F53\u524D\u7528\u6237\u6709\u6743\u9650\u8BBF\u95EE\u7684\u4EFB\u52A1 \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/MyViewsProperty/config_zh_CN.properties b/core/src/main/resources/hudson/model/MyViewsProperty/config_zh_CN.properties deleted file mode 100644 index e55b3d3499c902880925799a10874434a711cc58..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/MyViewsProperty/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Default\ View=\u9ED8\u8BA4\u89C6\u56FE -description=\u5F53\u5BFC\u822A\u4E3A\u7528\u6237\u7684\u79C1\u6709\u89C6\u56FE\u65F6\u5C06\u4F1A\u9ED8\u8BA4\u9009\u62E9\u8BE5\u89C6\u56FE \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/MyViewsProperty/newView_zh_CN.properties b/core/src/main/resources/hudson/model/MyViewsProperty/newView_zh_CN.properties deleted file mode 100644 index 809ee9a34374fb2d331ea4bdfaad1c6ef96c6655..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/MyViewsProperty/newView_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -View\ name=\u89C6\u56FE\u540D\u79F0 diff --git a/core/src/main/resources/hudson/model/NoFingerprintMatch/index_zh_CN.properties b/core/src/main/resources/hudson/model/NoFingerprintMatch/index_zh_CN.properties deleted file mode 100644 index 29bb36be7dc81452867284783aada2b3ee4ae1d9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/NoFingerprintMatch/index_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Dashboard=\u8FD4\u56DE\u63A7\u5236\u53F0 -No\ matching\ record\ found=\u6CA1\u6709\u5BF9\u5E94\u7684\u8BB0\u5F55\u5B58\u5728 diff --git a/core/src/main/resources/hudson/model/Node/help-numExecutors.html b/core/src/main/resources/hudson/model/Node/help-numExecutors.html index 6e23a37a77c4a499d7a65b12283dfd5bd65c01d1..62ee113d3564c7762d2bfc4ebadbae8b58ecc3bc 100644 --- a/core/src/main/resources/hudson/model/Node/help-numExecutors.html +++ b/core/src/main/resources/hudson/model/Node/help-numExecutors.html @@ -1,6 +1,6 @@
The maximum number of concurrent builds that Jenkins may perform on this - agent. + node.

A good value to start with would be the number of CPU cores on the machine. Setting a higher value would cause each build to take longer, but could @@ -9,10 +9,12 @@ the second build could take advantage of the spare I/O capacity at that moment.

- Agents must have at least one executor. To temporarily prevent any builds from - being executed on an agent, use the Mark this node temporarily offline - button on the agent's page. + Agents (nodes that are not the master) must have at least one executor. + To temporarily prevent any builds from being executed on an agent, use the + Mark this node temporarily offline button on the agent's page.

- This does not apply to the Jenkins master — setting the number of - executors to zero will prevent any builds from being executed on the master. + For the master, set the number of executors to zero to prevent it from + executing builds locally. + Note: master will always be able to run flyweight tasks including + Pipeline's top-level task.

diff --git a/core/src/main/resources/hudson/model/ParametersAction/index_zh_CN.properties b/core/src/main/resources/hudson/model/ParametersAction/index_zh_CN.properties deleted file mode 100644 index a798e8e21612f82b0538399326895cb4ae29e945..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ParametersAction/index_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Build=\u6267\u884C -Parameters=\u6784\u5EFA\u53C2\u6570 diff --git a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config-details_zh_CN.properties b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config-details_zh_CN.properties deleted file mode 100644 index 572578efad90ad55c3f899f7c4afaed0022fba93..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config-details_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Add\ Parameter=\u6DFB\u52A0\u53C2\u6570 diff --git a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index_zh_CN.properties b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index_zh_CN.properties deleted file mode 100644 index b7919804a21edff99b2a11b6928141c403ddbfca..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index_zh_CN.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -Build=\u5F00\u59CB\u6784\u5EFA -LOADING=\u8F7D\u5165 -description=\u9700\u8981\u5982\u4E0B\u53C2\u6570\u7528\u4E8E\u6784\u5EFA\u9879\u76EE: diff --git a/core/src/main/resources/hudson/model/PasswordParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/PasswordParameterDefinition/config_zh_CN.properties deleted file mode 100644 index 67cd3a03981ff442519690cf79e6bc6f77ac74ed..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/PasswordParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Name=\u540D\u79F0 -Default\ Value=\u9ED8\u8BA4\u503C -Description=\u63CF\u8FF0 diff --git a/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link_zh_CN.properties b/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link_zh_CN.properties deleted file mode 100644 index 43b90f890c174f7bcf4d6d49d57e89674e3fd992..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -format={0}({1}),{2}\u4E4B\u524D diff --git a/core/src/main/resources/hudson/model/Run/KeepLogBuildBadge/badge_zh_CN.properties b/core/src/main/resources/hudson/model/Run/KeepLogBuildBadge/badge_zh_CN.properties deleted file mode 100644 index 41cf883a61f1fedaaccdfe0bb676b9b8e6230b77..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/KeepLogBuildBadge/badge_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Keep\ this\ build\ forever=\u6C38\u4E45\u4FDD\u5B58\u6B64\u6B21\u7F16\u8BD1 diff --git a/core/src/main/resources/hudson/model/Run/configure_nl.properties b/core/src/main/resources/hudson/model/Run/configure_nl.properties index c5d14d628bc97839abe23c32b4bc31190e4b2606..a9ad89fb543cae19b2ca683b99511bfa3f3cdeef 100644 --- a/core/src/main/resources/hudson/model/Run/configure_nl.properties +++ b/core/src/main/resources/hudson/model/Run/configure_nl.properties @@ -23,4 +23,4 @@ Description=Omschrijving DisplayName=Naam LOADING=LADEN -Save=Bewaar +Save=Opslaan diff --git a/core/src/main/resources/hudson/model/Run/configure_zh_CN.properties b/core/src/main/resources/hudson/model/Run/configure_zh_CN.properties deleted file mode 100644 index f6696de4f6e65f95f221a384d51710ea4a66b3a8..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/configure_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Description=\u63CF\u8FF0 -DisplayName=\u663E\u793A\u540D\u79F0 -LOADING=\u52A0\u8F7D\u4E2D -Save=\u4FDD\u5B58 diff --git a/core/src/main/resources/hudson/model/Run/confirmDelete_nl.properties b/core/src/main/resources/hudson/model/Run/confirmDelete_nl.properties index bc1c2ab8e465641fdf6e8f41e807324ee8d02b64..f5a28e9d531f230415ce245455f385d32a6b3890 100644 --- a/core/src/main/resources/hudson/model/Run/confirmDelete_nl.properties +++ b/core/src/main/resources/hudson/model/Run/confirmDelete_nl.properties @@ -21,5 +21,5 @@ # THE SOFTWARE. Warning=Waarschuwing -Are\ you\ sure\ about\ deleting\ the\ build?=Bent u zeker dat deze bouwpoging verwijderd mag worden? +Are\ you\ sure\ about\ deleting\ the\ build?=Weet u zeker dat deze bouwpoging verwijderd mag worden? Yes=Ja diff --git a/core/src/main/resources/hudson/model/Run/confirmDelete_zh_CN.properties b/core/src/main/resources/hudson/model/Run/confirmDelete_zh_CN.properties deleted file mode 100644 index 6b705c7accf1a1db5f9794f11b3a5cf68ac20785..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/confirmDelete_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Are\ you\ sure\ about\ deleting\ the\ build?=\u4F60\u662F\u5426\u786E\u5B9A\u8981\u5220\u9664\u5F53\u524D\u6784\u5EFA\uFF1F -Yes=\u786E\u5B9A diff --git a/core/src/main/resources/hudson/model/Run/console_zh_CN.properties b/core/src/main/resources/hudson/model/Run/console_zh_CN.properties deleted file mode 100644 index f8f564e64523b0b0d67b7f87d797f623ebd0ae69..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/console_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Console\ Output=\u63A7\u5236\u53F0\u8F93\u51FA -skipSome=\u8DF3\u8FC7 {0,number,integer} KB.. \u5B8C\u6574\u65E5\u5FD7 diff --git a/core/src/main/resources/hudson/model/Run/delete_zh_CN.properties b/core/src/main/resources/hudson/model/Run/delete_zh_CN.properties deleted file mode 100644 index be22c42d225a205f221d4735939e13ee745e2d5b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/delete_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Delete\ this\ build=\u5220\u9664\u672C\u6B21\u751F\u6210 diff --git a/core/src/main/resources/hudson/model/Run/logKeep_nl.properties b/core/src/main/resources/hudson/model/Run/logKeep_nl.properties index ebc3d9e7cc082f69514da44eadf0d7ab9059f6d3..12e3a0f3d7c72d92524aa2de4d60eaba4944613b 100644 --- a/core/src/main/resources/hudson/model/Run/logKeep_nl.properties +++ b/core/src/main/resources/hudson/model/Run/logKeep_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Don't\ keep\ this\ build\ forever=Deze bouwpoging niet eeuwig bijhouden. -Keep\ this\ build\ forever=Bewaar deze Build eeuwig +Don't\ keep\ this\ build\ forever=Deze bouwpoging niet voor altijd bijhouden. +Keep\ this\ build\ forever=Bewaar deze bouwpoging voor altijd. diff --git a/core/src/main/resources/hudson/model/Run/logKeep_zh_CN.properties b/core/src/main/resources/hudson/model/Run/logKeep_zh_CN.properties deleted file mode 100644 index 5a043373e530e3850e843b0c9f0f3cd3a181e2cb..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/Run/logKeep_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Don''t\ keep\ this\ build\ forever=\u53D6\u6D88\u6C38\u4E45\u4FDD\u7559 -Keep\ this\ build\ forever=\u6C38\u4E45\u4FDD\u7559\u8FD9\u6B21\u7F16\u8BD1 diff --git a/core/src/main/resources/hudson/model/RunParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/RunParameterDefinition/config_zh_CN.properties deleted file mode 100644 index 22a181a57a095d187851178674046c2b5fe61a0b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/RunParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,31 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Name=\u540D\u79F0 -Project=\u9879\u76EE -Description=\u63CF\u8FF0 - -All\ Builds=\u6240\u6709\u6784\u5EFA -Successful\ Builds\ Only=\u6210\u529F\u7684\u6784\u5EFA -Completed\ Builds\ Only=\u6267\u884C\u5B8C\u6210\u7684\u6784\u5EFA -Stable\ Builds\ Only=\u7A33\u5B9A\u7684\u6784\u5EFA - diff --git a/core/src/main/resources/hudson/model/StringParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/StringParameterDefinition/config_zh_CN.properties deleted file mode 100644 index f05191610391b0c370a6042766c3cec407c2a9bf..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/StringParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -Default\ Value=\u9ED8\u8BA4\u503C -Description=\u63CF\u8FF0 -Name=\u540D\u79F0 -Trim\ the\ string=\u6E05\u9664\u7A7A\u767D\u5B57\u7B26 diff --git a/core/src/main/resources/hudson/model/TaskAction/log_nl.properties b/core/src/main/resources/hudson/model/TaskAction/log_nl.properties index 49b4c79475db06456518fbb521e3752d2e01c2f1..0d251dd05bae656cf73d12aac27e872a52f7d08e 100644 --- a/core/src/main/resources/hudson/model/TaskAction/log_nl.properties +++ b/core/src/main/resources/hudson/model/TaskAction/log_nl.properties @@ -20,4 +20,4 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Clear\ error\ to\ retry=Fout verwijderen om opnieuw te proberen +Clear\ error\ to\ retry=Verwijder fout om opnieuw te proberen diff --git a/core/src/main/resources/hudson/model/TextParameterDefinition/config_zh_CN.properties b/core/src/main/resources/hudson/model/TextParameterDefinition/config_zh_CN.properties deleted file mode 100644 index 67cd3a03981ff442519690cf79e6bc6f77ac74ed..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/TextParameterDefinition/config_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Name=\u540D\u79F0 -Default\ Value=\u9ED8\u8BA4\u503C -Description=\u63CF\u8FF0 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/ConnectionCheckJob/row_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/ConnectionCheckJob/row_zh_CN.properties deleted file mode 100644 index 48f55253520b93b271947276e00111bc31a1c4be..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/ConnectionCheckJob/row_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Preparation=\u51C6\u5907 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Failure/status_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Failure/status_zh_CN.properties deleted file mode 100644 index 786ad6f9205c4667cadc5dee8551b84cbb227e82..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Failure/status_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Details=\u8BE6\u7EC6 -Failure=\u5931\u8D25 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Installing/status_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Installing/status_zh_CN.properties deleted file mode 100644 index 0afb260980b404f2c823b76d34c6226f2462e631..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Installing/status_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Installing=\u5B89\u88C5\u4E2D diff --git a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Pending/status_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Pending/status_zh_CN.properties deleted file mode 100644 index 758c6895e6204c39aa0505dc1c9fb59b1346b113..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Pending/status_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Pending=\u7B49\u5F85 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Success/status_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Success/status_zh_CN.properties deleted file mode 100644 index f1c7c351641baaa7f30878468f1de4c5b915b5ae..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Success/status_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Success=\u5B8C\u6210 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/Pending/status_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/Pending/status_zh_CN.properties deleted file mode 100644 index 758c6895e6204c39aa0505dc1c9fb59b1346b113..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/Pending/status_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Pending=\u7B49\u5F85 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/row_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/row_zh_CN.properties deleted file mode 100644 index 9cf0c29304a3484845c1547a2b46c5160b1eae8b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/RestartJenkinsJob/row_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Restarting\ Jenkins=\u91CD\u542F Jenkins diff --git a/core/src/main/resources/hudson/model/UpdateCenter/body_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/body_zh_CN.properties deleted file mode 100644 index 1992f0236dff42eb6a6c86c5378a905e738335aa..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/body_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Go\ back\ to\ the\ top\ page=\u8FD4\u56DE\u9996\u9875 -warning=\u5B89\u88C5\u5B8C\u6210\u540E\u91CD\u542FJenkins(\u7A7A\u95F2\u65F6) -you\ can\ start\ using\ the\ installed\ plugins\ right\ away=\u8FD4\u56DE\u9996\u9875\u4F7F\u7528\u5DF2\u7ECF\u5B89\u88C5\u597D\u7684\u63D2\u4EF6 diff --git a/core/src/main/resources/hudson/model/UpdateCenter/index_nl.properties b/core/src/main/resources/hudson/model/UpdateCenter/index_nl.properties index 90a06f16d187ac72cef90ebb6f090d80cda5f8dc..13d1ba72a454d2202e73e3aa5b9fcd09e06c71f8 100644 --- a/core/src/main/resources/hudson/model/UpdateCenter/index_nl.properties +++ b/core/src/main/resources/hudson/model/UpdateCenter/index_nl.properties @@ -21,4 +21,4 @@ # THE SOFTWARE. Installing\ Plugins/Upgrades=Plugins/upgrades installeren -warning=Wanneer de installatie is gedaan, moet Jenkins herstart worden opdat de wijzigingen zouden toegepast worden. +warning=Wanneer de installatie is gedaan, moet Jenkins herstart worden zodat de wijzigingen toegepast kunnen worden. diff --git a/core/src/main/resources/hudson/model/UpdateCenter/index_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/index_zh_CN.properties deleted file mode 100644 index 760601e6ae91e9b9df9774f020611057ed4a3987..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/index_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Installing\ Plugins/Upgrades=\u5B89\u88C5/\u66F4\u65B0 \u63D2\u4EF6\u4E2D diff --git a/core/src/main/resources/hudson/model/UpdateCenter/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/UpdateCenter/sidepanel_zh_CN.properties deleted file mode 100644 index 37d62385821d1284a3639b539719b393c7c8ec17..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UpdateCenter/sidepanel_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2011, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Dashboard=\u8FD4\u56DE -Manage\ Jenkins=\u7cfb\u7edf\u7ba1\u7406 -Manage\ Plugins=\u7BA1\u7406\u63D2\u4EF6 diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.html deleted file mode 100644 index 37b1e5cc6501c14d9b9cbe2340409f3cc7351bf7..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.html +++ /dev/null @@ -1,21 +0,0 @@ -
- Knowing how Jenkins is used is a tremendous help in guiding the direction of the development, especially - for open-source projects where users are inherently hard to track down. When this option is enabled, - Jenkins periodically send information about your usage of Jenkins. - -

- Specifically, it contains the following information: - -

    -
  • Your Jenkins version -
  • For your master and each agent, OS type, and # of executors -
  • Plugins that are installed and their versions -
  • Number of jobs per each job type in your Jenkins -
- -

- The information doesn't contain anything that identifies you or allows us to contact you - (except information inherently revealed by HTTP, such as the IP address). Tabulated - data of these usage statistics submissions will be shared with the community. - -

diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly new file mode 100644 index 0000000000000000000000000000000000000000..1286d8294f7b11e4d138ee6af4ef9b6f393961e6 --- /dev/null +++ b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly @@ -0,0 +1,51 @@ + + +
+ For any project, it's critical to know how the software is used, but tracking usage data is inherently difficult in open-source projects. + Anonymous usage statistics address this need. + When enabled, Jenkins periodically sends information to the Jenkins project. + The Jenkins project uses this information to set development priorities. +
+

General usage statistics

+
+

Jenkins reports the following general usage statistics:

+
    +
  • Your Jenkins version
  • +
  • For your master and each agent, the OS type and number of executors
  • +
  • Installed plugins and their versions
  • +
  • Number of items (like jobs) of each item type
  • +
+

+ This does not report any personally identifiable information. The only information reported by Jenkins is information inherently revealed by the HTTP protocol, such as the IP address. + + These usage statistics are aggregated, updated monthly, and published to stats.jenkins.io +

+
+

Telemetry collection

+
+

+ In addition to the general usage statistics listed above, the Jenkins project collects telemetry data from specific trials to inform future development. + Each trial has a specific purpose and a defined end date, after which collection stops, independent of the installed versions of Jenkins or plugins. + Once a trial is complete, the trial results may be aggregated and shared with the developer community. +

+

+ The following trials defined on this instance are active now or in the future: +

+ + +
+ + +
${collector.displayName}
+
+ +

+ Start date: ${collector.start}
+ End date: ${collector.end} +

+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_bg.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_bg.html deleted file mode 100644 index 20aacefce320b3335fa9ee1d47a6e531b6b170b6..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_bg.html +++ /dev/null @@ -1,22 +0,0 @@ -
- От голяма помощ е да знаем как Jenkins се ползва. Това може да определи - посоката на разработка, което иначе е трудно, защото няма как да се проследяват - потребителите на проект с отворен код. Като изберете тази опция Jenkins - периодично ще изпраща анонимни данни за използването. - -

- Това е пълното описание на включената информация: - -

    -
  • версията на jenkins; -
  • операционната система и броя изпълнявани изграждания от основния и подчинените компютри; -
  • списък с инсталираните приставки и версиите им; -
  • броят задачи за всеки вид задача в инсталацията на Jenkins -
- -

- Информацията не съдържа нищо, които да ви идентифицира или да позволява да се свържем - с вас (с изключение на информацията указана поради естеството на HTTP, като IP адреси). - Тези данни ще бъдат споделени с общността в табличен вид. - -

diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_fr.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_fr.html deleted file mode 100644 index 8d4df6df9de874f58bd40ced028d0d3351ab502c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_fr.html +++ /dev/null @@ -1,25 +0,0 @@ -
- Comprendre comment Jenkins est utilisé est d'une grande aide pour guider les développements, - particulièrement pour les projets open-source où les utilisateurs sont typiquement difficiles - à connaitre. Quand cette option est activée, Jenkins envoie périodiquement des informations sur votre - utilisation de Jenkins. - -

- Plus précisément, il envoie les informations suivantes: - -

    -
  • Votre version de Jenkins -
  • Pour le Jenkins maître et pour chaque esclave, le type d'OS et le nombre d'exécuteurs -
  • Les plugins qui sont installés et leurs versions -
  • Le nombre de jobs pour chaque type de job dans votre installation de Jenkins -
- -

- Cette information ne contient rien qui vous identifie ou qui nous permette de vous contacter - (hors certaines informations intrinsèques à HTTP, comme votre adresse IP). - Les données compilées de ces statistiques d'utilisation seront partagées avec la communauté. - -

- N'hésitez pas à lire - la discussion sur ce mécanisme et à contribuer aux débats. -

diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_it.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_it.html deleted file mode 100644 index 2fc1e8a8df5b759b92f2fd7898835b20f1ec7855..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_it.html +++ /dev/null @@ -1,25 +0,0 @@ -
- Sapere come viene utilizzato Jenkins aiuta notevolmente a guidare la - direzione dello sviluppo, specialmente nel caso dei progetti open source - in cui è inerentemente difficile rintracciare gli utenti. Quando - quest'opzione è abilitata, Jenkins invierà periodicamente informazioni - sull'utilizzo di Jenkins. - -

- Specificamente, tali informazioni conterranno quanto segue: - -

    -
  • La versione di Jenkins -
  • Per il master e ogni agente, il tipo di sistema operativo e il numero di esecutori -
  • I plugin installati e le rispettive versioni -
  • Il numero di processi per ogni tipo di processo in Jenkins -
- -

- Le informazioni non conterranno nulla che possa identificare l'utente o - consentirci di contattare l'utente (ad eccezione delle informazioni rivelate - naturalmente dal protocollo HTTP, come l'indirizzo IP). I dati tabulati - relativi agli invii di queste statistiche di utilizzo saranno condivisi con - la comunità. - -

diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_ja.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_ja.html deleted file mode 100644 index 7db3dac478e17637554c87dcc048572da28d9ff7..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_ja.html +++ /dev/null @@ -1,22 +0,0 @@ -
- Jenkinsがどのように使用されているか把握することは、特にユーザーを特定できないオープンソースプロジェクトにとって、 - 開発の方向性を決定する上で非常に役に立ちます。このオプションを有効にすると、Jenkinsの利用状況を定期的に送信します。 - -

- 送信する情報は次の情報を含んでいます。 - -

    -
  • Jenkinsのバージョン -
  • マスターと各スレーブの、OSとエグゼキューターの数 -
  • インストールされているプラグインとそのバージョン -
  • ジョブのタイプ毎のジョブ数 -
- -

- 情報には、あなたを特定できる、もしくはあなたに連絡できるようなものは含みません(ただし、IPアドレスのようなHTTP関連の情報は除きます)。 - 送信された利用状況の一覧データは、コミュニティで共有されます。 - -

- - コミュニティでのこの仕組みに関するディスカッションを参考にしてください。ご意見はこちらにお願いします。 -

diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_pt_BR.html b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_pt_BR.html deleted file mode 100644 index bde77e44f1cb38d581a251799ede138f18b80d04..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected_pt_BR.html +++ /dev/null @@ -1,20 +0,0 @@ -
- Saber como Jenkins é utilizado é uma tremenda ajuda guiando-nos no desenvolvimento, especificamente para - projetos de código livre onde os usuários são inerentemente difíceis de rastrear. Ao ativar esta opção, - Jenkins periodicamente enviará informações sobre o seu uso. - -

- Especificamente, as informações enviadas serão: - -

    -
  • A versão do seu Jenkins -
  • Para cada master e cada slave, o tipo de Sistema Operacional, e número de executores -
  • Os plugins instalados e suas versões -
  • Número de jobs por tipo de job no seu Jenkins -
- -

- Essas informaçãos não contem nada o identifique ou nos permita contatá-lo - (exceto pelas informações reveladas via HTTP, tal como Endereço IP). - Dados das estatísticas de uso submetidas são compartilhados com a comunidade. -

diff --git a/core/src/main/resources/hudson/model/User/builds_zh_CN.properties b/core/src/main/resources/hudson/model/User/builds_zh_CN.properties deleted file mode 100644 index 88416f2ccaefa26f3fad0da4d66ced9a79a68e81..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/User/builds_zh_CN.properties +++ /dev/null @@ -1,22 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. -title={0}\u7684\u6784\u5EFA diff --git a/core/src/main/resources/hudson/model/User/configure_nl.properties b/core/src/main/resources/hudson/model/User/configure_nl.properties index 858b4f1840f92b51838f94461295fe8bfd7a559b..7756763a1a74f8ef0626ea7e1b29391ef2828d53 100644 --- a/core/src/main/resources/hudson/model/User/configure_nl.properties +++ b/core/src/main/resources/hudson/model/User/configure_nl.properties @@ -22,4 +22,4 @@ Full\ name=Uw naam Description=Omschrijving -Save=Bewaar +Save=Opslaan diff --git a/core/src/main/resources/hudson/model/User/configure_zh_CN.properties b/core/src/main/resources/hudson/model/User/configure_zh_CN.properties deleted file mode 100644 index 3de7eb49cee62a0b6956793790affa1905b47a48..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/User/configure_zh_CN.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -title=\u7528\u6237 ''{0}'' \u914D\u7F6E -Description=\u63cf\u8ff0 -Full\ name=\u8d26\u53f7\u540d\u79f0 -Save=\u4FDD\u5B58 \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/User/delete_zh_CN.properties b/core/src/main/resources/hudson/model/User/delete_zh_CN.properties deleted file mode 100644 index 2ea812e80a0bd5f6a5faf6a7256d6b2e58453cc9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/User/delete_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Are\ you\ sure\ about\ deleting\ the\ user\ from\ Jenkins?=\u4F60\u786E\u5B9A\u8981\u4ECEJenkins\u4E2D\u5220\u9664\u8BE5\u7528\u6237\u5417\uFF1F -Yes=\u786E\u5B9A diff --git a/core/src/main/resources/hudson/model/User/index.jelly b/core/src/main/resources/hudson/model/User/index.jelly index d4895ca5a119095265cdee1d4a5d6417197327e8..b16f2b3e63761c7db7856335bafe3e1a2b7a0043 100644 --- a/core/src/main/resources/hudson/model/User/index.jelly +++ b/core/src/main/resources/hudson/model/User/index.jelly @@ -32,7 +32,7 @@ THE SOFTWARE.
- ${%Jenkins User Id}: ${it.id} + ${%Jenkins User ID}: ${it.id}
diff --git a/core/src/main/resources/hudson/model/User/index_zh_CN.properties b/core/src/main/resources/hudson/model/User/index_zh_CN.properties deleted file mode 100644 index a4f9f03909d1ad8bec9ca9b01ec052fae5a52970..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/User/index_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Jenkins\ User\ Id=Jenkins \u7528\u6237 Id diff --git a/core/src/main/resources/hudson/model/User/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/User/sidepanel_zh_CN.properties deleted file mode 100644 index cfe5746e78345f20b9172c28544b194a5dc9b7d3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/User/sidepanel_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -People=\u7528\u6237 -Builds=\u6784\u5efa -Configure=\u8bbe\u7f6e -Delete=\u5220\u9664 -My\ Views=\u6211\u7684\u89c6\u56fe -Status=\u72b6\u6001 diff --git a/core/src/main/resources/hudson/model/View/AsynchPeople/index.jelly b/core/src/main/resources/hudson/model/View/AsynchPeople/index.jelly index 8bcfe21fc7debdf881861869da12e1f0eb483697..ffdcd6d2f351a75f4e1fe6ecc9912259dbfe8ed6 100644 --- a/core/src/main/resources/hudson/model/View/AsynchPeople/index.jelly +++ b/core/src/main/resources/hudson/model/View/AsynchPeople/index.jelly @@ -101,7 +101,7 @@ THE SOFTWARE. + diff --git a/core/src/main/resources/hudson/model/View/AsynchPeople/index_zh_CN.properties b/core/src/main/resources/hudson/model/View/AsynchPeople/index_zh_CN.properties deleted file mode 100644 index 8c79dbcf4d6b771dc8f6b0252e02242005e16c62..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/AsynchPeople/index_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Last\ Active=\u6700\u540E\u6D3B\u52A8\u65F6\u95F4 -Name=\u540D\u79F0 -On=\u5728\u7EBF -People=\u7528\u6237 -User\ Id=\u7528\u6237\u6807\u8BC6 -blurb=\u5305\u542B\u6240\u6709\u5DF2\u77E5\u201C\u7528\u6237\u201D\uFF0C\u5305\u62EC\u5F53\u524D\u5B89\u5168\u57DF\u4E2D\u7684\u767B\u5F55ID\u548C\u5728\u53D8\u66F4\u8BB0\u5F55\u7684\u63D0\u4EA4\u4FE1\u7684\u606F\u91CC\u7684\u4EBA diff --git a/core/src/main/resources/hudson/model/View/People/index_zh_CN.properties b/core/src/main/resources/hudson/model/View/People/index_zh_CN.properties deleted file mode 100644 index 129e7ddf3b14ea8bb21fec20224a202d73e52d95..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/People/index_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -People=\u7528\u6237 diff --git a/core/src/main/resources/hudson/model/View/builds_nl.properties b/core/src/main/resources/hudson/model/View/builds_nl.properties index 611ac055b02b840175b7ae17cd5e619f3a59ba2b..73187828031a87d01a4bb2bb9078e6dc5ebe4978 100644 --- a/core/src/main/resources/hudson/model/View/builds_nl.properties +++ b/core/src/main/resources/hudson/model/View/builds_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Export\ as\ plain\ XML=Exporteert als XML +Export\ as\ plain\ XML=Exporteer als XML buildHistory=Overzicht bouwpogingen diff --git a/core/src/main/resources/hudson/model/View/builds_zh_CN.properties b/core/src/main/resources/hudson/model/View/builds_zh_CN.properties deleted file mode 100644 index fca6a32716dcbb901d22595f332bc7f2eef91236..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/builds_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Export\ as\ plain\ XML=\u5BFC\u51FA\u4E3AXML -buildHistory={0} \u7684\u6784\u5efa\u5386\u53f2 diff --git a/core/src/main/resources/hudson/model/View/configure_zh_CN.properties b/core/src/main/resources/hudson/model/View/configure_zh_CN.properties deleted file mode 100644 index d9d3dd4d7dbfc4931cacb93e0109f78ce6961501..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/configure_zh_CN.properties +++ /dev/null @@ -1,8 +0,0 @@ -# This file is under the MIT License by authors - -Description=\u63CF\u8FF0 -Filter\ build\ executors=\u8FC7\u6EE4\u6784\u5EFA\u6267\u884C\u5668 -Filter\ build\ queue=\u8FC7\u6EE4\u6784\u5EFA\u961F\u5217 -Name=\u540D\u79F0 -OK=\u4FDD\u5B58 -Apply=\u5E94\u7528 diff --git a/core/src/main/resources/hudson/model/View/delete_nl.properties b/core/src/main/resources/hudson/model/View/delete_nl.properties index 5b0394212334f75650dc503c5adb33a75a188478..baa4c0b2fb1e5a78fa222169a58625e09cb5f15b 100644 --- a/core/src/main/resources/hudson/model/View/delete_nl.properties +++ b/core/src/main/resources/hudson/model/View/delete_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Are\ you\ sure\ about\ deleting\ the\ view?=Bent u zeker dat u deze overzichtstab wilt verwijderen? +Are\ you\ sure\ about\ deleting\ the\ view?=Weet u zeker dat u deze overzichtstab wilt verwijderen? Yes=Ja diff --git a/core/src/main/resources/hudson/model/View/delete_zh_CN.properties b/core/src/main/resources/hudson/model/View/delete_zh_CN.properties deleted file mode 100644 index a5babeee825431a11c0390a194108751bbed7d3e..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/delete_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Are\ you\ sure\ about\ deleting\ the\ view?=\u4F60\u786E\u5B9A\u8981\u5220\u9664\u8BE5\u89C6\u56FE\u5417\uFF1F -Yes=\u786E\u5B9A diff --git a/core/src/main/resources/hudson/model/View/newJobButtonBar_zh_CN.properties b/core/src/main/resources/hudson/model/View/newJobButtonBar_zh_CN.properties deleted file mode 100644 index bfae4d0e434e5f657159632b4dce126b77a7879d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/newJobButtonBar_zh_CN.properties +++ /dev/null @@ -1 +0,0 @@ -OK=\u786E\u5B9A \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/View/newJob_zh_CN.properties b/core/src/main/resources/hudson/model/View/newJob_zh_CN.properties deleted file mode 100644 index 9f6b147f28ab68e2175cbc58279494c04c838f40..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/newJob_zh_CN.properties +++ /dev/null @@ -1,32 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -CopyExisting=\u590D\u5236\u5DF2\u6709\u7684{0} - -JobName={0}\u540D\u79F0 -ItemName.validation.required=\u8BE5\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u8F93\u5165\u4E00\u4E2A\u5408\u6CD5\u7684\u540D\u79F0 -ItemName.label=\u8F93\u5165\u4E00\u4E2A\u4EFB\u52A1\u540D\u79F0 -ItemName.help=\u5FC5\u586B\u9879 -CopyOption.label=\u590D\u5236 -CopyOption.placeholder=\u8F93\u5165\u81EA\u52A8\u5B8C\u6210 -CopyOption.description=\u5982\u679C\u4F60\u60F3\u6839\u636E\u4E00\u4E2A\u5DF2\u7ECF\u5B58\u5728\u7684\u4EFB\u52A1\u521B\u5EFA\uFF0C\u53EF\u4EE5\u4F7F\u7528\u8FD9\u4E2A\u9009\u9879 -NewJob=\u65B0\u5EFA{0} diff --git a/core/src/main/resources/hudson/model/View/noJob_zh_CN.properties b/core/src/main/resources/hudson/model/View/noJob_zh_CN.properties deleted file mode 100644 index f4d55a81c45961734d3c59f244ceb71a56156a69..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/noJob_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -description_1=\u8BE5\u89C6\u56FE\u4E0B\u76EE\u524D\u6CA1\u6709\u76F8\u5173\u7684\u4EFB\u52A1\u3002 -description_2=\u4F60\u53EF\u4EE5\u6DFB\u52A0\u73B0\u6709\u7684\u4EFB\u52A1\u5230\u8BE5\u89C6\u56FE\u4E2D\u6216\u8005\u5728\u8BE5\u8BE5\u89C6\u56FE\u4E2D\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684\u4EFB\u52A1\u3002 diff --git a/core/src/main/resources/hudson/model/View/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/View/sidepanel_zh_CN.properties deleted file mode 100644 index 182c409c8ed8b891e5747a325cfa1480c59b01da..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/model/View/sidepanel_zh_CN.properties +++ /dev/null @@ -1,30 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2017, Sun Microsystems, Inc., suren -# -# 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. - -Build\ History=\u6784\u5EFA\u5386\u53F2 -Check\ File\ Fingerprint=\u68C0\u67E5\u6587\u4EF6\u6307\u7EB9 -Delete\ View=\u5220\u9664\u89C6\u56FE -Edit\ View=\u7F16\u8F91\u89C6\u56FE -NewJob=\u65B0\u5EFA{0} -People=\u7528\u6237 -Project\ Relationship=\u9879\u76EE\u5173\u7CFB -Manage\ Jenkins=\u7BA1\u7406Jenkins diff --git a/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseLabelIsBusy/summary.properties b/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseLabelIsBusy/summary.properties index 7f61c09183a15f9b07a6eed583013e8c51524a3f..b287883239054c572af466d2df6b7b4c22e7ecb0 100644 --- a/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseLabelIsBusy/summary.properties +++ b/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseLabelIsBusy/summary.properties @@ -21,4 +21,4 @@ # THE SOFTWARE. # note for translators: this message is referenced from st:structuredMessageFormat -description=Waiting for next available executor on {0} \ No newline at end of file +description=Waiting for next available executor on \u2018{0}\u2019 diff --git a/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseNodeIsBusy/summary.properties b/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseNodeIsBusy/summary.properties index e8e094ee2f0a86477e9c8a449d337d693a67d695..35996788e5f691a792d305d3bc20e80be9b9064c 100644 --- a/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseNodeIsBusy/summary.properties +++ b/core/src/main/resources/hudson/model/queue/CauseOfBlockage/BecauseNodeIsBusy/summary.properties @@ -21,4 +21,4 @@ # THE SOFTWARE. # note for translators: this message is referenced from st:structuredMessageFormat -description=Waiting for next available executor on {0} \ No newline at end of file +description=Waiting for next available executor on \u2018{0}\u2019 diff --git a/core/src/main/resources/hudson/node_monitors/AbstractDiskSpaceMonitor/config_zh_CN.properties b/core/src/main/resources/hudson/node_monitors/AbstractDiskSpaceMonitor/config_zh_CN.properties deleted file mode 100644 index 62039bac23c65faa40f40d0f1ef0aea8b7ceab7b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/node_monitors/AbstractDiskSpaceMonitor/config_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Free\ Space\ Threshold=\u6700\u5C0F\u53EF\u7528\u7A7A\u95F4 diff --git a/core/src/main/resources/hudson/node_monitors/Messages.properties b/core/src/main/resources/hudson/node_monitors/Messages.properties index 00b905cf66b55db99608b6c26907c3fb3f859058..658a75aeb90636379ed1196cbbd578eeb70b658d 100644 --- a/core/src/main/resources/hudson/node_monitors/Messages.properties +++ b/core/src/main/resources/hudson/node_monitors/Messages.properties @@ -26,7 +26,7 @@ DiskSpaceMonitor.MarkedOffline=Making {0} offline temporarily due to the lack of DiskSpaceMonitor.MarkedOnline=Putting {0} back online as there is enough disk space again DiskSpaceMonitor.DisplayName=Free Disk Space ResponseTimeMonitor.DisplayName=Response Time -ResponseTimeMonitor.MarkedOffline=Making {0} offline because it\u2019s not responding +ResponseTimeMonitor.MarkedOffline=Making {0} offline because it is not responding ResponseTimeMonitor.TimeOut=Timed out for last {0} attempts SwapSpaceMonitor.DisplayName=Free Swap Space TemporarySpaceMonitor.DisplayName=Free Temp Space diff --git a/core/src/main/resources/hudson/node_monitors/Messages_pt_BR.properties b/core/src/main/resources/hudson/node_monitors/Messages_pt_BR.properties index 5f7378571e2ad215c95451c71ac0796ba7234f1b..ee3fe556069ef7283c278c3650bfec249754450c 100644 --- a/core/src/main/resources/hudson/node_monitors/Messages_pt_BR.properties +++ b/core/src/main/resources/hudson/node_monitors/Messages_pt_BR.properties @@ -33,7 +33,7 @@ ResponseTimeMonitor.DisplayName=Tempo de resposta DiskSpaceMonitor.MarkedOffline=Temporariamente indispon\u00edvel por falta de espa\u00e7o em disco # Free Swap Space SwapSpaceMonitor.DisplayName=Espa\u00e7o de swap livre -# Making {0} offline temporarily because it''s not responding +# Making {0} offline temporarily because it is not responding ResponseTimeMonitor.MarkedOffline= Indispon\u00edvel temporariamente por que n\u00e3o est\u00e1 respondendo # Not yet AbstractNodeMonitorDescriptor.NoDataYet=Nada ainda diff --git a/core/src/main/resources/hudson/node_monitors/MonitorMarkedNodeOffline/message_zh_CN.properties b/core/src/main/resources/hudson/node_monitors/MonitorMarkedNodeOffline/message_zh_CN.properties deleted file mode 100644 index 89f20d137bc4e3fd08fdd10303b6de5e7bb79d9c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/node_monitors/MonitorMarkedNodeOffline/message_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Dismiss=\u5FFD\u7565 diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_tr.properties b/core/src/main/resources/hudson/scheduler/Messages_zh_CN.properties similarity index 53% rename from core/src/main/resources/hudson/model/AbstractModelObject/editDescription_tr.properties rename to core/src/main/resources/hudson/scheduler/Messages_zh_CN.properties index 9bbde41249370e2b1bfbafb933d213084b8e9a5e..56ab74da142561c2e4d8ce6d322138e26d518a7c 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_tr.properties +++ b/core/src/main/resources/hudson/scheduler/Messages_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Oguz Dag +# Copyright (c) 2018, linuxsuren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Submit=G\u00f6nder +BaseParser.StartEndReversed=\u60A8\u7684\u610F\u601D\u662F\u4ECE {0} \u5230 {1}\uFF1F +BaseParser.MustBePositive=\u95F4\u9694\u503C\u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u800C\u4E0D\u662F {0} +BaseParser.OutOfRange={0} \u662F\u4E00\u4E2A\u975E\u6CD5\u503C\u3002\u5FC5\u987B\u5728 {1} \u548C {2} \u4E4B\u95F4 +CronTab.do_you_really_mean_every_minute_when_you=\u5F53\u60A8\u8F93\u5165 "{0}" \u65F6\uFF0C\u610F\u601D\u4E3A"\u6BCF\u5206\u949F"\uFF1F\u4E5F\u8BB8\u60A8\u5E0C\u671B "{1}" \u6BCF\u5C0F\u65F6\u8F6E\u8BE2 +CronTab.short_cycles_in_the_day_of_month_field_w=\u4EE5\u6708\u4E3A\u5468\u671F\u7684\u77ED\u5FAA\u73AF\u5B57\u6BB5\u5728\u6708\u672B\u65F6\u53EF\u80FD\u4F1A\u6709\u5947\u602A\u7684\u884C\u4E3A +CronTab.spread_load_evenly_by_using_rather_than_=\u5206\u6563\u8D1F\u8F7D\u5E94\u8BE5\u7528 \u2018{0}\u2019 \u800C\u4E0D\u662F \u2018{1}\u2019 +CronTabList.InvalidInput=\u975E\u6CD5\u8F93\u5165\uFF1A"{0}": {1} diff --git a/core/src/main/resources/hudson/scm/EmptyChangeLogSet/digest_zh_CN.properties b/core/src/main/resources/hudson/scm/EmptyChangeLogSet/digest_zh_CN.properties deleted file mode 100644 index 89f54c50788e691545f5311eabcc036936a61908..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/scm/EmptyChangeLogSet/digest_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -No\ changes.=\u6CA1\u6709\u53D8\u5316\u3002 diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_zh_CN.properties b/core/src/main/resources/hudson/scm/Messages_zh_CN.properties similarity index 79% rename from core/src/main/resources/hudson/PluginManager/checkUpdates_zh_CN.properties rename to core/src/main/resources/hudson/scm/Messages_zh_CN.properties index beed5fbf9a7d169773db91451d1dcacca892b071..1068a5a268c74804e146d968ed1462d7b131e371 100644 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_zh_CN.properties +++ b/core/src/main/resources/hudson/scm/Messages_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2018, suren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Checking\ Updates...=\u68C0\u67E5\u66F4\u65B0 -Done=\u5B8C\u6210 -Go\ back\ to\ update\ center=\u8FD4\u56DE\u66F4\u65B0\u4E2D\u5FC3 -Update\ Center=\u66F4\u65B0\u4E2D\u5FC3 +NullSCM.DisplayName=\u65e0 +SCM.Permissions.Title=SCM +SCM.TagPermission.Description=\ + \u8be5\u6743\u9650\u5141\u8bb8\u7528\u6237\u5728\u4ee3\u7801\u5e93\u4e2d\u7ed9\u6307\u5b9a\u7684\u6784\u5efa\u521b\u5efa\u65b0\u7684\u6807\u7b7e \ No newline at end of file diff --git a/core/src/main/resources/hudson/scm/SCM/project-changes_zh_CN.properties b/core/src/main/resources/hudson/scm/SCM/project-changes_zh_CN.properties deleted file mode 100644 index 7262342d01662f146c9e4bc0a40ea5c274b73d09..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/scm/SCM/project-changes_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -No\ changes\ in\ any\ of\ the\ builds.=\u6CA1\u6709\u4EFB\u4F55\u53D8\u66F4\u3002 -detail=\u8BE6\u7EC6\u4FE1\u606F diff --git a/core/src/main/resources/hudson/search/Search/search-failed.jelly b/core/src/main/resources/hudson/search/Search/search-failed.jelly index df50cd1a4ff949fa02ca27e1323bb4df3c8ba678..da02086828544ce88425ef2ead0e15b6a63a3b5d 100644 --- a/core/src/main/resources/hudson/search/Search/search-failed.jelly +++ b/core/src/main/resources/hudson/search/Search/search-failed.jelly @@ -48,8 +48,8 @@ THE SOFTWARE. - - result has been truncated, see 20 more + + result has been truncated, see 100 more diff --git a/core/src/main/resources/hudson/search/UserSearchProperty/config_zh_CN.properties b/core/src/main/resources/hudson/search/UserSearchProperty/config_zh_CN.properties deleted file mode 100644 index cf758a77fc7b460ffe0ce93e0b4744e9626ef0f0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/search/UserSearchProperty/config_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Case-sensitivity=\u5927\u5C0F\u5199\u533A\u5206 -Insensitive\ search\ tool=\u641C\u7D22\u4E0D\u533A\u5206\u5927\u5C0F\u5199 diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_nl.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_nl.properties index 79184d0961169541f2a75d5362353e5514df3190..237afcde2f7225827d132822660304f2445ecd7b 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_nl.properties +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_nl.properties @@ -21,7 +21,7 @@ # THE SOFTWARE. Global\ properties=Globale eigenschappen -Home\ directory=Jenkins hoofdfolder +Home\ directory=Jenkins hoofdmap System\ Message=Systeemboodschap Default\ view=Standaard view Master/Slave\ Support=Hoofd/Slaaf-node ondersteuning @@ -33,10 +33,10 @@ slaves.description=\ name=Naam launch\ command=Lanceer commando description=Omschrijving -remote\ FS\ root=Hoofdfolder op het bestandssyteem op afstand +remote\ FS\ root=Hoofdmap op het bestandssyteem op afstand statsBlurb=Help Jenkins verbeteren door het opsturen van anonieme gebruiksstatistieken en crashrapporten naar het Jenkinsproject. usage=Gebruik JDKs=JDKs JDK\ installations=Installatie JDKs -List\ of\ JDK\ installations\ on\ this\ system=Lijst van de ge\u00EFnstalleerde JDKs op dit systeem +List\ of\ JDK\ installations\ on\ this\ system=Lijst van ge\u00EFnstalleerde JDKs op dit systeem no.such.JDK=JDK bestaat niet op dit system diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_zh_CN.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_zh_CN.properties deleted file mode 100644 index 19cb3476abceeea6da3554916983ce4d88decbeb..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/config_zh_CN.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Home\ directory=\u4e3b\u76ee\u5f55 -System\ Message=\u7cfb\u7edf\u6d88\u606f -Global\ properties=\u5168\u5c40\u5c5e\u6027 -JDKs=JDK\u5149\u5b50 -JDK\ installations=JDK\u5b9e\u4f8b -List\ of\ JDK\ installations\ on\ this\ system=\u7cfb\u7edfJDK\u5b9e\u4f8b\u5217\u8868 diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-slaveAgentPort.html b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-slaveAgentPort.html index 8681f55f8f4553c43f8502a5f4b94a6bc3520fdc..6c1a2cdb380b43178a16dbc27d536c7cf1503e68 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-slaveAgentPort.html +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-slaveAgentPort.html @@ -1,7 +1,10 @@
+ If you are not using JNLP agents, it's recommended that you disable this + entirely, which is the default installation behavior. Jenkins uses a TCP port to communicate with agents launched via JNLP. - Normally this port is chosen randomly to avoid collisions, but this would - make securing the system difficult. If you are not using JNLP agents, - it's recommend to disable this TCP port. Alternatively, you can specify - the fixed port number so that you can configure your firewall accordingly. + If you're going to use JNLP agents, you can allow the system to randomly + select a port at launch (this avoids interfering with other programs, + including other Jenkins instances). + As it's hard for firewalls to secure a random port, you can instead + specify a fixed port number and configure your firewall accordingly.
diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy index 9d1b0917e9fceb4a42bc22160ad0ddc23df046c2..e7fe82bba4f836cdddd522290b898b7dbab2662f 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy @@ -32,8 +32,8 @@ l.layout(norefresh:true, permission:app.ADMINISTER, title:my.displayName, csscla f.entry(title:_("Access Control")) { table(style:"width:100%") { - f.descriptorRadioList(title:_("Security Realm"),varName:"realm", instance:app.securityRealm, descriptors:SecurityRealm.all()) - f.descriptorRadioList(title:_("Authorization"), varName:"authorization", instance:app.authorizationStrategy, descriptors:AuthorizationStrategy.all()) + f.descriptorRadioList(title:_("Security Realm"),varName:"realm", instance:app.securityRealm, descriptors:h.filterDescriptors(app, SecurityRealm.all())) + f.descriptorRadioList(title:_("Authorization"), varName:"authorization", instance:app.authorizationStrategy, descriptors:h.filterDescriptors(app, AuthorizationStrategy.all())) } } } diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_nl.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_nl.properties index 38a67ec667c9d6bc1d4614619da07e84f8b3689f..de9c1bae48f32979f61c36a1f691b541e8360321 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_nl.properties +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_nl.properties @@ -25,5 +25,5 @@ Enable\ security=Activeer beveiliging Markup\ Formatter= Access\ Control=Toegangscontrole Security\ Realm=Beveiligingszone -Authorization=Toelatingen -Save=Bewaar +Authorization=Authorisatie +Save=Opslaan diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_zh_CN.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_zh_CN.properties deleted file mode 100644 index d8ee8ee3c0f6f30d236a618e60da537c1151bcc4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_zh_CN.properties +++ /dev/null @@ -1,33 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2012, Seiji Sogabe -# -# 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. - -LOADING=\u52A0\u8F7D\u4E2D -Enable\ security=\u542F\u7528\u5B89\u5168 -Markup\ Formatter= - -Agents=\u4EE3\u7406 -Agent\ protocols=\u4EE3\u7406\u534F\u8BAE - -Access\ Control=\u8BBF\u95EE\u63A7\u5236 -Security\ Realm=\u5B89\u5168\u57DF -Authorization=\u6388\u6743\u7B56\u7565 -Save=\u4FDD\u5B58 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/Details/config_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/Details/config_zh_CN.properties deleted file mode 100644 index 02680d9dcd9e2653be07884adca900374b9b1215..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/Details/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Confirm\ Password=\u786e\u8ba4\u5bc6\u7801 -Password=\u5bc6\u7801 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly index a4e2f822f2e7807c37bc351832968ece7be4f329..1b096dd34cf4dcd44e831b14ee4a4d195b8beb06 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly @@ -60,7 +60,7 @@ THE SOFTWARE.
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_nl.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_nl.properties index ec635b90794536371dab1c3b95fbb46ba10f8fc7..d0e41c9d2378008b92fcc3dfdd5c25a13987b831 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_nl.properties +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_nl.properties @@ -1,7 +1,7 @@ # This file is under the MIT License by authors Confirm\ password=Bevestig wachtwoord -E-mail\ address=Emailadres +E-mail\ address=E-mailadres Full\ name=Naam Password=Wachtwoord Username=Gebruikersnaam diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_zh_CN.properties deleted file mode 100644 index f4a55dc3c971cb3ecb44c79dfeaca99a7517fea3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm_zh_CN.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Confirm\ password=\u786e\u8ba4\u5bc6\u7801 -E-mail\ address=\u7535\u5b50\u90ae\u4ef6\u5730\u5740 -Full\ name=\u5168\u540d -Password=\u5bc6\u7801 -Username=\u7528\u6237\u540d diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser_zh_CN.properties deleted file mode 100644 index dc51223a10d814400ae5ea2d325ee286b62efbf0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Create\ User=\u65b0\u5efa\u7528\u6237 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_nl.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_nl.properties index b0d875026f81b64803570bc3839505e443273ad7..b2202d9d9ea9fa3119ae63b867867eb77e8b9210 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_nl.properties +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_nl.properties @@ -20,4 +20,4 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Allow\ users\ to\ sign\ up=Sta gebruikers toe in te loggen +Allow\ users\ to\ sign\ up=Sta gebruikers toe zichzelf te registreren diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_zh_CN.properties deleted file mode 100644 index 828426c2437a71b73284a8e7ea6325d03cea32e3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Allow\ users\ to\ sign\ up=\u5141\u8bb8\u7528\u6237\u6ce8\u518c diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_zh_CN.properties deleted file mode 100644 index 5014b275c1b4010ad045b7be1a0ef676eda05ce6..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -Create\ First\ Admin\ User=\u5EFA\u7ACB\u7B2C\u4E00\u4E2A\u7BA1\u7406\u5458 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly index 9b7fe9d0fae2be471385ab3aecdaddf90f254aa0..a7cad84726f1fda839af513793310c15b4aff4e3 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly @@ -33,7 +33,7 @@ THE SOFTWARE.
- ${%User Id}${%User ID} ${%Name} ${%Last Commit Activity} ${%On}${%Enter text as shown}:
- [captcha] + [${%captcha}]
+ diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index_zh_CN.properties deleted file mode 100644 index 52b283a774d2b6b0bdfbfde56b35f2ac5c98c681..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Name=\u540d\u79f0 -User\ Id=\u7528\u6237ID -Users=\u7528\u6237\u5217\u8868 -blurb=\u8FD9\u4E9B\u7528\u6237\u80FD\u591F\u767B\u5F55\u5230Jenkins\u3002\u8FD9\u662F\u5217\u8868\u7684\u5B50\u96C6\uFF0C\u4E5F\u5305\u62EC\u90A3\u4E9B\u53EA\u662F\u63D0\u4EA4\u4E86\u4EE3\u7801\u5230\u67D0\u4E9B\u9879\u76EE\u4F46\u662F\u4ECE\u672A\u767B\u5F55Jenkins\u800C\u81EA\u52A8\u521B\u5EFA\u7684\u7528\u6237\u3002 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/loginLink_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/loginLink_zh_CN.properties deleted file mode 100644 index 7b062cc601878206dd5aa5a2951cdb2315d53a76..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/loginLink_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -sign\ up=\u6ce8\u518c diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel_zh_CN.properties deleted file mode 100644 index 74be461a52f8170eac5771dbb075b3841c9d910b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Back\ to\ Dashboard=\u8FD4\u56DE\u9762\u677F -Create\ User=\u65B0\u5EFA\u7528\u6237 -Manage\ Jenkins=\u7cfb\u7edf\u7ba1\u7406 diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly index f2477da1df27b8d967884626afd7833e2bf731ce..f588306700bee5fbaf2bbe15672e5ad773f723f8 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly @@ -26,6 +26,275 @@ THE SOFTWARE. User self sign up page. --> - - + + + + + + + + + + + ${h.initPageVariables(context)} + + + + + + + ${%Create an account! [Jenkins]} + + + + + + + +
+ +
+ +
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity_zh_CN.properties deleted file mode 100644 index 907d5faa39196b6e2284e9854daddbec4e721d86..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Sign\ up=\u6ce8\u518c diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup_zh_CN.properties deleted file mode 100644 index 907d5faa39196b6e2284e9854daddbec4e721d86..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Sign\ up=\u6ce8\u518c diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_zh_CN.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_zh_CN.properties deleted file mode 100644 index 5aae56d59b48916c87e3a62b7ff17c8114f518f8..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -Success=\u64CD\u4F5C\u6210\u529F -description=\u4f60\u73b0\u5728\u5df2\u7ecf\u767b\u5f55.\u8fd4\u56de\u9996\u9875. diff --git a/core/src/main/resources/hudson/security/LegacySecurityRealm/config_nl.properties b/core/src/main/resources/hudson/security/LegacySecurityRealm/config_nl.properties index 7ddc52fb73675c39b1ab6d6c00e901e33e452b46..29b1f6632af930ef21fdb7ecd99ead3ce35707b8 100644 --- a/core/src/main/resources/hudson/security/LegacySecurityRealm/config_nl.properties +++ b/core/src/main/resources/hudson/security/LegacySecurityRealm/config_nl.properties @@ -1,4 +1,4 @@ # This file is under the MIT License by authors Unprotected\ URLs=Onbeschermde URLs -blurb=Deze URLs (en URLs met deze prefix plus een /) vereisen geen authentificatie. Configureer uw container indien mogelijk om deze requests direct naar Jenkins te sturen, zonder login. +blurb=Deze URLs (en URLs met deze prefix plus een /) vereisen geen authenticatie. Indien mogelijk, configureer uw container om deze requests direct naar Jenkins te sturen zonder te hoeven in te loggen. diff --git a/core/src/main/resources/hudson/security/LegacySecurityRealm/config_zh_CN.properties b/core/src/main/resources/hudson/security/LegacySecurityRealm/config_zh_CN.properties deleted file mode 100644 index 6a7d746893b7f46967563eebe195b843b6d846ba..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/LegacySecurityRealm/config_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Unprotected\ URLs=\u4E0D\u53D7\u4FDD\u62A4\u7684\u94FE\u63A5 diff --git a/core/src/main/resources/hudson/security/Messages.properties b/core/src/main/resources/hudson/security/Messages.properties index a1de6958a118b6ee1ab00005a0593fe2616163e8..06df37e2319ab81e1041b0dfc661fd31b8e9f35f 100644 --- a/core/src/main/resources/hudson/security/Messages.properties +++ b/core/src/main/resources/hudson/security/Messages.properties @@ -37,6 +37,8 @@ HudsonPrivateSecurityRealm.CreateAccount.TextNotMatchWordInImage=Text didn''t ma HudsonPrivateSecurityRealm.CreateAccount.PasswordNotMatch=Password didn''t match HudsonPrivateSecurityRealm.CreateAccount.PasswordRequired=Password is required HudsonPrivateSecurityRealm.CreateAccount.UserNameRequired=User name is required +HudsonPrivateSecurityRealm.CreateAccount.UserNameInvalidCharacters=User name must only contain alphanumeric characters, underscore and dash +HudsonPrivateSecurityRealm.CreateAccount.UserNameInvalidCharactersCustom=User name must match the following expression: {0} HudsonPrivateSecurityRealm.CreateAccount.InvalidEmailAddress=Invalid e-mail address HudsonPrivateSecurityRealm.CreateAccount.UserNameAlreadyTaken=User name is already taken diff --git a/core/src/main/resources/hudson/security/Messages_nl.properties b/core/src/main/resources/hudson/security/Messages_nl.properties index 14a9af3ec4642d095d93c278424e83561c7be41e..1070161720cddc55f497a0a104b08e6db04fb601 100644 --- a/core/src/main/resources/hudson/security/Messages_nl.properties +++ b/core/src/main/resources/hudson/security/Messages_nl.properties @@ -22,10 +22,10 @@ LegacyAuthorizationStrategy.DisplayName=Legacy-mode -HudsonPrivateSecurityRealm.Details.DisplayName=Paswoord +HudsonPrivateSecurityRealm.Details.DisplayName=Wachtwoord HudsonPrivateSecurityRealm.Details.PasswordError=\ - Het bevestigingspaswoord is niet gelijk aan het opgegeven paswoord. \ - Gelieve erop toe te zien dat u tweemaal hetzelfde paswoord ingeeft. + Het bevestigingswachtwoord is niet gelijk aan het opgegeven wachtwoord. \ + Gelieve erop toe te zien dat u tweemaal hetzelfde wachtwoord ingeeft. UserDetailsServiceProxy.UnableToQuery=Kon de gebruikersinformatie niet opvragen: {0} diff --git a/core/src/main/resources/hudson/security/SecurityRealm/loginLink_es.properties b/core/src/main/resources/hudson/security/SecurityRealm/loginLink_es.properties index caf5fd839329b980a2c3c85e3f4de5cb4669f801..be0e40f1c076f52f657bec8eed5ff9ab5bfe6cde 100644 --- a/core/src/main/resources/hudson/security/SecurityRealm/loginLink_es.properties +++ b/core/src/main/resources/hudson/security/SecurityRealm/loginLink_es.properties @@ -20,4 +20,4 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -login=Iniciar Sesion +login=Iniciar sesi\u00F3n diff --git a/core/src/main/resources/hudson/security/SecurityRealm/loginLink_zh_CN.properties b/core/src/main/resources/hudson/security/SecurityRealm/loginLink_zh_CN.properties deleted file mode 100644 index b4a696d3fffd75252fa7a5ba784fac782ac801f9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/SecurityRealm/loginLink_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -login=\u767b\u5f55 diff --git a/core/src/main/resources/hudson/security/csrf/DefaultCrumbIssuer/config_zh_CN.properties b/core/src/main/resources/hudson/security/csrf/DefaultCrumbIssuer/config_zh_CN.properties deleted file mode 100644 index d7b627e8d1086fd69c6e30f4bfd4318004c0a93a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/csrf/DefaultCrumbIssuer/config_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Enable\ proxy\ compatibility=\u6FC0\u6D3B\u4EE3\u7406\u517C\u5BB9 diff --git a/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_nl.properties b/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_nl.properties index 51d499a2266af677bc1537d396f7bfbd8509aaad..929e8bed0cad42c38c2807d5dc977ea7c079ee39 100644 --- a/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_nl.properties +++ b/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_nl.properties @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Prevent\ Cross\ Site\ Request\ Forgery\ exploits=Ga Cross-site Request Forgery exploits tegen +Prevent\ Cross\ Site\ Request\ Forgery\ exploits=Voorkom Cross-site Request Forgery exploits Crumb\ Algorithm=Kruimelalgoritme Crumbs=Kruimels diff --git a/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_zh_CN.properties b/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_zh_CN.properties deleted file mode 100644 index 1cb9b1d202584c7b6931330fcab735cd182db913..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/security/csrf/GlobalCrumbIssuerConfiguration/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Prevent\ Cross\ Site\ Request\ Forgery\ exploits=\u9632\u6b62\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 - diff --git a/core/src/main/resources/hudson/security/csrf/Messages_zh_CN.properties b/core/src/main/resources/hudson/security/csrf/Messages_zh_CN.properties new file mode 100644 index 0000000000000000000000000000000000000000..730423c9ddbc1a5c99189098c26488d712009e48 --- /dev/null +++ b/core/src/main/resources/hudson/security/csrf/Messages_zh_CN.properties @@ -0,0 +1,23 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +DefaultCrumbIssuer.DisplayName=\u9ED8\u8BA4\u788E\u7247\u751F\u6210\u5668 diff --git a/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries.jelly b/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries.jelly index c06a9e1d3522a8a1c46e35b92566c13d88f9ed0c..d9e46f50b6d6b766b83983d4ecce263fe267be4e 100644 --- a/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries.jelly +++ b/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries.jelly @@ -33,7 +33,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries_zh_CN.properties b/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries_zh_CN.properties deleted file mode 100644 index ec8eb777e305ea6ba0a61b70d1eb4720a9a45d65..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/DumbSlave/configure-entries_zh_CN.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Description=\u63CF\u8FF0 -\#\ of\ executors=\u5E76\u53D1\u6784\u5EFA\u6570 -Labels=\u6807\u7B7E -Remote\ root\ directory=\u8FDC\u7A0B\u5DE5\u4F5C\u76EE\u5F55 -Launch\ method=\u542F\u52A8\u65B9\u5F0F -Availability=\u53EF\u7528\u6027 -Node\ Properties=\u8282\u70B9\u5C5E\u6027 diff --git a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config.jelly b/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config.jelly index b2becef5dcbae82e4b2d81bc94e03c8c0480af7d..a97222848442c0c9bce8d6a7510a9c0905f609aa 100644 --- a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config.jelly +++ b/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config.jelly @@ -25,7 +25,7 @@ THE SOFTWARE. - +
- ${%User Id}${%User ID} ${%Name}
diff --git a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_nl.properties b/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_nl.properties index 47de859a2bf1bee4901c661a05209ecf73e2cf42..6c03a4f70ff6d9c88626c6be0665241c28db42c2 100644 --- a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_nl.properties +++ b/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_nl.properties @@ -20,6 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -List\ of\ variables=Lijst van sleutel-waarde-paren -Name=naam -Value=waarde +List\ of\ variables=Lijst van variabelen +Name=Naam +Value=Waarde diff --git a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_zh_CN.properties b/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_zh_CN.properties deleted file mode 100644 index e70d102c3c05bc4a0c620292d2b83c3f6b34bb8d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/EnvironmentVariablesNodeProperty/config_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Alan Harder, Eric Lefevre-Ardant -# -# 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. - -List\ of\ variables=\u952E\u503C\u5BF9\u5217\u8868 -Name=\u952E -Value=\u503c diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html new file mode 100644 index 0000000000000000000000000000000000000000..7ea86c5025bd3d012b8c8dc64c6da0ec62cc68f0 --- /dev/null +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html @@ -0,0 +1,5 @@ +
+ 如果代理的Java虚拟机启动时需要附加参数,例如 "-Xmx256m",可以在这里设置。 + 这里 + 列出了所有可用的参数。 +
\ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties b/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties deleted file mode 100644 index 1f1a4b8bdc87434d5f57f4a5c2002c7acd2adae3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Connected\ via\ JNLP\ agent.=\u5DF2\u901A\u8FC7JNLP Agent\u8FDE\u63A5 diff --git a/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties b/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties index c8ef214cd4c955297a9aed22c5cea0b2ee0f1fa1..3c136db31f7df2fab9cfebd8df33dcc2e7a9dd49 100644 --- a/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties +++ b/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties @@ -28,7 +28,7 @@ JNLPLauncher.displayName=\u901A\u8FC7Java Web\u542F\u52A8\u4EE3\u7406 ComputerLauncher.unexpectedError=\u542F\u52A8\u4EE3\u7406\u662F\u51FA\u73B0\u4F4D\u7F6E\u9519\u8BEF\u3002\u8FD9\u53EF\u80FD\u662FJenkins\u7684\u95EE\u9898\u3002 ComputerLauncher.abortedLaunch=\u4EE3\u7406\u542F\u52A8\u8FC7\u7A0B\u88AB\u4E2D\u6B62\u3002 CommandLauncher.NoLaunchCommand=\u6CA1\u6709\u6307\u5B9A\u542F\u52A8\u547D\u4EE4 -DumbSlave.displayName=\u56FA\u5B9A\u4EE3\u7406 +DumbSlave.displayName=\u56fa\u5b9a\u8282\u70b9 NodeProvisioner.EmptyString= OfflineCause.LaunchFailed=\u7531\u4E8E\u4EE3\u7406\u542F\u52A8\u8FC7\u7A0B\u5931\u8D25\u5BFC\u81F4\u7684\u79BB\u7EBF\u3002 OfflineCause.connection_was_broken_=\u8FDE\u63A5\u65AD\u5F00\uFF1A{0} diff --git a/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause_zh_CN.properties b/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause_zh_CN.properties deleted file mode 100644 index 4cb665671b5b498c1dd74a207e83fddb49dfc614..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -Connection\ was\ broken=\u8FDE\u63A5\u4E2D\u65AD diff --git a/core/src/main/resources/hudson/slaves/OfflineCause/LaunchFailed/cause_zh_CN.properties b/core/src/main/resources/hudson/slaves/OfflineCause/LaunchFailed/cause_zh_CN.properties deleted file mode 100644 index 2daf3239d0217c53eca968feae508148ce6c3690..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/OfflineCause/LaunchFailed/cause_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -See\ log\ for\ more\ details=\u4ECE\u65E5\u5FD7\u91CC\u53EF\u4EE5\u770B\u5230\u66F4\u8BE6\u7EC6\u7684\u5185\u5BB9 diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect_zh_CN.properties b/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect_zh_CN.properties deleted file mode 100644 index 815e1b088d8b8d074c62d0e5b27f9891108d20f8..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -blurb=\u4F60\u53EF\u4EE5\u6DFB\u52A0\u8BA9\u8BE5\u8282\u70B9\u4E0B\u7EBF\u7684\u7406\u7531\uFF0C\u8FD9\u6837\u5176\u4ED6\u4EBA\u5C31\u80FD\u770B\u5230\u539F\u56E0\u4E86\u3002 -Yes=\u662F -disconnect=\u65AD\u5F00\u8FDE\u63A5 -Are\ you\ sure\ about\ disconnecting?=\u4F60\u786E\u5B9A\u8981\u65AD\u5F00\u8FDE\u63A5\uFF1F diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel2_zh_CN.properties b/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel2_zh_CN.properties deleted file mode 100644 index e0be0ef6d809bf90312f6974d0a1d0cb8f246143..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel2_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Disconnect=\u65AD\u5F00\u8FDE\u63A5 -Log=\u65E5\u5FD7 -System\ Information=\u7CFB\u7EDF\u4FE1\u606F diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel_nl.properties b/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel_nl.properties index a54d528dc3ce93b6b9c0082b7df7c077588e1c7b..3c9d6555ad6031cb3c472af4cfb83833305be52e 100644 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel_nl.properties +++ b/core/src/main/resources/hudson/slaves/SlaveComputer/sidepanel_nl.properties @@ -23,6 +23,6 @@ Back\ to\ List=Terug naar de lijst Status=Status Build\ History=Overzicht bouwpogingen -Log=Log +Log=Logboek System\ Information=Systeeminformatie -Disconnect=Loskoppelen +Disconnect=Verbinding verbreken diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_nl.properties b/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_nl.properties index ab617e0494edb85987efa87d06d5938592be581e..f9865fcd0a64ed9332964dc1e3b755c8e94de3d2 100644 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_nl.properties +++ b/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Environment\ Variables=Omgevings variabelen -System\ Properties=Systeem eigenschappen +Environment\ Variables=Omgevingsvariabelen +System\ Properties=Systeemeigenschappen diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_zh_CN.properties b/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_zh_CN.properties deleted file mode 100644 index e37e96e1e661f9902eb16a380cbaecba8fd0fde4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017, suren -# -# 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. - -System\ Information=\u7CFB\u7EDF\u4FE1\u606F -System\ Properties=\u7CFB\u7EDF\u5C5E\u6027 -Environment\ Variables=\u73AF\u5883\u53D8\u91CF -Thread\ Dump=\u7EBF\u7A0B\u6253\u5370 diff --git a/core/src/main/resources/hudson/tasks/ArtifactArchiver/config_zh_CN.properties b/core/src/main/resources/hudson/tasks/ArtifactArchiver/config_zh_CN.properties deleted file mode 100644 index 93a85cee7bce2a3a2a40ac28738483f08a9a3c98..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tasks/ArtifactArchiver/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Excludes=\u4e0d\u5305\u542b -Files\ to\ archive=\u7528\u4e8e\u5b58\u6863\u7684\u6587\u4ef6 diff --git a/core/src/main/resources/hudson/tasks/BatchFile/config_zh_CN.properties b/core/src/main/resources/hudson/tasks/BatchFile/config_zh_CN.properties deleted file mode 100644 index 0ae2866a5ed58d35d5777ce41a39c5b8fa09630b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tasks/BatchFile/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Command=\u547D\u4EE4 -description=\u53C2\u9605 \u53EF\u7528\u73AF\u5883\u53D8\u91CF\u5217\u8868 diff --git a/core/src/main/resources/hudson/tasks/BuildTrigger/config_zh_CN.properties b/core/src/main/resources/hudson/tasks/BuildTrigger/config_zh_CN.properties deleted file mode 100644 index eb304144f5afde813e77c88bbc54ed47ced93a37..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tasks/BuildTrigger/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Projects\ to\ build=\u8981\u6784\u5EFA\u7684\u9879\u76EE -Trigger\ even\ if\ the\ build\ is\ unstable=\u5728\u6784\u5EFA\u4E0D\u7A33\u5B9A\u65F6\u4F9D\u7136\u89E6\u53D1 diff --git a/core/src/main/resources/hudson/tasks/Fingerprinter/FingerprintAction/index_zh_CN.properties b/core/src/main/resources/hudson/tasks/Fingerprinter/FingerprintAction/index_zh_CN.properties deleted file mode 100644 index 26183207e08f0b92cd0bae71e915ca1dd198baa3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tasks/Fingerprinter/FingerprintAction/index_zh_CN.properties +++ /dev/null @@ -1,9 +0,0 @@ -# This file is under the MIT License by authors - -Age=\u6784\u5EFA\u5386\u53F2 -File=\u6587\u4EF6 -Original\ owner=\u539F\u4E3B -Recorded\ Fingerprints=\u6587\u4EF6\u6307\u7EB9\u6863\u6848 -more\ details=\u8BE6\u7EC6\u4FE1\u606F -outside\ Jenkins=\u975Ejenkins -this\ build=\u672C\u6B21\u6784\u5EFA diff --git a/core/src/main/resources/hudson/tasks/LogRotator/config_zh_CN.properties b/core/src/main/resources/hudson/tasks/LogRotator/config_zh_CN.properties deleted file mode 100644 index 68ed7733b6e4f4b3e6d2b10faae4d0ebb854d7fc..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tasks/LogRotator/config_zh_CN.properties +++ /dev/null @@ -1,30 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Days\ to\ keep\ artifacts=\u53D1\u5E03\u5305\u4FDD\u7559\u5929\u6570 -Days\ to\ keep\ builds=\u4FDD\u6301\u6784\u5EFA\u7684\u5929\u6570 -Max\ #\ of\ builds\ to\ keep=\u4FDD\u6301\u6784\u5EFA\u7684\u6700\u5927\u4E2A\u6570 -Max\ #\ of\ builds\ to\ keep\ with\ artifacts=\u53D1\u5E03\u5305\u6700\u5927\u4FDD\u7559#\u4E2A\u6784\u5EFA -if\ not\ empty,\ artifacts\ from\ builds\ older\ than\ this\ number\ of\ days\ will\ be\ deleted,\ but\ the\ logs,\ history,\ reports,\ etc\ for\ the\ build\ will\ be\ kept=\u5982\u679C\u975E\u7A7A\uFF0C\u6BD4\u6B64\u65E9\u7684\u53D1\u5E03\u5305\u5C06\u88AB\u5220\u9664\uFF0C\u4F46\u6784\u5EFA\u7684\u65E5\u5FD7\u3001\u64CD\u4F5C\u5386\u53F2\u3001\u62A5\u544A\u7B49\u5C06\u88AB\u4FDD\u7559 -if\ not\ empty,\ build\ records\ are\ only\ kept\ up\ to\ this\ number\ of\ days=\u5982\u679C\u975E\u7A7A\uFF0C\u6784\u5EFA\u8BB0\u5F55\u5C06\u4FDD\u5B58\u6B64\u5929\u6570 -if\ not\ empty,\ only\ up\ to\ this\ number\ of\ build\ records\ are\ kept=\u5982\u679C\u975E\u7A7A\uFF0C\u6700\u591A\u6B64\u6570\u76EE\u7684\u6784\u5EFA\u8BB0\u5F55\u5C06\u88AB\u4FDD\u5B58 -if\ not\ empty,\ only\ up\ to\ this\ number\ of\ builds\ have\ their\ artifacts\ retained=\u5982\u679C\u975E\u7A7A\uFF0C\u6700\u591A\u6B64\u6570\u76EE\u5927\u6784\u5EFA\u5C06\u4FDD\u7559\u4ED6\u4EEC\u7684\u53D1\u5E03\u5305 diff --git a/core/src/main/resources/hudson/tasks/Messages.properties b/core/src/main/resources/hudson/tasks/Messages.properties index fa673df13c5ce42cfda48a76f6f1b83e3446543d..6db0726a91818f1ca2532f67fdb7185c7e1b88f7 100644 --- a/core/src/main/resources/hudson/tasks/Messages.properties +++ b/core/src/main/resources/hudson/tasks/Messages.properties @@ -31,7 +31,6 @@ Ant.ProjectConfigNeeded= Maybe you need to configure the job to choose one of yo ArtifactArchiver.ARCHIVING_ARTIFACTS=Archiving artifacts ArtifactArchiver.DisplayName=Archive the artifacts ArtifactArchiver.SkipBecauseOnlyIfSuccessful=Skipped archiving because build is not successful -ArtifactArchiver.FailedToArchive=Failed to archive artifacts: {0} ArtifactArchiver.NoIncludes=\ No artifacts are configured for archiving.\n\ You probably forgot to set the file pattern, so please go back to the configuration and specify it.\n\ diff --git a/core/src/main/resources/hudson/tasks/Messages_bg.properties b/core/src/main/resources/hudson/tasks/Messages_bg.properties index 2cd51a9c5f26b09c910eef8c4ad91703708cba9f..fb0c09d7cb34a6c61350b48a2460dcffa9c96012 100644 --- a/core/src/main/resources/hudson/tasks/Messages_bg.properties +++ b/core/src/main/resources/hudson/tasks/Messages_bg.properties @@ -41,8 +41,6 @@ ArtifactArchiver.DisplayName=\ \u0410\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0438\u0442\u0435 ArtifactArchiver.SkipBecauseOnlyIfSuccessful=\ \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e, \u0437\u0430\u0449\u043e\u0442\u043e \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e. -ArtifactArchiver.FailedToArchive=\ - \u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0438: {0} ArtifactArchiver.NoIncludes=\ \u041d\u0435 \u0441\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0438 \u0437\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u043d\u0435.\n\ \u041f\u0440\u043e\u0431\u0432\u0430\u0439\u0442\u0435 \u0434\u0430 \u0443\u043a\u0430\u0436\u0435\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u0430 \u0438\u043c\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435.\n\ diff --git a/core/src/main/resources/hudson/tasks/Messages_da.properties b/core/src/main/resources/hudson/tasks/Messages_da.properties index 7e803576009e9e64bf444e656bb6f899cb271462..a0b4e57eb2f3061f46687075614ceb38ecb6749b 100644 --- a/core/src/main/resources/hudson/tasks/Messages_da.properties +++ b/core/src/main/resources/hudson/tasks/Messages_da.properties @@ -57,7 +57,6 @@ JavadocArchiver.NoMatchFound=Ingen Javadoc fundet i {0}: {1} Maven.NoExecutable=Kunne ikke finde en eksekverbar i {0} BuildTrigger.NoSuchProject=Intet s\u00e5kaldt projekt ''{0}''. Mente du ''{1}''? Fingerprinter.Action.DisplayName=Se Filfingeraftryk -ArtifactArchiver.FailedToArchive=Fejlede under arkivering af artifakter: {0} Maven.NotADirectory={0} er ikke et direktorie BuildTrigger.Disabled={0} er sl\u00e5et fra, starter ikke Fingerprinter.Recording=Opsamler filfingeraftryk diff --git a/core/src/main/resources/hudson/tasks/Messages_de.properties b/core/src/main/resources/hudson/tasks/Messages_de.properties index 9c2c5d095b2ce97366ec04cbd893614c604a5ce2..0b6522d91a30a8a4d6fe1c259e385a8ad159332e 100644 --- a/core/src/main/resources/hudson/tasks/Messages_de.properties +++ b/core/src/main/resources/hudson/tasks/Messages_de.properties @@ -30,7 +30,6 @@ Ant.ProjectConfigNeeded=Eventuell m\u00FCssen Sie f\u00FCr das Projekt noch eine ArtifactArchiver.ARCHIVING_ARTIFACTS=Archiviere Artefakte ArtifactArchiver.DisplayName=Artefakte archivieren -ArtifactArchiver.FailedToArchive=Artefakte konnten nicht archiviert werden: {0} ArtifactArchiver.NoIncludes=Es sind keine Artefakte zur Archivierung konfiguriert.\n\u00DCberpr\u00FCfen Sie, ob in den Einstellungen ein Dateisuchmuster angegeben ist.\nWenn Sie alle Dateien archivieren m\u00F6chten, geben Sie ** an. ArtifactArchiver.NoMatchFound=Keine Artefakte gefunden, die mit dem Dateisuchmuster \u201E{0}\u201C \u00FCbereinstimmen. Ein Konfigurationsfehler? ArtifactArchiver.SkipBecauseOnlyIfSuccessful=Archivierung wird \u00FCbersprungen, da der Build nicht erfolgreich ist. diff --git a/core/src/main/resources/hudson/tasks/Messages_es.properties b/core/src/main/resources/hudson/tasks/Messages_es.properties index 1338b1660b60a71b37b4c0a2fedda9b76993e5e0..b5870ec434f1d0dd4fec20228bf67c4b7d0d21e0 100644 --- a/core/src/main/resources/hudson/tasks/Messages_es.properties +++ b/core/src/main/resources/hudson/tasks/Messages_es.properties @@ -30,7 +30,6 @@ Ant.ProjectConfigNeeded= Es posible que tengas que configurar la tarea para que ArtifactArchiver.ARCHIVING_ARTIFACTS=Guardando archivos ArtifactArchiver.DisplayName=Guardar los archivos generados -ArtifactArchiver.FailedToArchive=Error al guardar los archivos generados: {0} ArtifactArchiver.NoIncludes=No hay archivos configurados para guardar.\nEs probable que olvidaras configurar el patr\u00f3n.\nSi lo que quieres es guardar todos los ficheros del espacio de trabajo, utiliza "**" ArtifactArchiver.NoMatchFound=No se encontraron archivos que cumplan el patr\u00f3n "{0}". Comprueba la configuraci\u00f3n diff --git a/core/src/main/resources/hudson/tasks/Messages_fr.properties b/core/src/main/resources/hudson/tasks/Messages_fr.properties index beb41a530151dcfe404293303bde9b8d1a938bc9..c06fb41131398edf4515ef6ccee7cc6885664425 100644 --- a/core/src/main/resources/hudson/tasks/Messages_fr.properties +++ b/core/src/main/resources/hudson/tasks/Messages_fr.properties @@ -29,7 +29,6 @@ Ant.NotAntDirectory={0} ne semble pas \u00eatre un r\u00e9pertoire Ant Ant.ProjectConfigNeeded=Avez-vous configur\u00e9 le job de fa\u00e7on \u00e0 choisir une de vos installations de Ant? ArtifactArchiver.DisplayName=Archiver des artefacts -ArtifactArchiver.FailedToArchive=Echec lors de l''archivage des artefacts: {0} ArtifactArchiver.NoIncludes=\ Aucun artefact n''est configur\u00e9 pour l''archivage.\n\ Vous avez probablement oubli\u00e9 de positionner le pattern pour les noms des fichiers; merci de retourner \u00e0 la configuration et de le sp\u00e9cifier.\n\ diff --git a/core/src/main/resources/hudson/tasks/Messages_it.properties b/core/src/main/resources/hudson/tasks/Messages_it.properties index df27728217c5d81eef8e910d03123942a9aebdfd..586c301611d09c8f1f8712a03cb1715486712b67 100644 --- a/core/src/main/resources/hudson/tasks/Messages_it.properties +++ b/core/src/main/resources/hudson/tasks/Messages_it.properties @@ -35,7 +35,6 @@ Ant.ExecFailed=esecuzione del comando non riuscita. BuildTrigger.Triggering=Attivazione di una nuova compilazione di {0} InstallFromApache=Installa da Apache Ant.DisplayName=Invoca Ant -ArtifactArchiver.FailedToArchive=Archiviazione degli artefatti non riuscita: {0} CommandInterpreter.UnableToDelete=Impossibile eliminare il file script {0} BatchFile.DisplayName=Esegui comando batch Windows Fingerprinter.Recording=Registrazione impronte in corso diff --git a/core/src/main/resources/hudson/tasks/Messages_ja.properties b/core/src/main/resources/hudson/tasks/Messages_ja.properties index 8e98e612b65d9645ccb74a09ecfa50acf32db3ec..8f44fddc91b4a054095abae21118f51de138a2eb 100644 --- a/core/src/main/resources/hudson/tasks/Messages_ja.properties +++ b/core/src/main/resources/hudson/tasks/Messages_ja.properties @@ -30,7 +30,6 @@ Ant.ProjectConfigNeeded=\u3069\u306eAnt\u3092\u4f7f\u3046\u304b\u30d7\u30ed\u30b ArtifactArchiver.ARCHIVING_ARTIFACTS=\u6210\u679c\u7269\u3092\u4fdd\u5b58\u4e2d ArtifactArchiver.DisplayName=\u6210\u679c\u7269\u3092\u4fdd\u5b58 -ArtifactArchiver.FailedToArchive=\u6210\u679c\u7269\u306e\u4fdd\u5b58\u306e\u5931\u6557\u3057\u307e\u3057\u305f ArtifactArchiver.NoIncludes=\u4fdd\u5b58\u3059\u308b\u6210\u679c\u7269\u304c\u4f55\u3082\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\n\u6050\u3089\u304f\u30d5\u30a1\u30a4\u30eb\u30d1\u30bf\u30fc\u30f3\u306e\u6307\u5b9a\u3092\u5fd8\u308c\u305f\u306e\u3067\u3057\u3087\u3046\u3002\u8a2d\u5b9a\u30da\u30fc\u30b8\u306b\u623b\u3063\u3066\u30d1\u30bf\u30fc\u30f3\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\n\u3082\u3057\u672c\u5f53\u306b\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u306e\u5168\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4fdd\u5b58\u3059\u308b\u3064\u3082\u308a\u306a\u3089\u3001"**"\u3068\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 ArtifactArchiver.NoMatchFound=\u6307\u5b9a\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u30d1\u30bf\u30fc\u30f3\u300c{0}\u300d\u306b\u5408\u81f4\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u8a2d\u5b9a\u30df\u30b9\uff1f diff --git a/core/src/main/resources/hudson/tasks/Messages_nl.properties b/core/src/main/resources/hudson/tasks/Messages_nl.properties index 5ff3634cb8cfeccd01431b8878699a44a012cf8b..967173122004bf5450dd7349e4bb4274f098aa8d 100644 --- a/core/src/main/resources/hudson/tasks/Messages_nl.properties +++ b/core/src/main/resources/hudson/tasks/Messages_nl.properties @@ -28,9 +28,7 @@ Ant.NotAntDirectory={0} is geen Ant folder Ant.ProjectConfigNeeded= Misschien dient u uw job te configureren om \u00e9\u00e9n van je Ant installaties te gebruiken? ArtifactArchiver.DisplayName=Archiveer de artefacten -ArtifactArchiver.FailedToArchive=Fout bij het archiveren van de artefacten: {0} -ArtifactArchiver.NoIncludes=Er werden geen artefacten voor archivering geconfigureerd.\nWaarschijnlijk werd geen bestands-selectiepatroon geconfigureerd. Gelieve dit te configureren.\n -Indien je alle bestanden in de werkplaats wenst te archiveren, gelieve dan "**" als patroon te configureren. +ArtifactArchiver.NoIncludes=Er werden geen artefacten voor archivering geconfigureerd.\nWaarschijnlijk werd geen bestands-selectiepatroon geconfigureerd. Gelieve dit te configureren.\n Indien je alle bestanden in de werkplaats wenst te archiveren, gelieve dan "**" als patroon te configureren. ArtifactArchiver.NoMatchFound=Er werden geen artefacten gevonden die voldoen aan het bestands-selectiepatroon "{0}". Misschien dient U uw configuratie aan te passen? BatchFile.DisplayName=Voer Windows batch commando uit. @@ -62,7 +60,7 @@ JavadocArchiver.Publishing=Javadoc wordt gepubliceerd JavadocArchiver.UnableToCopy=Kon Javadoc niet copi\u00ebren van {0} naar {1} Maven.DisplayName=Voer top-niveau Maven taken uit -Maven.ExecFailed=uitvoer commando is gefaald +Maven.ExecFailed=uitvoer commando is gefaald Maven.NotMavenDirectory={0} is geen Maven folder Maven.NoExecutable=Kon geen uitvoerbaar bestand vinde in {0} Maven.NotADirectory={0} is geen folder diff --git a/core/src/main/resources/hudson/tasks/Messages_pt_BR.properties b/core/src/main/resources/hudson/tasks/Messages_pt_BR.properties index 654a51657407fbd748920d5c0829acacab6daede..bd20ca67afc90de4fdd54886e8eef86254393e1a 100644 --- a/core/src/main/resources/hudson/tasks/Messages_pt_BR.properties +++ b/core/src/main/resources/hudson/tasks/Messages_pt_BR.properties @@ -28,7 +28,6 @@ Ant.NotAntDirectory={0} n\u00e3o parece ser um diret\u00f3rio Ant Ant.ProjectConfigNeeded= \u00c9 necess\u00e1rio configurar a job para escolher uma de suas instala\u00e7\u00f5es do Ant. ArtifactArchiver.DisplayName=Arquivar os artefatos -ArtifactArchiver.FailedToArchive=Falha ao arquivar os artefatos: {0} ArtifactArchiver.NoIncludes=Nenhum artefato est\u00e1 configurado para arquivamento.\n \u00c9 necess\u00e1rio informar o padr\u00e3o de arquivo, volte para a configura\u00e7\u00e3o e especifique-o.\nSe necessitar arquivar todos os arquivos do workspace, por favor especifique "**" ArtifactArchiver.NoMatchFound=Nenhum artefato encontrado casa com o padr\u00e3o de arquivo "{0}". Erro de configura\u00e7\u00e3o? diff --git a/core/src/main/resources/hudson/tasks/Messages_ru.properties b/core/src/main/resources/hudson/tasks/Messages_ru.properties index 81eb29449f1037b899a5cace479d118a90961877..54d6a3de8a58f68a29d97b7b19873edb2eab955e 100644 --- a/core/src/main/resources/hudson/tasks/Messages_ru.properties +++ b/core/src/main/resources/hudson/tasks/Messages_ru.properties @@ -28,7 +28,6 @@ Ant.NotAntDirectory={0} \u043d\u0435 \u043f\u043e\u0445\u043e\u0436\u0430 \u043d Ant.ProjectConfigNeeded= \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0433\u0434\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0430\u0448\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u044f Ant? ArtifactArchiver.DisplayName=\u0417\u0430\u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b -ArtifactArchiver.FailedToArchive=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b\: {0} ArtifactArchiver.NoIncludes=\ \u041d\u0435\u0442 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u043e\u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u0440\u0445\u0438\u0432\u0430\u0446\u0438\u0438.\n\ \u041f\u043e\u0445\u043e\u0436\u0435, \u0432\u044b \u0437\u0430\u0431\u044b\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438 \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u0435\u0433\u043e.\n\ diff --git a/core/src/main/resources/hudson/tasks/Messages_sr.properties b/core/src/main/resources/hudson/tasks/Messages_sr.properties index e2ff5d5fca93fb5bd042f00beb0f98ec5d79fcab..ad9189854218181c9ed3bdde3f56b90bfa51db7e 100644 --- a/core/src/main/resources/hudson/tasks/Messages_sr.properties +++ b/core/src/main/resources/hudson/tasks/Messages_sr.properties @@ -10,7 +10,6 @@ Ant.ProjectConfigNeeded=\u041F\u043E\u043A\u0443\u0448\u0430\u0458\u0442\u0435 \ ArtifactArchiver.ARCHIVING_ARTIFACTS=\u0410\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u045A\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u0438 ArtifactArchiver.DisplayName=\u0410\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u0458 \u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u0438 ArtifactArchiver.SkipBecauseOnlyIfSuccessful=\u041F\u0440\u0435\u0441\u043A\u043E\u045B\u0430\u0432\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u045A\u0435 \u0437\u0430\u0448\u0442\u043E \u0458\u0435 \u0438\u0437\u0433\u0440\u0430\u0434\u045A\u0430 \u043D\u0435\u0443\u0441\u043F\u0435\u0448\u043D\u0430 -ArtifactArchiver.FailedToArchive=\u041D\u0435\u0443\u0441\u043F\u0440\u0435\u0448\u043D\u043E \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u045A\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u0438: {0} ArtifactArchiver.NoIncludes=\u041D\u0435\u043C\u0430 \u043D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u0445 \u043F\u0440\u0435\u0434\u043C\u0435\u0442\u0430 \u0437\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u045A\u0435.\n\ \u0418\u0437\u0433\u043B\u0435\u0434\u0430 \u0434\u0430 \u043D\u0438\u0441\u0442\u0435 \u043D\u0430\u0432\u0435\u043B\u0438 \u0448\u0430\u0431\u043B\u043E\u043D \u0438\u043C\u0435\u043D\u0430 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0435. \u041C\u043E\u043B\u0438\u043C\u043E \u0432\u0430\u0441, \u0432\u0440\u0430\u0442\u0438\u0442\u0435 \u0441\u0435 \u043D\u0430 \u0442\u0430\u0458 \u0435\u043A\u0440\u0430\u043D \u0438 \u0443\u043D\u0435\u0441\u0438\u0442\u0435 \u0433\u0430.\n\ \u0410\u043A\u043E \u0437\u0430\u0438\u0441\u0442\u0430 \u0436\u0435\u043B\u0438\u0442\u0435 \u0434\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430 \u0441\u0432\u0435 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0435 \u0443 \u0440\u0430\u0434\u043D\u043E\u043C \u043F\u0440\u043E\u0441\u0442\u043E\u0440\u0443, \u0443\u043D\u0435\u0441\u0438\u0442\u0435 "**" diff --git a/core/src/main/resources/hudson/tasks/Messages_tr.properties b/core/src/main/resources/hudson/tasks/Messages_tr.properties index c7d5f9437d3cc2b82b359b64d1fd60f265fc4323..60abb6fde83188b972c5f7544e84e1f3a00723d1 100644 --- a/core/src/main/resources/hudson/tasks/Messages_tr.properties +++ b/core/src/main/resources/hudson/tasks/Messages_tr.properties @@ -28,7 +28,6 @@ Ant.NotAntDirectory={0}, bir Ant dizinine benzemiyor Ant.ProjectConfigNeeded= \u00c7al\u0131\u015ft\u0131rd\u0131\u011f\u0131n\u0131z i\u015f i\u00e7in bir Ant kurulumu se\u00e7meniz gerekiyor olabilir? ArtifactArchiver.DisplayName=Artefaktlar\u0131 Ar\u015fivle -ArtifactArchiver.FailedToArchive=Artefakt ar\u015fivleme ba\u015far\u0131s\u0131z oldu\: {0} ArtifactArchiver.NoIncludes=\ Herhangi bir artefakt, ar\u015fivleme i\u00e7in ayarlanmad\u0131.\n\ Konfig\u00fcrasyon k\u0131sm\u0131nda File Pattern ayarlar\u0131n\u0131 kontrol edin.\n\ diff --git a/core/src/main/resources/hudson/tasks/Messages_zh_CN.properties b/core/src/main/resources/hudson/tasks/Messages_zh_CN.properties new file mode 100644 index 0000000000000000000000000000000000000000..4f86fe42871e118280a9a6ddd4a613ab6fe0d327 --- /dev/null +++ b/core/src/main/resources/hudson/tasks/Messages_zh_CN.properties @@ -0,0 +1,78 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +Ant.DisplayName=\u8c03\u7528 Ant +Ant.ExecFailed=\u547d\u4ee4\u6267\u884c\u5931\u8d25\u3002 +Ant.ExecutableNotFound=\u5728\u4f60\u6240\u9009\u7684 Ant \u76ee\u5f55 "{0}" \u4e2d\u627e\u4e0d\u5230\u53ef\u6267\u884c\u811a\u672c +Ant.GlobalConfigNeeded=\u4f60\u53ef\u80fd\u9700\u8981\u914d\u7f6e Ant \u5b89\u88c5\u76ee\u5f55\uff1f +Ant.NotADirectory={0} \u4e0d\u662f\u76ee\u5f55 +Ant.NotAntDirectory={0} \u4f3c\u4e4e\u4e0d\u662f Ant \u76ee\u5f55 +Ant.ProjectConfigNeeded=\u4f60\u53ef\u80fd\u9700\u8981\u5728\u4efb\u52a1\u4e2d\u9009\u62e9\u5df2\u7ecf\u914d\u7f6e\u597d\u7684\u5b89\u88c5\u76ee\u5f55\uff1f + +ArtifactArchiver.ARCHIVING_ARTIFACTS=\u5f52\u6863\u6210\u54c1 +ArtifactArchiver.DisplayName=\u5f52\u6863\u6210\u54c1 +ArtifactArchiver.SkipBecauseOnlyIfSuccessful=\u7531\u4e8e\u6784\u5efa\u5931\u8d25\u800c\u8df3\u8fc7\u5f52\u6863 +ArtifactArchiver.NoMatchFound=\u6839\u636e\u6587\u4ef6\u6a21\u5f0f "{0}"\u65e0\u6cd5\u5339\u914d\u5230\u6210\u54c1\u3002\u662f\u5426\u914d\u7f6e\u6709\u8bef\uff1f + +BatchFile.DisplayName=\u6267\u884c Windows \u6279\u5904\u7406\u547d\u4ee4 +BatchFile.invalid_exit_code_range=\u975e\u6cd5\u7684\u9000\u51fa\u7801: {0}\u3002\u8bf7\u67e5\u770b\u5e2e\u52a9 +BatchFile.invalid_exit_code_zero=\u9000\u51fa\u7801\u4e3a0\u65f6\u5ffd\u7565\uff0c\u4e0d\u4f1a\u4f7f\u5f97\u6784\u5efa\u4e0d\u7a33\u5b9a + +BuildTrigger.Disabled={0} \u88ab\u7981\u7528\u3002\u89e6\u53d1\u88ab\u8df3\u8fc7 +BuildTrigger.DisplayName=\u6784\u5efa\u5176\u4ed6\u5de5\u7a0b +BuildTrigger.InQueue={0} \u5df2\u7ecf\u5728\u961f\u5217\u4e2d +BuildTrigger.NoSuchProject=\u6ca1\u6709\u8fd9\u4e2a\u5de5\u7a0b \u2018{0}\u2019\u3002 \u4f60\u6307\u5b9a\u662f \u2018{1}\u2019? +BuildTrigger.NoProjectSpecified=\u6ca1\u6709\u6307\u5b9a\u5de5\u7a0b +BuildTrigger.NotBuildable={0} \u4e0d\u53ef\u6784\u5efa +BuildTrigger.Triggering=\u89e6\u53d1\u65b0\u7684\u6784\u5efa {0} +BuildTrigger.you_have_no_permission_to_build_=\u4f60\u6ca1\u6709\u6784\u5efa {0} \u7684\u6743\u9650 + +CommandInterpreter.CommandFailed=\u547d\u4ee4\u6267\u884c\u5931\u8d25 +CommandInterpreter.UnableToDelete=\u65e0\u6cd5\u5220\u9664\u811a\u672c\u6587\u4ef6 {0} +CommandInterpreter.UnableToProduceScript=\u4e0d\u80fd\u591f\u4ea7\u751f\u811a\u672c\u6587\u4ef6 + +Fingerprinter.Aborted=\u4e2d\u6b62 +Fingerprinter.Action.DisplayName=\u67e5\u770b\u6307\u7eb9 +Fingerprinter.DigestFailed=\u8ba1\u7b97 {0} \u7684\u6458\u8981\u5931\u8d25 +Fingerprinter.DisplayName=\u8bb0\u5f55\u6587\u4ef6\u7684\u6307\u7eb9\u7528\u4e8e\u8ffd\u8e2a +Fingerprinter.Failed=\u8bb0\u5f55\u6307\u7eb9\u5931\u8d25 +Fingerprinter.FailedFor=\u8bb0\u5f55 {0} \u7684\u6307\u7eb9\u5931\u8d25 +Fingerprinter.Recording=\u8bb0\u5f55\u6307\u7eb9 + +InstallFromApache=\u4ece Apache \u5b89\u88c5 + +JavadocArchiver.DisplayName=\u53d1\u5e03 Javadoc +JavadocArchiver.DisplayName.Generic=\u6587\u6863 +JavadocArchiver.DisplayName.Javadoc=Javadoc +TestJavadocArchiver.DisplayName.Javadoc=\u6d4b\u8bd5 Javadoc +JavadocArchiver.NoMatchFound=\u5728 {0}: {1} \u4e2d\u627e\u4e0d\u5230 JavaDoc +JavadocArchiver.Publishing=\u53d1\u5e03 Javadoc +JavadocArchiver.UnableToCopy=\u65e0\u6cd5\u4ece {0} \u62f7\u8d1d Javadoc \u5230 {1} + +Maven.DisplayName=\u8c03\u7528\u9876\u5c42 Maven \u76ee\u6807 +Maven.ExecFailed=\u547d\u4ee4\u6267\u884c\u5931\u8d25 +Maven.NotMavenDirectory={0} \u4f3c\u4e4e\u4e0d\u662f\u4e00\u4e2a Maven \u76ee\u5f55 +Maven.NoExecutable=\u5728 {0} \u4e2d\u627e\u4e0d\u5230\u4efb\u4f55\u53ef\u6267\u884c\u811a\u672c + +Shell.DisplayName=\u6267\u884c shell +Shell.invalid_exit_code_range=\u975e\u6cd5\u7684\u9000\u51fa\u7801: {0}\u3002\u8bf7\u67e5\u770b\u5e2e\u52a9 +Shell.invalid_exit_code_zero=\u9000\u51fa\u7801\u4e3a0\u65f6\u5ffd\u7565\uff0c\u4e0d\u4f1a\u4f7f\u5f97\u6784\u5efa\u4e0d\u7a33\u5b9a diff --git a/core/src/main/resources/hudson/tasks/Messages_zh_TW.properties b/core/src/main/resources/hudson/tasks/Messages_zh_TW.properties index aeef7ecacec6ef8c360f2382f0228097ac1c9e1d..74e72413e331075a61003546bb9e7bb9f78a0738 100644 --- a/core/src/main/resources/hudson/tasks/Messages_zh_TW.properties +++ b/core/src/main/resources/hudson/tasks/Messages_zh_TW.properties @@ -30,7 +30,6 @@ Ant.ProjectConfigNeeded=\u4e5f\u8a31\u60a8\u61c9\u8a72\u8a2d\u5b9a\u5c08\u6848\u ArtifactArchiver.ARCHIVING_ARTIFACTS=\u5c01\u5b58\u6210\u54c1 ArtifactArchiver.DisplayName=\u5c01\u5b58\u6210\u54c1 -ArtifactArchiver.FailedToArchive=\u7121\u6cd5\u5c01\u5b58\u6210\u54c1: {0} ArtifactArchiver.NoIncludes=\ \u6c92\u8981\u6210\u54c1\u88ab\u8a2d\u70ba\u9700\u8981\u5c01\u5b58\u3002\n\ \u53ef\u80fd\u662f\u60a8\u5fd8\u4e86\u8a2d\u5b9a\u6a94\u6848\u6a23\u5f0f\uff0c\u8acb\u56de\u5230\u8a2d\u5b9a\u9801\u6aa2\u67e5\u770b\u770b\u3002\n\ diff --git a/core/src/main/resources/hudson/tools/AbstractCommandInstaller/config_zh_CN.properties b/core/src/main/resources/hudson/tools/AbstractCommandInstaller/config_zh_CN.properties deleted file mode 100644 index b56768c498c70f5fdfcba7cf64b08f259d46899c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/AbstractCommandInstaller/config_zh_CN.properties +++ /dev/null @@ -1,2 +0,0 @@ -Command=\u547d\u4ee4 -Tool\ Home=\u5de5\u5177\u76ee\u5f55 diff --git a/core/src/main/resources/hudson/tools/DownloadFromUrlInstaller/config_zh_CN.properties b/core/src/main/resources/hudson/tools/DownloadFromUrlInstaller/config_zh_CN.properties deleted file mode 100644 index 83bc36fed96bfd5855eb344317683ad58726a440..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/DownloadFromUrlInstaller/config_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Version=\u7248\u672c diff --git a/core/src/main/resources/hudson/tools/InstallSourceProperty/config_zh_CN.properties b/core/src/main/resources/hudson/tools/InstallSourceProperty/config_zh_CN.properties deleted file mode 100644 index e349cd159c34d1691bef536ccd4ff54ad82012fc..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/InstallSourceProperty/config_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -Add\ Installer=\u65b0\u589e\u5b89\u88c5 -Delete\ Installer=\u5220\u9664\u5b89\u88c5 \ No newline at end of file diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_bg.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_bg.properties deleted file mode 100644 index c9a4c88d9ee987bb1b1b8a322bb18171438c1790..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_bg.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, Alexander Shopov -# -# 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. - -Credential\ is\ saved=\ - \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f\u0442\u0430 \u0435 \u0437\u0430\u043f\u0430\u0437\u0435\u043d\u0430 -Close=\ - \u0417\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_de.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_de.properties deleted file mode 100644 index 655d59a10b416c2642077d19556f2a3636b604ee..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Close=Schlie\u00DFen -Credential\ is\ saved=Zugangsdaten gespeichert diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_es.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_es.properties deleted file mode 100644 index 67f1788a2bd149e5c9b1d6ea25c5b6f44aaaa2c9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_es.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Close=Cerrar -Credential\ is\ saved=La credencial se ha guardado diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_it.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_it.properties deleted file mode 100644 index 65bf7f41ba41c7b3272df6319079bcc96e2b1f11..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_it.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -Credential\ is\ saved=Credenziali salvate -Close=Chiudi diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_ja.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_ja.properties deleted file mode 100644 index 4bd382d6cd919060a2a71bf82e3bda2a289d6ccd..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_ja.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2011, CloudBees, Inc. Seiji Sogabe -# -# 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. - -Credential\ is\ saved=\u8a8d\u8a3c\u60c5\u5831\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f\u3002 -Close=\u9589\u3058\u308b - diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_pt_BR.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_pt_BR.properties deleted file mode 100644 index 7c48f45d69d009bad1edd588565d4bab152ae71b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_pt_BR.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Close=Fechar -Credential\ is\ saved=Senha est\u00E1 salva diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_sr.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_sr.properties deleted file mode 100644 index fef7da675572e9d473493dd306b3dec4e8d50790..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_sr.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Credential\ is\ saved=\u0421\u0430\u0447\u0443\u0432\u0430\u043D\u043E \u0458\u0435 \u0430\u043A\u0440\u0435\u0434\u0438\u0442\u0438\u0432\u0430\u0442 -Close=\u0417\u0430\u0432\u0440\u0448\u0438 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_zh_TW.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_zh_TW.properties deleted file mode 100644 index 6fe8c3aa75e85669fd1c12678ffef0d9f81bbe9d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/credentialOK_zh_TW.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -Credential\ is\ saved=\u8a8d\u8b49\u8cc7\u6599\u5df2\u5132\u5b58 -Close=\u95dc\u9589 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.properties deleted file mode 100644 index b71d56349f14c94cd79fd93af63cbc8848d1ab52..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2011, CloudBees, Inc. -# -# 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. - -description=To access older versions of JDK, you need to have Oracle Account. \ No newline at end of file diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_bg.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_bg.properties deleted file mode 100644 index fa475cad59e386832286749cf5a10afd41a36db0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_bg.properties +++ /dev/null @@ -1,34 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, 2017, Alexander Shopov -# -# 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. - -# To access older versions of JDK, you need to have Oracle Account. -description=\ - \u0414\u043e\u0441\u0442\u044a\u043f\u044a\u0442 \u0434\u043e \u043f\u043e-\u0441\u0442\u0430\u0440\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u043d\u0430 JDK \u0438\u0437\u0438\u0441\u043a\u0432\u0430 \u0434\u0430 \u0438\u043c\u0430\u0442\u0435\ - \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043a\u044a\u043c Oracle. -OK=\ - \u0414\u043e\u0431\u0440\u0435 -Enter\ Your\ Oracle\ Account=\ - \u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u0437\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0432\u0438 \u043a\u044a\u043c Oracle -Password=\ - \u041f\u0430\u0440\u043e\u043b\u0430 -Username=\ - \u0418\u043c\u0435 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_de.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_de.properties deleted file mode 100644 index 672bfce5c0063883939954a91adc151952e40f31..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_de.properties +++ /dev/null @@ -1,7 +0,0 @@ -# This file is under the MIT License by authors - -Enter\ Your\ Oracle\ Account=Bitte Oracle-Account eintragen -OK=Ok -Password=Passwort -Username=Benutzername -description=Um \u00E4ltere Versionen des JDKs zu erlangen, ben\u00F6tigen Sie einen Oracle Account. diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_es.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_es.properties deleted file mode 100644 index 7d6223c69f9bb5199dab10f1720526526b73a11d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_es.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Password=Contrasea -Username=Usuario -# To access older versions of JDK, you need to have Oracle Account. -description=Desafortunadamente Oracle exige que para descargar antiguas versiones del JDK uses una Cuenta de Oracle. -Enter\ Your\ Oracle\ Account=Introduce un usuario y contrasea vlidos -OK=Aceptar diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_it.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_it.properties deleted file mode 100644 index bbfeef5056b7a95b1144e17b0bc3e534e5cfdd91..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_it.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -Password=Password -OK=OK -description=Per ottenere accesso alle versioni meno recenti del JDK necessario disporre di un account Oracle. -Enter\ Your\ Oracle\ Account=Inserire le credenziali del proprio account Oracle -Username=Nome utente diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_ja.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_ja.properties deleted file mode 100644 index 219b0f812778a17d2a85ba0c4fc4c316389a74c1..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_ja.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2012, CloudBees, Inc. Seiji Sogabe -# -# 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. - -Enter\ Your\ Oracle\ Account=Oracle\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u5165\u529b -Username=\u30e6\u30fc\u30b6\u540d -Password=\u30d1\u30b9\u30ef\u30fc\u30c9 -description=\ - JDK\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u306b\u306f\u3001Oracle\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u304c\u5fc5\u8981\u3067\u3059\u3002 -OK=OK diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_pt_BR.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_pt_BR.properties deleted file mode 100644 index 92d284a3511d8524e91b5911c48c227e8cd2e342..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_pt_BR.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Username=Usu\u00e1rio -Password=Senha -OK=OK -Enter\ Your\ Oracle\ Account=Informe sua conta da Oracle -# To access older versions of JDK, you need to have Oracle Account. -description=Para acessar vers\u00f5es antigas do JDK, voc\u00ea deve possuir uma conta da Oracle. diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_sr.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_sr.properties deleted file mode 100644 index c77ad735c4501ffe448c1e9988b503e216f6add9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_sr.properties +++ /dev/null @@ -1,7 +0,0 @@ -# This file is under the MIT License by authors - -Enter\ Your\ Oracle\ Account=\u0423\u043D\u0435\u0441\u0438\u0442\u0435 \u0432\u0430\u0448 Oracle \u043D\u0430\u043B\u043E\u0433 -description=\u0414\u0430 \u043F\u0440\u0438\u0441\u0442\u0443\u043F\u0438\u0442\u0435 \u0441\u0442\u0430\u0440\u0438\u0458\u0438\u043C \u0432\u0435\u0440\u0437\u0438\u0458\u0430\u043C\u0430 JDK, \u043C\u043E\u0440\u0430\u0442\u0435 \u0438\u043C\u0430\u0442\u0438 Oracle \u043D\u0430\u043B\u043E\u0433. -Username=\u041A\u043E\u0440\u0438\u0441\u043D\u0438\u0447\u043A\u043E \u0438\u043C\u0435 -Password=\u041B\u043E\u0437\u0438\u043D\u043A\u0430 -OK=\u0423\u0440\u0435\u0434\u0443 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_zh_TW.properties b/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_zh_TW.properties deleted file mode 100644 index a3262debbe5edf35fddeda7e5a2c2587d30ab3b3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/DescriptorImpl/enterCredential_zh_TW.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -Enter\ Your\ Oracle\ Account=\u8f38\u5165\u60a8\u7684 Oracle \u5e33\u865f -description=\u60a8\u8981\u6709 Oracle \u5e33\u865f\u624d\u80fd\u62ff\u5230\u820a\u7248 JDK\u3002 -Username=\u4f7f\u7528\u8005\u540d\u7a31 -Password=\u5bc6\u78bc -OK=\u78ba\u5b9a diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_bg.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_bg.properties deleted file mode 100644 index a538c952b17b413603100fd896b6cbb3c0ad7d8f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_bg.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, Alexander Shopov -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\ - \u041f\u0440\u0438\u0435\u043c\u0430\u043c \u043b\u0438\u0446\u0435\u043d\u0437\u043d\u043e\u0442\u043e \u0441\u043f\u043e\u0440\u0430\u0437\u0443\u043c\u0435\u043d\u0438\u0435 \u043d\u0430 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0430 \u0437\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u0446\u0438 \u043d\u0430 Java SE -Version=\ - \u0412\u0435\u0440\u0441\u0438\u044f diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_ca.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_ca.properties deleted file mode 100644 index 66a7996365d058554aa986d90d890d7f4453b568..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_ca.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Accepto les condicions de la llic\u00E8ncia de Java SE Development Kit -Version=Versi\u00F3 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_da.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_da.properties deleted file mode 100644 index 6249f91df89878472e759d0e81b9e0da2756c42b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_da.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Jeg accepterer Java SE Development Kit Licens Aftalen -Version=Version diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_de.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_de.properties deleted file mode 100644 index bbb3853e3ab210d19f09c470841fa7d0d13c1c20..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_de.properties +++ /dev/null @@ -1,3 +0,0 @@ -Version=Version -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\ - Ich stimme der Lizenzvereinbarung des Java SE Development Kits zu diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_en_GB.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_en_GB.properties deleted file mode 100644 index b68ce953238e8e342084046c61a8171b24257e5f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_en_GB.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=I agree to the Java SE Development Kit Licence Agreement diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_es.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_es.properties deleted file mode 100644 index 82f1ac18fabd83a4c68144197353bd6cdd361eab..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_es.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Version=Versin -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Estoy deacuerdo con el acuerdo de licencia de kit de desarrollo de ''Java SE'' diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_fi.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_fi.properties deleted file mode 100644 index 52f34d7f98be8217abb47864aab3b40d9dea9d6a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_fi.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Version=Versio diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_fr.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_fr.properties deleted file mode 100644 index 2b39a726ae755884eef3cf1f050c00ec762cb05f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_fr.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=J''approuve l''accord de licence Java SE Development Kit -Version=Version diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_he.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_he.properties deleted file mode 100644 index 11379f478e87e23318600c3d682c71c247783edb..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_he.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\u05D0\u05E0\u05D9 \u05DE\u05E1\u05DB\u05D9\u05DD \u05DC\u05EA\u05E0\u05D0\u05D9 \u05D4\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF \u05E9\u05DC Java SE Development Kit -Version=\u05D2\u05D9\u05E8\u05E1\u05D0 diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_it.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_it.properties deleted file mode 100644 index 717778a20dbf5c7c8bd9e93ef67154b74c86cb06..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_it.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Accetto il Contratto di Licenza di Java SE Development Kit -Version=Versione diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_ja.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_ja.properties deleted file mode 100644 index 5a2e6b2737c4571340004f90bed87277878826a9..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_ja.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Seiji Sogabe -# -# 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. - -Version=\u30D0\u30FC\u30B8\u30E7\u30F3 -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\ - Java SE Development Kit\u306E\u4F7F\u7528\u8A31\u8AFE\u306B\u540C\u610F\u3059\u308B - diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_lt.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_lt.properties deleted file mode 100644 index 9f493071ba5c5d6a7e0c649bd8417bbcc177c762..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_lt.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Sutinku su Java SE Development Kit licenzija -Version=Versija diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_lv.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_lv.properties deleted file mode 100644 index 9f6893037403ea3cbbf2498ed2b3916a66bf726d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_lv.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Es piekr\u012Btu JAVA SE Izstr\u0101des R\u012Bku Licences L\u012Bgumam diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_nl.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_nl.properties deleted file mode 100644 index b6e78396fc1fa34b7b78dbd575cefa88bb58b4af..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_nl.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Ik ga akkoord met de Java SE Development Kit gebruiksovereenkomst -Version=Versie diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_pl.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_pl.properties deleted file mode 100644 index 20bb0b1dfc43ee75cdafd5c3d602439fd6e531c8..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_pl.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Version=Wersja diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_BR.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_BR.properties deleted file mode 100644 index a0eea3eb9e403002bcf707e8e8406c15ba9b9104..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_BR.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva -# -# 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. - -Version=Vers\u00e3o -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Eu concordo com a licen\u00e7a do Java SE Development Kit diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_PT.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_PT.properties deleted file mode 100644 index 7488995900ff3639493caad3e1853be875ae981d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_pt_PT.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Aceito os termos da Licen\u00E7a de Utiliza\u00E7\u00E3o do Java SE Development Kit -Version=Vers\u00E3o diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_ru.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_ru.properties deleted file mode 100644 index 222a4d37bf04756d1e22f4ad85101bd2d4ba8a76..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_ru.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\u042F \u0441\u043E\u0433\u043B\u0430\u0441\u0435\u043D \u0441 \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u043E\u043D\u043D\u044B\u043C \u0441\u043E\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u0435\u043C Java SE Development Kit -Version=\u0412\u0435\u0440\u0441\u0438\u044F diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_sk.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_sk.properties deleted file mode 100644 index 73175d4e395bfbd54c701bfa878a642e6e76eab8..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_sk.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Version=Verzia diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_sr.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_sr.properties deleted file mode 100644 index 5d1762543aff2701514aa739d2ab9c6559362fb3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_sr.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Version=\u0412\u0435\u0440\u0437\u0438\u0458\u0430 -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\u041F\u0440\u0438\u0441\u0442\u0430\u0458\u0435\u043C \u043D\u0430 \u0443\u0433\u043E\u0432\u043E\u0440 \u043E \u043B\u0438\u0446\u0435\u043D\u0446\u0438 Java SE Development Kit diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_sv_SE.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_sv_SE.properties deleted file mode 100644 index 04c972dcfc4b7c2f00dfa8eb11172bac28ef8b5f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_sv_SE.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=Jag samtycker till Java SE Development Kit -Version=Version diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_CN.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_CN.properties deleted file mode 100644 index 8a93bead72822d2ee40ed0297cb00bfba57ce2b4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_CN.properties +++ /dev/null @@ -1,2 +0,0 @@ -Version=\u7248\u672c -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\u6211\u540C\u610F Java SE Development Kit \u7684\u8BB8\u53EF\u534F\u8BAE diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_TW.properties b/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_TW.properties deleted file mode 100644 index c17db00834f4bb96a854beb1aeb27bb95b9b0ae3..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config_zh_TW.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2013, Sun Microsystems, Inc., Chunghwa Telecom Co., Ltd., -# and Pei-Tang Huang -# -# 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. - -Version=\u7248\u672c -I\ agree\ to\ the\ Java\ SE\ Development\ Kit\ License\ Agreement=\u6211\u540c\u610f Jave SE \u958b\u767c\u5957\u4ef6\u6388\u6b0a\u5354\u8b70\u5167\u5bb9 diff --git a/core/src/main/resources/hudson/tools/Messages.properties b/core/src/main/resources/hudson/tools/Messages.properties index a59e064de78dec0a38f361709807922d7bebcb47..5eeb9f12f03a445845d9ee8a1735bc621211fc48 100644 --- a/core/src/main/resources/hudson/tools/Messages.properties +++ b/core/src/main/resources/hudson/tools/Messages.properties @@ -25,16 +25,10 @@ CommandInstaller.DescriptorImpl.displayName=Run Shell Command CommandInstaller.no_command=Must provide a command to run. CommandInstaller.no_toolHome=Must provide a tool home directory. BatchCommandInstaller.DescriptorImpl.displayName=Run Batch Command -JDKInstaller.FailedToInstallJDK=Failed to install JDK. Exit code={0} -JDKInstaller.RequireOracleAccount=Installing JDK requires Oracle account. Please enter your username/password -JDKInstaller.UnableToInstallUntilLicenseAccepted=Unable to auto-install JDK until the license is accepted. ZipExtractionInstaller.DescriptorImpl.displayName=Extract *.zip/*.tar.gz ZipExtractionInstaller.bad_connection=Server rejected connection. ZipExtractionInstaller.malformed_url=Malformed URL. ZipExtractionInstaller.could_not_connect=Could not connect to URL. InstallSourceProperty.DescriptorImpl.displayName=Install automatically -JDKInstaller.DescriptorImpl.displayName=Install from java.sun.com -JDKInstaller.DescriptorImpl.doCheckId=Define JDK ID -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=You must agree to the license to download the JDK. ToolDescriptor.NotADirectory={0} is not a directory on the Jenkins master (but perhaps it exists on some agents) CannotBeInstalled=Installer "{0}" cannot be used to install "{1}" on the node "{2}" diff --git a/core/src/main/resources/hudson/tools/Messages_bg.properties b/core/src/main/resources/hudson/tools/Messages_bg.properties index 15217e34b438bed55e6c0ceff0e0bc23aa70898d..018cd217879e281e8ce35bbbc137f5d9ce839f43 100644 --- a/core/src/main/resources/hudson/tools/Messages_bg.properties +++ b/core/src/main/resources/hudson/tools/Messages_bg.properties @@ -30,16 +30,6 @@ CommandInstaller.no_toolHome=\ \u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0443\u043a\u0430\u0436\u0435\u0442\u0435 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438\u0442\u0435 BatchCommandInstaller.DescriptorImpl.displayName=\ \u0418\u0437\u043f\u044a\u043b\u043d\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u0444\u0430\u0439\u043b -JDKInstaller.FailedToInstallJDK=\ - \u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 JDK (\u0440\u0430\u0437\u0432\u043e\u0439\u043d\u0438\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438 \u0437\u0430 Java). \u0418\u0437\u0445\u043e\u0434\u043d\u0438\u044f\u0442 \u043a\u043e\u0434\ - \u0435 {0} -JDKInstaller.RequireOracleAccount=\ - \u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 JDK (\u0440\u0430\u0437\u0432\u043e\u0439\u043d\u0438\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438 \u0437\u0430 Java) \u0438\u0437\u0438\u0441\u043a\u0432\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f\ - \u043a\u044a\u043c Oracle. \u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e\u0442\u043e \u0441\u0438 \u0438\u043c\u0435 \u0438\ - \u043f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 -JDKInstaller.UnableToInstallUntilLicenseAccepted=\ - \u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043f\u0440\u0438\u0435\u043c\u0435\u0442\u0435 \u043b\u0438\u0446\u0435\u043d\u0437\u0430 \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 JDK (\u0440\u0430\u0437\u0432\u043e\u0439\u043d\u0438\u0442\u0435\ - \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438 \u0437\u0430 Java). ZipExtractionInstaller.DescriptorImpl.displayName=\ \u0420\u0430\u0437\u0430\u0440\u0445\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u0435\u0442\u0435 \u0441 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435 *.zip/*.tar.gz ZipExtractionInstaller.bad_connection=\ @@ -50,12 +40,6 @@ ZipExtractionInstaller.could_not_connect=\ \u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u043a\u044a\u043c \u0430\u0434\u0440\u0435\u0441\u0430. InstallSourceProperty.DescriptorImpl.displayName=\ \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 -JDKInstaller.DescriptorImpl.displayName=\ - \u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043e\u0442 \u0430\u0434\u0440\u0435\u0441\u0430 java.sun.com -JDKInstaller.DescriptorImpl.doCheckId=\ - \u0423\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043d\u0430 JDK (\u0440\u0430\u0437\u0432\u043e\u0439\u043d\u0438\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438 \u0437\u0430 Java) -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=\ - \u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043f\u0440\u0438\u0435\u043c\u0435\u0442\u0435 \u043b\u0438\u0446\u0435\u043d\u0437\u0430, \u0437\u0430 \u0434\u0430 \u0441\u0432\u0430\u043b\u0438\u0442\u0435 JDK (\u0440\u0430\u0437\u0432\u043e\u0439\u043d\u0438\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0438 \u0437\u0430 Java). # {0} is not a directory on the Jenkins master (but perhaps it exists on some agents) ToolDescriptor.NotADirectory=\ \u201e{0}\u201c \u043d\u0435 \u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 \u043d\u0430 Jenkins, \u043d\u043e \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\ diff --git a/core/src/main/resources/hudson/tools/Messages_da.properties b/core/src/main/resources/hudson/tools/Messages_da.properties index ac8f0e496763e2b6717a33c0ca65ffd5bfbbaaff..f9921b790a52e6aae05f31431dc296924cd0d1d5 100644 --- a/core/src/main/resources/hudson/tools/Messages_da.properties +++ b/core/src/main/resources/hudson/tools/Messages_da.properties @@ -20,17 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -JDKInstaller.UnableToInstallUntilLicenseAccepted=Kan ikke auto-installere JDK''en f\u00f8r licensen er accepteret. ZipExtractionInstaller.bad_connection=Server afviste forbindelsen. ZipExtractionInstaller.DescriptorImpl.displayName=Udtr\u00e6k fra *.zip/*.tar.gz arkiv CommandInstaller.DescriptorImpl.displayName=K\u00f8r kommando ZipExtractionInstaller.could_not_connect=Kunne ikke forbinde til URL. InstallSourceProperty.DescriptorImpl.displayName=Installer automatisk ToolLocationNodeProperty.displayName=V\u00e6rkt\u00f8jsplaceringer -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=Du skal acceptere licensen for at hente JDK''en. CommandInstaller.no_command=Der skal angives en kommando der skal k\u00f8res. ZipExtractionInstaller.malformed_url=Vanskabt URL CommandInstaller.no_toolHome=Der skal angives et v\u00e6rkt\u00f8jshjemmedirektorie -JDKInstaller.DescriptorImpl.displayName=Installer fra java.sun.com -JDKInstaller.FailedToInstallJDK=Kunne ikke installere JDK. Exit kode={0} -JDKInstaller.DescriptorImpl.doCheckId=Definer JDK ID diff --git a/core/src/main/resources/hudson/tools/Messages_de.properties b/core/src/main/resources/hudson/tools/Messages_de.properties index c4ae69317ab1056783b6dd634d9012909f609fdd..2966103216b5da8480cdd980b28d5c073903a28c 100644 --- a/core/src/main/resources/hudson/tools/Messages_de.properties +++ b/core/src/main/resources/hudson/tools/Messages_de.properties @@ -30,11 +30,5 @@ ZipExtractionInstaller.bad_connection=Der Server verweigerte den Verbindungsaufb ZipExtractionInstaller.malformed_url=Ung\u00FCltige URL ZipExtractionInstaller.could_not_connect=URL konnte nicht kontaktiert werden. InstallSourceProperty.DescriptorImpl.displayName=Automatisch installieren -JDKInstaller.DescriptorImpl.displayName=Installiere von java.sun.com -JDKInstaller.DescriptorImpl.doCheckId=JDK-ID definieren -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=Sie m\u00FCssen der Lizenzvereinbarung zustimmen, um das JDK herunterzuladen. -JDKInstaller.FailedToInstallJDK=JDK konnte nicht installiert werden. -JDKInstaller.RequireOracleAccount=F\u00FCr die Installation des JDK ben\u00F6tigen Sie einen Oracle Account. Bitte geben Sie Benutzername/Passwort ein -JDKInstaller.UnableToInstallUntilLicenseAccepted=JDK kann nicht automatisch installiert werden, solange die Lizenzvereinbarung nicht akzeptiert wurde. CannotBeInstalled=Das Installationsverfahren \u201E{0}\u201C kann nicht verwendet werden, um \u201E{1}\u201C auf dem Knoten \u201E{2}\u201C zu installieren. ToolDescriptor.NotADirectory=Das Verzeichnis {0} existiert nicht auf dem Master-Knoten (aber vielleicht auf Agenten) diff --git a/core/src/main/resources/hudson/tools/Messages_es.properties b/core/src/main/resources/hudson/tools/Messages_es.properties index 054be9f4dc1c182b9d96b6a2f903fe7fdd6fe9e2..080b0c31dacf6499e77ef35f2d8d3d22c11eb398 100644 --- a/core/src/main/resources/hudson/tools/Messages_es.properties +++ b/core/src/main/resources/hudson/tools/Messages_es.properties @@ -24,14 +24,8 @@ ToolLocationNodeProperty.displayName=Localizaci CommandInstaller.DescriptorImpl.displayName=Ejecutar comando CommandInstaller.no_command=Debes especificar el comando para ejecutar. CommandInstaller.no_toolHome=Debes especificar el directorio raiz de la herramienta. -JDKInstaller.FailedToInstallJDK=Fallo al installar el JDK -JDKInstaller.UnableToInstallUntilLicenseAccepted=No se puede instalar el JDK hasta que se acepte la licencia de uso. ZipExtractionInstaller.DescriptorImpl.displayName=Extraer *.zip/*.tar.gz ZipExtractionInstaller.bad_connection=El servidor rechaz la conexin ZipExtractionInstaller.malformed_url=Direccin web incorrecta. ZipExtractionInstaller.could_not_connect=No se puede conectar a la direccin InstallSourceProperty.DescriptorImpl.displayName=Instalar automticamente -JDKInstaller.DescriptorImpl.displayName=Instalar desde java.sun.com -JDKInstaller.DescriptorImpl.doCheckId=Definir un identificador para el JDK -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=Debes aceptar la licencia si quieres descargar el JDK. -JDKInstaller.RequireOracleAccount=Desafortunadamente, para instalar el JDK se necesita una cuenta en Oracle. Introduce un usuario y contrasea vlidos diff --git a/core/src/main/resources/hudson/tools/Messages_it.properties b/core/src/main/resources/hudson/tools/Messages_it.properties index 35fc555853f423f395ff39648fc16af4ccb58d4c..d592b3a8f1f4de6db313d743f36fb9423754e17d 100644 --- a/core/src/main/resources/hudson/tools/Messages_it.properties +++ b/core/src/main/resources/hudson/tools/Messages_it.properties @@ -23,18 +23,12 @@ CommandInstaller.no_command= necessario fornire un comando da eseguire. CannotBeInstalled=Il programma di installazione "{0}" non pu essere utilizzato per installare "{1}" sul nodo "{2}" BatchCommandInstaller.DescriptorImpl.displayName=Esegui comando batch -JDKInstaller.RequireOracleAccount=L''installazione di un JDK richiede un account Oracle. Immettere i propri nome utente/password ZipExtractionInstaller.malformed_url=URL malformato. ZipExtractionInstaller.DescriptorImpl.displayName=Estrai *.zip/*.tar.gz ZipExtractionInstaller.could_not_connect=Impossibile connettersi all''URL. CommandInstaller.no_toolHome= necessario fornire la directory home dello strumento. -JDKInstaller.DescriptorImpl.doCheckId=Definisci ID JDK ToolLocationNodeProperty.displayName=Percorsi strumenti CommandInstaller.DescriptorImpl.displayName=Esegui comando shell -JDKInstaller.DescriptorImpl.doCheckAcceptLicense= necessario accettare la licenza per scaricare il JDK. ToolDescriptor.NotADirectory={0} non una directory sul nodo master Jenkins (ma forse esiste su alcuni agenti) ZipExtractionInstaller.bad_connection=Il server ha rifiutato la connessione. -JDKInstaller.UnableToInstallUntilLicenseAccepted=Impossibile installare automaticamente il JDK fino all''accettazione della licenza. InstallSourceProperty.DescriptorImpl.displayName=Installa automaticamente -JDKInstaller.FailedToInstallJDK=Installazione del JDK non riuscita. Codice d''uscita={0} -JDKInstaller.DescriptorImpl.displayName=Installa da java.sun.com diff --git a/core/src/main/resources/hudson/tools/Messages_ja.properties b/core/src/main/resources/hudson/tools/Messages_ja.properties index 2e82dec80a1a3be71dd03e1314673dc0d7bf7ca3..35b7fa20d0e9edcb265f859502dc748a1b123d18 100644 --- a/core/src/main/resources/hudson/tools/Messages_ja.properties +++ b/core/src/main/resources/hudson/tools/Messages_ja.properties @@ -25,15 +25,8 @@ CommandInstaller.DescriptorImpl.displayName=\u30b3\u30de\u30f3\u30c9\u5b9f\u884c CommandInstaller.no_command=\u5b9f\u884c\u3059\u308b\u30b3\u30de\u30f3\u30c9\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 CommandInstaller.no_toolHome=\u30c4\u30fc\u30eb\u306e\u30db\u30fc\u30e0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 BatchCommandInstaller.DescriptorImpl.displayName=\u30d0\u30c3\u30c1\u30b3\u30de\u30f3\u30c9\u3092\u8d77\u52d5\u3057\u307e\u3059\u3002 -JDKInstaller.FailedToInstallJDK=JDK\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002Exit code={0} -JDKInstaller.UnableToInstallUntilLicenseAccepted=\u4f7f\u7528\u8a31\u8afe\u306b\u540c\u610f\u3057\u306a\u3044\u3068\u81ea\u52d5\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u304d\u307e\u305b\u3093\u3002 -JDKInstaller.RequireOracleAccount=\ - JDK\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u306fOracle\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u304c\u5fc5\u8981\u3067\u3059\u3002\u30e6\u30fc\u30b6\u30fc\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 ZipExtractionInstaller.DescriptorImpl.displayName=*.zip/*.tar.gz\u5c55\u958b ZipExtractionInstaller.bad_connection=\u30b5\u30fc\u30d0\u304c\u63a5\u7d9a\u3092\u62d2\u5426\u3057\u307e\u3057\u305f\u3002 ZipExtractionInstaller.malformed_url=URL\u306e\u5f62\u5f0f\u304c\u8aa4\u3063\u3066\u3044\u307e\u3059\u3002 ZipExtractionInstaller.could_not_connect=URL\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3002 InstallSourceProperty.DescriptorImpl.displayName=\u81ea\u52d5\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb -JDKInstaller.DescriptorImpl.displayName=java.sun.com\u304b\u3089\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb -JDKInstaller.DescriptorImpl.doCheckId=JDK\u306eID\u3092\u5b9a\u7fa9\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=JDK\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u4f7f\u7528\u8a31\u8afe\u306b\u540c\u610f\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 diff --git a/core/src/main/resources/hudson/tools/Messages_pt_BR.properties b/core/src/main/resources/hudson/tools/Messages_pt_BR.properties index b69eff530a86f19684693da67e29731f32b6e3f2..ea3c2cf2802819d81a882275603649600c2a4fcb 100644 --- a/core/src/main/resources/hudson/tools/Messages_pt_BR.properties +++ b/core/src/main/resources/hudson/tools/Messages_pt_BR.properties @@ -28,28 +28,16 @@ ZipExtractionInstaller.could_not_connect=N\u00e3o foi poss\u00edvel pode conecta ToolLocationNodeProperty.displayName=Ferramentas locais # Must provide a command to run. CommandInstaller.no_command=Forne\u00e7a um comando para ser executado -# Install from java.sun.com -JDKInstaller.DescriptorImpl.displayName=Instalar de java.sun.com -# Failed to install JDK -JDKInstaller.FailedToInstallJDK=Falha ao instalar o JDK -# Define JDK ID -JDKInstaller.DescriptorImpl.doCheckId=Definir ID do JDK -# Unable to auto-install JDK until the license is accepted. -JDKInstaller.UnableToInstallUntilLicenseAccepted=N\u00e3o \u00e9 poss\u00edvel de auto-instalar o JDK at\u00e9 que a licen\u00e7a seja aceita # Server rejected connection. ZipExtractionInstaller.bad_connection=Conex\u00e3o rejeitada pelo servidor # Run Command CommandInstaller.DescriptorImpl.displayName=Executar comando # Install automatically InstallSourceProperty.DescriptorImpl.displayName=Instalar automaticamente -# You must agree to the license to download the JDK. -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=\u00c9 necess\u00e1rio aceitar o termo de licen\u00e7a para baixar o JDK. # Malformed URL. ZipExtractionInstaller.malformed_url=URL n\u00e3o reconhecida. # Must provide a tool home directory. CommandInstaller.no_toolHome=\u00c9 necess\u00e1rio fornecer um diret\u00f3rio home para a ferramenta. -# Installing JDK requires Oracle account. Please enter your username/password -JDKInstaller.RequireOracleAccount=Instalar o JDK exige uma conta na Oracle. Por favor informe seu usu\u00e1ro/senha # Run Batch Command BatchCommandInstaller.DescriptorImpl.displayName=Executar comando em lotes diff --git a/core/src/main/resources/hudson/tools/Messages_sr.properties b/core/src/main/resources/hudson/tools/Messages_sr.properties index 4266f9e1801f13cc0a909213e70fc21fbbc4d36e..518234dec49553097037c44883f794c8dadd8557 100644 --- a/core/src/main/resources/hudson/tools/Messages_sr.properties +++ b/core/src/main/resources/hudson/tools/Messages_sr.properties @@ -5,15 +5,9 @@ CommandInstaller.DescriptorImpl.displayName=\u0418\u0437\u0432\u0440\u0448\u0438 CommandInstaller.no_command=\u041C\u043E\u0440\u0430\u0442\u0435 \u043D\u0430\u0432\u0435\u0441\u0442\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u0437\u0430 \u0438\u0437\u0432\u0440\u0448\u0430\u0432\u0430\u045A\u0435 CommandInstaller.no_toolHome=\u041C\u043E\u0440\u0430\u0442\u0435 \u043D\u0430\u0432\u0435\u0441\u0442\u0438 \u0433\u043B\u0430\u0432\u043D\u0438 \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u0458\u0443\u043C \u0437\u0430 \u0430\u043B\u0430\u0442\u0435. BatchCommandInstaller.DescriptorImpl.displayName=\u0418\u0437\u0432\u0440\u0448\u0438 batch \u043A\u043E\u043C\u0430\u043D\u0434\u0443 -JDKInstaller.FailedToInstallJDK=\u0418\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u045A\u0435 JDK \u043D\u0438\u0458\u0435 \u0443\u0441\u043F\u0435\u043B\u043E. Exit code={0} -JDKInstaller.RequireOracleAccount=\u0418\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u045A\u0435 JDK-\u0430 \u0437\u0430\u0445\u0442\u0435\u0432\u0430 Oracle \u043D\u0430\u043B\u043E\u0433. \u041C\u043E\u043B\u0438\u043C\u043E \u0443\u043D\u0435\u0441\u0438\u0442\u0435 \u043A\u043E\u0440\u0438\u0438\u0441\u043D\u0438\u0447\u043A\u043E \u0438\u043C\u0435/\u043B\u043E\u0437\u0438\u043D\u043A\u0443 -JDKInstaller.UnableToInstallUntilLicenseAccepted=\u041C\u043E\u0440\u0430\u0442\u0435 \u043F\u0440\u0438\u0445\u0432\u0430\u0442\u0438\u0442\u0438 \u043B\u0438\u0446\u0435\u043D\u0446\u0443 \u0437\u0430 \u0430\u0443\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043A\u0443 \u0438\u043D\u0441\u0442\u0430\u043B\u0430\u0446\u0438\u0458\u0443 JDK-\u0430. ZipExtractionInstaller.DescriptorImpl.displayName=\u0420\u0430\u0441\u043A\u043F\u0430\u043A\u0443\u0458 *.zip/*.tar.gz ZipExtractionInstaller.malformed_url=\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u043D\u0430 URL \u0430\u0434\u0440\u0435\u0441\u0430. ZipExtractionInstaller.bad_connection=\u0421\u0435\u0440\u0432\u0435\u0440 \u0458\u0435 \u043E\u0434\u0431\u0438\u043E \u043F\u043E\u0432\u0435\u0437\u0438\u0432\u0430\u045A\u0435. ZipExtractionInstaller.could_not_connect=\u041D\u0438\u0458\u0435 \u043C\u043E\u0433\u043B\u043E \u0443\u0441\u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0432\u0435\u0437\u0443 \u0441\u0430 URL-\u0430\u0434\u0440\u0435\u0441\u043E\u043C. InstallSourceProperty.DescriptorImpl.displayName=\u0418\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u0458 \u0430\u0443\u0442\u043E\u043C\u0430\u0442\u0441\u043A\u043E -JDKInstaller.DescriptorImpl.displayName=\u0418\u043D\u0441\u0442\u0430\u043B\u0438\u0440\u0430\u0458 \u0441\u0430 \u0430\u0434\u0440\u0435\u0441\u0435 java.sun.com -JDKInstaller.DescriptorImpl.doCheckId=\u041D\u0430\u0432\u0435\u0434\u0438 JDK ID -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=\u041C\u043E\u0440\u0430\u0442\u0435 \u043F\u0440\u0438\u0445\u0432\u0430\u0442\u0438\u0442\u0438 \u043B\u0438\u0446\u0435\u043D\u0446\u0443 \u0434\u0430 \u043F\u0440\u0435\u0443\u0437\u043C\u0435\u0442\u0435 JDK. ToolDescriptor.NotADirectory={0} \u043D\u0438\u0458\u0435 \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u0458\u0443\u043C \u043D\u0430 Jenkins \u043C\u0430\u0448\u0438\u043D\u0438 (\u0430\u043B\u0438 \u043C\u043E\u0433\u0443\u045B\u0435 \u0434\u0430 \u043F\u043E\u0441\u0442\u043E\u0458\u0438 \u043D\u0430 \u043D\u0435\u043A\u0438\u043C \u043E\u0434 \u0430\u0433\u0435\u043D\u0430\u0442\u0430) \ No newline at end of file diff --git a/core/src/main/resources/hudson/tools/Messages_zh_CN.properties b/core/src/main/resources/hudson/tools/Messages_zh_CN.properties index 03c08991d1500617e2539b03617c02cedc6b1c3a..6a6451e149a04e72cee553ae6c7af9d9e19e3f17 100644 --- a/core/src/main/resources/hudson/tools/Messages_zh_CN.properties +++ b/core/src/main/resources/hudson/tools/Messages_zh_CN.properties @@ -20,16 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +ToolLocationNodeProperty.displayName=\u5DE5\u5177\u4F4D\u7F6E CommandInstaller.DescriptorImpl.displayName=\u8FD0\u884C\u547D\u4EE4 CommandInstaller.no_command=\u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u8FD0\u884C\u547D\u4EE4. CommandInstaller.no_toolHome=\u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u5DE5\u5177\u6839\u76EE\u5F55. -JDKInstaller.FailedToInstallJDK=\u5B89\u88C5JDK\u5931\u8D25. \u9519\u8BEF\u4EE3\u7801={0} -JDKInstaller.UnableToInstallUntilLicenseAccepted=\u6CA1\u6709\u63A5\u53D7\u8BB8\u53EF\u4E4B\u524D\u4E0D\u80FD\u591F\u81EA\u52A8\u5B89\u88C5. ZipExtractionInstaller.DescriptorImpl.displayName=\u89E3\u538B *.zip/*.tar.gz ZipExtractionInstaller.bad_connection=\u670D\u52A1\u5668\u62D2\u7EDD\u94FE\u63A5. ZipExtractionInstaller.malformed_url=\u9519\u8BEF\u7684URL. ZipExtractionInstaller.could_not_connect=\u4E0D\u80FD\u94FE\u63A5URL. InstallSourceProperty.DescriptorImpl.displayName=\u81EA\u52A8\u5B89\u88C5 -JDKInstaller.DescriptorImpl.displayName=\u4ECE java.sun.com\u5B89\u88C5 -JDKInstaller.DescriptorImpl.doCheckId=\u5B9A\u4E49JDK ID -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=\u4F60\u5FC5\u987B\u63A5\u53D7\u8BB8\u53EF\u624D\u80FD\u4E0B\u8F7DJDK. \ No newline at end of file diff --git a/core/src/main/resources/hudson/tools/Messages_zh_TW.properties b/core/src/main/resources/hudson/tools/Messages_zh_TW.properties index 800de55010a0e36bd65618efbf7f26e287c9920c..10156f9d77d28dfa813f8ef8060248d77538a07a 100644 --- a/core/src/main/resources/hudson/tools/Messages_zh_TW.properties +++ b/core/src/main/resources/hudson/tools/Messages_zh_TW.properties @@ -24,14 +24,8 @@ ToolLocationNodeProperty.displayName=\u5de5\u5177\u4f4d\u7f6e CommandInstaller.DescriptorImpl.displayName=\u57f7\u884c\u6307\u4ee4 CommandInstaller.no_command=\u5fc5\u9808\u63d0\u4f9b\u6307\u4ee4\u624d\u80fd\u57f7\u884c\u3002 CommandInstaller.no_toolHome=\u5fc5\u9808\u63d0\u4f9b\u5de5\u5177\u4e3b\u76ee\u9304\u3002 -JDKInstaller.FailedToInstallJDK=JDK \u5b89\u88dd\u5931\u6557\u3002\u7d50\u675f\u4ee3\u78bc={0} -JDKInstaller.RequireOracleAccount=\u9700\u8981 Oracle \u5e33\u865f\u624d\u80fd\u5b89\u88dd JDK\u3002\u8acb\u8f38\u5165\u60a8\u7684\u4f7f\u7528\u8005\u540d\u7a31\u53ca\u5bc6\u78bc -JDKInstaller.UnableToInstallUntilLicenseAccepted=\u9664\u975e\u6388\u53d7\u6388\u6b0a\uff0c\u5426\u5247\u7121\u6cd5\u81ea\u52d5\u5b89\u88dd JDK\u3002 ZipExtractionInstaller.DescriptorImpl.displayName=\u89e3\u58d3\u7e2e *.zip/*.tar.gz ZipExtractionInstaller.bad_connection=\u9023\u7dda\u88ab\u4f3a\u670d\u5668\u62d2\u7d55\u3002 ZipExtractionInstaller.malformed_url=URL \u4e0d\u6b63\u78ba\u3002 ZipExtractionInstaller.could_not_connect=\u9023\u4e0d\u5230 URL\u3002 InstallSourceProperty.DescriptorImpl.displayName=\u81ea\u52d5\u5b89\u88dd -JDKInstaller.DescriptorImpl.displayName=\u7531 java.sun.com \u5b89\u88dd -JDKInstaller.DescriptorImpl.doCheckId=\u5b9a\u7fa9 JDK ID -JDKInstaller.DescriptorImpl.doCheckAcceptLicense=\u60a8\u5fc5\u9808\u540c\u610f\u6388\u6b0a\u624d\u80fd\u4e0b\u8f09 JDK\u3002 diff --git a/core/src/main/resources/hudson/tools/ToolInstallation/config_nl.properties b/core/src/main/resources/hudson/tools/ToolInstallation/config_nl.properties index 01b489226e312fc22d9bafa75f64726dc0c27534..f4fd8ecbea0d31acf33d246707bc3bd9768d9ab5 100644 --- a/core/src/main/resources/hudson/tools/ToolInstallation/config_nl.properties +++ b/core/src/main/resources/hudson/tools/ToolInstallation/config_nl.properties @@ -1,3 +1,3 @@ # This file is under the MIT License by authors -Installation\ directory=Installatie directory +Installation\ directory=Installatiemap diff --git a/core/src/main/resources/hudson/tools/ToolInstallation/config_zh_CN.properties b/core/src/main/resources/hudson/tools/ToolInstallation/config_zh_CN.properties deleted file mode 100644 index 466780bb3b01b570f9dcbcf1b4de363a7348e63e..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/ToolInstallation/config_zh_CN.properties +++ /dev/null @@ -1,2 +0,0 @@ -Name=\u522b\u540d -Installation\ directory=\u5b89\u88c5\u76ee\u5f55 diff --git a/core/src/main/resources/hudson/tools/ToolInstallation/global.jelly b/core/src/main/resources/hudson/tools/ToolInstallation/global.jelly index 11c349a26ae2da4492139244a4ea40473a71d4b9..265662ce90da54f31bec975f0b1aa16ccb5affbc 100644 --- a/core/src/main/resources/hudson/tools/ToolInstallation/global.jelly +++ b/core/src/main/resources/hudson/tools/ToolInstallation/global.jelly @@ -28,7 +28,7 @@ THE SOFTWARE. + add="${%label.add(descriptor.displayName)}" header="${descriptor.displayName}" enableTopButton="true">
diff --git a/core/src/main/resources/hudson/tools/ToolInstallation/global_fr.properties b/core/src/main/resources/hudson/tools/ToolInstallation/global_fr.properties index c7b318631c4912202a7ff41980534abb6117c814..b1eebde9a8855b9fd73ce1381086a50385261f2c 100644 --- a/core/src/main/resources/hudson/tools/ToolInstallation/global_fr.properties +++ b/core/src/main/resources/hudson/tools/ToolInstallation/global_fr.properties @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -description=Nombre d''installations {0} sur ce syst\u00E8me +description=Liste des installations {0} sur ce syst\u00E8me label.add=Ajouter {0} label.delete=Supprimer {0} title=Installations {0} diff --git a/core/src/main/resources/hudson/tools/ToolInstallation/global_zh_CN.properties b/core/src/main/resources/hudson/tools/ToolInstallation/global_zh_CN.properties deleted file mode 100644 index 101b7c054467bf22cc7e27428855a14c8d4b3f6e..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/ToolInstallation/global_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Seiji Sogabe -# -# 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. - -title={0} \u5b89\u88c5 -description=\u7CFB\u7EDF\u4E0B{0} \u5B89\u88C5\u5217\u8868 -label.add=\u65b0\u589e {0} -label.delete=\u5220\u9664 {0} diff --git a/core/src/main/resources/hudson/tools/ToolLocationNodeProperty/config_zh_CN.properties b/core/src/main/resources/hudson/tools/ToolLocationNodeProperty/config_zh_CN.properties deleted file mode 100644 index 68bb205d00ecb7d1c60e4509da24f4f5ca45355c..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/ToolLocationNodeProperty/config_zh_CN.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Seiji Sogabe, Eric Lefevre-Ardant -# -# 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. - -List\ of\ tool\ locations=\u5DE5\u5177\u4F4D\u7F6E\u5217\u8868 -Name=\u540D\u79F0 -Home=\u76ee\u5f55 diff --git a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config.jelly b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config.jelly index 952041a19f1d1cc6c9dac8372e9fad028c21f2cf..dc5212de0811c940262bbb5e008672a049721a3a 100644 --- a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config.jelly +++ b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config.jelly @@ -25,7 +25,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_nl.properties b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_nl.properties index 73d983584aec33527db53eeedd4a1402f222c5ca..6d995bac5b6e3d8f545a3c55eb77bf4fde767c72 100644 --- a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_nl.properties +++ b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_nl.properties @@ -20,5 +20,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Download\ URL\ for\ binary\ archive=Download-URL voor binair archief +Download\ URL\ for\ binary\ archive=Download URL voor binair archief Subdirectory\ of\ extracted\ archive=Subdirectory van uitgepakt archief diff --git a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_zh_CN.properties b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_zh_CN.properties deleted file mode 100644 index a0f06c72a3350e14274e8bc8190f110146b5fca1..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/config_zh_CN.properties +++ /dev/null @@ -1,2 +0,0 @@ -Download\ URL\ for\ binary\ archive=\u538b\u7f29\u5305(\u4e8c\u8fdb\u5236)\u7684\u4e0b\u8f7dURL -Subdirectory\ of\ extracted\ archive=\u89e3\u538b\u76ee\u5f55 diff --git a/core/src/main/resources/hudson/tools/label_zh_CN.properties b/core/src/main/resources/hudson/tools/label_zh_CN.properties deleted file mode 100644 index 6b2432d7a6c9188305442f5e4ae721f78212bbd0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/tools/label_zh_CN.properties +++ /dev/null @@ -1 +0,0 @@ -Label=\u6807\u7b7e diff --git a/core/src/main/resources/hudson/triggers/Messages_zh_CN.properties b/core/src/main/resources/hudson/triggers/Messages_zh_CN.properties new file mode 100644 index 0000000000000000000000000000000000000000..447ed23da345576016c17176c866ac079d91dac4 --- /dev/null +++ b/core/src/main/resources/hudson/triggers/Messages_zh_CN.properties @@ -0,0 +1,36 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +SCMTrigger.DisplayName=\u8f6e\u8be2 SCM +SCMTrigger.getDisplayName={0} \u8f6e\u8be2\u65e5\u5fd7 +SCMTrigger.BuildAction.DisplayName=\u8f6e\u8be2\u65e5\u5fd7 +SCMTrigger.no_schedules_no_hooks=\u63d0\u4ea4\u540e\u7684\u94a9\u5b50\u88ab\u5ffd\u7565\uff0c\u5e76\u4e14\u65e0\u8ba1\u5212\uff0c\u56e0\u6b64 SCM \u53d8\u66f4\u4e5f\u4e0d\u4f1a\u8fd0\u884c +SCMTrigger.no_schedules_hooks=\u65e0\u8ba1\u5212\uff0c\u56e0\u6b64\u53ea\u6709\u88ab post-commit \u94a9\u5b50\u89e6\u53d1\u540e\u8fd0\u884c +SCMTrigger.SCMTriggerCause.ShortDescription=\u7531 SCM \u53d8\u66f4\u542f\u52a8 +TimerTrigger.DisplayName=\u5b9a\u65f6\u6784\u5efa +TimerTrigger.MissingWhitespace=\u5728 * \u548c * \u4e4b\u95f4\u7f3a\u5c11\u7a7a\u683c\u3002 +TimerTrigger.no_schedules_so_will_never_run=\u65e0\u8ba1\u5212\u56e0\u6b64\u4e0d\u4f1a\u8fd0\u884c +TimerTrigger.TimerTriggerCause.ShortDescription=\u7531\u5b9a\u65f6\u5668\u542f\u52a8 +TimerTrigger.would_last_have_run_at_would_next_run_at=\u4e0a\u6b21\u8fd0\u884c\u7684\u65f6\u95f4 {0}; \u4e0b\u6b21\u8fd0\u884c\u7684\u65f6\u95f4 {1}. +TimerTrigger.the_specified_cron_tab_is_rare_or_impossible=\u8be5\u8ba1\u5212\u53ea\u4f1a\u5339\u914d\u65e5\u671f\uff08\u4f8b\u5982\uff1a2\u670829\u65e5\uff09\u800c\u4e0d\u662f\uff08\u516d\u670831\u65e5\uff09\uff0c\u56e0\u6b64\u8be5\u4efb\u52a1\u5f88\u5c11\u88ab\u89e6\u53d1\u3002 +Trigger.init=\u4e3a\u89e6\u53d1\u5668\u521d\u59cb\u5316\u5b9a\u65f6\u5668 +SCMTrigger.AdministrativeMonitorImpl.DisplayName=SCM \u8f6e\u8be2\u7ebf\u7a0b\u8fc7\u591a diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/SCMAction/index_zh_CN.properties b/core/src/main/resources/hudson/triggers/SCMTrigger/SCMAction/index_zh_CN.properties deleted file mode 100644 index 6a139eb916b289b24e52ad68399be1f80c364d41..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/SCMAction/index_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -title=9999999999999 diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/config_zh_CN.properties b/core/src/main/resources/hudson/triggers/SCMTrigger/config_zh_CN.properties deleted file mode 100644 index 3e87810019feb7341358198d583158f714f5d799..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/config_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Schedule=\u65E5\u7A0B\u8868 diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount.html index b4e1e8c45ea2826ab873a79eb398aa70a115fb30..5d45a865d2d1d9df90af67bd3f296881bd4b6f86 100644 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount.html +++ b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount.html @@ -1,9 +1,5 @@
- If you use SCM polling to trigger a new build for a large number of projects, - you may want to limit the number of concurrent polling activities to avoid - overloading the server. - + This option configures the maximum number of concurrent threads that are used by Jenkins to poll for SCM changes.

- Setting a positive number sets the upper bound to the number of concurrent polling. - Leaving the field empty will make it unbounded. + Depending on your network connection, SCM service, and Jenkins configuration, you may need to increase or decrease the number of threads to achieve optimal results.

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_bg.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_bg.html deleted file mode 100644 index d3a0eb94077737d26612f8fc7d0164ca9b15e25d..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_bg.html +++ /dev/null @@ -1,10 +0,0 @@ -
- Ако много проекти се изграждат след слушане за промени в системата за контрол - на версиите, може да искате да ограничите общия брой едновременни заявки към - сървъра за версиите, за да не бъде претоварен. - -

- Попълването на положително цяло число ограничава броя на едновременните заявки - за проверка. Ако оставите полето празно, няма да има никакви ограничения в - броя на едновременните заявки. -

diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_de.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_de.html deleted file mode 100644 index 846b1801b50da019fbe997ff5bd83abd8b9fddc2..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_de.html +++ /dev/null @@ -1,10 +0,0 @@ -
- Um eine Überlastung des Servers zu vermeiden, kann es bei einer großen Anzahl - von zu bauenden Projekten sinnvoll sein, die Anzahl gleichzeitiger SCM-Anfragen - zu begrenzen. - -

- Geben Sie eine positive Zahl an, um die Anzahl der gleichzeitigen SCM-Anfragen - auf diesen Wert zu begrenzen. Lassen Sie das Feld frei, wenn Sie keine - Begrenzung wünschen. -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_fr.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_fr.html deleted file mode 100644 index 832dfde213c43a382d632d71ac1ba72a0f00c27b..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_fr.html +++ /dev/null @@ -1,11 +0,0 @@ -
- Si vous utilisez la scrutation de l'outil de gestion de version pour - déclencher un nouveau build pour un grand nombre de projets, vous - souhaiterez peut-être limiter le nombre de scrutations parallèles, afin - d'éviter de surcharger le serveur. - -

- Un nombre positif indique la limite supérieure du nombre de scrutations - parallèles. - Laissez le champ vide pour supprimer toute limite supérieure. -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_it.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_it.html deleted file mode 100644 index 8b6c74881fce17274aaaad6ca48c6a1ee78755fa..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_it.html +++ /dev/null @@ -1,10 +0,0 @@ -
- Se si utilizza il polling del sistema di controllo del codice sorgente per - attivare una nuova compilazione per un numero elevato di progetti, si - potrebbe desiderare di limitare il numero di polling concorrenti per evitare - di sovraccaricare il server. - -

- Impostando un numero positivo si imposta il limite superiore al numero dei - polling concorrenti. Lasciando il campo vuoto non si imposta alcun limite. -

diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ja.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ja.html deleted file mode 100644 index 5bcac2d21cf2a478385fd2a46f8b9411b704a5fe..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ja.html +++ /dev/null @@ -1,8 +0,0 @@ -
- SCMのポーリングを使用してたくさんのプロジェクトの新規ビルドを起動するのなら、 - サーバーの過負荷を避けるために、並行して動くポーリング数を制限してください。 - -

- 正の数を設定すると、並行してポーリングする数の上限を設定します。 - 項目を空欄のままにすると、無制限になります。 -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_nl.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_nl.html deleted file mode 100644 index c4e33a4f95d9fc0a49914bd840574018cd71bb9f..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_nl.html +++ /dev/null @@ -1,10 +0,0 @@ -
- Indien U voor een groot aantal projecten, bouwpogingen start omwille van het - bemonsteren van u versiecontrolesysteem, kan het nuttig zijn om het aantal - parallelle bemonsteringsopdrachten te limiteren, teneinde het versiecontrolesysteem - niet te overbelasten. - -

- Een positieve waarde bepaalt het maximaal aantal parallelle bemonsteringsopdrachten. - Standaard is dit maximum onbegrensd. -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_pt_BR.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_pt_BR.html deleted file mode 100644 index e92297111f075b577d0f5582c8679a87dd1b6d0a..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_pt_BR.html +++ /dev/null @@ -1,9 +0,0 @@ -
- Se você usa a votação de SCM para disparar uma nova construção para um grande número de projetos, - você pode querer limitar o número de atividades de votação concorrentes para evitar - sobrecarregar o servidor. - -

- Informando um número positivo indica o limite superior do número de votações concorrentes. - Deixar o campo vazio o tornará ilimitado. -

diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ru.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ru.html deleted file mode 100644 index 00cfe9e52134c846431ef88ecbf96c0886fc7127..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_ru.html +++ /dev/null @@ -1,9 +0,0 @@ -
- Если вы используете опрос сервера с системой контроля версий для инициации - новой сборки для большого количества проектов, для предотвращения перегрузки - сервера рекомендуется ограничить количество одновременных опросов. -

- Установив положительное значение в это поле, вы зададите верхнюю границу - количества конкурирующих опросов, оставив поле пустым - ограничение - установлено не будет. -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_tr.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_tr.html deleted file mode 100644 index 2cf77917654f7a1f8add1f847792e22ce94e19bf..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_tr.html +++ /dev/null @@ -1,9 +0,0 @@ -
- Çok sayıda proje üzerinde, yapılandırmaları tetiklemek için SCM kontrolleri kullanıyorsanız, - SCM sunucusunu çok fazla yorabilirsiniz. Bunu engellemek için eşzamanlı SCM kontrol sayısını - sınırlamak isteyebilirsiniz. - -

- Bu alana pozitif bir sayı yazarsanız, eşzamanlı SCM kontrolü sayısının üst limiti olacaktır. - Eğer boş bırakırsanız, herhangi bir sınır kullanılmayacaktır. -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_zh_TW.html b/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_zh_TW.html deleted file mode 100644 index 63617d80d3f9d4d32b30cf9c1549318aa94b39e4..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/SCMTrigger/help-pollingThreadCount_zh_TW.html +++ /dev/null @@ -1,6 +0,0 @@ -
- 如果很多專案的建置是由 SCM 輪詢機制觸發,您或許會想限制同時輪詢活動的上限數,免得讓伺服器過忙。 - -

- 正數代表同時輪詢的上限數。不填則代表不限制。 -

\ No newline at end of file diff --git a/core/src/main/resources/hudson/triggers/TimerTrigger/config_zh_CN.properties b/core/src/main/resources/hudson/triggers/TimerTrigger/config_zh_CN.properties deleted file mode 100644 index 3e87810019feb7341358198d583158f714f5d799..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/triggers/TimerTrigger/config_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Schedule=\u65E5\u7A0B\u8868 diff --git a/core/src/main/resources/hudson/util/DoubleLaunchChecker/index_nl.properties b/core/src/main/resources/hudson/util/DoubleLaunchChecker/index_nl.properties index 2f6949342c813ee0b8f7d96a281a2f188e2b7899..83047c54f2f50f5b26c80168cbbc7dd68a2656e8 100644 --- a/core/src/main/resources/hudson/util/DoubleLaunchChecker/index_nl.properties +++ b/core/src/main/resources/hudson/util/DoubleLaunchChecker/index_nl.properties @@ -22,7 +22,7 @@ Error=Fout message=\ - Jenkins heeft meerdere instanties gedetecteerd, die dezelfde \ hoofdfolder ''{0}'' gebruiken. \ + Jenkins heeft meerdere instanties gedetecteerd, die dezelfde hoofdmap ''{0}'' gebruiken. \ Dit kan Jenkins onstabiel maken. \ U loopt het risico geconfronteerd te worden met willekeurig foutief gedrag. \ Gelieve dit probleem te corrigeren. diff --git a/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly b/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly index 625be9422f467f91eb2e3d661d8459a15dfd28d3..75157ee49a48ea46e2dbad8b1f2d271646d498c5 100644 --- a/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly +++ b/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly @@ -23,22 +23,41 @@ THE SOFTWARE. --> - - - - - - - - -

- ${%Please wait while Jenkins is getting ready to work}... -

-

- ${%Your browser will reload automatically when Jenkins is ready.} -

- -
-
+ + + + + + + + + + + ${%Starting Jenkins} + + + + + + + +
+ +
+ + + +
\ No newline at end of file diff --git a/core/src/main/resources/hudson/util/HudsonIsLoading/index_nl.properties b/core/src/main/resources/hudson/util/HudsonIsLoading/index_nl.properties index f67188d6970d7d5dedabd420ec1ce3f5625ca066..9020673e1798b27d5e69b14f0c02029df27f964c 100644 --- a/core/src/main/resources/hudson/util/HudsonIsLoading/index_nl.properties +++ b/core/src/main/resources/hudson/util/HudsonIsLoading/index_nl.properties @@ -21,4 +21,4 @@ # THE SOFTWARE. Please\ wait\ while\ Jenkins\ is\ getting\ ready\ to\ work=Jenkins is aan het opstarten. Gelieve even te wachten. -Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=Uw browser zal uw vraag automatisch herbehandelen wanneer Jenkins volledig opgestart is. +Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=Uw browser zal zich vernieuwen wanneer Jenkins gereed is. diff --git a/core/src/main/resources/hudson/util/HudsonIsLoading/index_zh_CN.properties b/core/src/main/resources/hudson/util/HudsonIsLoading/index_zh_CN.properties deleted file mode 100644 index a19e0c0f16f23ea04ffe83cfd13239675c1f8773..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/util/HudsonIsLoading/index_zh_CN.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -Please\ wait\ while\ Jenkins\ is\ getting\ ready\ to\ work=Jenkins\u6B63\u5728\u542F\u52A8\uFF0C\u8BF7\u7A0D\u540E... -Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=Jenkins\u542F\u52A8\u5B8C\u6210\u540E\u6D4F\u89C8\u5668\u5C06\u81EA\u52A8\u5237\u65B0 diff --git a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly index 65e8bb6e4574591d77bfbd7e21343cefa7ac6cf9..aafd2cfc609b81d5128e354b68ec4a44379ff802 100644 --- a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly +++ b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly @@ -23,22 +23,41 @@ THE SOFTWARE. --> - - - - - - - - -

- ${%Please wait while Jenkins is restarting}... -

-

- ${%Your browser will reload automatically when Jenkins is ready.} -

- -
-
-
\ No newline at end of file + + + + + + + + + + + ${%Restarting Jenkins} + + + + + + + +
+ +
+ + + + +
diff --git a/core/src/main/resources/hudson/util/HudsonIsRestarting/index_nl.properties b/core/src/main/resources/hudson/util/HudsonIsRestarting/index_nl.properties index 579159acbc5145288fd668b9d151ddfa6e2f8af0..343d92136586d32f8119bf4a50d1a01043116050 100644 --- a/core/src/main/resources/hudson/util/HudsonIsRestarting/index_nl.properties +++ b/core/src/main/resources/hudson/util/HudsonIsRestarting/index_nl.properties @@ -21,4 +21,4 @@ # THE SOFTWARE. Please\ wait\ while\ Jenkins\ is\ restarting=Jenkins wordt herstart. Gelieve even te wachten. -Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=Uw browser zal uw vraag automatisch herbehandelen wanneer Jenkins volledig opgestart is. +Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=Uw browser zal zich vernieuwen wanneer Jenkins gereed is. diff --git a/core/src/main/resources/hudson/util/HudsonIsRestarting/index_zh_CN.properties b/core/src/main/resources/hudson/util/HudsonIsRestarting/index_zh_CN.properties deleted file mode 100644 index 4049dcf1b238b3c6efbd677595206fbaec2ca143..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/util/HudsonIsRestarting/index_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready.=\u5F53Jenkins\u5C31\u7EEA\u7684\u65F6\u5019\uFF0C\u4F60\u7684\u6D4F\u89C8\u5668\u4F1A\u81EA\u52A8\u5237\u65B0 diff --git a/core/src/main/resources/hudson/util/IncompatibleServletVersionDetected/index_nl.properties b/core/src/main/resources/hudson/util/IncompatibleServletVersionDetected/index_nl.properties index 5678a1df609a4267d90028f045ca967d9f8ee093..1479f8f9b955896d828a537c7cce9e7f65f05352 100644 --- a/core/src/main/resources/hudson/util/IncompatibleServletVersionDetected/index_nl.properties +++ b/core/src/main/resources/hudson/util/IncompatibleServletVersionDetected/index_nl.properties @@ -22,5 +22,5 @@ Error=Fout errorMessage=\ - Blijkbaar ondersteunt uw servlet container de servlet versie 2.4 niet!\ + We merken dat uw servletcontainer de servlet versie 2.4 niet ondersteunt!\ (De servlet api wordt vanuit volgend pad geladen. : {0}) \ No newline at end of file diff --git a/core/src/main/resources/hudson/util/Messages_zh_CN.properties b/core/src/main/resources/hudson/util/Messages_zh_CN.properties new file mode 100644 index 0000000000000000000000000000000000000000..2e90a1cb7c00105adba954a3a6cb37f15a972df3 --- /dev/null +++ b/core/src/main/resources/hudson/util/Messages_zh_CN.properties @@ -0,0 +1,29 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +ClockDifference.InSync=\u5DF2\u540C\u6B65 +ClockDifference.Ahead={0} \u9760\u524D +ClockDifference.Behind={0} \u843D\u540E +ClockDifference.Failed=\u68C0\u67E5\u5931\u8D25 +FormValidation.ValidateRequired=\u5FC5\u586B\u9879 +FormValidation.Error.Details=\uFF08\u663E\u793A\u8BE6\u60C5\uFF09 +HttpResponses.Saved=\u5DF2\u4FDD\u5B58 diff --git a/core/src/main/resources/hudson/util/NoTempDir/index_nl.properties b/core/src/main/resources/hudson/util/NoTempDir/index_nl.properties index b53ae654106de283577a8e2369b26e73d8f81593..d30aa6e24ce6160a93957058b9ec5e5647477fe0 100644 --- a/core/src/main/resources/hudson/util/NoTempDir/index_nl.properties +++ b/core/src/main/resources/hudson/util/NoTempDir/index_nl.properties @@ -25,6 +25,6 @@ description=\ Kon geen tijdelijk bestand creëeren! De meest waarschijnlijke oorzaak\ hiervoor is een verkeerde configuratie van de container.\ De JVM werd geconfigureerd om volgende folder als tijdelijke \ -folder te gebruiken :{0}\ -Bestaat deze folder? Is deze folder beschrijfbaar door de gebruiker\ +map te gebruiken :{0}\ +Bestaat deze map? Is deze map wijzigbaar door de gebruiker\ waarmee de container opgestart werd? \ No newline at end of file diff --git a/core/src/main/resources/hudson/views/DefaultMyViewsTabBar/myViewTabs_zh_CN.properties b/core/src/main/resources/hudson/views/DefaultMyViewsTabBar/myViewTabs_zh_CN.properties deleted file mode 100644 index 5a8256b5761defbee65fc0549f029027cabeca20..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/DefaultMyViewsTabBar/myViewTabs_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -New\ View=\u65B0\u5EFA\u89C6\u56FE diff --git a/core/src/main/resources/hudson/views/DefaultViewsTabBar/viewTabs_zh_CN.properties b/core/src/main/resources/hudson/views/DefaultViewsTabBar/viewTabs_zh_CN.properties deleted file mode 100644 index 5a8256b5761defbee65fc0549f029027cabeca20..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/DefaultViewsTabBar/viewTabs_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -New\ View=\u65B0\u5EFA\u89C6\u56FE diff --git a/core/src/main/resources/hudson/views/LastDurationColumn/columnHeader_zh_CN.properties b/core/src/main/resources/hudson/views/LastDurationColumn/columnHeader_zh_CN.properties deleted file mode 100644 index ef6873d12980d15b33438e39e505c7d112f54151..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastDurationColumn/columnHeader_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Last\ Duration=\u4E0A\u6B21\u6301\u7EED\u65F6\u95F4 - diff --git a/core/src/main/resources/hudson/views/LastDurationColumn/column_zh_CN.properties b/core/src/main/resources/hudson/views/LastDurationColumn/column_zh_CN.properties deleted file mode 100644 index 1188f1b183cd595a7bca658b9b3cf59f68c4a7f0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastDurationColumn/column_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -N/A=\u65E0 diff --git a/core/src/main/resources/hudson/views/LastFailureColumn/columnHeader_zh_CN.properties b/core/src/main/resources/hudson/views/LastFailureColumn/columnHeader_zh_CN.properties deleted file mode 100644 index 6a2ab75e6678176bb7c1a1f379d95483ff77b299..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastFailureColumn/columnHeader_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Last\ Failure=\u4E0A\u6B21\u5931\u8D25 diff --git a/core/src/main/resources/hudson/views/LastStableColumn/columnHeader_zh_CN.properties b/core/src/main/resources/hudson/views/LastStableColumn/columnHeader_zh_CN.properties deleted file mode 100644 index 9ca68bea5a42249cfdc3157bece7e81f8b35bf25..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastStableColumn/columnHeader_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -Last\ Stable=\u4E0A\u6B21\u6210\u529F diff --git a/core/src/main/resources/hudson/views/LastSuccessColumn/columnHeader_zh_CN.properties b/core/src/main/resources/hudson/views/LastSuccessColumn/columnHeader_zh_CN.properties deleted file mode 100644 index eafe1f82a79d836bfcdba1728a544578c35738bd..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastSuccessColumn/columnHeader_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Last\ Success=\u4E0A\u6B21\u6210\u529F diff --git a/core/src/main/resources/hudson/views/LastSuccessColumn/column_zh_CN.properties b/core/src/main/resources/hudson/views/LastSuccessColumn/column_zh_CN.properties deleted file mode 100644 index ff31e231573ef46b185aa241b09a9c38db7e02d0..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/LastSuccessColumn/column_zh_CN.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -N/A=\u6CA1\u6709 diff --git a/core/src/main/resources/hudson/views/WeatherColumn/columnHeader_zh_CN.properties b/core/src/main/resources/hudson/views/WeatherColumn/columnHeader_zh_CN.properties deleted file mode 100644 index 7da9858119c5210812f3c8dea90f06dc067c4cdc..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/views/WeatherColumn/columnHeader_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Weather\ report\ showing\ aggregated\ status\ of\ recent\ builds=\u7F16\u8BD1\u6674\u96E8\u8868 diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/entry_zh_CN.properties b/core/src/main/resources/hudson/widgets/HistoryWidget/entry_zh_CN.properties deleted file mode 100644 index 50dd98d86ff7156afd52d0d28376bc20f07bb451..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/entry_zh_CN.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -Console\ Output=\u63A7\u5236\u53F0\u8F93\u51FA diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/index_zh_CN.properties b/core/src/main/resources/hudson/widgets/HistoryWidget/index_zh_CN.properties deleted file mode 100644 index 6ad6d3a61d20da835a148a35b01be62dc8d8c769..0000000000000000000000000000000000000000 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/index_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. -# -# 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. - -More\ ...=\u66F4\u591A... -for\ all=\u5168\u90E8 -for\ failures=\u5931\u8D25 -trend=\u6784\u5EFA\u5386\u53F2 diff --git a/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message_zh_CN.properties b/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message_zh_CN.properties deleted file mode 100644 index c4963491f523b0def0ca88be19ef7fbd5167eaca..0000000000000000000000000000000000000000 --- a/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message_zh_CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright 2017 Gentle Yang, vivo.com -# -# 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. - -blurb=\ - \u5f00\u542fjenkins cli\u7684 -remoting \u5de5\u4f5c\u6a21\u5f0f\u662f\u6bd4\u8f83\u5371\u9669\u7684\uff0c\u901a\u5e38\u6ca1\u6709\u5fc5\u8981\u3002\ - \u5efa\u8bae\u7981\u6b62\u8be5\u6a21\u5f0f\u3002 \ - \u8bf7\u53c2\u8003 cli\u6587\u6863 \u4e86\u89e3\u8be6\u60c5\u3002 diff --git a/core/src/main/resources/jenkins/diagnostics/Messages.properties b/core/src/main/resources/jenkins/diagnostics/Messages.properties index 38362ae234a080525b6e242188ddf4a589b5a575..6eb87fc3586fadb960acf35235292b38e2e601f2 100644 --- a/core/src/main/resources/jenkins/diagnostics/Messages.properties +++ b/core/src/main/resources/jenkins/diagnostics/Messages.properties @@ -1,3 +1,4 @@ CompletedInitializationMonitor.DisplayName=Jenkins Initialization Monitor SecurityIsOffMonitor.DisplayName=Disabled Security URICheckEncodingMonitor.DisplayName=Check URI Encoding +RootUrlNotSetMonitor.DisplayName=Root URL not configured Monitor diff --git a/core/src/main/resources/hudson/views/StatusColumn/columnHeader_zh_CN.properties b/core/src/main/resources/jenkins/diagnostics/Messages_zh_CN.properties similarity index 75% rename from core/src/main/resources/hudson/views/StatusColumn/columnHeader_zh_CN.properties rename to core/src/main/resources/jenkins/diagnostics/Messages_zh_CN.properties index a59cd56ef2fbdfc83acbe0dc17809ee79d71d76d..4616baa24972a5289377ba62181823a54957cc43 100644 --- a/core/src/main/resources/hudson/views/StatusColumn/columnHeader_zh_CN.properties +++ b/core/src/main/resources/jenkins/diagnostics/Messages_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2018, suren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Status\ of\ the\ last\ build=\u4E0A\u6B21\u6784\u5EFA\u72B6\u6001 +CompletedInitializationMonitor.DisplayName=Jenkins \u521D\u59CB\u5316\u76D1\u63A7\u5668 +SecurityIsOffMonitor.DisplayName=\u7981\u7528\u5B89\u5168 +URICheckEncodingMonitor.DisplayName=\u68C0\u67E5 URI \u7F16\u7801 +RootUrlNotSetMonitor.DisplayName=\u76D1\u89C6\u5668\u7684\u6839 URL \u6CA1\u6709\u914D\u7F6E diff --git a/core/src/main/resources/hudson/tools/JDKInstaller/config.jelly b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly similarity index 58% rename from core/src/main/resources/hudson/tools/JDKInstaller/config.jelly rename to core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly index 83d22dbd9be6239941377ace7034684f5e644ae2..13d15b4cdd0a3cae604941e6a2cf08a3d25866cc 100644 --- a/core/src/main/resources/hudson/tools/JDKInstaller/config.jelly +++ b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly @@ -1,7 +1,7 @@ - - +
+
+ + + + ${%actionUrlContent} + + - - - - + + ${%urlIsNull} - + ${%urlIsInvalid} - - - - +

+ +

diff --git a/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties new file mode 100644 index 0000000000000000000000000000000000000000..d1153159c74f9ab9c84a34e6c0e8f0f69a53795c --- /dev/null +++ b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties @@ -0,0 +1,6 @@ +urlIsNull=Jenkins root URL is empty but is required for the proper operation of many Jenkins features like email notifications, \ +PR status update, and environment variables such as BUILD_URL. +urlIsInvalid=Jenkins root URL seems to be invalid. It is required for the proper operation of many Jenkins features like email notifications, \ +PR status update, and environment variables such as BUILD_URL. +actionToTake=Please provide an accurate value in {0}. +actionUrlContent=Jenkins configuration diff --git a/core/src/main/resources/jenkins/diagnostics/SecurityIsOffMonitor/message_zh_CN.properties b/core/src/main/resources/jenkins/diagnostics/SecurityIsOffMonitor/message_zh_CN.properties deleted file mode 100644 index 920e3df570e452eeef681a880bbbf433b3898159..0000000000000000000000000000000000000000 --- a/core/src/main/resources/jenkins/diagnostics/SecurityIsOffMonitor/message_zh_CN.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -Dismiss=\u5FFD\u7565 -Setup\ Security=\u5B89\u5168\u8BBE\u7F6E -blurb=\u4E0D\u5B89\u5168\u7684Jenkins\u5141\u8BB8\u7F51\u7EDC\u4E0A\u7684\u4EFB\u4F55\u4EBA\u4EE5\u4F60\u7684\u8EAB\u4EFD\u8BBF\u95EE\u7A0B\u5E8F\u3002\u8003\u8651\u81F3\u5C11\u542F\u7528\u8EAB\u4EFD\u9A8C\u8BC1\u6765\u963B\u6B62\u6EE5\u7528\u3002 diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_fr.properties b/core/src/main/resources/jenkins/install/Messages.properties similarity index 75% rename from core/src/main/resources/hudson/model/AbstractModelObject/editDescription_fr.properties rename to core/src/main/resources/jenkins/install/Messages.properties index 526a39d3d025bb51117149865bc0e881f94093be..52b1b94367abd8ffbc25002a868de37644e0ee26 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_fr.properties +++ b/core/src/main/resources/jenkins/install/Messages.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant +# Copyright 2018 CloudBees, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -19,5 +19,6 @@ # 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. - -Submit=Envoyer +SetupWizard_ConfigureInstance_ValidationErrors=Some settings are invalid. See the error messages for details. +SetupWizard_ConfigureInstance_RootUrl_Empty=The URL cannot be empty +SetupWizard_ConfigureInstance_RootUrl_Invalid=The URL is invalid, please ensure you are using http:// or https:// with a valid domain. diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ja.properties b/core/src/main/resources/jenkins/install/Messages_zh_CN.properties similarity index 71% rename from core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ja.properties rename to core/src/main/resources/jenkins/install/Messages_zh_CN.properties index 71ba9fccbb703f58495dd40adbb19c3d99e29c0f..4baea254d148fb45b11fec241b63839a3e2eb976 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription_ja.properties +++ b/core/src/main/resources/jenkins/install/Messages_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe +# Copyright 2018 suren # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Submit=\u4fdd\u5b58 +SetupWizard_ConfigureInstance_ValidationErrors=\u6709\u975E\u6CD5\u8BBE\u7F6E\u3002\u8BF7\u67E5\u770B\u9519\u8BEF\u4FE1\u606F\u4E2D\u8BE6\u60C5\u3002 +SetupWizard_ConfigureInstance_RootUrl_Empty=URL \u4E0D\u80FD\u4E3A\u7A7A +SetupWizard_ConfigureInstance_RootUrl_Invalid=URL \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u786E\u8BA4\u4F60\u4F7F\u7528 http:// \u6216 https:// \u7B49\u5408\u6CD5\u7684\u57DF\u540D\u3002 diff --git a/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token_zh_CN.properties b/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token_zh_CN.properties deleted file mode 100644 index 9f59b2197597ddc0e7e16da9b9a04e9c713dfd18..0000000000000000000000000000000000000000 --- a/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token_zh_CN.properties +++ /dev/null @@ -1,32 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017-, vivo.com -# Chinese (Simplified) translated by Gentle Yang -# -# 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. - -authenticate-security-token.getting.started=\u5165\u95e8 -authenticate-security-token.unlock.jenkins=\u89e3\u9501jenkins -jenkins.install.findSecurityTokenMessage=\u4e3a\u4e86\u786e\u4fdd\u7ba1\u7406\u5458\u5b89\u5168\u5730\u5b89\u88c5jenkins\uff0c\ -\u5bc6\u7801\u5df2\u5199\u5165\u5230\u65e5\u5fd7\u4e2d\uff08\u4e0d\u77e5\u9053\u5728\u54ea\u91cc\uff1f\uff09\u8be5\u6587\u4ef6\u5728\u670d\u52a1\u5668\u4e0a\uff1a

{0}

-authenticate-security-token.copy.password=\u8bf7\u4ece\u672c\u5730\u590d\u5236\u5bc6\u7801\u5e76\u7c98\u8d34\u5230\u4e0b\u9762\u3002 -authenticate-security-token.error=\u9519\u8bef\uff1a -authenticate-security-token.password.incorrect=\u8f93\u5165\u7684\u5bc6\u7801\u6709\u8bef\uff0c\u8bf7\u68c0\u67e5\u6587\u4ef6\u4ee5\u786e\u8ba4\u5bc6\u7801\u6b63\u786e -authenticate-security-token.password.administrator=\u7ba1\u7406\u5458\u5bc6\u7801 -authenticate-security-token.continue=\u7ee7\u7eed diff --git a/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration_zh_CN.properties b/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration_zh_CN.properties deleted file mode 100644 index 75ddbfd75276638413a4c8735d1121295b2d0321..0000000000000000000000000000000000000000 --- a/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration_zh_CN.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017-, vivo.com -# Chinese (Simplified) translated by Gentle Yang -# -# 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. - -HTTP\ Proxy\ Configuration=\u914d\u7f6eHTTP\u4ee3\u7406 diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly new file mode 100644 index 0000000000000000000000000000000000000000..6db422d7138804f0d258a0348dbcecfa845811f0 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly @@ -0,0 +1,130 @@ + + + + + + + + + +
+
+

${%Instance Configuration}

+
+
+ + + + + + + +
+ +
+
+
${%jenkinsURL_help}
+
+
+ + + + + +
diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties new file mode 100644 index 0000000000000000000000000000000000000000..4d08f82ba28a6fd463925f0cb41cfe3d95f71270 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties @@ -0,0 +1,6 @@ +Instance\ Configuration=Instance Configuration +Jenkins\ URL=Jenkins URL +jenkinsURL_help=The Jenkins URL is used to provide the root URL for absolute links to various Jenkins resources. \ +That means this value is required for proper operation of many Jenkins features including email notifications, PR status updates, and the BUILD_URL environment variable provided to build steps.
\ +The proposed default value shown is not saved yet and is generated from the current request, if possible. \ +The best practice is to set this value to the URL that users are expected to use. This will avoid confusion when sharing or viewing links. diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties new file mode 100644 index 0000000000000000000000000000000000000000..7c8aae3d617e3be91be3b4edcad266e9bd261d28 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties @@ -0,0 +1,2 @@ +Instance\ Configuration=Configuration de l''instance +Jenkins\ URL=Jenkins URL diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly index 788b353a9eaca45056f820dcc29c51520b6e97e0..96e972b9cad73b2d41cae51d0b68d79da1564698 100644 --- a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly @@ -7,7 +7,7 @@