提交 f69080ea 编写于 作者: G gusreiber

merging 2.0

......@@ -54,12 +54,21 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class="bug">
Under some conditions Jenkins startup could fail because of incorrectly linked extensions; now recovering more gracefully.
(<a href-"https://issues.jenkins-ci.org/browse/JENKINS-25440">issue 25440</a>)
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v1.651>What's new in 1.651</a> (2016/02/28)</h3>
<ul class=image>
<li class="rfe">
Move periodic task log files from <code>JENKINS_HOME/*.log</code> to <code>JENKINS_HOME/logs/tasks/*.log</code> and rotate them periodically rather than overwrite every execution
Move periodic task log files from <code>JENKINS_HOME/*.log</code> to <code>JENKINS_HOME/logs/tasks/*.log</code> and rotate them periodically rather than overwrite every execution.
(<a href-"https://issues.jenkins-ci.org/browse/JENKINS-33068">issue 33068</a>)
<li class="bug">
Fix documentation of proxy configuration.
(<a href="https://github.com/jenkinsci/jenkins/pull/2060">pull 2060</a>)
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v1.650>What's new in 1.650</a> (2016/02/24)</h3>
<ul class=image>
<li class="major bug">
......
......@@ -39,7 +39,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.239</stapler.version>
<stapler.version>1.240</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
<groovy.version>1.8.9</groovy.version>
</properties>
......
......@@ -282,7 +282,6 @@ public abstract class ExtensionFinder implements ExtensionPoint {
LOGGER.log(Level.SEVERE, "Failed to create Guice container from all the plugins",e);
// failing to load all bindings are disastrous, so recover by creating minimum that works
// by just including the core
// TODO this recovery is pretty much useless; startup crashes anyway
container = Guice.createInjector(new SezpozModule(loadSezpozIndices(Jenkins.class.getClassLoader())));
}
......@@ -479,7 +478,11 @@ public abstract class ExtensionFinder implements ExtensionPoint {
m.invoke(ecl, c);
c.getConstructors();
c.getMethods();
c.getFields();
for (Field f : c.getFields()) {
if (f.getAnnotation(javax.inject.Inject.class) != null || f.getAnnotation(com.google.inject.Inject.class) != null) {
resolve(f.getType());
}
}
LOGGER.log(Level.FINER, "{0} looks OK", c);
while (c != Object.class) {
c.getGenericSuperclass();
......
......@@ -1189,7 +1189,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
break;
}
updateCenter.persistInstallStatus();
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLED);
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState());
InstallUtil.saveLastExecVersion();
}
}.start();
......
......@@ -57,7 +57,8 @@ import jenkins.install.InstallState;
import jenkins.install.InstallUtil;
import jenkins.model.Jenkins;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.apache.commons.codec.binary.Base64;
......@@ -73,7 +74,6 @@ import javax.annotation.Nonnull;
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.ServletException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
......@@ -81,12 +81,12 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
......@@ -302,6 +302,27 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
if (checkJob != null) {
boolean isOffline = false;
for (ConnectionStatus status : checkJob.connectionStates.values()) {
if(ConnectionStatus.FAILED.equals(status)) {
isOffline = true;
break;
}
}
if (isOffline) {
// retry connection states if determined to be offline
checkJob.run();
isOffline = false;
for (ConnectionStatus status : checkJob.connectionStates.values()) {
if(ConnectionStatus.FAILED.equals(status)) {
isOffline = true;
break;
}
}
if(!isOffline) { // also need to download the metadata
updateAllSites();
}
}
return HttpResponses.okJSON(checkJob.connectionStates);
} else {
return HttpResponses.errorJSON(String.format("Unknown site '%s'.", siteId));
......@@ -311,19 +332,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
/**
* Called to bypass install wizard
*/
@Restricted(DoNotUse.class) // WebOnly
public HttpResponse doCompleteInstall() {
if(isRestartRequiredForCompletion()) {
Jenkins.getActiveInstance().setInstallState(InstallState.RESTART);
}
InstallUtil.saveLastExecVersion();
Jenkins.getActiveInstance().setInstallState(InstallState.INITIAL_PLUGINS_INSTALLED);
return HttpResponses.okJSON();
}
/**
* Called to determine if there was an incomplete installation, what the statuses of the plugins are
*/
......@@ -379,7 +387,10 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
public HttpResponse doInstallStatus(StaplerRequest request) {
try {
String correlationId = request.getParameter("correlationId");
Map<String,Object> response = new HashMap<>();
response.put("state", Jenkins.getInstance().getInstallState().name());
List<Map<String, String>> installStates = new ArrayList<>();
response.put("jobs", installStates);
List<UpdateCenterJob> jobCopy = getJobs();
for (UpdateCenterJob job : jobCopy) {
......@@ -400,7 +411,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
}
return HttpResponses.okJSON(JSONArray.fromObject(installStates));
return HttpResponses.okJSON(JSONObject.fromObject(response));
} catch (Exception e) {
return HttpResponses.errorJSON(String.format("ERROR: %s", e.getMessage()));
}
......
......@@ -27,6 +27,7 @@ import hudson.Extension;
import hudson.ExtensionList;
import hudson.FilePath;
import hudson.Util;
import hudson.slaves.WorkspaceList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
......@@ -89,6 +90,7 @@ public class WorkspaceCleanupThread extends AsyncPeriodicWork {
listener.getLogger().println("Deleting " + ws + " on " + node.getDisplayName());
try {
ws.deleteRecursive();
WorkspaceList.tempDir(ws).deleteRecursive();
} catch (IOException x) {
x.printStackTrace(listener.error("Failed to delete " + ws + " on " + node.getDisplayName()));
} catch (InterruptedException x) {
......
......@@ -29,6 +29,7 @@ import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import javax.inject.Inject;
import java.util.Collections;
......@@ -36,30 +37,52 @@ import java.util.List;
/**
* {@link AuthorizationStrategy} that grants full-control to authenticated user
* (other than anonymous users.)
* and optionally read access to anonymous users
*
* @author Kohsuke Kawaguchi
*/
public class FullControlOnceLoggedInAuthorizationStrategy extends AuthorizationStrategy {
/**
* Whether to allow anonymous read access, for backward compatibility
* default is to allow it
*/
private boolean denyAnonymousReadAccess = false;
@DataBoundConstructor
public FullControlOnceLoggedInAuthorizationStrategy() {
}
@Override
public ACL getRootACL() {
return THE_ACL;
return denyAnonymousReadAccess ? AUTHENTICATED_READ : ANONYMOUS_READ;
}
public List<String> getGroups() {
return Collections.emptyList();
}
private static final SparseACL THE_ACL = new SparseACL(null);
/**
* If true, anonymous read access will be allowed
*/
public boolean isAllowAnonymousRead() {
return !denyAnonymousReadAccess;
}
@DataBoundSetter
public void setAllowAnonymousRead(boolean allowAnonymousRead) {
this.denyAnonymousReadAccess = !allowAnonymousRead;
}
private static final SparseACL AUTHENTICATED_READ = new SparseACL(null);
private static final SparseACL ANONYMOUS_READ = new SparseACL(null);
static {
THE_ACL.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
THE_ACL.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
THE_ACL.add(ACL.ANONYMOUS,Permission.READ,true);
ANONYMOUS_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
ANONYMOUS_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
ANONYMOUS_READ.add(ACL.ANONYMOUS, Permission.READ,true);
AUTHENTICATED_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER, true);
AUTHENTICATED_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER, false);
}
/**
......
......@@ -29,6 +29,8 @@ import hudson.ExtensionList;
import hudson.Util;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor;
import jenkins.install.InstallState;
import jenkins.install.SetupWizard;
import jenkins.model.Jenkins;
import hudson.model.ManagementLink;
import hudson.model.ModelObject;
......@@ -279,15 +281,37 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
* This can be run by anyone, but only to create the very first user account.
*/
public void doCreateFirstAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if(hasSomeUser()) {
boolean inSetup = !Jenkins.getInstance().getInstallState().isSetupComplete();
if(!inSetup && hasSomeUser()) {
rsp.sendError(SC_UNAUTHORIZED,"First user was already created");
return;
}
User u = createAccount(req, rsp, false, "firstUser.jelly");
User admin = null;
try {
String view = "firstUser.jelly";
if(inSetup) {
admin = getUser(SetupWizard.initialSetupAdminUserName);
if(admin != null) {
admin.delete(); // assume the new user may well be 'admin'
}
view = "setupWizardFirstUser.jelly";
}
User u = createAccount(req, rsp, false, view);
if (u!=null) {
tryToMakeAdmin(u);
if(admin != null) {
admin = null;
}
Jenkins.getInstance().setInstallState(InstallState.CREATE_ADMIN_USER.getNextState());
loginAndTakeBack(req, rsp, u);
}
} finally {
if(admin != null) {
admin.save(); // recreate this initial user if something failed
}
}
}
/**
......
......@@ -26,6 +26,7 @@ package hudson.slaves;
import hudson.FilePath;
import hudson.Functions;
import hudson.model.Computer;
import hudson.model.DirectoryBrowserSupport;
import java.io.Closeable;
import java.util.Date;
......@@ -283,6 +284,25 @@ public final class WorkspaceList {
};
}
/**
* Locates a conventional temporary directory to be associated with a workspace.
* <p>This directory is suitable for temporary files to be deleted later in the course of a build,
* or caches and local repositories which should persist across builds done in the same workspace.
* (If multiple workspaces are present for a single job built concurrently, via {@link #allocate(FilePath)}, each will get its own temporary directory.)
* <p>It may also be used for security-sensitive files which {@link DirectoryBrowserSupport} ought not serve,
* acknowledging that these will be readable by builds of other jobs done on the same node.
* <p>Each plugin using this directory is responsible for specifying sufficiently unique subdirectory/file names.
* {@link FilePath#createTempFile} may be used for this purpose if desired.
* <p>The resulting directory may not exist; you may call {@link FilePath#mkdirs} on it if you need it to.
* It may be deleted alongside the workspace itself during cleanup actions.
* @param ws a directory such as a build workspace
* @return a sibling directory, for example {@code …/something@tmp} for {@code …/something}
* @since 1.652
*/
public static FilePath tempDir(FilePath ws) {
return ws.sibling(ws.getName() + COMBINATOR + "tmp");
}
private static final Logger LOGGER = Logger.getLogger(WorkspaceList.class.getName());
/**
......
......@@ -34,32 +34,63 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
@Restricted(NoExternalUse.class)
public enum InstallState {
/**
* New Jenkins install.
* The initial set up has been completed
*/
INITIAL_SETUP_COMPLETED(true, null),
/**
* Creating an admin user for an initial Jenkins install.
*/
NEW,
CREATE_ADMIN_USER(false, INITIAL_SETUP_COMPLETED),
/**
* New Jenkins install. The user has kicked off the process of installing an
* initial set of plugins (via the install wizard).
*/
INITIAL_PLUGINS_INSTALLING,
INITIAL_PLUGINS_INSTALLING(false, CREATE_ADMIN_USER),
/**
* New Jenkins install. The initial set of plugins are now installed.
* New Jenkins install.
*/
INITIAL_PLUGINS_INSTALLED,
NEW(false, INITIAL_PLUGINS_INSTALLING),
/**
* Restart of an existing Jenkins install.
*/
RESTART,
RESTART(true, INITIAL_SETUP_COMPLETED),
/**
* Upgrade of an existing Jenkins install.
*/
UPGRADE,
UPGRADE(true, INITIAL_SETUP_COMPLETED),
/**
* Downgrade of an existing Jenkins install.
*/
DOWNGRADE,
DOWNGRADE(true, INITIAL_SETUP_COMPLETED),
/**
* Jenkins started in test mode (JenkinsRule).
*/
TEST
TEST(true, INITIAL_SETUP_COMPLETED),
/**
* Jenkins started in development mode: Bolean.getBoolean("hudson.Main.development").
* Can be run normally with the -Djenkins.install.runSetupWizard=true
*/
DEVELOPMENT(true, INITIAL_SETUP_COMPLETED);
private final boolean isSetupComplete;
private final InstallState nextState;
private InstallState(boolean isSetupComplete, InstallState nextState) {
this.isSetupComplete = isSetupComplete;
this.nextState = nextState;
}
/**
* Indicates the initial setup is complete
*/
public boolean isSetupComplete() {
return isSetupComplete;
}
/**
* Gets the next state
*/
public InstallState getNextState() {
return nextState;
}
}
......@@ -61,6 +61,7 @@ public class InstallUtil {
private static final Logger LOGGER = Logger.getLogger(InstallUtil.class.getName());
// tests need this to be 1.0
private static final VersionNumber NEW_INSTALL_VERSION = new VersionNumber("1.0");
/**
......@@ -68,15 +69,28 @@ public class InstallUtil {
* @return The type of "startup" currently under way in Jenkins.
*/
public static InstallState getInstallState() {
// install wizard will always run if environment specified
if (!Boolean.getBoolean("jenkins.install.runSetupWizard")) {
if (Functions.getIsUnitTest()) {
return InstallState.TEST;
}
if (Boolean.getBoolean("hudson.Main.development")) {
return InstallState.DEVELOPMENT;
}
}
VersionNumber lastRunVersion = new VersionNumber(getLastExecVersion());
// Neither the top level config or the lastExecVersionFile have a version
// stored in them, which means it's a new install.
if (lastRunVersion.compareTo(NEW_INSTALL_VERSION) == 0) {
// Edge case: used Jenkins 1 but did not save the system config page,
// the version is not persisted and returns 1.0, so try to check if
// they actually did anything
if (!Jenkins.getInstance().getItemMap().isEmpty()) {
return InstallState.UPGRADE;
}
return InstallState.NEW;
}
......
package jenkins.install;
import java.io.IOException;
import java.util.Locale;
import java.util.UUID;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import hudson.BulkChange;
import hudson.ExtensionList;
import hudson.model.User;
import hudson.model.UserProperty;
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
import hudson.security.HudsonPrivateSecurityRealm;
import hudson.security.PermissionAdder;
import hudson.security.SecurityRealm;
import hudson.security.csrf.DefaultCrumbIssuer;
import hudson.util.HttpResponses;
import hudson.util.PluginServletFilter;
import jenkins.model.Jenkins;
import jenkins.security.s2m.AdminWhitelistRule;
/**
* A Jenkins instance used during first-run to provide a limited set of services while
* initial installation is in progress
*/
public class SetupWizard {
/**
* The security token parameter name
*/
public static String initialSetupAdminUserName = "admin";
private final Logger LOGGER = Logger.getLogger(SetupWizard.class.getName());
public SetupWizard(Jenkins j) throws IOException {
User admin;
// Create an admin user by default with a
// difficult password
if(j.getSecurityRealm() == null || j.getSecurityRealm() == SecurityRealm.NO_AUTHENTICATION) { // this seems very fragile
BulkChange bc = new BulkChange(j);
HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null);
j.setSecurityRealm(securityRealm);
String randomUUID = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ENGLISH);
admin = securityRealm.createAccount(SetupWizard.initialSetupAdminUserName, randomUUID);
admin.addProperty(new SetupWizard.AuthenticationKey(randomUUID));
// Lock Jenkins down:
FullControlOnceLoggedInAuthorizationStrategy authStrategy = new FullControlOnceLoggedInAuthorizationStrategy();
authStrategy.setAllowAnonymousRead(false);
j.setAuthorizationStrategy(authStrategy);
// Shut down all the ports we can by default:
j.setSlaveAgentPort(-1); // -1 to disable
// require a crumb issuer
j.setCrumbIssuer(new DefaultCrumbIssuer(false));
// set master -> slave security:
j.getInjector().getInstance(AdminWhitelistRule.class)
.setMasterKillSwitch(false);
try{
j.save(); // !!
} finally {
bc.commit();
}
}
else {
admin = j.getUser(SetupWizard.initialSetupAdminUserName);
}
String setupKey = null;
if(admin != null && admin.getProperty(SetupWizard.AuthenticationKey.class) != null) {
setupKey = admin.getProperty(SetupWizard.AuthenticationKey.class).getKey();
}
if(setupKey != null) {
LOGGER.info("\n\n*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "\n"
+ "Jenkins initial setup is required. A security token is required to proceed. \n"
+ "Please use the following security token to proceed to installation: \n"
+ "\n"
+ "" + setupKey + "\n"
+ "\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n");
}
try {
PluginServletFilter.addFilter(FORCE_SETUP_WIZARD_FILTER);
} catch (ServletException e) {
throw new AssertionError(e);
}
}
/**
* Remove the setupWizard filter, ensure all updates are written to disk, etc
*/
public HttpResponse doCompleteInstall(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Jenkins j = Jenkins.getActiveInstance();
j.setInstallState(InstallState.INITIAL_SETUP_COMPLETED);
InstallUtil.saveLastExecVersion();
PluginServletFilter.removeFilter(FORCE_SETUP_WIZARD_FILTER);
// Also, clean up the setup wizard if it's completed
j.setSetupWizard(null);
return HttpResponses.okJSON();
}
// Stores a user property for the authentication key, which is really the auto-generated user's password
public static class AuthenticationKey extends UserProperty {
String key;
public AuthenticationKey() {
}
public AuthenticationKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
/**
* This filter will validate that the security token is provided
*/
private final Filter FORCE_SETUP_WIZARD_FILTER = new Filter() {
@Override
public void init(FilterConfig cfg) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// As an extra measure of security, the install wizard generates a security token, and
// requires the user to enter it before proceeding through the installation. Once set
// we'll set a cookie so the subsequent operations succeed
if (request instanceof HttpServletRequest) {
HttpServletRequest req = (HttpServletRequest)request;
//if (!Pattern.compile(".*[.](css|ttf|gif|woff|eot|png|js)").matcher(req.getRequestURI()).matches()) {
// Allow js & css requests through
if((req.getContextPath() + "/").equals(req.getRequestURI())) {
chain.doFilter(new HttpServletRequestWrapper(req) {
public String getRequestURI() {
return getContextPath() + "/setupWizard/";
}
}, response);
return;
}
// fall through to handling the request normally
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
};
}
......@@ -194,6 +194,7 @@ import jenkins.ExtensionRefreshException;
import jenkins.InitReactorRunner;
import jenkins.install.InstallState;
import jenkins.install.InstallUtil;
import jenkins.install.SetupWizard;
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import jenkins.security.ConfidentialKey;
import jenkins.security.ConfidentialStore;
......@@ -333,7 +334,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
/**
* The Jenkins instance startup type i.e. NEW, UPGRADE etc
*/
private InstallState installState;
private transient InstallState installState = InstallState.NEW;
/**
* If we're in the process of an initial setup,
* this will be set
*/
private transient SetupWizard setupWizard;
/**
* Number of executors of the master node.
......@@ -834,6 +841,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
if(KILL_AFTER_LOAD)
System.exit(0);
if(!installState.isSetupComplete()) {
// Start immediately with the setup wizard for new installs
setupWizard = new SetupWizard(this);
}
launchTcpSlaveAgentListener();
if (UDPBroadcastThread.PORT != -1) {
......@@ -913,6 +925,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Get the Jenkins {@link jenkins.install.InstallState install state}.
* @return The Jenkins {@link jenkins.install.InstallState install state}.
*/
@Nonnull
@Restricted(NoExternalUse.class)
public InstallState getInstallState() {
return installState;
......@@ -3892,6 +3905,20 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
return ManagementLink.all();
}
/**
* If set, a currently active setup wizard - e.g. installation
*/
public SetupWizard getSetupWizard() {
return setupWizard;
}
/**
* Sets the setup wizard
*/
public void setSetupWizard(SetupWizard setupWizard) {
this.setupWizard = setupWizard;
}
/**
* Exposes the current user to <tt>/me</tt> URL.
*/
......
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson"
xmlns:f="/lib/form">
<f:entry field="allowAnonymousRead">
<f:checkbox default="true" title="${%Allow anonymous read access}"/>
</f:entry>
</j:jelly>
<div>
If checked, this will allow users who are not authenticated to access Jenkins in a read-only mode.
</div>
......@@ -25,19 +25,6 @@ THE SOFTWARE.
<!-- tag file sed by both signup.jelly and addUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout norefresh="true" title="${%Sign up}">
<l:header>
<style>
<!-- match width with captcha image -->
INPUT {
width:200px;
}
</style>
</l:header>
<l:hasPermission permission="${app.READ}" it="${host}">
<st:include page="sidepanel.jelly" it="${host}" />
</l:hasPermission>
<l:main-panel>
<h1>${title}</h1>
<div style="margin: 2em;">
<j:if test="${data.errorMessage!=null}">
......@@ -45,7 +32,6 @@ THE SOFTWARE.
${data.errorMessage}
</div>
</j:if>
<form action="${rootURL}/securityRealm/${action}" method="post" style="text-size:smaller">
<table>
<tr>
<td>${%Username}:</td>
......@@ -77,12 +63,5 @@ THE SOFTWARE.
</tr>
</j:if>
</table>
<f:submit value="${title}" />
<script>
$('username').focus();
</script>
</form>
</div>
</l:main-panel>
</l:layout>
</j:jelly>
<!--
The MIT License
Copyright (c) 2004-2016, Sun Microsystems, Inc., CloudBees, Inc., Kohsuke Kawaguchi, Seiji Sogabe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!-- tag file used by both signup.jelly and addUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout norefresh="true" title="${title}">
<l:header>
<style>
<!-- match width with captcha image -->
INPUT {
width:200px;
}
</style>
</l:header>
<l:hasPermission permission="${app.READ}" it="${host}">
<st:include page="sidepanel.jelly" it="${host}" />
</l:hasPermission>
<l:main-panel>
<form action="${rootURL}/securityRealm/${action}" method="post" style="text-size:smaller">
<local:_entryForm title="${title}" action="${action}" captcha="${captcha}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<f:submit value="${title}" />
<script>
$('username').focus();
</script>
</form>
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryFormPage host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
......@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryFormPage host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
<!--
This is used to create the first user.
-->
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<style type="text/css">
@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css);
@import url(https://fonts.googleapis.com/css?family=Roboto:400,300,500,900,700);
#main-panel {
margin: 0;
padding: 0;
}
tr td {
padding-bottom: 2px;
}
body {
padding: 20px 20px 20px 100px;
font-family: 'roboto';
}
form > div {
margin: 0 !important;
}
h1 {
font-family: 'roboto', sans-serif;
font-size: 48px;
margin-top: 30px;
font-weight: 500;
}
h1 img {
position: absolute;
right: -80px;
top: 38px;
}
tr td, input {
line-height: 25px;
margin-bottom: 6px;
}
input[type=text], input[type=password] {
border: 1px solid #ddd;
border-radius: 2px;
padding: 1px 8px;
}
</style>
<form action="${rootURL}/securityRealm/${action}" method="post">
<local:_entryForm host="${app.securityRealm}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<script>
$('username').focus();
</script>
</form>
</l:main-panel>
</l:html>
......@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccount" captcha="${it.isEnableCaptcha()}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccount" captcha="${it.isEnableCaptcha()}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
......@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${app.instance.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<form action="${app.instance.securityRealm.authenticationGatewayUrl}" method="POST">
<div class="plugin-setup-wizard bootstrap-3">
<div class="modal fade in" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">${%Security Token}</h4>
</div>
<div class="modal-body">
<i class="water-mark icon-service"></i>
<div class="jumbotron welcome-panel offline">
<h1>${%Security Token}</h1>
<p>${%As an extra measure of security, please enter the setup security token to proceed.}</p>
<p>${%It can be found in the logs for this Jenkins instance.}</p>
<j:if test="${error}">
<div class="alert alert-danger">
<strong>${%ERROR:} </strong>
${%There is a problem with your security token, please check the server logs for the correct token}
</div>
</j:if>
<div class="form-group ${error ? 'has-error' : ''}">
<label class="control-label" for="security-token">${%Security token}</label>
<input name="j_username" value="${j.setupWizard.initialSetupAdminUserName}" type="hidden"/>
<input id="security-token" class="form-control" name="j_password"/>
<link rel="stylesheet" href="${j.installWizardPath}.css" type="text/css" />
</div>
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary set-security-key" value="${%Continue}"/>
</div>
</div>
</div>
</div>
</div>
</form>
</l:main-panel>
</l:html>
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<div class="plugin-setup-wizard-container"></div>
<script src="${resURL}/${j.installWizardPath}.js" type="text/javascript"/>
<link rel="stylesheet" href="${resURL}/${j.installWizardPath}.css" type="text/css" />
</l:main-panel>
</l:html>
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<h1>${%HTTP Proxy Configuration}</h1>
<f:form method="post" action="/pluginManager/proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${app.pluginManager.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</j:scope>
</f:form>
</l:main-panel>
</l:html>
installWizard_welcomePanel_title=Getting Started
installWizard_welcomePanel_banner=Initial Jenkins Setup
installWizard_welcomePanel_message=Choose plugins. This will add features to your Jenkins environment.
installWizard_welcomePanel_recommendedActionTitle=Start with recommended plugins
installWizard_welcomePanel_recommendedActionDetails=Install the set of plugins the community finds most useful
installWizard_welcomePanel_customizeActionTitle=Customize your plugins
installWizard_welcomePanel_customizeActionDetails=Select from a community approved list of plugins
installWizard_welcomePanel_banner=Customize Jenkins
installWizard_welcomePanel_message=Plugins extend Jenkins with additional features to support many different needs.
installWizard_welcomePanel_recommendedActionTitle=Install suggested plugins
installWizard_welcomePanel_recommendedActionDetails=Install plugins the Jenkins community finds most useful.
installWizard_welcomePanel_customizeActionTitle=Select plugins to install
installWizard_welcomePanel_customizeActionDetails=Select and install plugins most suitable for your needs.
installWizard_offline_title=Offline
installWizard_offline_message=This Jenkins instance appears to be offline. \
<p style="font-size:18px; margin-top: 6%"> \
For information about installing Jenkins without an internet connection, see the \
<a href="https://wiki.jenkins-ci.org/display/JENKINS/Offline+Jenkins+Installation" target="_blank">Offline Jenkins Installation Documentation</a>. <br/><br/> \
If you need to configure a proxy, you may close this wizard and use the Plugin Manager. \
You may choose to continue by configuring a proxy or skipping plugin installation. \
</p>
installWizard_error_header=An error occurred
installWizard_error_message=An error occurred during installation:
......@@ -28,12 +28,23 @@ installWizard_installing_title=Installing...
installWizard_installing_detailsLink=Details...
installWizard_installComplete_title=Installed
installWizard_installComplete_banner=Jenkins is ready!
installWizard_installComplete_message=Your plugin installations are complete.
installWizard_installComplete_finishButtonLabel=Get Started
installWizard_pluginsInstalled_message=Your plugin installations are complete.
installWizard_installComplete_message=Your Jenkins setup is complete.
installWizard_installComplete_finishButtonLabel=Start using Jenkins
installWizard_installComplete_restartRequiredMessage=Some plugins require Jenkins to be restarted.
installWizard_installComplete_restartLabel=Restart
installWizard_installIncomplete_title=Resume Installation
installWizard_installIncomplete_banner=Resume Installation
installWizard_installIncomplete_message=Jenknins was restarted during installation and some plugins didn't seem to get installed.
installWizard_installIncomplete_message=Jenkins was restarted during installation and some plugins didn't seem to get installed.
installWizard_installIncomplete_resumeInstallationButtonLabel=Resume
installWizard_saveFirstUser=Save and Finish
installWizard_skipFirstUser=Skip
installWizard_firstUserSkippedMessage=<div class="alert alert-warning fade in">\
You've skipped creating an admin user. To log in, use the username: 'admin' and \
the security token you used to access the setup wizard.\
</div>
installWizard_addFirstUser_title=Create an Admin User
installWizard_configureProxy_label=Configure Proxy
installWizard_configureProxy_save=Save and Continue
installWizard_skipPluginInstallations=Skip Plugin Installations
installWizard_installIncomplete_dependenciesLabel=Dependencies
......@@ -24,6 +24,10 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:if test="${it.setupWizard != null}">
<st:include it="${it.setupWizard}" page="authenticate-security-token"/>
</j:if>
<j:if test="${it.setupWizard == null}">
<l:layout norefresh="true">
<l:hasPermission permission="${app.READ}">
<st:include page="sidepanel.jelly" />
......@@ -70,4 +74,5 @@ THE SOFTWARE.
</div>
</l:main-panel>
</l:layout>
</j:if>
</j:jelly>
......@@ -27,6 +27,10 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:choose>
<j:new var="h" className="hudson.Functions" />
<j:when test="${app.setupWizard != null}">
<j:set var="error" value="true"/>
<st:include it="${app.setupWizard}" page="authenticate-security-token"/>
</j:when>
<j:when test="${app.isUseSecurity() and h.isAnonymous()}">
<!--
The only time the error message makes sense is when Jenkins is protected and the user failed to authenticate.
......
......@@ -44,6 +44,9 @@ THE SOFTWARE.
<st:attribute name="onclick" />
<st:attribute name="class" />
<st:attribute name="negative" />
<st:attribute name="readonly">
If set to true, this will take precedence over the onclick attribute and prevent the state of the checkbox from being changed.
</st:attribute>
<st:attribute name="field">
Used for databinding. TBD.
</st:attribute>
......@@ -63,7 +66,7 @@ THE SOFTWARE.
name="${name}"
value="${attrs.value}"
title="${attrs.tooltip}"
onclick="${attrs.onclick}" id="${attrs.id}" class="${attrs.class} ${attrs.negative!=null ? 'negative' : null} ${attrs.checkUrl!=null?'validated':''}"
onclick="${attrs.readonly=='true' ? 'return false;' : attrs.onclick}" id="${attrs.id}" class="${attrs.class} ${attrs.negative!=null ? 'negative' : null} ${attrs.checkUrl!=null?'validated':''}"
checkUrl="${attrs.checkUrl}" checkDependsOn="${attrs.checkDependsOn}" json="${attrs.json}"
checked="${value ? 'true' : null}"/>
<j:if test="${attrs.title!=null}">
......
......@@ -44,7 +44,11 @@ Behaviour.specify("SELECT.select", 'select', 1000, function(e) {
function hasChanged(selectEl, originalValue) {
// seems like a race condition allows this to fire before the 'selectEl' is defined. If that happens, exit..
<<<<<<< HEAD
if(!selectEl || !selectEl.options || !selectEl.options[0])
=======
if(!selectEl || !selectEl.options || !selectEl[0])
>>>>>>> 219481a2926a6a6e2d86753f250449ba73f198ba
return false;
var firstValue = selectEl.options[0].value;
var selectedValue = selectEl.value;
......
<!--
The MIT License
Copyright (c) 2004-2016, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Daniel Dyer, Seiji Sogabe, Tom Huybrechts, Manufacture Francaise des Pneumatiques
Michelin, Romain Seguy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
<st:documentation>
Outer-most tag for a normal (non-AJAX) HTML rendering.
This is used with nested &lt;header>, &lt;side-panel>, and &lt;main-panel>
to form Jenkins's basic HTML layout.
<st:attribute name="title" use="required">
Title of the HTML page. Rendered into &lt;title> tag.
</st:attribute>
<st:attribute name="norefresh">
If non-null and not "false", auto refresh is disabled on this page.
This is necessary for pages that include forms.
</st:attribute>
<st:attribute name="css" deprecated="true">
specify path that starts from "/" for loading additional CSS stylesheet.
path is interprted as relative to the context root. e.g.,
{noformat}&lt;l:layout css="/plugin/mysuperplugin/css/myneatstyle.css">{noformat}
This was originally added to allow plugins to load their stylesheets, but
*the use of thie attribute is discouraged now.*
plugins should now do so by inserting &lt;style> elements and/or &lt;script> elements
in &lt;l:header/> tag.
</st:attribute>
<st:attribute name="permission">
If given, this page is only made available to users that has the specified permission.
(The permission will be checked against the "it" object.)
</st:attribute>
</st:documentation>
<st:setHeader name="Expires" value="0" />
<st:setHeader name="Cache-Control" value="no-cache,no-store,must-revalidate" />
<st:setHeader name="X-Hudson-Theme" value="default" />
<st:contentType value="text/html;charset=UTF-8" />
<j:new var="h" className="hudson.Functions" /><!-- instead of JSP functions -->
${h.initPageVariables(context)}
<!--
depending on what tags are used, we can later end up discovering that we needed a session,
but it's too late because the headers are already committed. so ensure we always have a session.
this also allows us to configure HttpSessionContextIntegrationFilter not to create sessions,
which I suspect can end up creating sessions for wrong resource types (such as static resources.)
-->
<j:set var="isMSIE" value="${userAgent.contains('MSIE')}"/>
<j:set var="_" value="${request.getSession()}"/>
<j:set var="_" value="${h.configureAutoRefresh(request, response, attrs.norefresh!=null and !attrs.norefresh.equals(false))}"/>
<j:set var="extensionsAvailable" value="${h.extensionsAvailable}"/>
<j:if test="${request.servletPath=='/' || request.servletPath==''}">
${h.advertiseHeaders(response)}
<j:if test="${extensionsAvailable}">
<j:forEach var="pd" items="${h.pageDecorators}">
<st:include it="${pd}" page="httpHeaders.jelly" optional="true"/>
</j:forEach>
</j:if>
</j:if>
<x:doctype name="html" />
<html>
<head data-rooturl="${rootURL}" data-resurl="${resURL}" resURL="${resURL}">
${h.checkPermission(it,permission)}
<j:if test="${isMSIE}">
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
</j:if>
<title>${h.appendIfNotNull(title, ' [Jenkins]', 'Jenkins')}</title>
<link rel="stylesheet" href="${resURL}/css/style.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/color.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/responsive-grid.css" type="text/css" />
<j:if test="${attrs.css!=null}">
<link rel="stylesheet" href="${resURL}${attrs.css}" type="text/css" />
</j:if>
<link rel="shortcut icon" href="${resURL}/favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="mask-icon" href="${rootURL}/images/mask-icon.svg" color="black" />
<!-- are we running as an unit test? -->
<script>var isRunAsTest=${h.isUnitTest}; var rootURL="${rootURL}"; var resURL="${resURL}";</script>
<script src="${resURL}/scripts/prototype.js" type="text/javascript"/>
<script src="${resURL}/scripts/behavior.js" type="text/javascript"/>
<!-- we include our own prototype.js, so don't let stapler pull in another. -->
<st:adjunct assumes="org.kohsuke.stapler.framework.prototype.prototype"
includes="org.kohsuke.stapler.bind"/>
<!-- To use the debug version of YUI, set the system property 'debug.YUI' to true -->
<j:set var="yuiSuffix" value="${h.yuiSuffix}" />
<l:yui module="yahoo" />
<l:yui module="dom" />
<l:yui module="event" />
<j:if test="${h.yuiSuffix=='debug'}">
<l:yui module="logger" />
</j:if>
<l:yui module="animation" />
<l:yui module="dragdrop" />
<l:yui module="container" />
<l:yui module="connection" />
<l:yui module="datasource" />
<l:yui module="autocomplete" />
<l:yui module="menu" />
<l:yui module="element" />
<l:yui module="button" />
<l:yui module="storage" />
<!--l:yui module="editor" suffix="-beta" /-->
<script src="${resURL}/scripts/hudson-behavior.js" type="text/javascript"></script>
<script src="${resURL}/scripts/sortable.js" type="text/javascript"/>
<j:if test="${extensionsAvailable}">
<script>
crumb.init("${h.getCrumbRequestField()}", "${h.getCrumb(request)}");
</script>
</j:if>
<link rel="stylesheet" href="${resURL}/scripts/yui/container/assets/container.css" type="text/css"/>
<link rel="stylesheet" href="${resURL}/scripts/yui/assets/skins/sam/skin.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/scripts/yui/container/assets/skins/sam/container.css" type="text/css"/>
<link rel="stylesheet" href="${resURL}/scripts/yui/button/assets/skins/sam/button.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/scripts/yui/menu/assets/skins/sam/menu.css" type="text/css" />
<!--link rel="stylesheet" href="${resURL}/scripts/yui/editor/assets/skins/sam/editor.css" type="text/css" /-->
<l:hasPermission permission="${app.READ}">
<link rel="search" type="application/opensearchdescription+xml" href="${rootURL}/opensearch.xml" title="Jenkins" />
</l:hasPermission>
<meta name="ROBOTS" content="INDEX,NOFOLLOW" />
<j:set var="mode" value="header" />
<d:invokeBody />
<j:if test="${extensionsAvailable}">
<j:forEach var="pd" items="${h.pageDecorators}">
<st:include it="${pd}" page="header.jelly" optional="true" />
</j:forEach>
</j:if>
<j:invokeStatic var="j" className="jenkins.model.Jenkins" method="getActiveInstance" />
<j:set var="installState" value="${j.installState.name()}" />
<j:if test="${installState == 'NEW' || installState == 'INITIAL_PLUGINS_INSTALLING'}">
<script src="${resURL}/${j.installWizardPath}.js" type="text/javascript"/>
<link rel="stylesheet" href="${resURL}/${j.installWizardPath}.css" type="text/css" />
</j:if>
<j:if test="${isMSIE}">
<script src="${resURL}/scripts/msie.js" type="text/javascript"/>
</j:if>
</head>
<body id="jenkins" class="yui-skin-sam jenkins-${h.version}" data-version="jenkins-${h.version}" data-model-type="${it.class.name}">
<div id="main-panel" style="margin-left: 0;">
<j:set var="mode" value="main-panel" />
<d:invokeBody />
</div>
</body>
</html>
</j:jelly>
......@@ -166,10 +166,6 @@ ${h.initPageVariables(context)}
<j:invokeStatic var="j" className="jenkins.model.Jenkins" method="getActiveInstance" />
<j:set var="installState" value="${j.installState.name()}" />
<j:if test="${installState == 'NEW' || installState == 'INITIAL_PLUGINS_INSTALLING'}">
<script src="${resURL}/${j.installWizardPath}.js" type="text/javascript"/>
<link rel="stylesheet" href="${resURL}/${j.installWizardPath}.css" type="text/css" />
</j:if>
<j:if test="${isMSIE}">
<script src="${resURL}/scripts/msie.js" type="text/javascript"/>
......
......@@ -58,7 +58,7 @@ THE SOFTWARE.
<connection>scm:git:git://github.com/jenkinsci/jenkins.git</connection>
<developerConnection>scm:git:ssh://git@github.com/jenkinsci/jenkins.git</developerConnection>
<url>https://github.com/jenkinsci/jenkins</url>
<tag>HEAD</tag>
<tag>jenkins-1.652</tag>
</scm>
<distributionManagement>
......
......@@ -65,6 +65,12 @@ THE SOFTWARE.
<artifactId>jenkins-test-harness</artifactId>
<version>2.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-war</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
......
......@@ -31,6 +31,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.JenkinsRule.WebClient;
import java.util.List;
......@@ -47,11 +48,15 @@ public class ManagementLinkTest {
*/
@Test
public void links() throws Exception {
HtmlPage page = j.createWebClient().goTo("manage");
WebClient wc = j.createWebClient();
for (int i=0; ; i++) {
HtmlPage page = wc.goTo("manage");
List<?> anchors = DomNodeUtil.selectNodes(page, "id('management-links')//*[@class='link']/a[not(@onclick)]");
assertTrue(anchors.size()>=8);
for(HtmlAnchor e : (List<HtmlAnchor>) anchors) {
e.click();
if (i==anchors.size()) return; // done
((HtmlAnchor)anchors.get(i)).click();
}
}
}
......@@ -75,7 +75,8 @@ public class UpdateCenterPluginInstallTest {
String correlationId = data.getString("correlationId");
JSONObject installStatus = jenkinsRule.getJSON("updateCenter/installStatus?correlationId=" + correlationId).getJSONObject();
Assert.assertEquals("ok", json.get("status"));
JSONArray states = installStatus.getJSONArray("data");
JSONObject status = installStatus.getJSONObject("data");
JSONArray states = status.getJSONArray("jobs");
Assert.assertEquals(2, states.size());
JSONObject pluginInstallState = states.getJSONObject(0);
......
......@@ -28,6 +28,7 @@ import hudson.FilePath;
import hudson.remoting.VirtualChannel;
import hudson.scm.NullSCM;
import hudson.slaves.DumbSlave;
import hudson.slaves.WorkspaceList;
import hudson.util.StreamTaskListener;
import java.io.File;
......@@ -180,6 +181,19 @@ public class WorkspaceCleanupThreadTest {
assertFalse(ws.exists());
}
@Issue("JENKINS-27152")
@Test
public void deleteTemporaryDirectory() throws Exception {
FreeStyleProject p = r.createFreeStyleProject();
FilePath ws = createOldWorkspaceOn(r.jenkins, p);
FilePath tmp = WorkspaceList.tempDir(ws);
tmp.child("stuff").write("content", null);
createOldWorkspaceOn(r.createOnlineSlave(), p);
performCleanup();
assertFalse(ws.exists());
assertFalse("temporary directory should be cleaned up as well", tmp.exists());
}
private FilePath createOldWorkspaceOn(Node slave, FreeStyleProject p) throws Exception {
p.setAssignedNode(slave);
FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
......
......@@ -149,7 +149,7 @@ exports.incompleteInstallStatus = function(handler, correlationId) {
* Call this to complete the installation without installing anything
*/
exports.completeInstall = function(handler) {
jenkins.get('/updateCenter/completeInstall', function() {
jenkins.get('/setupWizard/completeInstall', function() {
handler.call({ isError: false });
}, {
timeout: pluginManagerErrorTimeoutMillis,
......
......@@ -8,17 +8,27 @@
// away from these.
//
exports.recommendedPlugins = [
"ant",
"antisamy-markup-formatter",
"credentials",
"build-monitor-plugin",
"build-timeout",
"cloudbees-folder",
"credentials-binding",
"email-ext",
"git",
"github-branch-source",
"junit",
"gradle",
"ldap",
"mailer",
"matrix-auth",
"script-security",
// "matrix-auth",
"pam-auth",
"pipeline-stage-view",
"ssh-slaves",
"subversion",
"translation",
"timestamper",
"workflow-aggregator",
"workflow-multibranch"
"workflow-multibranch",
"ws-cleanup"
];
//
......@@ -27,50 +37,84 @@ exports.recommendedPlugins = [
//
exports.availablePlugins = [
{
"category": "General",
"description": "(a collection of things I cannot think of a better name for)",
"category":"Organization and Administration",
"plugins": [
{ "name": "external-monitor-job" },
{ "name": "translation" }
// { "name": "dashboard-view" },
{ "name": "build-monitor-plugin" },
{ "name": "cloudbees-folder" },
{ "name": "antisamy-markup-formatter" }
]
},
{
"category":"Organization and Administration",
"category":"Build Features",
"description":"Add general purpose features to your jobs",
"plugins": [
{ "name": "antisamy-markup-formatter" }
{ "name": "ansicolor" },
// { "name": "build-name-setter" },
{ "name": "build-timeout" },
{ "name": "config-file-provider" },
{ "name": "credentials-binding" },
{ "name": "rebuild" },
{ "name": "ssh-agent" },
// { "name": "throttle-concurrents" },
{ "name": "timestamper" }
// { "name": "ws-cleanup" }
]
},
{
"category":"Build Tools",
"plugins": [
{ "name": "ant" },
{ "name": "maven-plugin" }
{ "name": "gradle" },
{ "name": "msbuild" },
{ "name": "nodejs" }
]
},
{
"category":"Build Analysis and Reporting",
"plugins": [
{ "name": "javadoc" },
{ "name": "junit" }
// { "name": "checkstyle" },
// { "name": "cobertura" },
{ "name": "htmlpublisher" },
{ "name": "junit" },
// { "name": "sonar" },
// { "name": "warnings" },
{ "name": "xunit" }
]
},
{
"category":"Pipelines and Continuous Delivery",
"plugins": [
{ "name": "workflow-aggregator" },
{ "name": "workflow-multibranch" },
{ "name": "github-branch-source" },
{ "name": "workflow-multibranch" }
{ "name": "pipeline-stage-view" },
{ "name": "build-pipeline-plugin" },
// { "name": "conditional-buildstep" },
// { "name": "jenkins-multijob-plugin" },
{ "name": "parameterized-trigger" },
{ "name": "copyartifact" }
]
},
{
"category":"SCM",
"category":"Source Code Management",
"plugins": [
{ "name": "bitbucket" },
{ "name": "clearcase" },
{ "name": "cvs" },
{ "name": "subversion" }
{ "name": "git" },
{ "name": "git-parameter" },
{ "name": "github" },
{ "name": "gitlab-plugin" },
{ "name": "p4" },
{ "name": "repo" },
{ "name": "subversion" },
{ "name": "teamconcert" },
{ "name": "tfs" }
]
},
{
"category":"Distributed Builds and Containers",
"category":"Distributed Builds",
"plugins": [
{ "name": "matrix-project" },
{ "name": "ssh-slaves" },
......@@ -80,18 +124,22 @@ exports.availablePlugins = [
{
"category":"User Management and Security",
"plugins": [
{ "name": "credentials" },
{ "name": "ldap" },
{ "name": "matrix-auth" },
// { "name": "matrix-auth" },
{ "name": "pam-auth" },
{ "name": "script-security" },
{ "name": "ssh-credentials" }
{ "name": "ldap" },
// { "name": "role-strategy" },
{ "name": "active-directory" }
]
},
{
"category":"Notifications and Publishing",
"plugins": [
{ "name": "mailer" }
{ "name": "email-ext" },
{ "name": "emailext-template" },
{ "name": "mailer" },
{ "name": "publish-over-ssh" },
{ "name": "slack" },
{ "name": "ssh" }
]
}
];
\ No newline at end of file
/**
* Provides a wrapper to interact with the security configuration
*/
var jenkins = require('../util/jenkins');
var jquery = require('jquery-detached');
var wh = require('window-handle');
/**
* Calls a stapler post method to save the first user settings
*/
exports.saveFirstUser = function($form, success, error) {
jenkins.staplerPost(
'/securityRealm/createFirstAccount',
$form,
success, {
dataType: 'html',
error: error
});
};
/**
* Calls a stapler post method to save the first user settings
*/
exports.saveProxy = function($form, success, error) {
jenkins.staplerPost(
'/pluginManager/proxyConfigure',
$form,
success, {
dataType: 'html',
error: error
});
};
......@@ -6,5 +6,10 @@ var pluginSetupWizard = require('./pluginSetupWizardGui');
// This entry point for the bundle only bootstraps the main module in a browser
$(function() {
pluginSetupWizard.init();
$('.plugin-setup-wizard-container').each(function() {
var $container = $(this);
if($container.children().length === 0) { // this may get double-initialized
pluginSetupWizard.init($container);
}
});
});
......@@ -7,9 +7,13 @@ 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');
var wh = require('window-handle');
window.zq = jquery.getJQuery();
// Setup the dialog, exported
var createPluginSetupWizard = function() {
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();
......@@ -109,6 +113,9 @@ var createPluginSetupWizard = function() {
var progressPanel = require('./templates/progressPanel.hbs');
var pluginSelectionPanel = require('./templates/pluginSelectionPanel.hbs');
var successPanel = require('./templates/successPanel.hbs');
var setupCompletePanel = require('./templates/setupCompletePanel.hbs');
var proxyConfigPanel = require('./templates/proxyConfigPanel.hbs');
var firstUserPanel = require('./templates/firstUserPanel.hbs');
var offlinePanel = require('./templates/offlinePanel.hbs');
var pluginSetupWizard = require('./templates/pluginSetupWizard.hbs');
var incompleteInstallationPanel = require('./templates/incompleteInstallationPanel.hbs');
......@@ -142,7 +149,7 @@ var createPluginSetupWizard = function() {
// Instantiate the wizard panel
var $wizard = $(pluginSetupWizard());
$wizard.appendTo('body');
$wizard.appendTo(appendTarget);
var $container = $wizard.find('.modal-content');
var currentPanel;
......@@ -177,7 +184,7 @@ var createPluginSetupWizard = function() {
decorations[i]($base);
}
};
var html = panel($.extend({translations: translations}, data));
var html = panel($.extend({translations: translations, baseUrl: jenkins.baseUrl}, data));
if(panel === currentPanel) { // just replace id-marked elements
var $upd = $(html);
$upd.find('*[id]').each(function() {
......@@ -201,6 +208,12 @@ var createPluginSetupWizard = function() {
$container.append(html);
decorate($container);
var $modalHeader = $container.find('.modal-header');
if($modalHeader.length > 0) {
$modalHeader.prepend(
'<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>');
}
if(oncomplete) {
oncomplete();
}
......@@ -306,13 +319,22 @@ var createPluginSetupWizard = function() {
};
// Define actions
var showInstallProgress = function() {
var showInstallProgress = function(state) {
if(state) {
if(/CREATE_ADMIN_USER/.test(state)) {
setupFirstUser();
return;
}
}
initInstallingPluginList();
setPanel(progressPanel, { installingPlugins : installingPlugins });
// call to the installStatus, update progress bar & plugin details; transition on complete
var updateStatus = function() {
pluginManager.installStatus(handleGenericError(function(jobs) {
pluginManager.installStatus(handleGenericError(function(data) {
var jobs = data.jobs;
var i, j;
var complete = 0;
var total = 0;
......@@ -402,10 +424,7 @@ var createPluginSetupWizard = function() {
else {
// mark complete
$('.progress-bar').css({width: '100%'});
setPanel(successPanel, {
installingPlugins : installingPlugins,
restartRequired: restartRequired
});
setupFirstUser();
}
}));
};
......@@ -416,7 +435,7 @@ var createPluginSetupWizard = function() {
// Called to complete the installation
var finishInstallation = function() {
jenkins.goTo('/');
closeInstaller();
};
// load the plugin data, callback
......@@ -647,6 +666,63 @@ var createPluginSetupWizard = function() {
}
};
var enableButtonsAfterFrameLoad = function() {
$('iframe[src]').load(function() {
var location = $(this).contents().get(0).location.href;
$('button').prop({disabled:false});
});
};
var setupFirstUser = function() {
setPanel(firstUserPanel, {}, enableButtonsAfterFrameLoad);
};
// call to submit the firstuser
var saveFirstUser = function() {
$('button').prop({disabled:true});
var handleSubmit = function(data) {
if(data.status && data.status > 200) {
// Nothing we can really do here
setPanel(errorPanel, { errorMessage: data.statusText });
return;
}
// we get 200 OK
var $page = $(data);
var $errors = $page.find('.error');
if($errors.length > 0) {
var $main = $page.find('#main-panel').detach();
if($main.length > 0) {
data = data.replace(/body([^>]*)[>](.|[\r\n])+[<][/]body/,'body$1>'+$main.html()+'</body');
}
var doc = $('iframe[src]').contents()[0];
doc.open();
doc.write(data);
doc.close();
}
else {
setPanel(setupCompletePanel);
}
};
securityConfig.saveFirstUser($('iframe[src]').contents().find('form:not(.no-json)'), handleSubmit, handleSubmit);
};
var skipFirstUser = function() {
$('button').prop({disabled:true});
setPanel(setupCompletePanel, {message: translations.installWizard_firstUserSkippedMessage});
};
// call to setup the proxy
var setupProxy = function() {
setPanel(proxyConfigPanel, {}, enableButtonsAfterFrameLoad);
};
// Save the proxy config
var saveProxyConfig = function() {
securityConfig.saveProxy($('iframe[src]').contents().find('form:not(.no-json)'), function() {
jenkins.goTo('/'); // this will re-run connectivity test
});
};
// Call this to resume an installation after restart
var resumeInstallation = function() {
// don't re-initialize installing plugins
......@@ -716,7 +792,12 @@ var createPluginSetupWizard = function() {
'.select-category': selectCategory,
'.close': closeInstaller,
'.resume-installation': resumeInstallation,
'.install-done-restart': restartJenkins
'.install-done-restart': restartJenkins,
'.save-first-user:not([disabled])': saveFirstUser,
'.skip-first-user': skipFirstUser,
'.show-proxy-config': setupProxy,
'.save-proxy-config': saveProxyConfig,
'.skip-plugin-installs': function() { installPlugins([]); }
};
for(var cls in actions) {
bindClickHandler(cls, actions[cls]);
......@@ -737,7 +818,9 @@ var createPluginSetupWizard = function() {
}
// check for updates when first loaded...
pluginManager.installStatus(handleGenericError(function(jobs) {
pluginManager.installStatus(handleGenericError(function(data) {
var jobs = data.jobs;
if(jobs.length > 0) {
if (installingPlugins.length === 0) {
// This can happen on a page reload if we are in the middle of
......@@ -752,10 +835,10 @@ var createPluginSetupWizard = function() {
selectedPluginNames.push(jobs[i].name);
}
}
showInstallProgress();
showInstallProgress(data.state);
}));
} else {
showInstallProgress();
showInstallProgress(data.state);
}
return;
}
......
<div class="modal-header">
<h4 class="modal-title">{{translations.installWizard_addFirstUser_title}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel security-panel">
<iframe src="{{baseUrl}}/securityRealm/setupWizardFirstUser"></iframe>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link skip-first-user" disabled>
{{translations.installWizard_skipFirstUser}}
</button>
<button type="button" class="btn btn-primary save-first-user" disabled>
{{translations.installWizard_saveFirstUser}}
</button>
</div>
<div class="modal-header">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{translations.installWizard_installIncomplete_title}}</h4>
</div>
<div class="modal-body">
......
<div class="modal-header">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{translations.installWizard_offline_title}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel offline">
<h1>{{translations.installWizard_offline_title}}</h1>
<p>{{{translations.installWizard_offline_message}}}</p>
<p>
<button type="button" class="btn btn-primary show-proxy-config">{{translations.installWizard_configureProxy_label}}</button>
<button type="button" class="btn btn-primary skip-plugin-installs">{{translations.installWizard_skipPluginInstallations}}</button>
</p>
</div>
</div>
<div class="modal-header">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{translations.installWizard_installCustom_title}}</h4>
</div>
<div class="modal-body plugin-selector">
......
<div class="modal-header">
<h4 class="modal-title">{{translations.installWizard_configureProxy_label}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel security-panel">
<iframe src="{{baseUrl}}/setupWizard/proxy-configuration"></iframe>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link install-home">
{{translations.installWizard_goBack}}
</button>
<button type="button" class="btn btn-primary save-proxy-config" disabled>
{{translations.installWizard_configureProxy_save}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title">{{translations.installWizard_installComplete_title}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel success-panel">
<h1>{{translations.installWizard_installComplete_banner}}</h1>
{{{message}}}
{{#if restartRequired}}
<p>{{translations.installWizard_installComplete_message}} {{translations.installWizard_installComplete_restartRequiredMessage}}</p>
<button type="button" class="btn btn-primary install-done-restart">
{{translations.installWizard_installComplete_restartLabel}}
</button>
{{else}}
<p>{{translations.installWizard_installComplete_message}}</p>
<button type="button" class="btn btn-primary install-done">
{{translations.installWizard_installComplete_finishButtonLabel}}
</button>
{{/if}}
</div>
</div>
<div class="modal-header">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{translations.installWizard_installComplete_title}}</h4>
<h4 class="modal-title">{{translations.installWizard_pluginsInstalled_title}}</h4>
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel success-panel">
<h1>{{translations.installWizard_installComplete_banner}}</h1>
<h1>{{translations.installWizard_pluginsInstalled_banner}}</h1>
{{#if restartRequired}}
<p>{{translations.installWizard_installComplete_message}} {{translations.installWizard_installComplete_restartRequiredMessage}}</p>
......
<div class="modal-header">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{translations.installWizard_welcomePanel_title}}</h4>
</div>
<div class="modal-body">
......
......@@ -100,15 +100,42 @@ exports.post = function(url, data, success, options) {
if(debug) {
console.log('post: ' + url);
}
var $ = jquery.getJQuery();
// handle crumbs
var headers = {};
var wnd = wh.getWindow();
var crumb;
if('crumb' in options) {
crumb = options.crumb;
}
else if('crumb' in wnd) {
crumb = wnd.crumb;
}
if(crumb) {
headers[crumb.fieldName] = crumb.value;
}
var formBody = data;
if(formBody instanceof Object) {
if(crumb) {
formBody = $.extend({}, formBody);
formBody[crumb.fieldName] = crumb.value;
}
formBody = exports.stringify(formBody);
}
var args = {
url: exports.baseUrl() + url,
type: 'POST',
cache: false,
dataType: 'json',
data: exports.stringify(data),
data: formBody,
contentType: "application/json",
success: success
success: success,
headers: headers
};
if(options instanceof Object) {
$.extend(args, options);
......@@ -186,3 +213,64 @@ exports.testConnectivity = function(handler) {
};
testConnectivity();
};
/**
* gets the window containing a form, taking in to account top-level iframes
*/
exports.getWindow = function($form) {
var $ = jquery.getJQuery();
$form = $($form);
var wnd = wh.getWindow();
$(top.document).find('iframe').each(function() {
var windowFrame = this.contentWindow;
var $f = $(this).contents().find('form');
if($f.length > 0 && $form[0] === $f[0]) {
wnd = windowFrame;
}
});
return wnd;
};
/**
* Builds a stapler form post
*/
exports.buildFormPost = function($form) {
var $ = jquery.getJQuery();
$form = $($form);
var wnd = exports.getWindow($form);
var form = $form[0];
if(wnd.buildFormTree(form)) {
return $form.serialize() +
'&core:apply=&Submit=Save&json=' + $form.find('input[name=json]').val();
}
return '';
};
/**
* Gets the crumb, if crumbs are enabled
*/
exports.getFormCrumb = function($form) {
var $ = jquery.getJQuery();
$form = $($form);
var wnd = exports.getWindow($form);
return wnd.crumb;
};
/**
* 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();
$form = $($form);
var postBody = exports.buildFormPost($form);
var crumb = exports.getFormCrumb($form);
exports.post(
url,
postBody,
success, $.extend({
processData: false,
contentType: 'application/x-www-form-urlencoded',
crumb: crumb
}, options));
};
......@@ -55,12 +55,16 @@
animation-name: none;
}
.modal {
padding: 20px 0;
}
.modal-dialog {
width: 90%;
height: 90%;
height: 100%;
padding: 0;
position: relative;
margin: 3% auto;
margin: 0 auto;
max-width:992px;
font-family:'roboto',sans-serif;
}
......@@ -715,6 +719,21 @@
}
}
}
.security-panel {
&.security-panel, > iframe {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
}
}
@-ms-keyframes spin {
......
......@@ -48,17 +48,23 @@ var ajaxMocks = function(responseMappings) {
},
'/jenkins/updateCenter/installStatus': new LastResponse([{
status: 'ok',
data: [] // first, return nothing by default, no ongoing install
data: { // first, return nothing by default, no ongoing install
state: 'NEW',
jobs: []
}
},
{
status: 'ok',
data: [
data: {
state: 'INSTALLING_PLUGINS',
jobs: [
{
name: 'subversion',
type: 'InstallJob',
installStatus: 'Success'
}
]
}
}]),
'/jenkins/pluginManager/plugins': {
status: 'ok',
......@@ -133,7 +139,7 @@ var test = function(test, ajaxMappings) {
var pluginSetupWizard = jsTest.requireSrcModule('pluginSetupWizardGui');
// exported init
pluginSetupWizard.init();
pluginSetupWizard.init('body');
test($, pluginSetupWizard);
});
......@@ -210,7 +216,7 @@ describe("pluginSetupWizard.js", function () {
var pluginSetupWizard = jsTest.requireSrcModule('pluginSetupWizardGui');
// exported init
pluginSetupWizard.init();
pluginSetupWizard.init('body');
expect($('.welcome-panel h1').text()).toBe('Offline');
......@@ -321,11 +327,16 @@ describe("pluginSetupWizard.js", function () {
var ajaxMappings = {
'/jenkins/updateCenter/installStatus': new LastResponse([{
status: 'ok',
data: [] // first, return nothing by default, no ongoing install
data: { // first, return nothing by default, no ongoing install
state: 'NEW',
jobs: []
}
},
{
status: 'ok',
data: [
data: {
state: 'INSTALLING_PLUGINS',
jobs: [
{
name: 'subversion',
type: 'InstallJob',
......@@ -333,6 +344,7 @@ describe("pluginSetupWizard.js", function () {
requiresRestart: 'true' // a string...
}
]
}
}])
};
test(function($) {
......@@ -342,7 +354,8 @@ describe("pluginSetupWizard.js", function () {
// validate a call to installPlugins with our defaults
setTimeout(function() {
expect($('.install-done').is(':visible')).toBe(false);
expect($('.install-done-restart').is(':visible')).toBe(true);
expect($('.save-first-user').is(':visible')).toBe(true);
done();
}, 500);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册