args = new ArrayList<>();
- Locale locale = Locale.getDefault();
- Charset encoding = Charset.defaultCharset();
- final PipedInputStream stdin = new PipedInputStream();
- final PipedOutputStream stdinMatch = new PipedOutputStream();
- ServerSideImpl(InputStream is, OutputStream os) throws IOException {
- super(is, os);
- stdinMatch.connect(stdin);
- }
- @Override
- protected void onArg(String text) {
- args.add(text);
- }
- @Override
- protected void onLocale(String text) {
- for (Locale _locale : Locale.getAvailableLocales()) {
- if (_locale.toString().equals(text)) {
- locale = _locale;
- return;
- }
- }
- LOGGER.log(Level.WARNING, "unknown client locale {0}", text);
- }
- @Override
- protected void onEncoding(String text) {
- try {
- encoding = Charset.forName(text);
- } catch (UnsupportedCharsetException x) {
- LOGGER.log(Level.WARNING, "unknown client charset {0}", text);
- }
- }
- @Override
- protected void onStart() {
- ready();
- }
- @Override
- protected void onStdin(byte[] chunk) throws IOException {
- stdinMatch.write(chunk);
- }
- @Override
- protected void onEndStdin() throws IOException {
- stdinMatch.close();
- }
- @Override
- protected void handleClose() {
- ready();
- Thread t = runningThread.get();
- if (t != null) {
- t.interrupt();
- }
- }
- private synchronized void ready() {
- ready = true;
- notifyAll();
- }
- }
- try (ServerSideImpl connection = new ServerSideImpl(upload, download)) {
- connection.begin();
- synchronized (connection) {
- while (!connection.ready) {
- connection.wait();
- }
- }
- PrintStream stdout = new PrintStream(connection.streamStdout(), false, connection.encoding.name());
- PrintStream stderr = new PrintStream(connection.streamStderr(), true, connection.encoding.name());
- if (connection.args.isEmpty()) {
- stderr.println("Connection closed before arguments received");
- connection.sendExit(2);
- return;
- }
- String commandName = connection.args.get(0);
- CLICommand command = CLICommand.clone(commandName);
- if (command == null) {
- stderr.println("No such command " + commandName);
- connection.sendExit(2);
- return;
- }
- command.setTransportAuth(Jenkins.getAuthentication());
- command.setClientCharset(connection.encoding);
- CLICommand orig = CLICommand.setCurrent(command);
- try {
- runningThread.set(Thread.currentThread());
- int exit = command.main(connection.args.subList(1, connection.args.size()), connection.locale, connection.stdin, stdout, stderr);
- stdout.flush();
- connection.sendExit(exit);
- try { // seems to avoid ReadPendingException from Jetty
- Thread.sleep(1000);
- } catch (InterruptedException x) {
- // expected; ignore
- }
- } finally {
- CLICommand.setCurrent(orig);
- runningThread.set(null);
- }
+ try (ServerSideImpl connection = new ServerSideImpl(new PlainCLIProtocol.FramedOutput(download), Jenkins.getAuthentication())) {
+ new PlainCLIProtocol.FramedReader(connection, upload).start();
+ connection.run();
}
}
};
diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
index 22237a7e47..68747a6a8c 100644
--- a/core/src/main/java/hudson/cli/CLICommand.java
+++ b/core/src/main/java/hudson/cli/CLICommand.java
@@ -244,8 +244,6 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
if (!(this instanceof HelpCommand || this instanceof WhoAmICommand))
Jenkins.get().checkPermission(Jenkins.READ);
p.parseArgument(args.toArray(new String[0]));
- if (!(this instanceof HelpCommand || this instanceof WhoAmICommand))
- Jenkins.get().checkPermission(Jenkins.READ);
LOGGER.log(Level.FINE, "Invoking CLI command {0}, with {1} arguments, as user {2}.",
new Object[] {getName(), args.size(), auth.getName()});
int res = run();
diff --git a/core/src/main/java/hudson/cli/ConnectNodeCommand.java b/core/src/main/java/hudson/cli/ConnectNodeCommand.java
index 16fc8f9bf5..0a9a152e22 100644
--- a/core/src/main/java/hudson/cli/ConnectNodeCommand.java
+++ b/core/src/main/java/hudson/cli/ConnectNodeCommand.java
@@ -26,9 +26,6 @@ package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.model.Computer;
-import hudson.model.ComputerSet;
-import hudson.util.EditDistance;
-import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
diff --git a/core/src/main/java/hudson/cli/OnlineNodeCommand.java b/core/src/main/java/hudson/cli/OnlineNodeCommand.java
index 4777afcd37..e4a56ef8ed 100644
--- a/core/src/main/java/hudson/cli/OnlineNodeCommand.java
+++ b/core/src/main/java/hudson/cli/OnlineNodeCommand.java
@@ -27,9 +27,6 @@ package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.model.Computer;
-import hudson.model.ComputerSet;
-import hudson.util.EditDistance;
-import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;
diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
index c45eebd711..856cbb0cec 100644
--- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
@@ -57,8 +57,6 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
index 52c327e350..00a82dbf69 100644
--- a/core/src/main/java/hudson/model/AbstractProject.java
+++ b/core/src/main/java/hudson/model/AbstractProject.java
@@ -665,6 +665,7 @@ public abstract class AbstractProject,R extends A
}
@Override
+ @Exported
public boolean isDisabled() {
return disabled;
}
diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
index fb45e0a5a6..0f02f1c2a2 100644
--- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
+++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
@@ -24,7 +24,6 @@
package hudson.model;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import hudson.ExtensionList;
import hudson.FilePath;
import hudson.Util;
import java.io.IOException;
diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java
index b9b0c6b680..5ddd52ecda 100644
--- a/core/src/main/java/hudson/model/ListView.java
+++ b/core/src/main/java/hudson/model/ListView.java
@@ -29,7 +29,6 @@ import hudson.Util;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.ItemListener;
-import hudson.search.CollectionSearchIndex;
import hudson.search.SearchIndexBuilder;
import hudson.security.ACL;
import hudson.security.ACLContext;
diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java
index 29856dbcfa..75f88ea92e 100644
--- a/core/src/main/java/hudson/model/Slave.java
+++ b/core/src/main/java/hudson/model/Slave.java
@@ -407,23 +407,19 @@ public abstract class Slave extends Node implements Serializable {
if (!ALLOWED_JNLPJARS_FILES.contains(name)) {
throw new MalformedURLException("The specified file path " + fileName + " is not allowed due to security reasons");
}
-
+
+ Class> owner = null;
if (name.equals("hudson-cli.jar") || name.equals("jenkins-cli.jar")) {
- File cliJar = Which.jarFile(CLI.class);
- if (cliJar.isFile()) {
- name = "jenkins-cli.jar";
- } else {
- URL res = findExecutableJar(cliJar, CLI.class);
- if (res != null) {
- return res;
- }
- }
+ owner = CLI.class;
} else if (name.equals("agent.jar") || name.equals("slave.jar") || name.equals("remoting.jar")) {
- File remotingJar = Which.jarFile(hudson.remoting.Launcher.class);
- if (remotingJar.isFile()) {
- name = "lib/" + remotingJar.getName();
+ owner = hudson.remoting.Launcher.class;
+ }
+ if (owner != null) {
+ File jar = Which.jarFile(owner);
+ if (jar.isFile()) {
+ name = "lib/" + jar.getName();
} else {
- URL res = findExecutableJar(remotingJar, hudson.remoting.Launcher.class);
+ URL res = findExecutableJar(jar, owner);
if (res != null) {
return res;
}
diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java
index 54f9db6c6a..c49c795ed2 100644
--- a/core/src/main/java/hudson/model/User.java
+++ b/core/src/main/java/hudson/model/User.java
@@ -31,7 +31,6 @@ import hudson.CopyOnWrite;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
-import hudson.FeedAdapter;
import hudson.Util;
import hudson.XmlFile;
import hudson.init.InitMilestone;
diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java
index d310c5295c..f51a3dd958 100644
--- a/core/src/main/java/hudson/model/View.java
+++ b/core/src/main/java/hudson/model/View.java
@@ -33,7 +33,6 @@ import hudson.Functions;
import hudson.Indenter;
import hudson.Util;
import hudson.model.Descriptor.FormException;
-import hudson.model.labels.LabelAtomPropertyDescriptor;
import hudson.model.listeners.ItemListener;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
diff --git a/core/src/main/java/hudson/slaves/ComputerLauncher.java b/core/src/main/java/hudson/slaves/ComputerLauncher.java
index 7efb9f4a41..38ee6d70be 100644
--- a/core/src/main/java/hudson/slaves/ComputerLauncher.java
+++ b/core/src/main/java/hudson/slaves/ComputerLauncher.java
@@ -180,7 +180,7 @@ public abstract class ComputerLauncher extends AbstractDescribableImpl contextClass, @Nonnull Descriptor descriptor) {
- return descriptor.clazz != JNLPLauncher.class || Jenkins.get().getTcpSlaveAgentListener() != null;
- }
}
/**
diff --git a/core/src/main/java/hudson/triggers/SafeTimerTask.java b/core/src/main/java/hudson/triggers/SafeTimerTask.java
index 47654b2550..73409e4ae4 100644
--- a/core/src/main/java/hudson/triggers/SafeTimerTask.java
+++ b/core/src/main/java/hudson/triggers/SafeTimerTask.java
@@ -50,6 +50,27 @@ import jenkins.util.Timer;
*/
public abstract class SafeTimerTask extends TimerTask {
+ /**
+ * Lambda-friendly means of creating a task.
+ * @since TODO
+ */
+ public static SafeTimerTask of(ExceptionRunnable r) {
+ return new SafeTimerTask() {
+ @Override
+ protected void doRun() throws Exception {
+ r.run();
+ }
+ };
+ }
+ /**
+ * @see #of
+ * @since TODO
+ */
+ @FunctionalInterface
+ public interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
/**
* System property to change the location where (tasks) logging should be sent.
* Beware: changing it while Jenkins is running gives no guarantee logs will be sent to the new location
diff --git a/core/src/main/java/hudson/util/FormValidation.java b/core/src/main/java/hudson/util/FormValidation.java
index e632e7bb1b..94a0c4b2ac 100644
--- a/core/src/main/java/hudson/util/FormValidation.java
+++ b/core/src/main/java/hudson/util/FormValidation.java
@@ -23,7 +23,6 @@
*/
package hudson.util;
-import hudson.EnvVars;
import hudson.Functions;
import hudson.Launcher;
import hudson.ProxyConfiguration;
diff --git a/core/src/main/java/jenkins/agents/WebSocketAgents.java b/core/src/main/java/jenkins/agents/WebSocketAgents.java
new file mode 100644
index 0000000000..965c3b67eb
--- /dev/null
+++ b/core/src/main/java/jenkins/agents/WebSocketAgents.java
@@ -0,0 +1,191 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2019 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.agents;
+
+import com.google.common.collect.ImmutableMap;
+import hudson.Extension;
+import hudson.ExtensionList;
+import hudson.model.Computer;
+import hudson.model.InvisibleAction;
+import hudson.model.UnprotectedRootAction;
+import hudson.remoting.AbstractByteArrayCommandTransport;
+import hudson.remoting.Capability;
+import hudson.remoting.Channel;
+import hudson.remoting.ChannelBuilder;
+import hudson.remoting.Engine;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jenkins.slaves.JnlpAgentReceiver;
+import jenkins.slaves.RemotingVersionInfo;
+import jenkins.websocket.WebSocketSession;
+import jenkins.websocket.WebSockets;
+import org.jenkinsci.remoting.engine.JnlpConnectionState;
+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;
+
+@Extension
+@Restricted(NoExternalUse.class)
+public final class WebSocketAgents extends InvisibleAction implements UnprotectedRootAction {
+
+ private static final Logger LOGGER = Logger.getLogger(WebSocketAgents.class.getName());
+
+ @Override
+ public String getUrlName() {
+ return WebSockets.isSupported() ? "wsagents" : null;
+ }
+
+ public HttpResponse doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException {
+ String agent = req.getHeader(JnlpConnectionState.CLIENT_NAME_KEY);
+ String secret = req.getHeader(JnlpConnectionState.SECRET_KEY);
+ String remoteCapabilityStr = req.getHeader(Capability.KEY);
+ if (agent == null || secret == null || remoteCapabilityStr == null) {
+ LOGGER.warning(() -> "incomplete headers: " + Collections.list(req.getHeaderNames()));
+ throw HttpResponses.errorWithoutStack(400, "This endpoint is only for use from agent.jar in WebSocket mode");
+ }
+ LOGGER.fine(() -> "receiving headers: " + Collections.list(req.getHeaderNames()));
+ if (!JnlpAgentReceiver.DATABASE.exists(agent)) {
+ LOGGER.warning(() -> "no such agent " + agent);
+ throw HttpResponses.errorWithoutStack(400, "no such agent");
+ }
+ if (!MessageDigest.isEqual(secret.getBytes(StandardCharsets.US_ASCII), JnlpAgentReceiver.DATABASE.getSecretOf(agent).getBytes(StandardCharsets.US_ASCII))) {
+ LOGGER.warning(() -> "incorrect secret for " + agent);
+ throw HttpResponses.forbidden();
+ }
+ JnlpConnectionState state = new JnlpConnectionState(null, ExtensionList.lookup(JnlpAgentReceiver.class));
+ state.setRemoteEndpointDescription(req.getRemoteAddr());
+ state.fireBeforeProperties();
+ LOGGER.fine(() -> "connecting " + agent);
+ state.fireAfterProperties(ImmutableMap.of(
+ // TODO or just pass all request headers?
+ JnlpConnectionState.CLIENT_NAME_KEY, agent,
+ JnlpConnectionState.SECRET_KEY, secret
+ ));
+ Capability remoteCapability = Capability.fromASCII(remoteCapabilityStr);
+ LOGGER.fine(() -> "received " + remoteCapability);
+ rsp.setHeader(Capability.KEY, new Capability().toASCII());
+ rsp.setHeader(Engine.REMOTING_MINIMUM_VERSION_HEADER, RemotingVersionInfo.getMinimumSupportedVersion().toString());
+ rsp.setHeader(JnlpConnectionState.COOKIE_KEY, JnlpAgentReceiver.generateCookie()); // TODO figure out what this is for, if anything
+ return WebSockets.upgrade(new Session(state, agent, remoteCapability));
+ }
+
+ private static class Session extends WebSocketSession {
+
+ private final JnlpConnectionState state;
+ private final String agent;
+ private final Capability remoteCapability;
+ private AbstractByteArrayCommandTransport.ByteArrayReceiver receiver;
+
+ Session(JnlpConnectionState state, String agent, Capability remoteCapability) {
+ this.state = state;
+ this.agent = agent;
+ this.remoteCapability = remoteCapability;
+ }
+
+ @Override
+ protected void opened() {
+ Computer.threadPoolForRemoting.submit(() -> {
+ LOGGER.fine(() -> "setting up channel for " + agent);
+ state.fireBeforeChannel(new ChannelBuilder(agent, Computer.threadPoolForRemoting));
+ state.fireAfterChannel(state.getChannelBuilder().build(new Transport()));
+ LOGGER.fine(() -> "set up channel for " + agent);
+ return null;
+ });
+ }
+
+ @Override
+ protected void binary(byte[] payload, int offset, int len) {
+ LOGGER.finest(() -> "reading block of length " + len + " from " + agent);
+ if (offset == 0 && len == payload.length) {
+ receiver.handle(payload);
+ } else {
+ receiver.handle(Arrays.copyOfRange(payload, offset, offset + len));
+ }
+ }
+
+ @Override
+ protected void closed(int statusCode, String reason) {
+ LOGGER.finest(() -> "closed " + statusCode + " " + reason);
+ IOException x = new ClosedChannelException();
+ receiver.terminate(x);
+ state.fireChannelClosed(x);
+ state.fireAfterDisconnect();
+ }
+
+ @Override
+ protected void error(Throwable cause) {
+ LOGGER.log(Level.WARNING, null, cause);
+ }
+
+ class Transport extends AbstractByteArrayCommandTransport {
+
+ @Override
+ public void setup(AbstractByteArrayCommandTransport.ByteArrayReceiver bar) {
+ receiver = bar;
+ }
+
+ @Override
+ public void writeBlock(Channel chnl, byte[] bytes) throws IOException {
+ LOGGER.finest(() -> "writing block of length " + bytes.length + " to " + agent);
+ try {
+ sendBinary(ByteBuffer.wrap(bytes)).get();
+ } catch (Exception x) {
+ x.printStackTrace();
+ throw new IOException(x);
+ }
+ }
+
+ @Override
+ public Capability getRemoteCapability() throws IOException {
+ return remoteCapability;
+ }
+
+ @Override
+ public void closeWrite() throws IOException {
+ LOGGER.finest(() -> "closeWrite");
+ close();
+ }
+
+ @Override
+ public void closeRead() throws IOException {
+ LOGGER.finest(() -> "closeRead");
+ close();
+ }
+
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
index 3a99bd199e..4f526e9774 100644
--- a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
+++ b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
@@ -7,7 +7,6 @@ import hudson.XmlFile;
import hudson.model.PersistentDescriptor;
import hudson.util.FormValidation;
import hudson.util.XStream2;
-import jenkins.security.ApiTokenProperty;
import jenkins.util.SystemProperties;
import jenkins.util.UrlHelper;
import org.jenkinsci.Symbol;
diff --git a/core/src/main/java/jenkins/mvn/FilePathGlobalSettingsProvider.java b/core/src/main/java/jenkins/mvn/FilePathGlobalSettingsProvider.java
index ee7b81cae7..3adc364c1e 100644
--- a/core/src/main/java/jenkins/mvn/FilePathGlobalSettingsProvider.java
+++ b/core/src/main/java/jenkins/mvn/FilePathGlobalSettingsProvider.java
@@ -1,14 +1,9 @@
package jenkins.mvn;
-import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
-import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.TaskListener;
-import hudson.util.IOUtils;
-
-import java.io.File;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
diff --git a/core/src/main/java/jenkins/mvn/FilePathSettingsProvider.java b/core/src/main/java/jenkins/mvn/FilePathSettingsProvider.java
index bbc95005fb..faaec2da0b 100644
--- a/core/src/main/java/jenkins/mvn/FilePathSettingsProvider.java
+++ b/core/src/main/java/jenkins/mvn/FilePathSettingsProvider.java
@@ -1,14 +1,9 @@
package jenkins.mvn;
-import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
-import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.TaskListener;
-import hudson.util.IOUtils;
-
-import java.io.File;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
diff --git a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
index 7f42c3836e..1d052cff3d 100644
--- a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
+++ b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
@@ -115,7 +115,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver {
+ "Set system property "
+ "jenkins.slaves.DefaultJnlpSlaveReceiver.disableStrictVerification=true to allow"
+ "connections until the plugin has been fixed.",
- new Object[]{clientName, event.getSocket().getRemoteSocketAddress(), computer.getLauncher().getClass()});
+ new Object[]{clientName, event.getRemoteEndpointDescription(), computer.getLauncher().getClass()});
event.reject(new ConnectionRefusalException(String.format("%s is not an inbound agent", clientName)));
return;
}
@@ -151,7 +151,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver {
final OutputStream log = computer.openLogFile();
state.setLog(log);
PrintWriter logw = new PrintWriter(log, true);
- logw.println("Inbound agent connected from " + event.getSocket().getInetAddress());
+ logw.println("Inbound agent connected from " + event.getRemoteEndpointDescription());
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
cc.onChannelBuilding(event.getChannelBuilder(), computer);
}
diff --git a/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java b/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
index 5464056fdb..b738b3316a 100644
--- a/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
+++ b/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
@@ -7,12 +7,13 @@ import hudson.model.Slave;
import java.security.SecureRandom;
import javax.annotation.Nonnull;
+import jenkins.agents.WebSocketAgents;
import jenkins.security.HMACConfidentialKey;
import org.jenkinsci.remoting.engine.JnlpClientDatabase;
import org.jenkinsci.remoting.engine.JnlpConnectionStateListener;
/**
- * Receives incoming agents connecting through {@link JnlpSlaveAgentProtocol4}.
+ * Receives incoming agents connecting through the likes of {@link JnlpSlaveAgentProtocol4} or {@link WebSocketAgents}.
*
*
* This is useful to establish the communication with other JVMs and use them
diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java
index 5eeaece2fd..2cd8aa6c9c 100644
--- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java
+++ b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java
@@ -104,7 +104,9 @@ public class MissingClassTelemetry extends Telemetry {
{"java.util.ResourceBundle$Control", "newBundle"},
//hundreds when a job is created
{"org.codehaus.groovy.control.ClassNodeResolver", "tryAsLoaderClassOrScript"},
- {"org.kohsuke.stapler.RequestImpl$TypePair", "convertJSON"}
+ {"org.kohsuke.stapler.RequestImpl$TypePair", "convertJSON"},
+ {"net.bull.javamelody.FilterContext", "isMojarraAvailable"} // JENKINS-60725
+
};
@Nonnull
diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java
index 36588141fb..6a2b5f8765 100644
--- a/core/src/main/java/jenkins/util/AntClassLoader.java
+++ b/core/src/main/java/jenkins/util/AntClassLoader.java
@@ -49,7 +49,6 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
diff --git a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
index d0cfc53911..7b70413a48 100644
--- a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
+++ b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
@@ -1,7 +1,5 @@
package jenkins.util;
-import org.apache.tools.ant.Project;
-
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
diff --git a/core/src/main/java/jenkins/websocket/WebSocketEcho.java b/core/src/main/java/jenkins/websocket/WebSocketEcho.java
new file mode 100644
index 0000000000..5aa8b432c2
--- /dev/null
+++ b/core/src/main/java/jenkins/websocket/WebSocketEcho.java
@@ -0,0 +1,67 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2019 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.websocket;
+
+import hudson.Extension;
+import hudson.model.InvisibleAction;
+import hudson.model.RootAction;
+import java.nio.ByteBuffer;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.stapler.HttpResponse;
+
+@Extension
+@Restricted(NoExternalUse.class)
+public class WebSocketEcho extends InvisibleAction implements RootAction {
+
+ @Override
+ public String getUrlName() {
+ return "wsecho";
+ }
+
+ public HttpResponse doIndex() {
+ Jenkins.get().checkPermission(Jenkins.ADMINISTER);
+ return WebSockets.upgrade(new WebSocketSession() {
+ @Override
+ protected void text(String message) {
+ sendText("hello " + message);
+ }
+ @Override
+ protected void binary(byte[] payload, int offset, int len) {
+ ByteBuffer data = ByteBuffer.allocate(len);
+ for (int i = 0; i < len; i++) {
+ byte b = payload[offset + i];
+ if (b >= 'a' && b <= 'z') {
+ b += 'A' - 'a';
+ }
+ data.put(i, b);
+ }
+ sendBinary(data);
+ }
+ });
+ }
+
+}
diff --git a/core/src/main/java/jenkins/websocket/WebSocketSession.java b/core/src/main/java/jenkins/websocket/WebSocketSession.java
new file mode 100644
index 0000000000..95a7ed7609
--- /dev/null
+++ b/core/src/main/java/jenkins/websocket/WebSocketSession.java
@@ -0,0 +1,150 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2019 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.websocket;
+
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jenkins.util.SystemProperties;
+import jenkins.util.Timer;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+/**
+ * One WebSocket connection.
+ * @see WebSockets
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public abstract class WebSocketSession {
+
+ /**
+ * Number of seconds between server-sent pings.
+ * Zero to disable.
+ *
nginx docs claim 60s timeout and this seems to match experiments.
+ * GKE docs says 30s
+ * but this is a total timeout, not inactivity, so you need to set {@code BackendConfigSpec.timeoutSec} anyway.
+ *
This is set for the whole Jenkins session rather than a particular service,
+ * since it has more to do with the environment than anything else.
+ * Certain services may have their own “keep alive” semantics,
+ * but for example {@link hudson.remoting.PingThread} may be too infrequent.
+ */
+ private static long PING_INTERVAL_SECONDS = SystemProperties.getLong("jenkins.websocket.pingInterval", 30L);
+
+ private static final Logger LOGGER = Logger.getLogger(WebSocketSession.class.getName());
+
+ private Object session;
+ private Object remoteEndpoint;
+ private ScheduledFuture> pings;
+
+ protected WebSocketSession() {}
+
+ Object onWebSocketSomething(Object proxy, Method method, Object[] args) throws Exception {
+ switch (method.getName()) {
+ case "onWebSocketConnect":
+ this.session = args[0];
+ this.remoteEndpoint = session.getClass().getMethod("getRemote").invoke(args[0]);
+ if (PING_INTERVAL_SECONDS != 0) {
+ pings = Timer.get().scheduleAtFixedRate(() -> {
+ try {
+ remoteEndpoint.getClass().getMethod("sendPing", ByteBuffer.class).invoke(remoteEndpoint, ByteBuffer.wrap(new byte[0]));
+ } catch (Exception x) {
+ error(x);
+ pings.cancel(true);
+ }
+ }, PING_INTERVAL_SECONDS / 2, PING_INTERVAL_SECONDS, TimeUnit.SECONDS);
+ }
+ opened();
+ return null;
+ case "onWebSocketClose":
+ if (pings != null) {
+ pings.cancel(true);
+ // alternately, check Session.isOpen each time
+ }
+ closed((Integer) args[0], (String) args[1]);
+ return null;
+ case "onWebSocketError":
+ error((Throwable) args[0]);
+ return null;
+ case "onWebSocketBinary":
+ binary((byte[]) args[0], (Integer) args[1], (Integer) args[2]);
+ return null;
+ case "onWebSocketText":
+ text((String) args[0]);
+ return null;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ protected void opened() {
+ }
+
+ protected void closed(int statusCode, String reason) {
+ }
+
+ protected void error(Throwable cause) {
+ LOGGER.log(Level.WARNING, "unhandled WebSocket service error", cause);
+ }
+
+ protected void binary(byte[] payload, int offset, int len) {
+ LOGGER.warning("unexpected binary frame");
+ }
+
+ protected void text(String message) {
+ LOGGER.warning("unexpected text frame");
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final Future sendBinary(ByteBuffer data) {
+ try {
+ return (Future) remoteEndpoint.getClass().getMethod("sendBytesByFuture", ByteBuffer.class).invoke(remoteEndpoint, data);
+ } catch (Exception x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final Future sendText(String text) {
+ try {
+ return (Future) remoteEndpoint.getClass().getMethod("sendStringByFuture", String.class).invoke(remoteEndpoint, text);
+ } catch (Exception x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ protected final void close() {
+ try {
+ session.getClass().getMethod("close").invoke(session);
+ } catch (Exception x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+}
diff --git a/core/src/main/java/jenkins/websocket/WebSockets.java b/core/src/main/java/jenkins/websocket/WebSockets.java
new file mode 100644
index 0000000000..2d856d5069
--- /dev/null
+++ b/core/src/main/java/jenkins/websocket/WebSockets.java
@@ -0,0 +1,117 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2019 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.websocket;
+
+import hudson.Extension;
+import hudson.ExtensionList;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+import org.kohsuke.stapler.HttpResponse;
+import org.kohsuke.stapler.HttpResponses;
+import org.kohsuke.stapler.Stapler;
+
+/**
+ * Support for serving WebSocket responses.
+ * @since TODO
+ */
+@Restricted(Beta.class)
+@Extension
+public class WebSockets {
+
+ private static final Logger LOGGER = Logger.getLogger(WebSockets.class.getName());
+
+ private static final String ATTR_SESSION = WebSockets.class.getName() + ".session";
+
+ // TODO ability to handle subprotocols?
+
+ public static HttpResponse upgrade(WebSocketSession session) {
+ return (req, rsp, node) -> {
+ try {
+ Object factory = ExtensionList.lookupSingleton(WebSockets.class).init();
+ if (!((Boolean) webSocketServletFactoryClass.getMethod("isUpgradeRequest", HttpServletRequest.class, HttpServletResponse.class).invoke(factory, req, rsp))) {
+ throw HttpResponses.errorWithoutStack(HttpServletResponse.SC_BAD_REQUEST, "only WS connections accepted here");
+ }
+ req.setAttribute(ATTR_SESSION, session);
+ if (!((Boolean) webSocketServletFactoryClass.getMethod("acceptWebSocket", HttpServletRequest.class, HttpServletResponse.class).invoke(factory, req, rsp))) {
+ throw HttpResponses.errorWithoutStack(HttpServletResponse.SC_BAD_REQUEST, "did not manage to upgrade");
+ }
+ } catch (HttpResponses.HttpResponseException x) {
+ throw x;
+ } catch (Exception x) {
+ LOGGER.log(Level.WARNING, null, x);
+ throw HttpResponses.error(x);
+ }
+ // OK!
+ };
+ }
+
+ private static ClassLoader cl;
+ private static Class> webSocketServletFactoryClass;
+
+ private static synchronized void staticInit() throws Exception {
+ if (webSocketServletFactoryClass == null) {
+ cl = ServletContext.class.getClassLoader();
+ webSocketServletFactoryClass = cl.loadClass("org.eclipse.jetty.websocket.servlet.WebSocketServletFactory");
+ }
+ }
+
+ public static boolean isSupported() {
+ try {
+ staticInit();
+ return true;
+ } catch (Exception x) {
+ LOGGER.log(Level.FINE, null, x);
+ return false;
+ }
+ }
+
+ private /*WebSocketServletFactory*/Object factory;
+
+ private synchronized Object init() throws Exception {
+ if (factory == null) {
+ staticInit();
+ Class> webSocketPolicyClass = cl.loadClass("org.eclipse.jetty.websocket.api.WebSocketPolicy");
+ factory = cl.loadClass("org.eclipse.jetty.websocket.servlet.WebSocketServletFactory$Loader").getMethod("load", ServletContext.class, webSocketPolicyClass).invoke(null, Stapler.getCurrent().getServletContext(), webSocketPolicyClass.getMethod("newServerPolicy").invoke(null));
+ webSocketServletFactoryClass.getMethod("start").invoke(factory);
+ Class> webSocketCreatorClass = cl.loadClass("org.eclipse.jetty.websocket.servlet.WebSocketCreator");
+ webSocketServletFactoryClass.getMethod("setCreator", webSocketCreatorClass).invoke(factory, Proxy.newProxyInstance(cl, new Class>[] {webSocketCreatorClass}, this::createWebSocket));
+ }
+ return factory;
+ }
+
+ private Object createWebSocket(Object proxy, Method method, Object[] args) throws Exception {
+ Object servletUpgradeRequest = args[0];
+ WebSocketSession session = (WebSocketSession) servletUpgradeRequest.getClass().getMethod("getServletAttribute", String.class).invoke(servletUpgradeRequest, ATTR_SESSION);
+ return Proxy.newProxyInstance(cl, new Class>[] {cl.loadClass("org.eclipse.jetty.websocket.api.WebSocketListener")}, session::onWebSocketSomething);
+ }
+
+}
diff --git a/core/src/main/resources/hudson/TcpSlaveAgentListener/index.jelly b/core/src/main/resources/hudson/TcpSlaveAgentListener/index.jelly
index b837be4264..1b04d3c2c9 100644
--- a/core/src/main/resources/hudson/TcpSlaveAgentListener/index.jelly
+++ b/core/src/main/resources/hudson/TcpSlaveAgentListener/index.jelly
@@ -40,7 +40,8 @@ THE SOFTWARE.
-
+
+
Jenkins
diff --git a/core/src/main/resources/hudson/cli/CLIAction/example.jelly b/core/src/main/resources/hudson/cli/CLIAction/example.jelly
index a7e6e2cc10..5a4c4cef32 100644
--- a/core/src/main/resources/hudson/cli/CLIAction/example.jelly
+++ b/core/src/main/resources/hudson/cli/CLIAction/example.jelly
@@ -34,6 +34,6 @@ THE SOFTWARE.
}
- java -jar jenkins-cli.jar -s ${h.inferHudsonURL(request)} ${commandArgs}
+ java -jar jenkins-cli.jar -s ${h.inferHudsonURL(request)} -webSocket ${commandArgs}
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_bg.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_bg.properties
index 1893ad5e2f..5c76d4878e 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_bg.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_bg.properties
@@ -29,7 +29,7 @@ UpgradeComplete=\
UpgradeFailed=\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 \u043a\u044a\u043c Jenkins {0}: {1}.
NewVersionAvailable=\
\u041d\u043e\u0432\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 Jenkins ({0}) \u0435 \u043d\u0430\u043b\u0438\u0447\u043d\u0430 \u0437\u0430 \u0438\u0437\u0442\u0435\u0433\u043b\u044f\u043d\u0435\
- (\u0441\u043f\u0438\u0441\u044a\u043a \u0441 \u043f\u0440\u043e\u043c\u0435\u043d\u0438\u0442\u0435).
+ (\u0441\u043f\u0438\u0441\u044a\u043a \u0441 \u043f\u0440\u043e\u043c\u0435\u043d\u0438\u0442\u0435).
Or\ Upgrade\ Automatically=\
\u0418\u043b\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435
UpgradeCompleteRestartNotSupported=\
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
index 691b46b7f0..2fbdd8a13f 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Nov\u00E1 verze Jenkins-u ({0}) je k dispozici ke sta\u017Een\u00ED (changelog).
+NewVersionAvailable=Nov\u00E1 verze Jenkins-u ({0}) je k dispozici ke sta\u017Een\u00ED (changelog).
Or\ Upgrade\ Automatically=Nebo Upgradovat Automaticky
UpgradeComplete=Upgrade na Jenkinse {0} je hotov, \u010Dek\u00E1 na restart.
UpgradeCompleteRestartNotSupported=Upgrade na Jenkinse {0} je hotov, \u010Dek\u00E1 na restart.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
index 8b5776e534..d23b600857 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
@@ -25,4 +25,4 @@ UpgradeCompleteRestartNotSupported=Opdatering til Jenkins {0} er fuldf\u00f8rt,
UpgradeProgress=Opdatering til Jenkins {0} er i gang eller er fejlet.
Or\ Upgrade\ Automatically=Eller opdater automatisk
NewVersionAvailable=En ny version af Jenkins ({0}) er tilg\u00e6ngelig til hentning \
- (changelog).
+ (changelog).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
index 344e457118..98b368fc36 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
@@ -23,5 +23,5 @@
UpgradeComplete=Aktualisierung auf Jenkins {0} abgeschlossen. Neustart von Jenkins erforderlich.
UpgradeCompleteRestartNotSupported=Aktualisierung auf Jenkins {0} abgeschlossen. Neustart von Jenkins erforderlich.
UpgradeProgress=Aktualisierung auf Jenkins {0} in Arbeit oder fehlgeschlagen.
-NewVersionAvailable=Eine neue Version von Jenkins ({0}) kann hier heruntergeladen werden (Was ist neu?).
+NewVersionAvailable=Eine neue Version von Jenkins ({0}) kann hier heruntergeladen werden (Was ist neu?).
Or\ Upgrade\ Automatically=Automatisch aktualisieren
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
index 6016d4d17c..77a67a8252 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Hay una nueva versi\u00f3n de Jenkins disponible ({0}). descargar (listado de cambios).
+NewVersionAvailable=Hay una nueva versi\u00f3n de Jenkins disponible ({0}). descargar (listado de cambios).
Or\ Upgrade\ Automatically=O actualizar autom\u00e1ticamente
UpgradeComplete=La actualizaci\u00f3n a Jenkins {0} se ha completado, esperando para reiniciar.
UpgradeCompleteRestartNotSupported=La actualizaci\u00f3n a Jenkins {0} se ha completado, esperando para reiniciar.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
index 1df6054b14..eee3e46823 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Jenkinsi uus versioon ({0}) on saadaval allalaadimiseks (muudatuste nimekiri).
+NewVersionAvailable=Jenkinsi uus versioon ({0}) on saadaval allalaadimiseks (muudatuste nimekiri).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
index 8243a6f685..162f2da6f5 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Uusi Jenkinsin versio ({0}) on saatavana (muutosloki).
+NewVersionAvailable=Uusi Jenkinsin versio ({0}) on saatavana (muutosloki).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
index 0dbdae69d7..df31af25d9 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Une nouvelle version de Jenkins ({0}) est disponible (changelog).
+NewVersionAvailable=Une nouvelle version de Jenkins ({0}) est disponible (changelog).
Or\ Upgrade\ Automatically=Ou mettre \u00E0 jour automatiquement
UpgradeProgress=Mise \u00E0 jour vers Jenkins {0} est en cours ou en \u00E9chec.
UpgradeComplete=La mise \u00E0 jour de Jenkins en {0} est termin\u00E9e, en attente de red\u00E9marrage.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
index 19587285ca..1f781852b2 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=A Jenkins \u00FAj verzi\u00F3ja ({0}) el\u00E9rhet\u0151 let\u00F6lt\u00E9sre (v\u00E1ltoz\u00E1sok).
+NewVersionAvailable=A Jenkins \u00FAj verzi\u00F3ja ({0}) el\u00E9rhet\u0151 let\u00F6lt\u00E9sre (v\u00E1ltoz\u00E1sok).
Or\ Upgrade\ Automatically=Vagy friss\u00EDtsen automatikusan
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
index 47ffb48755..4102ffeced 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
@@ -21,7 +21,7 @@
# THE SOFTWARE.
NewVersionAvailable=Una nuova versione di Jenkins ({0}) \u00e8 disponibile per il download \
- (log delle modifiche).
+ (log delle modifiche).
Retry=Riprova
UpgradeCompleteRestartNotSupported=Aggiornamento a Jenkins {0} completato, in attesa del riavvio.
UpgradeProgress=Aggiornamento a Jenkins {0} in corso.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
index 5738b71b76..3f32d0544b 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jenkins\u306E\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3({0})\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059 (\u5909\u66F4\u5C65\u6B74)\u3002
+NewVersionAvailable=Jenkins\u306E\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3({0})\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059 (\u5909\u66F4\u5C65\u6B74)\u3002
Or\ Upgrade\ Automatically=\u81EA\u52D5\u3067\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9
UpgradeComplete=Jenkins {0} \u3078\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306F\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002\u518D\u8D77\u52D5\u5F85\u3061\u3067\u3059\u3002
UpgradeProgress=Jenkins {0}\u3078\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306F\u5B9F\u884C\u4E2D\u304B\u3001\u5931\u6557\u3057\u307E\u3057\u305F\u3002
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
index f5f6a77276..d7d6899922 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jenkins \uC2E0\uADDC\uBC84\uC804({0})\uC744 \uC5EC\uAE30\uC11C \uBC1B\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.(\uBCC0\uACBD\uC0AC\uD56D).
+NewVersionAvailable=Jenkins \uC2E0\uADDC\uBC84\uC804({0})\uC744 \uC5EC\uAE30\uC11C \uBC1B\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.(\uBCC0\uACBD\uC0AC\uD56D).
Or\ Upgrade\ Automatically=\uB610\uB294 \uC790\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
index 2a16d979f6..09a40375ad 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Galima parsisi\u0173sti nauj\u0105 ({0}) Jenkins versij\u0105. (pakeitimai).
+NewVersionAvailable=Galima parsisi\u0173sti nauj\u0105 ({0}) Jenkins versij\u0105. (pakeitimai).
Or\ Upgrade\ Automatically=Parsi\u0173sti automati\u0161kai
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
index 9193a28385..77c8207456 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jauna Jenkins ({0}) versija ir pieejama lejupl\u0101dei (izmai\u0146as).
+NewVersionAvailable=Jauna Jenkins ({0}) versija ir pieejama lejupl\u0101dei (izmai\u0146as).
Or\ Upgrade\ Automatically=Vai atjaunin\u0101t autom\u0101tiski
UpgradeComplete=Atjaunin\u0101s\u0101na uz Jenkins {0} ir pabeigta; gaidu p\u0101rstart\u0113\u0161anos.
UpgradeCompleteRestartNotSupported=Atjaunin\u0101s\u0101na uz Jenkins {0} ir pabeigta; gaidu p\u0101rstart\u0113\u0161anos.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
index b15161b442..c5cac2d1bf 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=En ny versjon av Jenkins ({0}) er n\u00E5 tilgjengelig (Endringer).
+NewVersionAvailable=En ny versjon av Jenkins ({0}) er n\u00E5 tilgjengelig (Endringer).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
index 9c291b0fdd..d9a5180a47 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Er is een nieuwere versie van Jenkins ({0}) beschikbaar. downloaden (changelog).
+NewVersionAvailable=Er is een nieuwere versie van Jenkins ({0}) beschikbaar. downloaden (changelog).
Or\ Upgrade\ Automatically=Of werk automatisch bij
UpgradeComplete=Upgrade naar Jenkins {0} is volledig; aan het wachten op herstart.
UpgradeCompleteRestartNotSupported=Upgrade naar Jenkins {0} is volledig; aan het wachten op herstart.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
index 18a2cbe3c3..5793e7f5da 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
@@ -19,7 +19,7 @@
# 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.
-NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do pobrania (historia zmian).
+NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do pobrania (historia zmian).
Or\ Upgrade\ Automatically=Uaktualnij automatycznie
UpgradeCompleteRestartNotSupported=Aktualizacja Jenkinsa do wersji {0} zako\u0144czy\u0142a si\u0119 pomy\u015Blnie, oczekiwanie na ponowne uruchomienie.
UpgradeProgress=Aktualizacja Jenkinsa do wersji {0} trwa lub nie powiod\u0142a si\u0119.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
index 216eee44d9..f604966589 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
@@ -27,5 +27,5 @@ UpgradeCompleteRestartNotSupported=Atualiza\u00e7\u00e3o do Jenkins {0} completa
UpgradeProgress=Atualiza\u00e7\u00e3o para Jenkins {0} est\u00e1 em andamento ou falhou.
Or\ Upgrade\ Automatically=Ou fazer a atualiza\u00e7\u00e3o automaticamente.
# New version of Jenkins ({0}) is available for download \
-# (changelog).
+# (changelog).
NewVersionAvailable=Nova vers\u00e3o do Jenkins ({0}) est\u00e1 dispon\u00edvel em download
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
index dd2d81456d..f927f4cf83 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Uma nova vers\u00E3o do Jenkins ({0}) est\u00E1 dispon\u00EDvel para download (registo de altera\u00E7\u00F5es).
+NewVersionAvailable=Uma nova vers\u00E3o do Jenkins ({0}) est\u00E1 dispon\u00EDvel para download (registo de altera\u00E7\u00F5es).
Or\ Upgrade\ Automatically=Ou atualizar automaticamente
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
index 7c3dc2351e..18d52aa12a 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=\u041d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Jenkins ({0}) \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (\u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439).
+NewVersionAvailable=\u041d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Jenkins ({0}) \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (\u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439).
Retry=\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c
UpgradeComplete=\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043e Jenkins {0} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e, \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.
UpgradeFailed=\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
index 96543df586..e5f4de3439 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Nov\u00E1 verzia Jenkins ({0}) je dostupn\u00E1 na stiahnutie (zoznam zmien).
+NewVersionAvailable=Nov\u00E1 verzia Jenkins ({0}) je dostupn\u00E1 na stiahnutie (zoznam zmien).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
index 649104bcf5..fa461eb53a 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Nova razli\u010Dica Jenkins-a ({0}) je na voljo za prenos (seznam sprememb).
+NewVersionAvailable=Nova razli\u010Dica Jenkins-a ({0}) je na voljo za prenos (seznam sprememb).
Or\ Upgrade\ Automatically=ali posodobi samodejno
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
index 625195c351..2c3fdbb332 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=\u041D\u043E\u0432\u0430 \u0432\u0435\u0440\u0441\u0456\u044F \u0414\u0436\u0435\u043D\u043A\u0456\u043D\u0441 ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F (\u0437\u043C\u0456\u043D\u0438).
+NewVersionAvailable=\u041D\u043E\u0432\u0430 \u0432\u0435\u0440\u0441\u0456\u044F \u0414\u0436\u0435\u043D\u043A\u0456\u043D\u0441 ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F (\u0437\u043C\u0456\u043D\u0438).
Or\ Upgrade\ Automatically=\u0410\u0431\u043E \u043E\u043D\u043E\u0432\u0456\u0442\u044C \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u043E
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
index 76967e8ea1..e6305137c8 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
+++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
@@ -24,5 +24,5 @@
UpgradeComplete=Jenkins {0} \u5347\u7248\u5b8c\u6210\uff0c\u7b49\u5019\u91cd\u65b0\u555f\u52d5\u3002
UpgradeCompleteRestartNotSupported=Jenkins {0} \u5347\u7248\u5b8c\u6210\uff0c\u7b49\u5019\u91cd\u65b0\u555f\u52d5\u3002
UpgradeProgress=Jenkins {0} \u5347\u7d1a\u4f5c\u696d\u6b63\u5728\u9032\u884c\u4e2d\u6216\u662f\u5df2\u7d93\u5931\u6557\u3002
-NewVersionAvailable=\u65b0\u7248\u7684 Jenkins ({0}) \u5df2\u7d93\u53ef\u4ee5\u4e0b\u8f09 (\u6539\u7248\u8a18\u9304)\u3002
+NewVersionAvailable=\u65b0\u7248\u7684 Jenkins ({0}) \u5df2\u7d93\u53ef\u4ee5\u4e0b\u8f09 (\u6539\u7248\u8a18\u9304)\u3002
Or\ Upgrade\ Automatically=\u6216\u662f\u81ea\u52d5\u5347\u7248
diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly
index d0765ddb5b..6b33de6b80 100644
--- a/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly
+++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly
@@ -28,6 +28,9 @@ THE SOFTWARE.
+
+
+
diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/help-webSocket.html b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-webSocket.html
new file mode 100644
index 0000000000..90f5c28f90
--- /dev/null
+++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-webSocket.html
@@ -0,0 +1,4 @@
+
+ Use WebSocket to connect to the Jenkins master rather than the TCP port.
+ See
JEP-222 for background.
+
diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly
index 1348373933..404e30b9f0 100644
--- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly
+++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly
@@ -25,7 +25,7 @@ THE SOFTWARE.
-
+
${%slaveAgentPort.disabled}
${%configure.link.text}.
diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly
index e07b789741..173a897e9c 100644
--- a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly
+++ b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly
@@ -63,6 +63,9 @@ THE SOFTWARE.
${it.jnlpMac}
${it.node.nodeName}
+
+ -webSocket
+
-tunnel
${launcher.tunnel}
diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo.jelly b/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo.jelly
index 279fc74807..8fa1b2ba2f 100644
--- a/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo.jelly
+++ b/core/src/main/resources/hudson/slaves/SlaveComputer/systemInfo.jelly
@@ -45,7 +45,7 @@ THE SOFTWARE.
- ${it.oSDescription} slave, version ${it.slaveVersion}
+ ${it.oSDescription} agent, version ${it.slaveVersion}
${instance.displayName}
diff --git a/core/src/main/resources/lib/layout/html.jelly b/core/src/main/resources/lib/layout/html.jelly
index 9a957defe8..0a56c939ad 100644
--- a/core/src/main/resources/lib/layout/html.jelly
+++ b/core/src/main/resources/lib/layout/html.jelly
@@ -161,6 +161,8 @@ THE SOFTWARE.
+
+
diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly
index f41699f953..cac4b0d893 100644
--- a/core/src/main/resources/lib/layout/layout.jelly
+++ b/core/src/main/resources/lib/layout/layout.jelly
@@ -173,6 +173,8 @@ THE SOFTWARE.
+
+
diff --git a/core/src/test/java/hudson/slaves/ComputerLauncherTest.java b/core/src/test/java/hudson/slaves/ComputerLauncherTest.java
index 93fac0cb29..4ff10d0dd5 100644
--- a/core/src/test/java/hudson/slaves/ComputerLauncherTest.java
+++ b/core/src/test/java/hudson/slaves/ComputerLauncherTest.java
@@ -32,6 +32,8 @@ import java.io.StringReader;
import org.apache.commons.io.output.NullOutputStream;
import org.junit.Test;
+import org.jvnet.hudson.test.Issue;
+
import static org.junit.Assert.*;
public class ComputerLauncherTest {
@@ -68,6 +70,34 @@ public class ComputerLauncherTest {
assertChecked("java version \"1.10.0_02\"\nJava(TM) SE Runtime Environment (build 1.10.0_02-b01)\nJava HotSpot(TM) Server VM (build 23.1-b03, mixed mode)\n", "1.10.0");
}
+ @Issue("JENKINS-60678")
+ @Test public void adoptOpenJDK11() throws IOException {
+ assertChecked("openjdk version \"11.0.5\" 2019-10-15\n" +
+ "OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.5+10)\n" +
+ "OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.5+10, mixed mode)", "11.0.5");
+ }
+
+ @Issue("JENKINS-60678")
+ @Test public void amazonCorrettoJDK11() throws IOException {
+ assertChecked("openjdk version \"11.0.6\" 2020-01-14 LTS\n" +
+ "OpenJDK Runtime Environment Corretto-11.0.6.10.1 (build 11.0.6+10-LTS)\n" +
+ "OpenJDK 64-Bit Server VM Corretto-11.0.6.10.1 (build 11.0.6+10-LTS, mixed mode)", "11.0.6");
+ }
+
+ @Issue("JENKINS-60678")
+ @Test public void openJDK1106() throws IOException {
+ assertChecked("openjdk version \"11.0.6\" 2020-01-14\n" +
+ "OpenJDK Runtime Environment 18.9 (build 11.0.6+10)\n" +
+ "OpenJDK 64-Bit Server VM 18.9 (build 11.0.6+10, mixed mode)", "11.0.6");
+ }
+
+ @Issue("JENKINS-60678")
+ @Test public void azulZuluOpenJDK11() throws IOException {
+ assertChecked("openjdk version \"11.0.5\" 2019-10-15 LTS\n" +
+ "OpenJDK Runtime Environment Zulu11.35+15-CA (build 11.0.5+10-LTS)\n" +
+ "OpenJDK 64-Bit Server VM Zulu11.35+15-CA (build 11.0.5+10-LTS, mixed mode)", "11.0.5");
+ }
+
private static void assertChecked(String text, String spec) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ComputerLauncher.checkJavaVersion(new PrintStream(os), "bin/java", new BufferedReader(new StringReader(text)));
diff --git a/core/src/test/java/hudson/slaves/NodeListTest.java b/core/src/test/java/hudson/slaves/NodeListTest.java
index dfd13673b4..ee379e24bf 100644
--- a/core/src/test/java/hudson/slaves/NodeListTest.java
+++ b/core/src/test/java/hudson/slaves/NodeListTest.java
@@ -36,7 +36,6 @@ import java.nio.charset.Charset;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
-import org.mockito.Mockito;
/**
* @author Kohsuke Kawaguchi
diff --git a/core/src/test/java/hudson/util/io/TarArchiverTest.java b/core/src/test/java/hudson/util/io/TarArchiverTest.java
index 279f17dcf8..8900f83193 100644
--- a/core/src/test/java/hudson/util/io/TarArchiverTest.java
+++ b/core/src/test/java/hudson/util/io/TarArchiverTest.java
@@ -167,6 +167,6 @@ public class TarArchiverTest {
throw ex;
}
}
- };
+ }
}
diff --git a/pom.xml b/pom.xml
index 6ae7bdb094..e4dd0ba1d4 100755
--- a/pom.xml
+++ b/pom.xml
@@ -76,7 +76,7 @@ THE SOFTWARE.
- 2.216
+ 2.219
-SNAPSHOT
- 3.40.1
+ 4.0.1
3.14
@@ -111,8 +111,8 @@ THE SOFTWARE.
${project.basedir}/../src/findbugs/findbugs-excludes.xml
- 1.14
- 1.14
+ 1.16
+ 1.16
8080
-
6.10.2
-
0.24.5
+
12.14.1
+
1.21.1
@@ -91,9 +91,7 @@ THE SOFTWARE.
${project.groupId}
cli
- jar-with-dependencies
${project.version}
- provided
org.jenkins-ci
winstone
- 5.4
+ 5.8
test
@@ -262,13 +260,6 @@ THE SOFTWARE.
-
- ${project.groupId}
- cli
- jar-with-dependencies
- ${project.build.directory}/${project.build.finalName}/WEB-INF
- jenkins-cli.jar
-
org.jenkins-ci
winstone
@@ -575,16 +566,14 @@ THE SOFTWARE.
-
- gulp-execution
+ yarn-execution
- gulpfile.js
+ package.json
@@ -647,27 +636,38 @@ THE SOFTWARE.
generate-sources
- gulp bundle
+ yarn build
- gulp
+ yarn
- jshint bundle
+ build
test
- gulp test
+ yarn test
- gulp
+ yarn
- bundle test
+ test
${skipTests}
+
+ test
+ yarn lint
+
+ yarn
+
+
+ lint
+ ${skipTests}
+
+
diff --git a/war/src/main/webapp/css/font-awesome/css/LICENSE.txt b/war/src/main/fonts/font-awesome/css/LICENSE.txt
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/css/LICENSE.txt
rename to war/src/main/fonts/font-awesome/css/LICENSE.txt
diff --git a/war/src/main/webapp/css/font-awesome/css/font-awesome.css b/war/src/main/fonts/font-awesome/css/font-awesome.css
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/css/font-awesome.css
rename to war/src/main/fonts/font-awesome/css/font-awesome.css
diff --git a/war/src/main/webapp/css/font-awesome/css/font-awesome.min.css b/war/src/main/fonts/font-awesome/css/font-awesome.min.css
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/css/font-awesome.min.css
rename to war/src/main/fonts/font-awesome/css/font-awesome.min.css
diff --git a/war/src/main/webapp/css/font-awesome/fonts/FontAwesome.otf b/war/src/main/fonts/font-awesome/fonts/FontAwesome.otf
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/FontAwesome.otf
rename to war/src/main/fonts/font-awesome/fonts/FontAwesome.otf
diff --git a/war/src/main/webapp/css/font-awesome/fonts/LICENSE.txt b/war/src/main/fonts/font-awesome/fonts/LICENSE.txt
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/LICENSE.txt
rename to war/src/main/fonts/font-awesome/fonts/LICENSE.txt
diff --git a/war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.eot b/war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.eot
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.eot
rename to war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.eot
diff --git a/war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.svg b/war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.svg
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.svg
rename to war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.svg
diff --git a/war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.ttf b/war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.ttf
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.ttf
rename to war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.ttf
diff --git a/war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.woff b/war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.woff
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.woff
rename to war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.woff
diff --git a/war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.woff2 b/war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.woff2
similarity index 100%
rename from war/src/main/webapp/css/font-awesome/fonts/fontawesome-webfont.woff2
rename to war/src/main/fonts/font-awesome/fonts/fontawesome-webfont.woff2
diff --git a/war/src/main/webapp/css/google-fonts/roboto/LICENSE.txt b/war/src/main/fonts/google-fonts/roboto/LICENSE.txt
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/LICENSE.txt
rename to war/src/main/fonts/google-fonts/roboto/LICENSE.txt
diff --git a/war/src/main/webapp/css/google-fonts/roboto/css/roboto.css b/war/src/main/fonts/google-fonts/roboto/css/roboto.css
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/css/roboto.css
rename to war/src/main/fonts/google-fonts/roboto/css/roboto.css
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.eot b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.eot
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.eot
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.eot
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.svg b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.svg
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.svg
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.svg
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.ttf b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.ttf
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.ttf
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.ttf
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff2 b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff2
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff2
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-300.woff2
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.eot b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.eot
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.eot
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.eot
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.svg b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.svg
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.svg
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.svg
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.ttf b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.ttf
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.ttf
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.ttf
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff2 b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff2
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff2
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-500.woff2
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.eot b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.eot
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.eot
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.eot
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.svg b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.svg
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.svg
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.svg
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.ttf b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.ttf
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.ttf
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.ttf
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff2 b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff2
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff2
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-700.woff2
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.eot b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.eot
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.eot
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.eot
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.svg b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.svg
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.svg
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.svg
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.ttf b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.ttf
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.ttf
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.ttf
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff2 b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff2
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff2
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-900.woff2
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.eot b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.eot
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.eot
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.eot
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.svg b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.svg
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.svg
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.svg
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.ttf b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.ttf
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.ttf
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.ttf
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff
diff --git a/war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff2 b/war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff2
similarity index 100%
rename from war/src/main/webapp/css/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff2
rename to war/src/main/fonts/google-fonts/roboto/fonts/roboto-v15-greek_latin-ext_latin_vietnamese_cyrillic_greek-ext_cyrillic-ext-regular.woff2
diff --git a/war/src/main/webapp/css/icomoon/css/icomoon.css b/war/src/main/fonts/icomoon/css/icomoon.css
similarity index 100%
rename from war/src/main/webapp/css/icomoon/css/icomoon.css
rename to war/src/main/fonts/icomoon/css/icomoon.css
diff --git a/war/src/main/webapp/css/icomoon/fonts/icomoon.eot b/war/src/main/fonts/icomoon/fonts/icomoon.eot
similarity index 100%
rename from war/src/main/webapp/css/icomoon/fonts/icomoon.eot
rename to war/src/main/fonts/icomoon/fonts/icomoon.eot
diff --git a/war/src/main/webapp/css/icomoon/fonts/icomoon.svg b/war/src/main/fonts/icomoon/fonts/icomoon.svg
similarity index 100%
rename from war/src/main/webapp/css/icomoon/fonts/icomoon.svg
rename to war/src/main/fonts/icomoon/fonts/icomoon.svg
diff --git a/war/src/main/webapp/css/icomoon/fonts/icomoon.ttf b/war/src/main/fonts/icomoon/fonts/icomoon.ttf
similarity index 100%
rename from war/src/main/webapp/css/icomoon/fonts/icomoon.ttf
rename to war/src/main/fonts/icomoon/fonts/icomoon.ttf
diff --git a/war/src/main/webapp/css/icomoon/fonts/icomoon.woff b/war/src/main/fonts/icomoon/fonts/icomoon.woff
similarity index 100%
rename from war/src/main/webapp/css/icomoon/fonts/icomoon.woff
rename to war/src/main/fonts/icomoon/fonts/icomoon.woff
diff --git a/war/src/main/js/add-item.js b/war/src/main/js/add-item.js
index b82a94c610..c018712959 100644
--- a/war/src/main/js/add-item.js
+++ b/war/src/main/js/add-item.js
@@ -1,5 +1,4 @@
-// Initialize all modules by requiring them. Also makes sure they get bundled (see gulpfile.js).
-var $ = require('jquery-detached').getJQuery();
+import $ from 'jquery';
var getItems = function() {
var d = $.Deferred();
@@ -9,7 +8,7 @@ var getItems = function() {
}
);
return d.promise();
-};
+};
var jRoot = $('head').attr('data-rooturl');
@@ -36,6 +35,7 @@ $.when(getItems()).done(function(data) {
if (desc.indexOf('<a href="') === -1) {
return desc;
}
+ // eslint-disable-next-line
var newDesc = desc.replace(/\</g,'<').replace(/\>/g,'>');
return newDesc;
}
diff --git a/war/src/main/js/api/pluginManager.js b/war/src/main/js/api/pluginManager.js
index 8032828e5e..ac78903bde 100644
--- a/war/src/main/js/api/pluginManager.js
+++ b/war/src/main/js/api/pluginManager.js
@@ -1,13 +1,14 @@
/**
* Provides a wrapper to interact with the plugin manager & update center
*/
-
-var jenkins = require('../util/jenkins');
+import jenkins from '../util/jenkins';
//Get plugin info (plugins + recommended plugin list) from update centers.
var plugins;
-
-exports.initialPluginList = function(handler) {
+
+var pluginManager = {};
+
+pluginManager.initialPluginList = function(handler) {
jenkins.get('/setupWizard/platformPluginList', function(response) {
if(response.status !== 'ok') {
handler.call({ isError: true, data: response.data });
@@ -22,10 +23,10 @@ exports.initialPluginList = function(handler) {
}
});
};
-
+
// Call this to initialize the plugin list
-exports.init = function(handler) {
- exports.initialPluginList(function(initialPluginCategories) {
+pluginManager.init = function(handler) {
+ pluginManager.initialPluginList(function(initialPluginCategories) {
plugins = {};
plugins.names = [];
plugins.recommendedPlugins = [];
@@ -52,7 +53,7 @@ exports.init = function(handler) {
}
handler();
});
-};
+};
// default 10 seconds for AJAX responses to return before triggering an error condition
var pluginManagerErrorTimeoutMillis = 10 * 1000;
@@ -61,7 +62,7 @@ var pluginManagerErrorTimeoutMillis = 10 * 1000;
* Get the curated list of plugins to be offered in the wizard.
* @returns The curated list of plugins to be offered in the wizard.
*/
-exports.plugins = function() {
+pluginManager.plugins = function() {
return plugins.availablePlugins;
};
@@ -69,7 +70,7 @@ exports.plugins = function() {
* Get the curated list of plugins to be offered in the wizard by name only.
* @returns The curated list of plugins to be offered in the wizard by name only.
*/
-exports.pluginNames = function() {
+pluginManager.pluginNames = function() {
return plugins.names;
};
@@ -79,7 +80,7 @@ exports.pluginNames = function() {
* The user can easily change this selection.
* @returns The subset of plugins (subset of the plugin list) that are recommended by default.
*/
-exports.recommendedPluginNames = function() {
+pluginManager.recommendedPluginNames = function() {
return plugins.recommendedPlugins.slice(); // copy this
};
@@ -89,7 +90,7 @@ exports.recommendedPluginNames = function() {
* If handler is called with this.isError, there will be a corresponding this.errorMessage indicating
* the failure reason
*/
-exports.installPlugins = function(plugins, handler) {
+pluginManager.installPlugins = function(plugins, handler) {
jenkins.post('/pluginManager/installPlugins', { dynamicLoad: true, plugins: plugins }, function(response) {
if(response.status !== 'ok') {
handler.call({ isError: true, errorMessage: response.message });
@@ -110,7 +111,7 @@ exports.installPlugins = function(plugins, handler) {
* to the handler function. If argument 2 is non-null, it will be treated as a correlationId, which
* must be retrieved from a prior installPlugins call.
*/
-exports.installStatus = function(handler, correlationId) {
+pluginManager.installStatus = function(handler, correlationId) {
var url = '/updateCenter/installStatus';
if(correlationId !== undefined) {
url += '?correlationId=' + correlationId;
@@ -137,7 +138,7 @@ exports.installStatus = function(handler, correlationId) {
* ...
* ]
*/
-exports.availablePlugins = function(handler) {
+pluginManager.availablePlugins = function(handler) {
jenkins.get('/pluginManager/plugins', function(response) {
if(response.status !== 'ok') {
handler.call({ isError: true, errorMessage: response.message });
@@ -159,7 +160,7 @@ exports.availablePlugins = function(handler) {
* to the handler function. If argument 2 is non-null, it will be treated as a correlationId, which
* must be retrieved from a prior installPlugins call.
*/
-exports.incompleteInstallStatus = function(handler, correlationId) {
+pluginManager.incompleteInstallStatus = function(handler, correlationId) {
var url = '/updateCenter/incompleteInstallStatus';
if(correlationId !== undefined) {
url += '?correlationId=' + correlationId;
@@ -182,7 +183,7 @@ exports.incompleteInstallStatus = function(handler, correlationId) {
/**
* Call this to complete the installation without installing anything
*/
-exports.completeInstall = function(handler) {
+pluginManager.completeInstall = function(handler) {
jenkins.post('/setupWizard/completeInstall', {}, function() {
handler.call({ isError: false });
}, {
@@ -196,7 +197,7 @@ exports.completeInstall = function(handler) {
/**
* Indicates there is a restart required to complete plugin installations
*/
-exports.getRestartStatus = function(handler) {
+pluginManager.getRestartStatus = function(handler) {
jenkins.get('/setupWizard/restartStatus', function(response) {
handler.call({ isError: false }, response.data);
}, {
@@ -210,7 +211,7 @@ exports.getRestartStatus = function(handler) {
/**
* Skip failed plugins, continue
*/
-exports.installPluginsDone = function(handler) {
+pluginManager.installPluginsDone = function(handler) {
jenkins.post('/pluginManager/installPluginsDone', {}, function() {
handler();
}, {
@@ -224,7 +225,7 @@ exports.installPluginsDone = function(handler) {
/**
* Restart Jenkins
*/
-exports.restartJenkins = function(handler) {
+pluginManager.restartJenkins = function(handler) {
jenkins.post('/updateCenter/safeRestart', {}, function() {
handler.call({ isError: false });
}, {
@@ -234,3 +235,5 @@ exports.restartJenkins = function(handler) {
}
});
};
+
+export default pluginManager;
diff --git a/war/src/main/js/api/securityConfig.js b/war/src/main/js/api/securityConfig.js
index 358e86c7ef..fcc06e345b 100644
--- a/war/src/main/js/api/securityConfig.js
+++ b/war/src/main/js/api/securityConfig.js
@@ -1,46 +1,47 @@
+import jenkins from '../util/jenkins';
+import { getWindow } from 'window-handle';
+
/**
* Provides a wrapper to interact with the security configuration
*/
-var jenkins = require('../util/jenkins');
-
-/**
+ /*
* Calls a stapler post method to save the first user settings
*/
-exports.saveFirstUser = function($form, success, error) {
+function saveFirstUser($form, success, error) {
jenkins.staplerPost(
'/setupWizard/createAdminUser',
$form,
function(response) {
var crumbRequestField = response.data.crumbRequestField;
if (crumbRequestField) {
- require('window-handle').getWindow().crumb.init(crumbRequestField, response.data.crumb);
+ getWindow().crumb.init(crumbRequestField, response.data.crumb);
}
success(response);
}, {
error: error
});
-};
+}
-exports.saveConfigureInstance = function($form, success, error){
+function saveConfigureInstance($form, success, error){
jenkins.staplerPost(
'/setupWizard/configureInstance',
$form,
function(response) {
var crumbRequestField = response.data.crumbRequestField;
if (crumbRequestField) {
- require('window-handle').getWindow().crumb.init(crumbRequestField, response.data.crumb);
+ getWindow().crumb.init(crumbRequestField, response.data.crumb);
}
success(response);
}, {
error: error
});
-};
+}
/**
* Calls a stapler post method to save the first user settings
*/
-exports.saveProxy = function($form, success, error) {
+function saveProxy($form, success, error) {
jenkins.staplerPost(
'/pluginManager/proxyConfigure',
$form,
@@ -48,4 +49,10 @@ exports.saveProxy = function($form, success, error) {
dataType: 'html',
error: error
});
+}
+
+export default {
+ saveFirstUser: saveFirstUser,
+ saveConfigureInstance: saveConfigureInstance,
+ saveProxy: saveProxy
};
diff --git a/war/src/main/js/config-scrollspy.js b/war/src/main/js/config-scrollspy.js
index 1ed2dca34e..80e0960d93 100644
--- a/war/src/main/js/config-scrollspy.js
+++ b/war/src/main/js/config-scrollspy.js
@@ -1,16 +1,22 @@
-var $ = require('jquery-detached').getJQuery();
-var page = require('./util/page.js');
-var windowHandle = require('window-handle');
+import $ from 'jquery';
+import windowHandle from 'window-handle';
+import page from './util/page';
+import * as tabBarWidget from './widgets/config/tabbar';
+
var isScrolling = false;
var ignoreNextScrollEvent = false;
var pageHeaderHeight = page.pageHeaderHeight();
var breadcrumbBarHeight = page.breadcrumbBarHeight();
// Some stuff useful for testing.
-exports.tabbars = [];
-exports.scrollspeed = 500;
+export var tabbars = [];
+export var scrollspeed = 500;
+// Used to set scrollspeed from the the test suite
+export function setScrollspeed(newScrollspeed) {
+ scrollspeed = newScrollspeed;
+}
var eventListeners = [];
-exports.on = function(listener) {
+export var on = function(listener) {
eventListeners.push(listener);
};
function notify(event) {
@@ -20,10 +26,8 @@ function notify(event) {
}
$(function() {
- var tabBarWidget = require('./widgets/config/tabbar.js');
-
tabBarWidget.addPageTabs('.config-table.scrollspy', function(tabBar) {
- exports.tabbars.push(tabBar);
+ tabbars.push(tabBar);
tabBarWidget.addFinderToggle(tabBar);
tabBar.onShowSection(function() {
@@ -50,7 +54,7 @@ function scrollTo(section, tabBar) {
isScrolling = true;
$('html,body').animate({
scrollTop: scrollTop
- }, exports.scrollspeed, function() {
+ }, scrollspeed, function() {
if (isScrolling) {
notify({
type: 'click_scrollto',
diff --git a/war/src/main/js/config-tabbar.js b/war/src/main/js/config-tabbar.js
index ada0ded36d..20d9022c45 100644
--- a/war/src/main/js/config-tabbar.js
+++ b/war/src/main/js/config-tabbar.js
@@ -1,14 +1,13 @@
-var $ = require('jquery-detached').getJQuery();
-var page = require('./util/page.js');
-var jenkinsLocalStorage = require('./util/jenkinsLocalStorage.js');
+import $ from 'jquery';
+import jenkinsLocalStorage from './util/jenkinsLocalStorage';
+import page from './util/page';
+import * as tabBarWidget from './widgets/config/tabbar';
-exports.tabs = []; // Useful for testing.
+export const tabs = []; // Useful for testing.
$(function() {
- var tabBarWidget = require('./widgets/config/tabbar.js');
-
tabBarWidget.addPageTabs('.config-table.tabbed', function(tabBar) {
- exports.tabs.push(tabBar);
+ tabs.push(tabBar);
// We want to merge some sections together.
// Merge the "Advanced" section into the "General" section.
diff --git a/war/src/main/js/handlebars-helpers/id.js b/war/src/main/js/handlebars-helpers/id.js
new file mode 100644
index 0000000000..bca0a7939b
--- /dev/null
+++ b/war/src/main/js/handlebars-helpers/id.js
@@ -0,0 +1,3 @@
+export default function id(str) {
+ return (''+str).replace(/\W+/g, '_');
+}
diff --git a/war/src/main/js/handlebars-helpers/ifeq.js b/war/src/main/js/handlebars-helpers/ifeq.js
new file mode 100644
index 0000000000..210389e530
--- /dev/null
+++ b/war/src/main/js/handlebars-helpers/ifeq.js
@@ -0,0 +1,5 @@
+export default function ifeq(o1, o2, options) {
+ if(o1 === o2) {
+ return options.fn();
+ }
+}
diff --git a/war/src/main/js/handlebars-helpers/ifneq.js b/war/src/main/js/handlebars-helpers/ifneq.js
new file mode 100644
index 0000000000..387e06712b
--- /dev/null
+++ b/war/src/main/js/handlebars-helpers/ifneq.js
@@ -0,0 +1,5 @@
+export default function ifneq(o1, o2, options) {
+ if(o1 !== o2) {
+ return options.fn();
+ }
+}
diff --git a/war/src/main/js/handlebars-helpers/in-array.js b/war/src/main/js/handlebars-helpers/in-array.js
new file mode 100644
index 0000000000..b57c0c791b
--- /dev/null
+++ b/war/src/main/js/handlebars-helpers/in-array.js
@@ -0,0 +1,5 @@
+export default function inArray(arr, val, options) {
+ if(arr.indexOf(val) >= 0) {
+ return options.fn();
+ }
+}
diff --git a/war/src/main/js/handlebars-helpers/replace.js b/war/src/main/js/handlebars-helpers/replace.js
new file mode 100644
index 0000000000..5eeb903fae
--- /dev/null
+++ b/war/src/main/js/handlebars-helpers/replace.js
@@ -0,0 +1,8 @@
+export default function replace() {
+ var val = arguments[0];
+ // second, through second to last - options is last
+ for (var i = 1; i < arguments.length - 1; i++) {
+ val = val.replace('{' + (i-1) + '}', arguments[i]);
+ }
+ return val;
+}
diff --git a/war/src/main/js/page-init.js b/war/src/main/js/page-init.js
index e7ddae3a6b..7ee89db454 100644
--- a/war/src/main/js/page-init.js
+++ b/war/src/main/js/page-init.js
@@ -1,10 +1,10 @@
+import $ from 'jquery';
+import jsModules from 'jenkins-js-modules';
+
/*
* Page initialisation tasks.
*/
-var $ = require('jquery-detached').getJQuery();
-var jsModules = require('jenkins-js-modules');
-
$(function() {
loadScripts();
loadCSS();
diff --git a/war/src/main/js/plugin-setup-wizard/bootstrap-detached.js b/war/src/main/js/plugin-setup-wizard/bootstrap-detached.js
new file mode 100644
index 0000000000..4660f05c8d
--- /dev/null
+++ b/war/src/main/js/plugin-setup-wizard/bootstrap-detached.js
@@ -0,0 +1,36 @@
+import { getWindow } from 'window-handle';
+
+/**
+ * Manual replacement for the bootstrap-detached library. We cannot use that one due to the way JS-imports are set
+ *
+ * Contrary to the bootstrap-detached lib, this one polutes the received jQuery instance
+ */
+export function enhanceJQueryWithBootstrap($) {
+ var window = getWindow();
+ var _$ = window.$;
+ var _jQuery = window.jQuery;
+
+ try {
+ var jQuery = $;
+
+ window.$ = $;
+ window.jQuery = $;
+
+ var document = window.document;
+
+ /* ---------------------------------------------------- Bootstrap 3 minified ---------------------------------------------------- */
+ /*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+ if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
+ /* ------------------------------------------------------------------------------------------------------------------------------ */
+
+ return $;
+ } finally {
+ window.$ = _$;
+ window.jQuery = _jQuery;
+ }
+}
diff --git a/war/src/main/js/pluginSetupWizard.js b/war/src/main/js/pluginSetupWizard.js
index a5d3af7222..e3817d7247 100644
--- a/war/src/main/js/pluginSetupWizard.js
+++ b/war/src/main/js/pluginSetupWizard.js
@@ -1,8 +1,6 @@
-// Initialize all modules by requiring them. Also makes sure they get bundled (see gulpfile.js).
-var $ = require('jquery-detached').getJQuery();
-
+import $ from 'jquery';
// This is the main module
-var pluginSetupWizard = require('./pluginSetupWizardGui');
+import pluginSetupWizard from './pluginSetupWizardGui';
// This entry point for the bundle only bootstraps the main module in a browser
$(function() {
diff --git a/war/src/main/js/pluginSetupWizardGui.js b/war/src/main/js/pluginSetupWizardGui.js
index 95efbcfe1b..f6a7000621 100644
--- a/war/src/main/js/pluginSetupWizardGui.js
+++ b/war/src/main/js/pluginSetupWizardGui.js
@@ -1,22 +1,37 @@
+import $ from 'jquery';
+import Handlebars from 'handlebars';
+import jenkins from './util/jenkins';
+import pluginManager from './api/pluginManager';
+import securityConfig from './api/securityConfig';
+import idIfy from './handlebars-helpers/id';
+import { enhanceJQueryWithBootstrap } from './plugin-setup-wizard/bootstrap-detached';
+import errorPanel from './templates/errorPanel.hbs';
+import loadingPanel from './templates/loadingPanel.hbs';
+import welcomePanel from './templates/welcomePanel.hbs';
+import progressPanel from './templates/progressPanel.hbs';
+import pluginSuccessPanel from './templates/successPanel.hbs';
+import pluginSelectionPanel from './templates/pluginSelectionPanel.hbs';
+import setupCompletePanel from './templates/setupCompletePanel.hbs';
+import proxyConfigPanel from './templates/proxyConfigPanel.hbs';
+import firstUserPanel from './templates/firstUserPanel.hbs';
+import configureInstancePanel from './templates/configureInstance.hbs';
+import offlinePanel from './templates/offlinePanel.hbs';
+import pluginSetupWizard from './templates/pluginSetupWizard.hbs';
+import incompleteInstallationPanel from './templates/incompleteInstallationPanel.hbs';
+import pluginSelectList from './templates/pluginSelectList.hbs';
+
/**
* Jenkins first-run install wizard
*/
-// Require modules here, make sure they get browserify'd/bundled
-var jquery = require('jquery-detached');
-var bootstrap = require('bootstrap-detached');
-var jenkins = require('./util/jenkins');
-var pluginManager = require('./api/pluginManager');
-var securityConfig = require('./api/securityConfig');
-window.zq = jquery.getJQuery();
+Handlebars.registerPartial('pluginSelectList', pluginSelectList);
+
+// TODO: see whether this is actually being used or if it can be removed
+window.zq = $;
// Setup the dialog, exported
var createPluginSetupWizard = function(appendTarget) {
- // call getJQuery / getBootstrap within the main function so it will work with tests -- if getJQuery etc is called in the main
- var $ = jquery.getJQuery();
- var $bs = bootstrap.getBootstrap();
-
- var Handlebars = jenkins.initHandlebars();
+ var $bs = enhanceJQueryWithBootstrap($);
// Necessary handlebars helpers:
// returns the plugin count string per category selected vs. available e.g. (5/44)
@@ -103,31 +118,16 @@ var createPluginSetupWizard = function(appendTarget) {
return options.fn();
}
});
-
- // Include handlebars templates here - explicitly require them and they'll be available by hbsfy as part of the bundle process
- var errorPanel = require('./templates/errorPanel.hbs');
- var loadingPanel = require('./templates/loadingPanel.hbs');
- var welcomePanel = require('./templates/welcomePanel.hbs');
- var progressPanel = require('./templates/progressPanel.hbs');
- var pluginSuccessPanel = require('./templates/successPanel.hbs');
- var pluginSelectionPanel = require('./templates/pluginSelectionPanel.hbs');
- var setupCompletePanel = require('./templates/setupCompletePanel.hbs');
- var proxyConfigPanel = require('./templates/proxyConfigPanel.hbs');
- var firstUserPanel = require('./templates/firstUserPanel.hbs');
- var configureInstancePanel = require('./templates/configureInstance.hbs');
- var offlinePanel = require('./templates/offlinePanel.hbs');
- var pluginSetupWizard = require('./templates/pluginSetupWizard.hbs');
- var incompleteInstallationPanel = require('./templates/incompleteInstallationPanel.hbs');
- var pluginSelectList = require('./templates/pluginSelectList.hbs');
-
- Handlebars.registerPartial('pluginSelectList', pluginSelectList);
// wrap calls with this method to handle generic errors returned by the plugin manager
var handleGenericError = function(success) {
return function() {
- if(this.isError) {
- var errorMessage = this.errorMessage;
- if(!errorMessage || this.errorMessage === 'timeout') {
+ // Workaround for webpack passing null context to anonymous functions
+ var self = this || window;
+
+ if(self.isError) {
+ var errorMessage = self.errorMessage;
+ if(!errorMessage || self.errorMessage === 'timeout') {
errorMessage = translations.installWizard_error_connection;
}
else {
@@ -136,7 +136,7 @@ var createPluginSetupWizard = function(appendTarget) {
setPanel(errorPanel, { errorMessage: errorMessage });
return;
}
- success.apply(this, arguments);
+ success.apply(self, arguments);
};
};
@@ -596,7 +596,7 @@ var createPluginSetupWizard = function(appendTarget) {
}
$txt.append($div);
- var $itemProgress = $('.selected-plugin[id="installing-' + jenkins.idIfy(j.name) + '"]');
+ var $itemProgress = $('.selected-plugin[id="installing-' + idIfy(j.name) + '"]');
if($itemProgress.length > 0 && !$itemProgress.is('.'+state)) {
$itemProgress.addClass(state);
}
@@ -1279,4 +1279,4 @@ var createPluginSetupWizard = function(appendTarget) {
};
// export wizard creation method
-exports.init = createPluginSetupWizard;
+export default { init: createPluginSetupWizard };
diff --git a/war/src/main/js/upgradeWizard.js b/war/src/main/js/upgradeWizard.js
index 514a4c0047..8be7ed8187 100644
--- a/war/src/main/js/upgradeWizard.js
+++ b/war/src/main/js/upgradeWizard.js
@@ -1,11 +1,12 @@
+import upgradePanel from './templates/upgradePanel.hbs';
+import upgradeSuccessPanel from './templates/upgradeSuccessPanel.hbs';
+import upgradeSkippedPanel from './templates/upgradeSkippedPanel.hbs';
+
/* globals onSetupWizardInitialized: true */
onSetupWizardInitialized(function(wizard) {
var jenkins = wizard.jenkins; // wizard-provided jenkins api
var pluginManager = wizard.pluginManager;
- var upgradePanel = require('./templates/upgradePanel.hbs');
- var upgradeSuccessPanel = require('./templates/upgradeSuccessPanel.hbs');
- var upgradeSkippedPanel = require('./templates/upgradeSkippedPanel.hbs');
wizard.addActions({
'.skip-recommended-plugins': function() {
diff --git a/war/src/main/js/util/behavior-shim.js b/war/src/main/js/util/behavior-shim.js
index 27d00b784a..1ae8455829 100644
--- a/war/src/main/js/util/behavior-shim.js
+++ b/war/src/main/js/util/behavior-shim.js
@@ -1,4 +1,6 @@
+function specify(selector, id, priority, behavior) {
+ // eslint-ignore-next-line
+ Behaviour.specify(selector, id, priority, behavior);
+};
-exports.specify = function(selector, id, priority, behavior) {
- Behaviour.specify(selector, id, priority, behavior); // jshint ignore:line
-};
\ No newline at end of file
+export default { specify };
diff --git a/war/src/main/js/util/jenkins.js b/war/src/main/js/util/jenkins.js
index cc962deff4..b5aa8d0e76 100644
--- a/war/src/main/js/util/jenkins.js
+++ b/war/src/main/js/util/jenkins.js
@@ -1,17 +1,15 @@
/**
* Jenkins JS Modules common utility functions
*/
-
-// Get the modules
-
-var jquery = require('jquery-detached');
-var wh = require('window-handle');
+import $ from 'jquery';
+import wh from 'window-handle';
+import Handlebars from 'handlebars';
var debug = false;
+var jenkins = {};
// gets the base Jenkins URL including context path
-exports.baseUrl = function() {
- var $ = jquery.getJQuery();
+jenkins.baseUrl = function() {
var u = $('head').attr('data-rooturl');
if(!u) {
u = '';
@@ -20,7 +18,7 @@ exports.baseUrl = function() {
};
// awful hack to get around JSONifying things with Prototype taking over wrong. ugh. Prototype is the worst.
-exports.stringify = function(o) {
+jenkins.stringify = function(o) {
if(Array.prototype.toJSON) { // Prototype f's this up something bad
var protoJSON = {
a: Array.prototype.toJSON,
@@ -56,31 +54,23 @@ exports.stringify = function(o) {
}
};
-/**
- * Take a string and replace non-id characters to make it a friendly-ish XML id
- */
-exports.idIfy = function(str) {
- return (''+str).replace(/\W+/g, '_');
-};
-
/**
* redirect
*/
-exports.goTo = function(url) {
- wh.getWindow().location.replace(exports.baseUrl() + url);
+jenkins.goTo = function(url) {
+ wh.getWindow().location.replace(jenkins.baseUrl() + url);
};
/**
* Jenkins AJAX GET callback.
* If last parameter is an object, will be extended to jQuery options (e.g. pass { error: function() ... } to handle errors)
*/
-exports.get = function(url, success, options) {
+jenkins.get = function(url, success, options) {
if(debug) {
console.log('get: ' + url);
}
- var $ = jquery.getJQuery();
var args = {
- url: exports.baseUrl() + url,
+ url: jenkins.baseUrl() + url,
type: 'GET',
cache: false,
dataType: 'json',
@@ -96,13 +86,11 @@ exports.get = function(url, success, options) {
* Jenkins AJAX POST callback, formats data as a JSON object post (note: works around prototype.js ugliness using stringify() above)
* If last parameter is an object, will be extended to jQuery options (e.g. pass { error: function() ... } to handle errors)
*/
-exports.post = function(url, data, success, options) {
+jenkins.post = function(url, data, success, options) {
if(debug) {
console.log('post: ' + url);
}
-
- var $ = jquery.getJQuery();
-
+
// handle crumbs
var headers = {};
var wnd = wh.getWindow();
@@ -124,11 +112,11 @@ exports.post = function(url, data, success, options) {
formBody = $.extend({}, formBody);
formBody[crumb.fieldName] = crumb.value;
}
- formBody = exports.stringify(formBody);
+ formBody = jenkins.stringify(formBody);
}
var args = {
- url: exports.baseUrl() + url,
+ url: jenkins.baseUrl() + url,
type: 'POST',
cache: false,
dataType: 'json',
@@ -144,49 +132,18 @@ exports.post = function(url, data, success, options) {
};
/**
- * handlebars setup, this does not seem to actually work or get called by the require() of this file, so have to explicitly call it
+ * handlebars setup, done for backwards compatibility because some plugins depend on it
*/
-exports.initHandlebars = function() {
- var Handlebars = require('handlebars');
-
- Handlebars.registerHelper('ifeq', function(o1, o2, options) {
- if(o1 === o2) {
- return options.fn();
- }
- });
-
- Handlebars.registerHelper('ifneq', function(o1, o2, options) {
- if(o1 !== o2) {
- return options.fn();
- }
- });
-
- Handlebars.registerHelper('in-array', function(arr, val, options) {
- if(arr.indexOf(val) >= 0) {
- return options.fn();
- }
- });
-
- Handlebars.registerHelper('id', exports.idIfy);
-
- Handlebars.registerHelper('replace', function() {
- var val = arguments[0];
- // second, through second to last - options is last
- for (var i = 1; i < arguments.length - 1; i++) {
- val = val.replace('{' + (i-1) + '}', arguments[i]);
- }
- return val;
- });
-
+jenkins.initHandlebars = function() {
return Handlebars;
-};
+}
/**
* Load translations for the given bundle ID, provide the message object to the handler.
* Optional error handler as the last argument.
*/
-exports.loadTranslations = function(bundleName, handler, onError) {
- exports.get('/i18n/resourceBundle?baseName=' +bundleName, function(res) {
+jenkins.loadTranslations = function(bundleName, handler, onError) {
+ jenkins.get('/i18n/resourceBundle?baseName=' +bundleName, function(res) {
if(res.status !== 'ok') {
if(onError) {
onError(res.message);
@@ -218,10 +175,10 @@ exports.loadTranslations = function(bundleName, handler, onError) {
/**
* Runs a connectivity test, calls handler with a boolean whether there is sufficient connectivity to the internet
*/
-exports.testConnectivity = function(siteId, handler) {
+jenkins.testConnectivity = function(siteId, handler) {
// check the connectivity api
var testConnectivity = function() {
- exports.get('/updateCenter/connectionStatus?siteId=' + siteId, function(response) {
+ jenkins.get('/updateCenter/connectionStatus?siteId=' + siteId, function(response) {
if(response.status !== 'ok') {
handler(false, true, response.message);
}
@@ -235,7 +192,7 @@ exports.testConnectivity = function(siteId, handler) {
else {
// Update site should be always reachable, but we do not require the internet connection
// if it's explicitly skipped by the update center
- if(response.status !== 'ok' || response.data.updatesite !== 'OK' ||
+ if(response.status !== 'ok' || response.data.updatesite !== 'OK' ||
(response.data.internet !== 'OK' && response.data.internet !== 'SKIPPED')) {
// no connectivity, but not fatal
handler(false, false);
@@ -246,7 +203,7 @@ exports.testConnectivity = function(siteId, handler) {
}
}, { error: function(xhr, textStatus, errorThrown) {
if (xhr.status === 403) {
- exports.goTo('/login');
+ jenkins.goTo('/login');
} else {
handler.call({ isError: true, errorMessage: errorThrown });
}
@@ -259,8 +216,7 @@ exports.testConnectivity = function(siteId, handler) {
/**
* gets the window containing a form, taking in to account top-level iframes
*/
-exports.getWindow = function($form) {
- var $ = jquery.getJQuery();
+jenkins.getWindow = function($form) {
$form = $($form);
var wnd = wh.getWindow();
$(top.document).find('iframe').each(function() {
@@ -278,13 +234,12 @@ exports.getWindow = function($form) {
/**
* Builds a stapler form post
*/
-exports.buildFormPost = function($form) {
- var $ = jquery.getJQuery();
+jenkins.buildFormPost = function($form) {
$form = $($form);
- var wnd = exports.getWindow($form);
+ var wnd = jenkins.getWindow($form);
var form = $form[0];
if(wnd.buildFormTree(form)) {
- return $form.serialize() + "&" + jquery.param({
+ return $form.serialize() + "&" + $.param({
'core:apply': '',
'Submit': 'Save',
'json': $form.find('input[name=json]').val()
@@ -296,10 +251,9 @@ exports.buildFormPost = function($form) {
/**
* Gets the crumb, if crumbs are enabled
*/
-exports.getFormCrumb = function($form) {
- var $ = jquery.getJQuery();
+jenkins.getFormCrumb = function($form) {
$form = $($form);
- var wnd = exports.getWindow($form);
+ var wnd = jenkins.getWindow($form);
return wnd.crumb;
};
@@ -307,12 +261,11 @@ exports.getFormCrumb = function($form) {
* Jenkins Stapler JSON POST callback
* If last parameter is an object, will be extended to jQuery options (e.g. pass { error: function() ... } to handle errors)
*/
-exports.staplerPost = function(url, $form, success, options) {
- var $ = jquery.getJQuery();
+jenkins.staplerPost = function(url, $form, success, options) {
$form = $($form);
- var postBody = exports.buildFormPost($form);
- var crumb = exports.getFormCrumb($form);
- exports.post(
+ var postBody = jenkins.buildFormPost($form);
+ var crumb = jenkins.getFormCrumb($form);
+ jenkins.post(
url,
postBody,
success, $.extend({
@@ -321,3 +274,5 @@ exports.staplerPost = function(url, $form, success, options) {
crumb: crumb
}, options));
};
+
+export default jenkins;
diff --git a/war/src/main/js/util/jenkinsLocalStorage.js b/war/src/main/js/util/jenkinsLocalStorage.js
index 44aa587fe6..948d112650 100644
--- a/war/src/main/js/util/jenkinsLocalStorage.js
+++ b/war/src/main/js/util/jenkinsLocalStorage.js
@@ -1,32 +1,39 @@
-var windowHandle = require('window-handle');
-var storage = require('./localStorage.js');
+import { getWindow } from 'window-handle';
+import storage from './localStorage'
/**
* Store a Jenkins globally scoped value.
*/
-exports.setGlobalItem = function(name, value) {
+function setGlobalItem(name, value) {
storage.setItem('jenkins:' + name, value);
-};
+}
/**
* Get a Jenkins globally scoped value.
*/
-exports.getGlobalItem = function(name, defaultVal) {
+function getGlobalItem(name, defaultVal) {
return storage.getItem('jenkins:' + name, defaultVal);
-};
+}
/**
* Store a Jenkins page scoped value.
*/
-exports.setPageItem = function(name, value) {
- name = 'jenkins:' + name + ':' + windowHandle.getWindow().location.href;
+function setPageItem(name, value) {
+ name = 'jenkins:' + name + ':' + getWindow().location.href;
storage.setItem(name, value);
-};
+}
/**
* Get a Jenkins page scoped value.
*/
-exports.getPageItem = function(name, defaultVal) {
- name = 'jenkins:' + name + ':' + windowHandle.getWindow().location.href;
+function getPageItem(name, defaultVal) {
+ name = 'jenkins:' + name + ':' + getWindow().location.href;
return storage.getItem(name, defaultVal);
-};
\ No newline at end of file
+}
+
+export default {
+ setGlobalItem,
+ getGlobalItem,
+ setPageItem,
+ getPageItem
+};
diff --git a/war/src/main/js/util/jquery-ext.js b/war/src/main/js/util/jquery-ext.js
index f7285a4c4a..af59a30b68 100644
--- a/war/src/main/js/util/jquery-ext.js
+++ b/war/src/main/js/util/jquery-ext.js
@@ -1,11 +1,17 @@
/*
* Some internal jQuery extensions.
+ *
+ * After migrating to webpack it modifies the provided version of jquery
*/
+import $ from 'jquery';
+import windowHandle from 'window-handle';
-var jQD = require('jquery-detached');
+/**
+ * TODO: look into other way of doing this
+ */
var $ext;
-exports.getJQuery = function() {
+export var getJQuery = function() {
if (!$ext) {
initJQueryExt();
}
@@ -15,15 +21,15 @@ exports.getJQuery = function() {
/*
* Clear the $ext instance if the window changes. Primarily for unit testing.
*/
-var windowHandle = require('window-handle');
windowHandle.getWindow(function() {
$ext = undefined;
});
+/**
+ * Adds the :containsci selector to jQuery
+ */
function initJQueryExt() {
- // We are going to be adding "stuff" to jQuery. We create a totally new jQuery instance
- // because we do NOT want to run the risk of polluting the shared instance.
- $ext = jQD.newJQuery();
+ $ext = $;
/**
* A pseudo selector that performs a case insensitive text contains search i.e. the same
diff --git a/war/src/main/js/util/localStorage.js b/war/src/main/js/util/localStorage.js
index 0628c1b547..0812370129 100644
--- a/war/src/main/js/util/localStorage.js
+++ b/war/src/main/js/util/localStorage.js
@@ -1,8 +1,8 @@
-var windowHandle = require('window-handle');
-var win = windowHandle.getWindow();
-var storage = win.localStorage;
+import { getWindow } from 'window-handle';
-exports.setMock = function() {
+let storage = getWindow().localStorage;
+
+function setMock() {
storage = {
storage: {},
setItem: function (name, value) {
@@ -15,26 +15,33 @@ exports.setMock = function() {
delete this.storage[name];
}
};
-};
+}
-exports.setItem = function(name, value) {
+function setItem(name, value) {
storage.setItem(name, value);
-};
+}
-exports.getItem = function(name, defaultVal) {
+function getItem(name, defaultVal) {
var value = storage.getItem(name);
if (!value) {
value = defaultVal;
}
return value;
-};
+}
-exports.removeItem = function(name) {
+function removeItem(name) {
return storage.removeItem(name);
-};
+}
if (typeof storage === "undefined") {
console.warn('HTML5 localStorage not supported by this browser.');
// mock it...
- exports.setMock();
-}
\ No newline at end of file
+ setMock();
+}
+
+export default {
+ setMock,
+ setItem,
+ getItem,
+ removeItem,
+};
diff --git a/war/src/main/js/util/page.js b/war/src/main/js/util/page.js
index 77052bb9d0..69e93919fb 100644
--- a/war/src/main/js/util/page.js
+++ b/war/src/main/js/util/page.js
@@ -1,5 +1,6 @@
-var jQD = require('jquery-detached');
-var windowHandle = require('window-handle');
+import $ from 'jquery';
+import { getWindow } from 'window-handle';
+
var timestamp = (new Date().getTime());
var loadedClass = 'jenkins-loaded-' + timestamp;
@@ -9,12 +10,10 @@ var loadedClass = 'jenkins-loaded-' + timestamp;
* A jQuery based alternative to Behaviour.specify. Grrrr.
* @param selector The jQuery selector.
* @param callback The callback to call after finding new elements. This
- * callback must return a boolean value of true if scanning is to continue.
+ * callback must return a boolean value of true if scanning is to continue.
* @param contextEl The jQuery selector context (optional).
*/
-exports.onload = function(selector, callback, contextEl) {
- var $ = jQD.getJQuery();
-
+function onload(selector, callback, contextEl) {
function registerRescan() {
setTimeout(scan, 50);
}
@@ -30,35 +29,32 @@ exports.onload = function(selector, callback, contextEl) {
}
}
scan();
-};
+}
-exports.winScrollTop = function() {
- var $ = jQD.getJQuery();
- var win = $(windowHandle.getWindow());
+function winScrollTop() {
+ var win = $(getWindow());
return win.scrollTop();
-};
+}
-exports.onWinScroll = function(callback) {
- var $ = jQD.getJQuery();
- $(windowHandle.getWindow()).on('scroll', callback);
-};
+function onWinScroll(callback) {
+ $(getWindow()).on('scroll', callback);
+}
-exports.pageHeaderHeight = function() {
+function pageHeaderHeight() {
return elementHeight('#page-head');
-};
+}
-exports.breadcrumbBarHeight = function() {
+function breadcrumbBarHeight() {
return elementHeight('#breadcrumbBar');
-};
+}
-exports.fireBottomStickerAdjustEvent = function() {
+function fireBottomStickerAdjustEvent() {
Event.fire(window, 'jenkins:bottom-sticker-adjust'); // jshint ignore:line
-};
+}
// YUI Drag widget does not like to work on elements with a relative position.
// This tells the element to switch to static position at the start of the drag, so it can work.
-exports.fixDragEvent = function(handle) {
- var $ = jQD.getJQuery();
+function fixDragEvent(handle) {
var isReady = false;
var $handle = $(handle);
var $chunk = $handle.closest('.repeated-chunk');
@@ -74,18 +70,27 @@ exports.fixDragEvent = function(handle) {
isReady = false;
$chunk.removeClass('dragging');
});
-};
+}
-exports.removeTextHighlighting = function(selector) {
- var $ = jQD.getJQuery();
+function removeTextHighlighting(selector) {
$('span.highlight-split', selector).each(function() {
var highlightSplit = $(this);
highlightSplit.before(highlightSplit.text());
highlightSplit.remove();
});
-};
+}
function elementHeight(selector) {
- var $ = jQD.getJQuery();
return $(selector).height();
-}
\ No newline at end of file
+}
+
+export default {
+ onload,
+ winScrollTop,
+ onWinScroll,
+ pageHeaderHeight,
+ breadcrumbBarHeight,
+ fireBottomStickerAdjustEvent,
+ fixDragEvent,
+ removeTextHighlighting
+}
diff --git a/war/src/main/js/widgets/config/model/ConfigRowGrouping.js b/war/src/main/js/widgets/config/model/ConfigRowGrouping.js
index 01bdda3e40..119af9f9f2 100644
--- a/war/src/main/js/widgets/config/model/ConfigRowGrouping.js
+++ b/war/src/main/js/widgets/config/model/ConfigRowGrouping.js
@@ -1,11 +1,9 @@
-var jQD = require('../../../util/jquery-ext.js');
-
-module.exports = ConfigRowGrouping;
+import { getJQuery } from '../../../util/jquery-ext';
/*
* =======================================================================================
* Configuration table row grouping i.e. row-set-*, optional-block-*, radio-block-* etc
- *
+ *
* A ConfigSection maintains a list of ConfigRowGrouping and then ConfigRowGrouping
* itself maintains a list i.e. it's hierarchical. See ConfigSection.gatherRowGroups().
* =======================================================================================
@@ -65,7 +63,7 @@ ConfigRowGrouping.prototype.updateVisibility = function() {
* the row-set rows should be made visible or not.
*/
ConfigRowGrouping.prototype.findToggleWidget = function(row) {
- var $ = jQD.getJQuery();
+ var $ = getJQuery();
var input = $(':input.block-control', row);
if (input.size() === 1) {
this.toggleWidget = input;
@@ -73,3 +71,5 @@ ConfigRowGrouping.prototype.findToggleWidget = function(row) {
input.addClass('disable-behavior');
}
};
+
+export default ConfigRowGrouping;
diff --git a/war/src/main/js/widgets/config/model/ConfigSection.js b/war/src/main/js/widgets/config/model/ConfigSection.js
index 8f8c7e88a7..74fc076e06 100644
--- a/war/src/main/js/widgets/config/model/ConfigSection.js
+++ b/war/src/main/js/widgets/config/model/ConfigSection.js
@@ -1,11 +1,9 @@
-var jQD = require('../../../util/jquery-ext.js');
-var util = require('./util.js');
-var page = require('../../../util/page.js');
-var ConfigRowGrouping = require('./ConfigRowGrouping.js');
+import { getJQuery } from '../../../util/jquery-ext';
+import page from '../../../util/page.js';
+import { toId } from './util';
+import ConfigRowGrouping from './ConfigRowGrouping';
var pageHeaderHeight = page.pageHeaderHeight();
-module.exports = ConfigSection;
-
/*
* =======================================================================================
* Configuration table section.
@@ -15,7 +13,7 @@ function ConfigSection(headerRow, parentCMD) {
this.headerRow = headerRow;
this.parentCMD = parentCMD;
this.title = headerRow.attr('title');
- this.id = util.toId(this.title);
+ this.id = toId(this.title);
this.rowGroups = undefined;
this.activator = undefined;
this.subSections = [];
@@ -149,7 +147,7 @@ ConfigSection.prototype.markRowsAsActive = function() {
};
ConfigSection.prototype.hasText = function(text) {
- var $ = jQD.getJQuery();
+ var $ = getJQuery();
var selector = ":containsci('" + text + "')";
var sectionRows = this.getRows();
@@ -255,7 +253,7 @@ ConfigSection.prototype.getRowGroupLabels = function() {
};
ConfigSection.prototype.highlightText = function(text) {
- var $ = jQD.getJQuery();
+ var $ = getJQuery();
var selector = ":containsci('" + text + "')";
var rows = this.getRows();
@@ -286,3 +284,5 @@ ConfigSection.prototype.highlightText = function(text) {
this.subSections[i2].highlightText(text);
}
};
+
+export default ConfigSection;
diff --git a/war/src/main/js/widgets/config/model/ConfigTableMetaData.js b/war/src/main/js/widgets/config/model/ConfigTableMetaData.js
index c77425b2f0..4e7d47748d 100644
--- a/war/src/main/js/widgets/config/model/ConfigTableMetaData.js
+++ b/war/src/main/js/widgets/config/model/ConfigTableMetaData.js
@@ -1,29 +1,29 @@
+import { getJQuery } from '../../../util/jquery-ext';
+import page from '../../../util/page';
+import ConfigSection from './ConfigSection';
+import { toId } from './util';
+
/*
* Internal support module for config tables.
*/
-var jQD = require('../../../util/jquery-ext.js');
-var ConfigSection = require('./ConfigSection.js');
-var page = require('../../../util/page.js');
-var util = require('./util.js');
-
-exports.markConfigTableParentForm = function(configTable) {
+function markConfigTableParentForm(configTable) {
var form = configTable.closest('form');
form.addClass('jenkins-config');
return form;
-};
+}
-exports.findConfigTables = function() {
- var $ = jQD.getJQuery();
+function findConfigTables() {
+ var $ = getJQuery();
// The config tables are the immediate child