提交 bc0ae8e0 编写于 作者: K Kohsuke Kawaguchi

Merge remote-tracking branch 'origin/rc' into rc

Conflicts:
	changelog.html
......@@ -62,7 +62,38 @@ Upcoming changes</a>
<!-- these changes are controlled by the release process. DO NOT MODIFY -->
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.455>What's new in 1.455</a> <!--=DATE=--></h3>
<!--=RC-CHANGES=-->
<ul class=image>
<li class=bug>
Fixed a regression in 1.453 with IE9
(<a href="https://github.com/jenkinsci/jenkins/pull/397">pull 397</a>)
<li class=bug>
Fixed NPE in Groovy script execution via CLI (<a href="https://issues.jenkins-ci.org/browse/JENKINS-12302">issue 12302</a>)
<li class=bug>
Fixed bug where a queued build could start while the previous build was still in its post production state.
<li class=bug>
Improved the error retry logic of update center retrieval.
<li class=bug>
Update JavaMail to 1.4.4.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-1152">issue 1152</a>, <a href="https://issues.jenkins-ci.org/browse/JENKINS-3983">issue 3983</a>)
<li class=rfe>
Integrated prototype.js 1.7
(<a href="https://groups.google.com/forum/#!topic/jenkinsci-dev/rzHstHyK9Lo/discussion">discussion</a>)
<li class=rfe>
Supported programmatic retrieval/update of slave <tt>config.xml</tt>
<li class=rfe>
Breadcrumb now supports drop-down menu for faster navigation
(<a href="https://groups.google.com/forum/#!topic/jenkinsci-dev/j9uCKnQB-Xw/discussion">discussion</a>)
<li class=rfe>
Configuration pages show a navigation drop-down menu in the breadcrumb bar to jump to sections
<li class=rfe>
Hyperlinks to model objects also supports drop-down menu for faster navigation.
(<a href="https://groups.google.com/forum/#!topic/jenkinsci-dev/j9uCKnQB-Xw/discussion">discussion</a>)
<li class=rfe>
New ExtensionPoint to enforce naming conventions for projects/jobs and two implementations: Default (no restriction), Pattern (regex).
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-12928">issue 12928</a>)
<li class=rfe>
<tt>java -jar jenkins.war</tt> now uses the HTTP only session cookie that's more robust against XSS vulnerability.
</ul>
</div><!--=END=-->
<h3><a name=v1.454>What's new in 1.454</a> (2012/03/05)</h3>
<ul class=image>
......
......@@ -196,7 +196,8 @@ public class CLI {
}
};
} else {
s = new Socket(endpoint.getHostName(),endpoint.getPort());
s = new Socket();
s.connect(endpoint,3000);
out = new SocketOutputStream(s);
}
......
......@@ -42,7 +42,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.178</stapler.version>
<stapler.version>1.180</stapler.version>
</properties>
<dependencies>
......@@ -144,6 +144,11 @@ THE SOFTWARE.
<artifactId>stapler-jrebel</artifactId>
<version>${stapler.version}</version>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>windows-package-checker</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-adjunct-timeline</artifactId>
......@@ -254,7 +259,7 @@ THE SOFTWARE.
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
<version>1.4.4</version>
<exclusions>
<exclusion><!-- using a patched version -->
<groupId>javax.activation</groupId>
......@@ -524,7 +529,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.1</version>
<version>1.4</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
......
......@@ -262,6 +262,14 @@ public abstract class ExtensionFinder implements ExtensionPoint {
sezpozIndex = loadSezpozIndices(Jenkins.getInstance().getPluginManager().uberClassLoader);
List<Module> modules = new ArrayList<Module>();
modules.add(new AbstractModule() {
@Override
protected void configure() {
Jenkins j = Jenkins.getInstance();
bind(Jenkins.class).toInstance(j);
bind(PluginManager.class).toInstance(j.getPluginManager());
}
});
modules.add(new SezpozModule(sezpozIndex));
for (ExtensionComponent<Module> ec : moduleFinder.find(Module.class, Hudson.getInstance())) {
......
......@@ -152,6 +152,16 @@ public class Functions {
public static String rfc822Date(Calendar cal) {
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
}
public static void initPageVariables(JellyContext context) {
String rootURL = Stapler.getCurrentRequest().getContextPath();
Functions h = new Functions();
context.setVariable("rootURL", rootURL);
context.setVariable("h", h);
context.setVariable("resURL",rootURL+getResourcePath());
context.setVariable("imagesURL",rootURL+getResourcePath()+"/images");
}
/**
* Given {@code c=MyList (extends ArrayList<Foo>), base=List}, compute the parameterization of 'base'
......@@ -792,7 +802,8 @@ public class Functions {
*/
public static String getIconFilePath(Action a) {
String name = a.getIconFileName();
if(name.startsWith("/"))
if (name==null) return null;
if (name.startsWith("/"))
return name.substring(1);
else
return "images/24x24/"+name;
......@@ -1162,6 +1173,24 @@ public class Functions {
return base;
}
/**
* Combine path components via '/' while handling leading/trailing '/' to avoid duplicates.
*/
public static String joinPath(String... components) {
StringBuilder buf = new StringBuilder();
for (String s : components) {
if (s.length()==0) continue;
if (buf.length()>0) {
if (buf.charAt(buf.length()-1)!='/')
buf.append('/');
if (s.charAt(0)=='/') s=s.substring(1);
}
buf.append(s);
}
return buf.toString();
}
/**
* Computes the hyperlink to actions, to handle the situation when the {@link Action#getUrlName()}
* returns absolute URL.
......@@ -1172,10 +1201,10 @@ public class Functions {
if(SCHEME.matcher(urlName).find())
return urlName; // absolute URL
if(urlName.startsWith("/"))
return Stapler.getCurrentRequest().getContextPath()+urlName;
return joinPath(Stapler.getCurrentRequest().getContextPath(),urlName);
else
// relative URL name
return Stapler.getCurrentRequest().getContextPath()+'/'+itUrl+urlName;
return joinPath(Stapler.getCurrentRequest().getContextPath()+'/'+itUrl,urlName);
}
/**
......
......@@ -25,6 +25,7 @@ package hudson.cli;
import groovy.lang.GroovyShell;
import groovy.lang.Binding;
import hudson.cli.util.ScriptLoader;
import hudson.model.AbstractProject;
import jenkins.model.Jenkins;
import hudson.model.Item;
......@@ -53,7 +54,7 @@ import java.net.MalformedURLException;
* @author Kohsuke Kawaguchi
*/
@Extension
public class GroovyCommand extends CLICommand implements Serializable {
public class GroovyCommand extends CLICommand {
@Override
public String getShortDescription() {
return Messages.GroovyCommand_ShortDescription();
......@@ -103,26 +104,7 @@ public class GroovyCommand extends CLICommand implements Serializable {
if (script.equals("="))
return IOUtils.toString(stdin);
return checkChannel().call(new Callable<String,IOException>() {
public String call() throws IOException {
File f = new File(script);
if(f.exists())
return FileUtils.readFileToString(f);
URL url;
try {
url = new URL(script);
} catch (MalformedURLException e) {
throw new AbortException("Unable to find a script "+script);
}
InputStream s = url.openStream();
try {
return IOUtils.toString(s);
} finally {
s.close();
}
}
});
return checkChannel().call(new ScriptLoader(script));
}
}
package hudson.cli.util;
import hudson.AbortException;
import hudson.remoting.Callable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
/**
*
* @author vjuranek
*
*/
public class ScriptLoader implements Callable<String,IOException> {
private final String script;
public ScriptLoader(String script){
this.script = script;
}
public String call() throws IOException {
File f = new File(script);
if(f.exists())
return FileUtils.readFileToString(f);
URL url;
try {
url = new URL(script);
} catch (MalformedURLException e) {
throw new AbortException("Unable to find a script "+script);
}
InputStream s = url.openStream();
try {
return IOUtils.toString(s);
} finally {
s.close();
}
}
}
......@@ -38,6 +38,7 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
......@@ -156,8 +157,8 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
/**
* Accepts submission from the configuration page.
*/
@RequirePOST
public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
requirePOST();
JSONObject src = req.getSubmittedForm();
String newName = src.getString("name"), redirect = ".";
......@@ -203,8 +204,8 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
/**
* Deletes this recorder, then go back to the parent.
*/
@RequirePOST
public synchronized void doDoDelete(StaplerResponse rsp) throws IOException, ServletException {
requirePOST();
getConfigFile().delete();
getParent().logRecorders.remove(name);
// Disable logging for all our targets,
......
......@@ -40,6 +40,7 @@ import jenkins.model.Jenkins;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import java.io.File;
import java.io.IOException;
......@@ -90,8 +91,8 @@ public class MatrixBuild extends AbstractBuild<MatrixProject,MatrixBuild> {
/**
* Deletes the build and all matrix configurations in this build when the button is pressed.
*/
@RequirePOST
public void doDoDeleteAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
requirePOST();
checkPermission(DELETE);
// We should not simply delete the build if it has been explicitly
......
......@@ -62,6 +62,7 @@ import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.HttpDeletable;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import javax.xml.transform.Transformer;
......@@ -478,8 +479,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
* Deletes this item.
*/
@CLIMethod(name="delete-job")
@RequirePOST
public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, InterruptedException {
requirePOST();
delete();
if (rsp != null) // null for CLI
rsp.sendRedirect2(req.getContextPath()+"/"+getParent().getUrl());
......
......@@ -34,6 +34,7 @@ import hudson.search.SearchableModelObject;
import hudson.search.Search;
import hudson.search.SearchIndexBuilder;
import hudson.search.SearchIndex;
import org.kohsuke.stapler.interceptor.RequirePOST;
/**
* {@link ModelObject} with some convenience methods.
......@@ -74,6 +75,9 @@ public abstract class AbstractModelObject implements SearchableModelObject {
/**
* Convenience method to verify that the current request is a POST request.
*
* @deprecated
* Use {@link RequirePOST} on your method.
*/
protected final void requirePOST() throws ServletException {
StaplerRequest req = Stapler.getCurrentRequest();
......
......@@ -90,6 +90,7 @@ import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
......@@ -1072,7 +1073,8 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
}
public CauseOfBlockage getCauseOfBlockage() {
if (isBuilding() && !isConcurrentBuild())
// Block builds until they are done with post-production
if (isLogUpdated() && !isConcurrentBuild())
return new BecauseOfBuildInProgress(getLastBuild());
if (blockBuildWhenDownstreamBuilding()) {
AbstractProject<?,?> bup = getBuildingDownstream();
......@@ -1650,8 +1652,8 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
* Deletes this project.
*/
@Override
@RequirePOST
public void doDoDelete(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
requirePOST();
delete();
if (req == null || rsp == null)
return;
......@@ -1772,16 +1774,16 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
}
@CLIMethod(name="disable-job")
@RequirePOST
public HttpResponse doDisable() throws IOException, ServletException {
requirePOST();
checkPermission(CONFIGURE);
makeDisabled(true);
return new HttpRedirect(".");
}
@CLIMethod(name="enable-job")
@RequirePOST
public HttpResponse doEnable() throws IOException, ServletException {
requirePOST();
checkPermission(CONFIGURE);
makeDisabled(false);
return new HttpRedirect(".");
......
......@@ -89,6 +89,7 @@ public interface Action extends ModelObject {
* and no task list item. The other case where this is useful is
* to avoid showing links that require a privilege when the user is anonymous.
* @see Functions#isAnonymous()
* @see Functions#getIconFilePath(Action)
*/
String getIconFileName();
......@@ -103,7 +104,7 @@ public interface Action extends ModelObject {
/**
* Gets the URL path name.
*
* <p>
* <p>tions
* For example, if this method returns "xyz", and if the parent object
* (that this action is associated with) is bound to /foo/bar/zot,
* then this action object will be exposed to /foo/bar/zot/xyz.
......@@ -122,6 +123,7 @@ public interface Action extends ModelObject {
* @return
* null if this action object doesn't need to be bound to web
* (when you do that, be sure to also return null from {@link #getIconFileName()}.
* @see Functions#getActionUrl(String, Action)
*/
String getUrlName();
}
......@@ -23,11 +23,24 @@
*/
package hudson.model;
import hudson.Functions;
import hudson.model.queue.Tasks;
import jenkins.model.ModelObjectWithContextMenu;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
import org.kohsuke.stapler.jelly.JellyFacet;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
......@@ -38,7 +51,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public abstract class Actionable extends AbstractModelObject {
public abstract class Actionable extends AbstractModelObject implements ModelObjectWithContextMenu {
/**
* Actions contributed to this model object.
*
......@@ -119,4 +132,8 @@ public abstract class Actionable extends AbstractModelObject {
}
return null;
}
public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws Exception {
return new ContextMenu().from(this,request,response);
}
}
......@@ -64,9 +64,11 @@ import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.args4j.Option;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
......@@ -87,6 +89,8 @@ import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Inet4Address;
import static javax.servlet.http.HttpServletResponse.*;
/**
* Represents the running state of a remote computer that holds {@link Executor}s.
*
......@@ -1056,40 +1060,70 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
}
req.getView(this,view).forward(req,rsp);
req.getView(this,view).forward(req, rsp);
}
/**
* Accepts the update to the node configuration.
*/
@RequirePOST
public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
checkPermission(CONFIGURE);
requirePOST();
String name = Util.fixEmptyAndTrim(req.getSubmittedForm().getString("name"));
Jenkins.checkGoodName(name);
final Jenkins app = Jenkins.getInstance();
Node result = getNode().reconfigure(req, req.getSubmittedForm());
replaceBy(result);
// take the user back to the slave top page.
rsp.sendRedirect2("../" + result.getNodeName() + '/');
}
/**
* Accepts <tt>config.xml</tt> submission, as well as serve it.
*/
@WebMethod(name = "config.xml")
public void doConfigDotXml(StaplerRequest req, StaplerResponse rsp)
throws IOException, ServletException {
checkPermission(Jenkins.ADMINISTER);
if (req.getMethod().equals("GET")) {
// read
rsp.setContentType("application/xml");
Jenkins.XSTREAM2.toXML(getNode(), rsp.getOutputStream());
return;
}
if (req.getMethod().equals("POST")) {
// submission
Node result = (Node)Jenkins.XSTREAM2.fromXML(req.getReader());
replaceBy(result);
return;
}
// huh?
rsp.sendError(SC_BAD_REQUEST);
}
/**
* Replaces the current {@link Node} by another one.
*/
private void replaceBy(Node newNode) throws ServletException, IOException {
final Jenkins app = Jenkins.getInstance();
// replace the old Node object by the new one
synchronized (app) {
List<Node> nodes = new ArrayList<Node>(app.getNodes());
int i = nodes.indexOf(getNode());
if(i<0) {
sendError("This slave appears to be removed while you were editing the configuration",req,rsp);
return;
throw new IOException("This slave appears to be removed while you were editing the configuration");
}
nodes.set(i,result);
nodes.set(i, newNode);
app.setNodes(nodes);
}
// take the user back to the slave top page.
rsp.sendRedirect2("../"+result.getNodeName()+'/');
}
/**
* Really deletes the slave.
*/
......
......@@ -40,6 +40,7 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
......@@ -303,11 +304,11 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
/**
* Accepts submission from the configuration page.
*/
@RequirePOST
public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
BulkChange bc = new BulkChange(MONITORS_OWNER);
try {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
requirePOST();
monitors.rebuild(req,req.getSubmittedForm(),getNodeMonitorDescriptors());
// add in the rest of instances are ignored instances
......
......@@ -29,9 +29,11 @@ import org.kohsuke.stapler.StaplerRequest;
import hudson.Extension;
import hudson.FilePath;
import hudson.cli.CLICommand;
import org.apache.commons.fileupload.FileItem;
import java.io.IOException;
import java.io.File;
import javax.servlet.ServletException;
/**
* {@link ParameterDefinition} for doing file upload.
......@@ -69,14 +71,32 @@ public class FileParameterDefinition extends ParameterDefinition {
@Override
public ParameterValue createValue(StaplerRequest req) {
throw new UnsupportedOperationException();
FileItem src;
try {
src = req.getFileItem( getName() );
} catch (ServletException e) {
// Not sure what to do here. We might want to raise this
// but that would involve changing the throws for this call
return null;
} catch (IOException e) {
// ditto above
return null;
}
if ( src == null ) {
// the requested file parameter wasn't uploaded
return null;
}
FileParameterValue p = new FileParameterValue(getName(), src);
p.setDescription(getDescription());
p.setLocation(getName());
return p;
}
@Override
public ParameterValue createValue(CLICommand command, String value) throws IOException, InterruptedException {
// capture the file to the server
FilePath src = new FilePath(command.checkChannel(),value);
File local = File.createTempFile("hudson","parameter");
File local = File.createTempFile("jenkins","parameter");
src.copyTo(new FilePath(local));
FileParameterValue p = new FileParameterValue(getName(), local, src.getName());
......
......@@ -216,6 +216,7 @@ public abstract class ItemGroupMixIn {
public synchronized TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException {
acl.checkPermission(Job.CREATE);
Jenkins.getInstance().getProjectNamingStrategy().checkName(name);
// place it as config.xml
File configXml = Items.getConfigFile(getRootDirFor(name)).getFile();
configXml.getParentFile().mkdirs();
......@@ -241,6 +242,7 @@ public abstract class ItemGroupMixIn {
throws IOException {
acl.checkPermission(Job.CREATE);
Jenkins.getInstance().getProjectNamingStrategy().checkName(name);
if(parent.getItem(name)!=null)
throw new IllegalArgumentException("Project of the name "+name+" already exists");
......
......@@ -50,8 +50,6 @@ import hudson.util.DataSetBuilder;
import hudson.util.DescribableList;
import hudson.util.FormApply;
import hudson.util.Graph;
import hudson.util.HttpResponses;
import hudson.util.IOException2;
import hudson.util.RunList;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.StackedAreaRenderer2;
......@@ -60,6 +58,7 @@ import hudson.widgets.HistoryWidget;
import hudson.widgets.HistoryWidget.Adapter;
import hudson.widgets.Widget;
import jenkins.model.Jenkins;
import jenkins.model.ProjectNamingStrategy;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.jfree.chart.ChartFactory;
......@@ -79,6 +78,7 @@ import org.kohsuke.stapler.StaplerOverridable;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.awt.*;
......@@ -252,6 +252,14 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
RunT b = getLastBuild();
return b!=null && b.isBuilding();
}
/**
* Returns true if the log file is still being updated.
*/
public boolean isLogUpdated() {
RunT b = getLastBuild();
return b!=null && b.isLogUpdated();
}
@Override
public String getPronoun() {
......@@ -951,10 +959,10 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
/**
* Accepts submission from the configuration page.
*/
@RequirePOST
public synchronized void doConfigSubmit(StaplerRequest req,
StaplerResponse rsp) throws IOException, ServletException, FormException {
checkPermission(CONFIGURE);
requirePOST();
description = req.getParameter("description");
......@@ -983,11 +991,16 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
save();
String newName = req.getParameter("name");
final ProjectNamingStrategy namingStrategy = Jenkins.getInstance().getProjectNamingStrategy();
if (newName != null && !newName.equals(name)) {
// check this error early to avoid HTTP response splitting.
Jenkins.checkGoodName(newName);
namingStrategy.checkName(newName);
rsp.sendRedirect("rename?newName=" + URLEncoder.encode(newName, "UTF-8"));
} else {
if(namingStrategy.isForceExistingJobs()){
namingStrategy.checkName(name);
}
FormApply.success(".").generateResponse(req, rsp, null);
}
} catch (JSONException e) {
......@@ -1187,10 +1200,10 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
/**
* Renames this job.
*/
@RequirePOST
public/* not synchronized. see renameTo() */void doDoRename(
StaplerRequest req, StaplerResponse rsp) throws IOException,
ServletException {
requirePOST();
// rename is essentially delete followed by a create
checkPermission(CREATE);
checkPermission(DELETE);
......
......@@ -165,6 +165,9 @@ public abstract class ParameterDefinition implements
* <p>
* If a {@link ParameterDefinition} can't really support this mode of creating a value,
* you may just always return null.
*
* @throws IllegalStateException
* If the parameter is deemed required but was missing in the submission.
*/
public abstract ParameterValue createValue(StaplerRequest req);
......
......@@ -141,8 +141,6 @@ public class ParametersDefinitionProperty extends JobProperty<AbstractProject<?,
ParameterValue value = d.createValue(req);
if (value != null) {
values.add(value);
} else {
throw new IllegalArgumentException("Parameter " + d.getName() + " was missing.");
}
}
......
......@@ -255,7 +255,7 @@ public class Queue extends ResourceController implements Saveable {
public void setLoadBalancer(LoadBalancer loadBalancer) {
if(loadBalancer==null) throw new IllegalArgumentException();
this.loadBalancer = loadBalancer;
this.loadBalancer = loadBalancer.sanitize();
}
public QueueSorter getSorter() {
......
......@@ -110,6 +110,8 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import com.thoughtworks.xstream.XStream;
import org.kohsuke.stapler.interceptor.RequirePOST;
import java.io.FileOutputStream;
import java.io.OutputStream;
......@@ -1778,8 +1780,8 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
/**
* Deletes the build when the button is pressed.
*/
@RequirePOST
public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
requirePOST();
checkPermission(DELETE);
// We should not simply delete the build if it has been explicitly
......@@ -1925,9 +1927,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
return project.getEstimatedDuration();
}
@RequirePOST
public HttpResponse doConfigSubmit( StaplerRequest req ) throws IOException, ServletException, FormException {
checkPermission(UPDATE);
requirePOST();
BulkChange bc = new BulkChange(this);
try {
JSONObject json = req.getSubmittedForm();
......
......@@ -82,6 +82,7 @@ import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.acegisecurity.context.SecurityContextHolder;
import org.kohsuke.stapler.interceptor.RequirePOST;
/**
......@@ -281,8 +282,8 @@ public class UpdateCenter extends AbstractModelObject implements Saveable {
/**
* Schedules a Jenkins upgrade.
*/
@RequirePOST
public void doUpgrade(StaplerResponse rsp) throws IOException, ServletException {
requirePOST();
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
HudsonUpgradeJob job = new HudsonUpgradeJob(getCoreSource(), Jenkins.getAuthentication());
if(!Lifecycle.get().canRewriteHudsonWar()) {
......@@ -367,8 +368,8 @@ public class UpdateCenter extends AbstractModelObject implements Saveable {
/**
* Performs hudson downgrade.
*/
@RequirePOST
public void doDowngrade(StaplerResponse rsp) throws IOException, ServletException {
requirePOST();
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
if(!isDowngradable()) {
sendError("Jenkins downgrade is not possible, probably backup does not exist");
......
......@@ -35,6 +35,7 @@ import hudson.util.FormValidation.Kind;
import hudson.util.HttpResponses;
import hudson.util.IOUtils;
import hudson.util.TextFile;
import hudson.util.TimeUnit2;
import hudson.util.VersionNumber;
import jenkins.model.Jenkins;
import net.sf.json.JSONException;
......@@ -107,6 +108,12 @@ public class UpdateSite {
*/
private transient volatile long lastAttempt = -1;
/**
* If the attempt to fetch data fails, we progressively use longer time out before retrying,
* to avoid overloading the server.
*/
private transient volatile long retryWindow;
/**
* ID string for this update source.
*/
......@@ -162,6 +169,7 @@ public class UpdateSite {
}
LOGGER.info("Obtained the latest update center data file for UpdateSource " + id);
retryWindow = 0;
getDataFile().write(json);
return FormValidation.ok();
}
......@@ -278,8 +286,14 @@ public class UpdateSite {
if(dataTimestamp==-1)
dataTimestamp = getDataFile().file.lastModified();
long now = System.currentTimeMillis();
boolean due = now - dataTimestamp > DAY && now - lastAttempt > 15000;
if(due) lastAttempt = now;
retryWindow = Math.max(retryWindow,SECONDS.toMillis(15));
boolean due = now - dataTimestamp > DAY && now - lastAttempt > retryWindow;
if(due) {
lastAttempt = now;
retryWindow = Math.min(retryWindow*2, HOURS.toMillis(1)); // exponential back off but at most 1 hour
}
return due;
}
......
......@@ -53,6 +53,7 @@ import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
......@@ -454,9 +455,9 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Accepts submission from the configuration page.
*/
@RequirePOST
public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
checkPermission(Jenkins.ADMINISTER);
requirePOST();
fullName = req.getParameter("fullName");
description = req.getParameter("description");
......@@ -491,8 +492,8 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Deletes this user from Hudson.
*/
@RequirePOST
public void doDoDelete(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
requirePOST();
checkPermission(Jenkins.ADMINISTER);
if (id.equals(Jenkins.getAuthentication().getName())) {
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Cannot delete self");
......
......@@ -52,6 +52,7 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.IOException;
......@@ -721,9 +722,9 @@ public abstract class View extends AbstractModelObject implements AccessControll
*
* Subtypes should override the {@link #submit(StaplerRequest)} method.
*/
@RequirePOST
public final synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
checkPermission(CONFIGURE);
requirePOST();
submit(req);
......@@ -750,8 +751,8 @@ public abstract class View extends AbstractModelObject implements AccessControll
/**
* Deletes this view.
*/
@RequirePOST
public synchronized void doDoDelete(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
requirePOST();
checkPermission(DELETE);
owner.deleteView(this);
......
......@@ -45,6 +45,7 @@ import hudson.util.XStream2;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
......@@ -199,11 +200,11 @@ public class LabelAtom extends Label implements Saveable {
/**
* Accepts the update to the node configuration.
*/
@RequirePOST
public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
final Jenkins app = Jenkins.getInstance();
app.checkPermission(Jenkins.ADMINISTER);
requirePOST();
properties.rebuild(req, req.getSubmittedForm(), getApplicablePropertyDescriptors());
updateTransientActions();
......
......@@ -52,6 +52,7 @@ import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
......@@ -127,8 +128,8 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
/**
* Called from the management screen.
*/
@RequirePOST
public HttpResponse doAct(StaplerRequest req) throws ServletException, IOException {
requirePOST();
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
if(req.hasParameter("n")) {
......@@ -215,8 +216,8 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
/**
* Called from the confirmation screen to actually initiate the migration.
*/
@RequirePOST
public void doStart(StaplerRequest req, StaplerResponse rsp, @QueryParameter String username, @QueryParameter String password) throws ServletException, IOException {
requirePOST();
Jenkins hudson = Jenkins.getInstance();
hudson.checkPermission(Jenkins.ADMINISTER);
......
/*
* The MIT License
*
* Copyright (c) 2012, Dominik Bartholdi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.Extension;
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
/**
* Configures the project naming strategy.
*
* @author Dominik Bartholdi (imod)
*/
@Extension(ordinal = 250)
public class GlobalProjectNamingStrategyConfiguration extends GlobalConfiguration {
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException {
// for compatibility reasons, the actual value is stored in Jenkins
Jenkins j = Jenkins.getInstance();
final JSONObject optJSONObject = json.optJSONObject("useProjectNamingStrategy");
if (optJSONObject != null) {
final JSONObject strategyObject = optJSONObject.getJSONObject("namingStrategy");
final String className = strategyObject.getString("stapler-class");
try {
Class clazz = Class.forName(className);
final ProjectNamingStrategy strategy = (ProjectNamingStrategy) req.bindJSON(clazz, strategyObject);
j.setProjectNamingStrategy(strategy);
} catch (ClassNotFoundException e) {
throw new FormException(e, "namingStrategy");
}
}
if (j.getProjectNamingStrategy() == null) {
j.setProjectNamingStrategy(DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY);
}
return true;
}
}
......@@ -192,6 +192,7 @@ import hudson.widgets.Widget;
import jenkins.ExtensionComponentSet;
import jenkins.ExtensionRefreshException;
import jenkins.InitReactorRunner;
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import net.sf.json.JSONObject;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AcegiSecurityException;
......@@ -211,6 +212,9 @@ import org.jvnet.hudson.reactor.TaskBuilder;
import org.jvnet.hudson.reactor.TaskGraphBuilder;
import org.jvnet.hudson.reactor.Reactor;
import org.jvnet.hudson.reactor.TaskGraphBuilder.Handle;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.kohsuke.stapler.Ancestor;
......@@ -228,6 +232,7 @@ import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.framework.adjunct.AdjunctManager;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.jelly.JellyClassLoaderTearOff;
import org.kohsuke.stapler.jelly.JellyRequestDispatcher;
import org.xml.sax.InputSource;
......@@ -293,7 +298,7 @@ import java.util.regex.Pattern;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLevelItem>, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner {
public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLevelItem>, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner, ModelObjectWithContextMenu {
private transient final Queue queue;
/**
......@@ -361,6 +366,11 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
* @see #setSecurityRealm(SecurityRealm)
*/
private volatile SecurityRealm securityRealm = SecurityRealm.NO_AUTHENTICATION;
/**
* The project naming strategy defines/restricts the names which can be given to a project/job. e.g. does the name have to follow a naming convention?
*/
private ProjectNamingStrategy projectNamingStrategy = DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
/**
* Root directory for the workspaces. This value will be variable-expanded against
......@@ -636,9 +646,25 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
}
};
/**
* Hook for a test harness to intercept Jenkins.getInstance()
*
* Do not use in the production code as the signature may change.
*/
public interface JenkinsHolder {
Jenkins getInstance();
}
static JenkinsHolder HOLDER = new JenkinsHolder() {
public Jenkins getInstance() {
return theInstance;
}
};
@CLIResolver
public static Jenkins getInstance() {
return theInstance;
return HOLDER.getInstance();
}
/**
......@@ -1767,6 +1793,12 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
* correctly, especially when a migration is involved), but the downside
* is that unless you are processing a request, this method doesn't work.
*
* Please note that this will not work in all cases if Jenkins is running behind a
* reverse proxy (e.g. when user has switched off ProxyPreserveHost, which is
* default setup or the actual url uses https) and you should use getRootUrl if
* you want to be sure you reflect user setup.
* See https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache
*
* @since 1.263
*/
public String getRootUrlFromRequest() {
......@@ -1837,6 +1869,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
public boolean isUseSecurity() {
return securityRealm!=SecurityRealm.NO_AUTHENTICATION || authorizationStrategy!=AuthorizationStrategy.UNSECURED;
}
public boolean isUseProjectNamingStrategy(){
return projectNamingStrategy != DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
}
/**
* If true, all the POST requests to Hudson would have to have crumb in it to protect
......@@ -1907,6 +1943,13 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
markupFormatter = null;
}
public void setProjectNamingStrategy(ProjectNamingStrategy ns) {
if(ns == null){
ns = DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
}
projectNamingStrategy = ns;
}
public Lifecycle getLifecycle() {
return Lifecycle.get();
}
......@@ -2015,6 +2058,14 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
public AuthorizationStrategy getAuthorizationStrategy() {
return authorizationStrategy;
}
/**
* The strategy used to check the project names.
* @return never <code>null</code>
*/
public ProjectNamingStrategy getProjectNamingStrategy() {
return projectNamingStrategy == null ? ProjectNamingStrategy.DEFAULT_NAMING_STRATEGY : projectNamingStrategy;
}
/**
* Returns true if Hudson is quieting down.
......@@ -2747,6 +2798,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
private String checkJobName(String name) throws Failure {
checkGoodName(name);
name = name.trim();
projectNamingStrategy.checkName(name);
if(getItem(name)!=null)
throw new Failure(Messages.Hudson_JobAlreadyExists(name));
// looks good
......@@ -2894,6 +2946,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
rsp.getWriter().println("GCed");
}
public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws IOException, JellyException {
return new ContextMenu().from(this,request,response);
}
/**
* Obtains the heap dump.
*/
......@@ -3177,9 +3233,9 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
*
* This is useful for system administration as well as unit testing.
*/
@RequirePOST
public void doEval(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
checkPermission(ADMINISTER);
requirePOST();
try {
MetaClass mc = WebApp.getCurrent().getMetaClass(getClass());
......@@ -3757,4 +3813,5 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
assert PERMISSIONS!=null;
assert ADMINISTER!=null;
}
}
package jenkins.model;
import hudson.Functions;
import hudson.Util;
import hudson.model.Action;
import hudson.model.Actionable;
import hudson.model.ModelObject;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.export.Flavor;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
import org.kohsuke.stapler.jelly.JellyFacet;
import org.xml.sax.helpers.DefaultHandler;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* {@link ModelObject} that has context menu in the breadcrumb.
*
* <p>
* When the user is visiting a particular page, all the ancestor objects that has {@link ModelObject}
* appears in the breadcrumbs. Among those which that also implements {@link ModelObjectWithContextMenu}
* shows the drop-down menu for providing quicker access to the actions to those objects.
*
* @author Kohsuke Kawaguchi
*/
public interface ModelObjectWithContextMenu extends ModelObject {
/**
* Generates the context menu.
*
* The typical implementation is {@code return new ContextMenu().from(this,request,response);},
* which implements the default behaviour. See {@link ContextMenu#from(ModelObjectWithContextMenu, StaplerRequest, StaplerResponse)}
* for more details of what it does. This should suit most implementations.
*/
public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws Exception;
/**
* Data object that represents the context menu.
*
* Via {@link HttpResponse}, this class is capable of converting itself to JSON that &lt;l:breadcrumb/> understands.
*/
@ExportedBean
public class ContextMenu implements HttpResponse {
/**
* The actual contents of the menu.
*/
@Exported(inline=true)
public final List<MenuItem> items = new ArrayList<MenuItem>();
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object o) throws IOException, ServletException {
rsp.serveExposedBean(req,this,Flavor.JSON);
}
public ContextMenu add(String url, String text) {
items.add(new MenuItem(url,null,text));
return this;
}
public ContextMenu addAll(Collection<? extends Action> actions) {
for (Action a : actions)
add(a);
return this;
}
public ContextMenu add(Action a) {
StaplerRequest req = Stapler.getCurrentRequest();
String text = a.getDisplayName();
String base = Functions.getIconFilePath(a);
if (base==null) return this;
String icon = Stapler.getCurrentRequest().getContextPath()+(base.startsWith("images/")?Functions.getResourcePath():"")+'/'+base;
String url = Functions.getActionUrl(req.findAncestor(ModelObject.class).getUrl(),a);
return add(url,icon,text);
}
public ContextMenu add(String url, String icon, String text) {
if (text != null && icon != null && url != null)
items.add(new MenuItem(url,icon,text));
return this;
}
/**
* Default implementation of the context menu generation.
*
* <p>
* This method uses {@code sidepanel.groovy} to run the side panel generation, captures
* the use of &lt;l:task> tags, and then converts those into {@link MenuItem}s. This is
* supposed to make this work with most existing {@link ModelObject}s that follow the standard
* convention.
*
* <p>
* Unconventional {@link ModelObject} implementations that do not use {@code sidepanel.groovy}
* can override {@link ModelObjectWithContextMenu#doContextMenu(StaplerRequest, StaplerResponse)}
* directly to provide alternative semantics.
*/
public ContextMenu from(ModelObjectWithContextMenu self, StaplerRequest request, StaplerResponse response) throws JellyException, IOException {
WebApp webApp = WebApp.getCurrent();
final Script s = webApp.getMetaClass(self).getTearOff(JellyClassTearOff.class).findScript("sidepanel");
if (s!=null) {
JellyFacet facet = webApp.getFacet(JellyFacet.class);
request.setAttribute("taskTags",this); // <l:task> will look for this variable and populate us
request.setAttribute("mode","side-panel");
// run sidepanel but ignore generated HTML
facet.scriptInvoker.invokeScript(request,response,new Script() {
public Script compile() throws JellyException {
return this;
}
public void run(JellyContext context, XMLOutput output) throws JellyTagException {
Functions.initPageVariables(context);
s.run(context,output);
}
},self,new XMLOutput(new DefaultHandler()));
} else
if (self instanceof Actionable) {
// fallback
this.addAll(((Actionable)self).getActions());
}
return this;
}
}
/**
* Menu item in {@link ContextMenu}
*/
@ExportedBean
public class MenuItem {
/**
* Target of the link.
*
* This can start with '/', but it must not be a relative URL, since
* you cannot really tell which page this context menu is used.
*/
@Exported
public String url;
/**
* Human readable caption of the menu item. Do not use HTML.
*/
@Exported
public String displayName;
/**
* Optional URL to the icon image. Rendered as 24x24.
*/
@Exported
public String icon;
public MenuItem(String url, String icon, String displayName) {
this.url = url;
this.icon = icon;
this.displayName = Util.escape(displayName);
}
}
}
/*
* The MIT License
*
* Copyright (c) 2012, Dominik Bartholdi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.DescriptorExtensionList;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Failure;
import java.io.Serializable;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* This ExtensionPoint allows to enforce the name of projects/jobs.
*
* @author Dominik Bartholdi (imod)
*/
public abstract class ProjectNamingStrategy implements Describable<ProjectNamingStrategy>, ExtensionPoint {
public ProjectNamingStrategyDescriptor getDescriptor() {
return (ProjectNamingStrategyDescriptor) Jenkins.getInstance().getDescriptor(getClass());
}
public static DescriptorExtensionList<ProjectNamingStrategy, ProjectNamingStrategyDescriptor> all() {
return Jenkins.getInstance().<ProjectNamingStrategy, ProjectNamingStrategyDescriptor> getDescriptorList(ProjectNamingStrategy.class);
}
/**
* Called when creating a new job.
*
* @param name
* the name given from the UI
* @throws Failure
* if the user has to be informed about an illegal name, forces the user to change the name before submitting. The message of the failure will be presented to the user.
*/
public void checkName(String name) throws Failure {
// no op
}
/**
* This flag can be used to force existing jobs to be migrated to a new naming strategy - if this method returns true, the naming will be enforced at every config change. If <code>false</code> is
* returned, only new jobs have to follow the strategy.
*
* @return <code>true</code> if existing jobs should be enforced to confirm to the naming standard.
*/
public boolean isForceExistingJobs() {
return false;
}
/**
* The default naming strategy which does not restrict the name of a job.
*/
public static final ProjectNamingStrategy DEFAULT_NAMING_STRATEGY = new DefaultProjectNamingStrategy();
/**
* Default implementation which does not restrict the name to any form.
*/
public static final class DefaultProjectNamingStrategy extends ProjectNamingStrategy implements Serializable {
private static final long serialVersionUID = 1L;
@DataBoundConstructor
public DefaultProjectNamingStrategy() {
}
@Override
public void checkName(String origName) throws Failure {
// default - should just do nothing (this is how Jenkins worked before introducing this ExtensionPoint)
}
/**
* DefaultProjectNamingStrategy is stateless, therefore save to keep the same instance
*/
private Object readResolve() {
return DEFAULT_NAMING_STRATEGY;
}
@Extension
public static final class DescriptorImpl extends ProjectNamingStrategyDescriptor {
@Override
public String getDisplayName() {
return "Default";
}
@Override
public String getHelpFile() {
return "/help/system-config/defaultJobNamingStrategy.html";
}
}
}
/**
* Naming strategy which allows the admin to define a pattern a job's name has to follow.
*/
public static final class PatternProjectNamingStrategy extends ProjectNamingStrategy implements Serializable {
private static final long serialVersionUID = 1L;
/**
* regex pattern a job's name has to follow
*/
private final String namePattern;
private boolean forceExistingJobs;
@DataBoundConstructor
public PatternProjectNamingStrategy(String namePattern, boolean forceExistingJobs) {
this.namePattern = namePattern;
this.forceExistingJobs = forceExistingJobs;
}
@Override
public void checkName(String name) throws Failure {
if (StringUtils.isNotBlank(namePattern) && StringUtils.isNotBlank(name)) {
if (!Pattern.matches(namePattern, name)) {
throw new Failure(jenkins.model.Messages._Hudson_JobNameConventionNotApplyed(name, namePattern).toString());
}
}
}
public String getNamePattern() {
return namePattern;
}
public boolean isForceExistingJobs() {
return forceExistingJobs;
}
@Extension
public static final class DescriptorImpl extends ProjectNamingStrategyDescriptor {
public static final String DEFAULT_PATTERN = ".*";
@Override
public String getDisplayName() {
return "Pattern";
}
@Override
public String getHelpFile() {
return "/help/system-config/patternJobNamingStrategy.html";
}
}
}
public static abstract class ProjectNamingStrategyDescriptor extends Descriptor<ProjectNamingStrategy> {
}
}
# The MIT License
#
# Copyright (c) 2004-2012, Dominik Bartholdi, 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.
PluginCycles=\
\u6b21\u306e\u30d7\u30e9\u30b0\u30a4\u30f3\u306f\u4f9d\u5b58\u95a2\u4fc2\u304c\u5faa\u74b0\u3057\u3066\u3044\u308b\u305f\u3081\u3001\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059\u3002 \u305f\u3076\u3093\u3001\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u306b\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3059\u308b\u3053\u3068\u3067\u89e3\u6c7a\u3067\u304d\u307e\u3059\u3002
......@@ -35,7 +35,7 @@ THE SOFTWARE.
<img src="${imagesURL}/24x24/grey.png" tooltip="${%Not run}" alt="${%Not run}" height="24" width="24"/>
</j:when>
<j:otherwise>
<a href="${p.shortUrl}/">
<a href="${p.shortUrl}/" class="model-link">
<img src="${imagesURL}/24x24/${b.buildStatusUrl}" tooltip="${p.tooltip} ${it.number!=b.number?(it.isBuilding()?'- pending' : '- skipped'):''}" alt="${p.tooltip}" style="${it.number!=b.number?'opacity:0.5':''}" height="24" width="24"/>
<j:if test="${empty(o.x) and empty(o.y)}">
${p.combination.toString(o.z)}
......
......@@ -34,7 +34,7 @@ THE SOFTWARE.
<img src="${imagesURL}/24x24/grey.png" tooltip="${%Not configured}" alt="${%Not configured}" height="24" width="24"/>
</j:when>
<j:otherwise>
<a href="${p.shortUrl}">
<a href="${p.shortUrl}" class="model-link">
<img src="${imagesURL}/24x24/${p.buildStatusUrl}" tooltip="${p.iconColor.description}" alt="${p.iconColor.description}" height="24" width="24"/>
<j:if test="${empty(o.x) and empty(o.y)}">
${p.combination.toString(o.z)}
......
......@@ -63,7 +63,7 @@ THE SOFTWARE.
<j:set var="atr" value="${it.lastCompletedBuild.aggregatedTestResultAction}"/>
<j:if test="${atr!=null}">
<t:summary icon="clipboard.png">
<a href="lastCompletedBuild/testReport/">${%Latest Test Result}</a>
<a href="lastCompletedBuild/testReport/" class="model-link">${%Latest Test Result}</a>
<st:nbsp/>
<t:test-result it="${atr}" />
</t:summary>
......
......@@ -76,11 +76,11 @@ THE SOFTWARE.
<ol>
<j:forEach var="dep" items="${depChanges.values()}">
<li>
<a href="${rootURL}/${dep.project.url}">${dep.project.displayName}</a>
<a href="${rootURL}/${dep.project.url}" class="model-link">${dep.project.displayName}</a>
<st:nbsp/>
<j:choose>
<j:when test="${dep.from!=null}">
<a href="${rootURL}/${dep.from.url}">
<a href="${rootURL}/${dep.from.url}" class="model-link">
<img src="${imagesURL}/16x16/${dep.from.buildStatusUrl}"
alt="${dep.from.iconColor.description}" height="16" width="16" />${dep.from.displayName}</a>
</j:when>
......@@ -91,7 +91,7 @@ THE SOFTWARE.
&#x2192; <!-- right arrow -->
<a href="${rootURL}/${dep.to.url}">
<a href="${rootURL}/${dep.to.url}" class="model-link">
<img src="${imagesURL}/16x16/${dep.to.buildStatusUrl}"
alt="${dep.to.iconColor.description}" height="16" width="16" />${dep.to.displayName}</a>
......@@ -125,7 +125,7 @@ THE SOFTWARE.
<ul style="list-style-type: none;">
<j:forEach var="item" items="${upstream}">
<li>
<a href="${rootURL}/${item.key.url}">${item.key.displayName}</a>
<a href="${rootURL}/${item.key.url}" class="model-link">${item.key.displayName}</a>
<t:buildLink job="${item.key}" number="${item.value}" />
</li>
</j:forEach>
......@@ -137,7 +137,7 @@ THE SOFTWARE.
<ul style="list-style-type: none;">
<j:forEach var="item" items="${downstream}">
<li>
<a href="${rootURL}/${item.key.url}">${item.key.displayName}</a>
<a href="${rootURL}/${item.key.url}" class="model-link">${item.key.displayName}</a>
<j:choose>
<j:when test="${item.value.isEmpty()}">
(${%none})
......
......@@ -23,7 +23,7 @@
Error\:\ no\ workspace=\u30a8\u30e9\u30fc\uff1a\u3000\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
A\ project\ won''t\ have\ any\ workspace\ until\ at\ least\ one\ build\ is\ performed.=\
\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30d3\u30eb\u30c9\u304c\u5b9f\u884c\u3055\u308c\u306a\u3044\u9650\u308a\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u306f\u4f5c\u6210\u3055\u308c\u307e\u305b\u3093\u3002
There's\ no\ workspace\ for\ this\ project.\ Possible\ reasons\ are\:=\
There''s\ no\ workspace\ for\ this\ project.\ Possible\ reasons\ are\:=\
\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\u4ee5\u4e0b\u306e\u7406\u7531\u304c\u8003\u3048\u3089\u308c\u307e\u3059\u3002
The\ project\ was\ renamed\ recently\ and\ no\ build\ was\ done\ under\ the\ new\ name.=\
\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u65b0\u3057\u3044\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u540d\u306b\u5909\u66f4\u3055\u308c\u305f\u5f8c\u306b\u3001\u305d\u306e\u540d\u524d\u3067\u30d3\u30eb\u30c9\u304c\u5b9f\u884c\u3055\u308c\u3066\u3044\u306a\u3044\u3002
......
......@@ -50,7 +50,7 @@ THE SOFTWARE.
<j:if test="${tr!=null}">
<j:if test="${tr.class.name != 'hudson.tasks.test.AggregatedTestResultAction'}">
<t:summary icon="clipboard.png">
<a href="lastCompletedBuild/testReport/">${%Latest Test Result}</a>
<a href="lastCompletedBuild/testReport/" class="model-link">${%Latest Test Result}</a>
<st:nbsp/>
<t:test-result it="${tr}" />
</t:summary>
......@@ -60,7 +60,7 @@ THE SOFTWARE.
<j:set var="atr" value="${it.lastCompletedBuild.aggregatedTestResultAction}"/>
<j:if test="${atr!=null}">
<t:summary icon="clipboard.png">
<a href="lastCompletedBuild/aggregatedTestReport/">${%Latest Aggregated Test Result}</a>
<a href="lastCompletedBuild/aggregatedTestReport/" class="model-link">${%Latest Aggregated Test Result}</a>
<st:nbsp/>
<t:test-result it="${atr}" />
</t:summary>
......
......@@ -28,4 +28,12 @@ THE SOFTWARE.
<p>
Load statistics of this computer has <a href="../loadStatistics/api/">its own separate API</a>.
</p>
<h2>Fetch/Update config.xml</h2>
<p>
To programmatically obtain <tt>config.xml</tt>, hit <a href="../config.xml">this URL</a>.
You can also POST an updated <tt>config.xml</tt> to the same URL to programmatically
update the configuration of a node.
</p>
</j:jelly>
......@@ -75,7 +75,7 @@ THE SOFTWARE.
<j:forEach var="entry" items="${it.node.labelCloud}">
<!-- Skip the label for this node -->
<j:if test="${entry.item!=it.node.selfLabel}">
<a class="${entry.className}" href="${rootURL}/label/${entry.item.name}">${entry.item.name}</a>
<a class="${entry.className} model-link" href="${rootURL}/label/${entry.item.name}">${entry.item.name}</a>
<st:nbsp/>
</j:if>
</j:forEach>
......
......@@ -56,7 +56,7 @@ THE SOFTWARE.
<td width="32" data="${c.icon}">
<img src="${imagesURL}/32x32/${c.icon}" width="32" height="32" alt="${c.iconAltText}"/>
</td>
<td><a href="${rootURL}/${c.url}">${c.displayName}</a></td>
<td><a href="${rootURL}/${c.url}" class="model-link">${c.displayName}</a></td>
<j:forEach var="m" items="${monitors}">
<j:if test="${m.columnCaption!=null}">
<j:set var="data" value="${m.data(c)}"/>
......
......@@ -73,7 +73,7 @@ THE SOFTWARE.
<td class="fingerprint-summary-header">
<j:choose>
<j:when test="${job!=null}">
<a href="${rootURL}/${job.url}">${j}</a>
<a href="${rootURL}/${job.url}" class="model-link">${j}</a>
</j:when>
<j:otherwise>
${j}
......
......@@ -57,7 +57,7 @@ THE SOFTWARE.
<img width="16" height="16" src="${imagesURL}/16x16/${r.buildStatusUrl}" alt="${r.iconColor.description}" />
</td>
<td data="${r.number}">
<a href="${r.number}">
<a href="${r.number}/" class="model-link">
${r.displayName}
</a>
</td>
......
......@@ -29,6 +29,7 @@ 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" xmlns:i="jelly:fmt">
<l:layout title="${it.displayName} Config" norefresh="true" permission="${it.EXTENDED_READ}">
<st:include page="sidepanel.jelly" />
<f:breadcrumb-config-outline />
<l:main-panel>
<div class="behavior-loading">${%LOADING}</div>
<f:form method="post" action="configSubmit" name="config">
......
......@@ -36,7 +36,7 @@ THE SOFTWARE.
${%Nodes:}
<j:forEach var="n" items="${it.nodes}">
<j:set var="c" value="${app.getComputer(n.nodeName)}"/>
<a href="${rootURL}/computer/${n.nodeName}">
<a href="${rootURL}/computer/${n.nodeName}" class="model-link">
<img src="${imagesURL}/16x16/${c.icon}" width="16" height="16" alt=""/>
${c.displayName}
</a>
......
......@@ -87,6 +87,9 @@ ComputerSet.NoSuchSlave=\u30b9\u30ec\u30fc\u30d6\u304c\u5b58\u5728\u3057\u307e\u
ComputerSet.SlaveAlreadyExists=''{0}''\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059
ComputerSet.SpecifySlaveToCopy=\u30b3\u30d4\u30fc\u3059\u308b\u30b9\u30ec\u30fc\u30d6\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002
ComputerSet.DisplayName=\u30ce\u30fc\u30c9
Descriptor.From=(from <a href="{1}">{0}</a>)
Executor.NotAvailable=N/A
ExternalJob.DisplayName=\u5916\u90e8\u30b8\u30e7\u30d6\u306e\u76e3\u8996
......
......@@ -32,7 +32,7 @@ THE SOFTWARE.
<j:set var="b" value="${it.resolve(job)}"/>
<j:if test="${b!=null}">
<li>
<a href="${it.id}/">${%format(it.displayName,b.displayName,b.timestampString)}</a>
<a class="model-link tl-tr" href="${it.id}/">${%format(it.displayName,b.displayName,b.timestampString)}</a>
</li>
</j:if>
</j:jelly>
......@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Don't\ keep\ this\ build\ forever=\u30D3\u30EB\u30C9\u3092\u4FDD\u5B58\u3057\u306A\u3044
Don''t\ keep\ this\ build\ forever=\u30D3\u30EB\u30C9\u3092\u4FDD\u5B58\u3057\u306A\u3044
Keep\ this\ build\ forever=\u30D3\u30EB\u30C9\u3092\u4FDD\u5B58
......@@ -27,6 +27,6 @@ THE SOFTWARE.
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry title="${it.name}" description="${it.description}">
<a href="${rootURL}/${it.run.url}">${it.run}</a>
<a href="${rootURL}/${it.run.url}" class="model-link">${it.run}</a>
</f:entry>
</j:jelly>
......@@ -45,12 +45,12 @@ THE SOFTWARE.
</tr>
<j:forEach var="p" items="${it.users}">
<tr>
<td><a href="${rootURL}/${p.user.url}/"><img src="${h.getUserAvatar(p.user,iconSize)}"
<td><a href="${rootURL}/${p.user.url}/" class="model-link"><img src="${h.getUserAvatar(p.user,iconSize)}"
alt="" class="icon${iconSize}"/></a></td>
<td><a href="${rootURL}/${p.user.url}/">${p.user.id}</a></td>
<td><a href="${rootURL}/${p.user.url}/">${p.user}</a></td>
<td data="${p.timeSortKey}">${p.lastChangeTimeString}</td>
<td><a href="${rootURL}/${p.project.url}">${p.project.fullDisplayName}</a></td>
<td><a href="${rootURL}/${p.project.url}" class="model-link">${p.project.fullDisplayName}</a></td>
</tr>
</j:forEach>
</table>
......
......@@ -26,7 +26,7 @@ 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" xmlns:i="jelly:fmt">
<st:structuredMessageFormat key="description">
<st:structuredMessageArgument>
<a href="${rootURL}/label/${it.label.name}">${it.label.name}</a>
<a href="${rootURL}/label/${it.label.name}" class="model-link">${it.label.name}</a>
</st:structuredMessageArgument>
</st:structuredMessageFormat>
</j:jelly>
\ No newline at end of file
......@@ -53,7 +53,7 @@ THE SOFTWARE.
&#8212;
<a href="${rootURL}/${c.author.url}/">${c.author}</a> /
<a href="${rootURL}/${c.author.url}/" class="model-link">${c.author}</a> /
<j:set var="cslink" value="${browser.getChangeSetLink(c)}"/>
<j:choose>
......
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
# Copyright (c) 2004-2012, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
......@@ -26,4 +26,4 @@ User/group=\u30e6\u30fc\u30b6\u30fc/\u30b0\u30eb\u30fc\u30d7
User/group\ to\ add=\u8ffd\u52a0\u3059\u308b\u30e6\u30fc\u30b6\u30fc/\u30b0\u30eb\u30fc\u30d7
Toggle\ all=\u3059\u3079\u3066\u53cd\u8ee2
Remove\ user\/group=\u30e6\u30fc\u30b6/\u30b0\u30eb\u30fc\u30d7\u3092\u9664\u53bb
Remove\ user/group=\u30e6\u30fc\u30b6/\u30b0\u30eb\u30fc\u30d7\u3092\u524a\u9664
......@@ -39,7 +39,7 @@ THE SOFTWARE.
</tr>
<j:forEach var="user" items="${it.allUsers}">
<tr>
<td><a href="${user.url}/"><img src="${h.getUserAvatar(user,'32x32')}" alt="" height="32" width="32"/></a></td>
<td><a href="${user.url}/" class="model-link"><img src="${h.getUserAvatar(user,'32x32')}" alt="" height="32" width="32"/></a></td>
<td><a href="${user.url}/">${user.id}</a></td>
<td><a href="${user.url}/">${user}</a></td>
<td>
......
......@@ -41,7 +41,7 @@ THE SOFTWARE.
<j:if test="${test != null}">
<tr>
<td class="pane">
<a href="${app.rootUrl}${b.url}testReport${p.url}">${b.fullDisplayName}</a>
<a href="${app.rootUrl}${b.url}testReport${p.url}" class="model-link">${b.fullDisplayName}</a>
<st:nbsp/>
<j:forEach var="badge" items="${test.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......
......@@ -36,7 +36,7 @@ THE SOFTWARE.
<j:forEach var="p" items="${it.children}" varStatus="status">
<tr>
<td class="pane">
<a href="${p.safeName}"><span style="${p.previousResult==null?'font-weight:bold':''}"><st:out value="${p.name}" /></span></a>
<a href="${p.safeName}" class="model-link"><span style="${p.previousResult==null?'font-weight:bold':''}"><st:out value="${p.name}" /></span></a>
<st:nbsp/>
<j:forEach var="badge" items="${p.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......
......@@ -40,7 +40,7 @@ THE SOFTWARE.
<j:if test="${p != null}">
<tr>
<td class="pane">
<a href="${app.rootUrl}${b.url}testReport${p.url}">${b.fullDisplayName}</a>
<a href="${app.rootUrl}${b.url}testReport${p.url}" class="model-link">${b.fullDisplayName}</a>
<st:nbsp/>
<j:forEach var="badge" items="${p.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......
......@@ -23,4 +23,4 @@
Test\ report\ XMLs=\u30c6\u30b9\u30c8\u7d50\u679cXML
description=\
<a href="{0}">Ant\u306e\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8includes\u5c5e\u6027\u306e\u66f8\u5f0f</a>\u306b\u5f93\u3063\u3066\u30d3\u30eb\u30c9\u304b\u3089\u751f\u6210\u3055\u308c\u308bXML\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u7fa4\u3092\u6307\u5b9a\u3057\u307e\u3059\uff08\u4f8b\uff1amyproject/target/test-reports/*.xml\uff09\u3002<a href="ws/">\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u30eb\u30fc\u30c8</a>\u304b\u3089\u306e\u76f8\u5bfe\u30d1\u30b9\u3067\u3059\u3002
Retain\ long\ standard\ output\/error=\u9577\u3044\u6a19\u6e96/\u30a8\u30e9\u30fc\u51fa\u529b\u3092\u305d\u306e\u307e\u307e\u4fdd\u6301\u3059\u308b
\ No newline at end of file
Retain\ long\ standard\ output/error=\u9577\u3044\u6a19\u6e96/\u30a8\u30e9\u30fc\u51fa\u529b\u3092\u305d\u306e\u307e\u307e\u4fdd\u6301\u3059\u308b
\ No newline at end of file
......@@ -61,7 +61,7 @@ THE SOFTWARE.
<j:forEach var="i" items="${it.didntRun}">
<tr>
<td class="pane">
<a href="${rootURL}/${i.url}">${i.fullDisplayName}</a>
<a href="${rootURL}/${i.url}" class="model-link">${i.fullDisplayName}</a>
(${%test result not available})
</td>
<td class="pane">
......@@ -75,7 +75,7 @@ THE SOFTWARE.
<j:forEach var="i" items="${it.noFingerprints}">
<tr>
<td class="pane">
<a href="${rootURL}/${i.url}">${i.fullDisplayName}</a>
<a href="${rootURL}/${i.url}" class="model-link">${i.fullDisplayName}</a>
(${%last successful job is not fingerprinted})
</td>
<td class="pane">
......
......@@ -42,7 +42,7 @@ THE SOFTWARE.
<j:forEach var="report" items="${it.childReports}">
<tr>
<td class="pane">
<a href="../${report.child.project.shortUrl}testReport">${report.child.project.name}</a>
<a href="../${report.child.project.shortUrl}testReport" class="model-link">${report.child.project.name}</a>
</td>
<td data="${report.result.duration}" class="pane" style="text-align:right">
${report.result.durationString}
......@@ -74,7 +74,7 @@ THE SOFTWARE.
<h3>
<a name="${report.child.project.name}"/>
<a href="../${report.child.project.shortUrl}testReport">${report.child.project.name}</a>
<a href="../${report.child.project.shortUrl}testReport" class="model-link">${report.child.project.name}</a>
</h3>
<table class="pane sortable">
......@@ -86,7 +86,7 @@ THE SOFTWARE.
<j:forEach var="f" items="${report.result.failedTests}" varStatus="i">
<tr>
<td class="pane">
<a href="../${report.child.project.shortUrl}testReport/${f.getRelativePathFrom(report.result)}">
<a href="../${report.child.project.shortUrl}testReport/${f.getRelativePathFrom(report.result)}" class="model-link">
<st:out value="${f.fullName}"/>
<st:nbsp/>
<j:forEach var="badge" items="${f.testActions}">
......
......@@ -63,7 +63,7 @@ THE SOFTWARE.
<a id="test-${f.fullName}-hidelink" style="display:none"
href="javascript:hideStackTrace('test-${h.jsStringEscape(f.fullName)}')">&lt;&lt;&lt;</a>
<st:nbsp/>
<a href="${f.getRelativePathFrom(it)}"><st:out value="${f.fullName}"/></a>
<a href="${f.getRelativePathFrom(it)}" class="model-link"><st:out value="${f.fullName}"/></a>
<st:nbsp/>
<j:forEach var="badge" items="${f.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......@@ -77,7 +77,7 @@ THE SOFTWARE.
${f.durationString}
</td>
<td class="pane" style="text-align:right;">
<a href="${rootURL}/${f.failedSinceRun.url}">${f.age}</a>
<a href="${rootURL}/${f.failedSinceRun.url}" class="model-link">${f.age}</a>
</td>
</tr>
</j:forEach>
......@@ -102,7 +102,7 @@ THE SOFTWARE.
<j:set var="prev" value="${p.previousResult}" />
<tr>
<td class="pane">
<a href="${p.safeName}/"><span style="${prev==null?'font-weight:bold':''}"><st:out value="${p.name}" /></span></a>
<a href="${p.safeName}/" class="model-link"><span style="${prev==null?'font-weight:bold':''}"><st:out value="${p.name}" /></span></a>
<st:nbsp/>
<j:forEach var="badge" items="${p.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......
......@@ -40,7 +40,7 @@ THE SOFTWARE.
<j:if test="${p != null}">
<tr>
<td class="pane">
<a href="${app.rootUrl}${b.url}testReport${p.url}">${b.fullDisplayName}</a>
<a href="${app.rootUrl}${b.url}testReport${p.url}" class="model-link">${b.fullDisplayName}</a>
<st:nbsp/>
<j:forEach var="badge" items="${p.testActions}">
<st:include it="${badge}" page="badge.jelly" optional="true"/>
......
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe
# Copyright (c) 2004-2012, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
......@@ -25,4 +25,5 @@ ClockDifference.Ahead=\ \u9032\u3093\u3067\u3044\u307e\u3059
ClockDifference.Behind=\ \u9045\u308c\u3066\u3044\u307e\u3059
ClockDifference.Failed=\u30c1\u30a7\u30c3\u30af\u306b\u5931\u6557\u3057\u307e\u3057\u305f
FormValidation.ValidateRequired=\u5fc5\u9808\u9805\u76ee\u3067\u3059\u3002
FormValidation.Error.Details=(\u8a73\u7d30)
\ No newline at end of file
FormValidation.Error.Details=(\u8a73\u7d30)
HttpResponses.Saved=\u4fdd\u5b58
......@@ -25,6 +25,6 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<td style="${indenter.getCss(job)}">
<a href="${jobBaseUrl}${job.shortUrl}" tooltip="${job.name}"> ${job.displayName}</a>
<a href="${jobBaseUrl}${job.shortUrl}" class='model-link'> ${job.displayName}</a>
</td>
</j:jelly>
\ No newline at end of file
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<j:choose>
<j:when test="${lfBuild!=null}">
${lfBuild.timestampString}
(<a href="${jobBaseUrl}${job.shortUrl}lastFailedBuild/">${lfBuild.displayName}</a>)
(<a href="${jobBaseUrl}${job.shortUrl}lastFailedBuild/" class="model-link">${lfBuild.displayName}</a>)
</j:when>
<j:otherwise>
${%N/A}
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<j:choose>
<j:when test="${lstBuild!=null}">
${lstBuild.timestampString}
(<a href="${jobBaseUrl}${job.shortUrl}lastStableBuild/">${lstBuild.displayName}</a>)
(<a href="${jobBaseUrl}${job.shortUrl}lastStableBuild/" class="model-link">${lstBuild.displayName}</a>)
</j:when>
<j:otherwise>
${%N/A}
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<j:choose>
<j:when test="${lsBuild!=null}">
${lsBuild.timestampString}
(<a href="${jobBaseUrl}${job.shortUrl}lastSuccessfulBuild/">${lsBuild.displayName}</a>)
(<a href="${jobBaseUrl}${job.shortUrl}lastSuccessfulBuild/" class="model-link">${lsBuild.displayName}</a>)
</j:when>
<j:otherwise>
${%N/A}
......
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Seiji Sogabe
# Copyright (c) 2004-2012, Sun Microsystems, Inc., Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
......@@ -28,3 +28,5 @@ LastStableColumn.DisplayName=\u6700\u65b0\u306e\u5b89\u5b9a\u30d3\u30eb\u30c9
LastSuccessColumn.DisplayName=\u6700\u65b0\u306e\u6210\u529f\u30d3\u30eb\u30c9
StatusColumn.DisplayName=\u30b9\u30c6\u30fc\u30bf\u30b9
WeatherColumn.DisplayName=\u7a7a\u6a21\u69d8
DefaultViewsTabsBar.DisplayName=Default Views TabBar
DefaultMyViewsTabsBar.DisplayName=Default My Views TabBar
\ No newline at end of file
......@@ -35,7 +35,7 @@ THE SOFTWARE.
${build.displayName}
</td>
<td style="padding-right:0">
<a class="tip" href="${link}">
<a class="tip model-link tl-tr" href="${link}">
<i:formatDate value="${build.timestamp.time}" type="both" dateStyle="medium" timeStyle="medium"/>
</a>
</td>
......
package jenkins.model.GlobalProjectNamingStrategyConfiguration
import jenkins.model.ProjectNamingStrategy
def f=namespace(lib.FormTagLib)
f.optionalBlock( field:"useProjectNamingStrategy", title:_("useNamingStrategy"), checked:app.useProjectNamingStrategy) {
f.entry(title:_("namingStrategyTitel")) {
table(style:"width:100%") {
f.descriptorRadioList(title:_("strategy"), varName:"namingStrategy", instance:app.projectNamingStrategy, descriptors:ProjectNamingStrategy.all())
}
}
}
# The MIT License
#
# Copyright (c) 2012, Dominik Bartholdi
#
# 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.
useNamingStrategy = Restrict project naming
namingStrategyTitel = Naming Strategy
strategy = Strategy
......@@ -29,6 +29,7 @@ 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">
<l:layout norefresh="true" permission="${it.ADMINISTER}" title="${%Configure System}">
<st:include page="sidepanel.jelly" />
<f:breadcrumb-config-outline />
<l:main-panel>
<div class="behavior-loading">${%LOADING}</div>
<f:form method="post" name="config" action="configSubmit">
......
......@@ -25,8 +25,5 @@ Home\ directory=\u30db\u30fc\u30e0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
Workspace\ Root\ Directory=\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9\u30fb\u30eb\u30fc\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
Build\ Record\ Root\ Directory=\u30d3\u30eb\u30c9\u30fb\u30eb\u30fc\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
System\ Message=\u30b7\u30b9\u30c6\u30e0\u30e1\u30c3\u30bb\u30fc\u30b8
Disable=\u306a\u3057
Save=\u4fdd\u5b58
statsBlurb=\
\u5229\u7528\u72b6\u6cc1\u3068\u30af\u30e9\u30c3\u30b7\u30e5\u30ec\u30dd\u30fc\u30c8\u3092Jenkins\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u533f\u540d\u3067\u5831\u544a
LOADING=\u30ed\u30fc\u30c9\u4e2d...
......@@ -47,7 +47,7 @@ THE SOFTWARE.
<j:if test="${it.checkURIEncodingEnabled}">
<script>
var url='checkURIEncoding';
var params='value=\u57f7\u4e8b';
var params= {value : '\u57f7\u4e8b'};
var checkAjax=new Ajax.Updater(
'message', url,
{
......
......@@ -33,6 +33,7 @@ Hudson.NoJavaInPath=java is not in your PATH. Maybe you need to <a href=''{0}/co
Hudson.NoName=No name is specified
Hudson.NodeBeingRemoved=Node is being removed
Hudson.UnsafeChar=''{0}'' is an unsafe character
Hudson.JobNameConventionNotApplyed=''{0}'' does not match the job name convention pattern {1}
Hudson.ViewAlreadyExists=A view already exists with the name "{0}"
Hudson.ViewName=All
Hudson.NotUsesUTF8ToDecodeURL=\
......
package jenkins.model.GlobalProjectNamingStrategyConfiguration
def f=namespace(lib.FormTagLib)
f.entry(title:_("namePattern")) {
f.textbox(name:"namePattern",value:h.defaulted(instance?.namePattern, descriptor.DEFAULT_PATTERN),class:"fixed-width")
}
f.entry(title:_("forceExistingJobs"), field:"forceExistingJobs") {
f.checkbox(name:"forceExistingJobs")
}
# The MIT License
#
# Copyright (c) 2012, Dominik Bartholdi
#
# 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.
namePattern = Name Pattern
forceExistingJobs = force existing
# The MIT License
#
# Copyright (c) 2012, Kohsuke Kawaguchi,
#
# 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.
Apply=\u9069\u7528
Saved=\u4fdd\u5b58\u3057\u307e\u3057\u305f
<!--
The MIT License
Copyright (c) 2012- 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.
-->
<?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>
Adds one more in-page breadcrumb that jumps to sections in the page.
Put this tag right before &lt;l:main-panel>
</st:documentation>
<l:breadcrumb title="${%configuration}" id="inpage-nav" />
<style>
#breadcrumbs #inpage-nav A { background: none; }
</style>
<!--
TODO: use human-readable ID
-->
<script>
Event.observe(window,"load",function(){
/** @type section.SectionNode */
var outline = section.buildTree();
var menu = new breadcrumbs.ContextMenu();
$A(outline.children).each(function (e) {
if (!e.section.id)
e.section.setAttribute("id","section"+(iota++));
menu.add('#'+e.section.id,null,e.getHTML());
});
breadcrumbs.attachMenu('inpage-nav',menu);
});
</script>
</j:jelly>
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2012, Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
configuration=\u8a2d\u5b9a
......@@ -36,10 +36,11 @@ THE SOFTWARE.
</st:attribute>
</st:documentation>
<st:adjunct includes="lib.form.section_" />
<f:rowSet name="${attrs.name}">
<j:if test="${attrs.title!=null}">
<f:block>
<div style="font-weight:bold; border-bottom: 1px solid black; margin-bottom:0.2em; margin-top:0.4em">
<div class="section-header">
${title}
</div>
</f:block>
......
.section-header {
font-weight: bold;
border-bottom: 1px solid black;
margin-bottom: 0.2em;
margin-top: 0.4em;
}
var section = (function (){
var SectionNode = function(e) {
this.section = e;
this.children = [];
};
SectionNode.prototype = {
/**
* Points to the DIV node of the section header.
* @type {HTMLElement}
*/
section : null,
/**
* Child sections.
*
* @type {Array<SectionNode>}
*/
children : null,
getHTML : function() {
return this.section.innerHTML;
}
};
return {
"SectionNode" : SectionNode,
/**
* Builds the tree of SectionNode that represents the section hierarchy.
*
* @param {HTMLElement|string} root
* The root DOM node or its ID from which we build the tree model.
* @return {SectionNode}
* Tree structure that represents the nesting of sections.
* For root node, the 'section' property refers to null.
*/
"buildTree" : function(root) {
root = $(root||document.body);
/**
* Recursively visit elements and find all section headers.
*
* @param {HTMLElement} dom
* Parent element
* @param {SectionNode} parent
* Function that returns the array to which discovered section headers and child elements are added.
*/
function visitor(dom,parent) {
for (var e=dom.firstChild; e!=null; e=e.nextSibling) {
if (e.nodeType==1) {
if (e.className=="section-header") {
var child = new SectionNode(e);
parent.children.push(child);
visitor(e,child);
} else {
visitor(e,parent);
}
}
}
}
var top = new SectionNode(null);
visitor(root,top);
return top;
}
};
})();
\ No newline at end of file
......@@ -29,5 +29,5 @@ THE SOFTWARE.
Displays a link when given an AbstractItem. It is assumed that that Abstract
Item is passed in ${it}
</st:documentation>
<a href="${it.absoluteUrl}" tooltip="${it.name}">${it.displayName}</a>
<a href="${it.absoluteUrl}" class="model-link">${it.displayName}</a>
</j:jelly>
\ No newline at end of file
......@@ -42,11 +42,11 @@ THE SOFTWARE.
<j:set var="r" value="${job.getBuildByNumber(number)}" />
<j:choose>
<j:when test="${r==null}">
<a href="${rootURL}/${job.url}">${jobName}</a>
<a href="${rootURL}/${job.url}" class="model-link">${jobName}</a>
#<!-- -->${number}
</j:when>
<j:otherwise>
<a href="${attrs.href ?: rootURL+'/'+r.url}">
<a href="${attrs.href ?: rootURL+'/'+r.url}" class="model-link">
<img src="${imagesURL}/16x16/${r.buildStatusUrl}"
alt="${r.iconColor.description}" height="16" width="16"/>${jobName_}#<!-- -->${number}</a>
</j:otherwise>
......
......@@ -47,15 +47,15 @@ THE SOFTWARE.
<j:forEach var="b" items="${h.subList(attrs.builds,50)}">
<tr>
<td data="${b.iconColor.ordinal()}">
<a href="${jobBaseUrl}${b.url}">
<a href="${jobBaseUrl}${b.url}" class="model-link">
<img src="${imagesURL}/${iconSize}/${b.buildStatusUrl}"
alt="${b.iconColor.description}" class="icon${iconSize}"/>
</a>
</td>
<td>
<a href="${jobBaseUrl}${b.parent.url}">${b.parent.fullDisplayName}</a>
<a href="${jobBaseUrl}${b.parent.url}" class="model-link">${b.parent.fullDisplayName}</a>
<st:nbsp/>
<a href="${jobBaseUrl}${b.url}">${b.displayName}</a>
<a href="${jobBaseUrl}${b.url}" class="model-link">${b.displayName}</a>
</td>
<td data="${b.timestampString2}" tooltip="${%Click to center timeline on event}" onclick="javascript:tl.getBand(0).scrollToCenter(Timeline.DateTime.parseGregorianDateTime('${b.timestampString2}'))">
${b.timestampString}
......
......@@ -20,7 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Build=\u30D3\u30EB\u30C9
Time\ Since=\u6642\u671F
Status=\u72B6\u614B
Console\ output=\u30B3\u30F3\u30BD\u30FC\u30EB\u51FA\u529B
\ No newline at end of file
Build=\u30d3\u30eb\u30c9
Time\ Since=\u6642\u671f
Status=\u72b6\u614b
Console\ output=\u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b
Click\ to\ center\ timeline\ on\ event=\u30af\u30ea\u30c3\u30af\u3067\u3001\u3053\u306e\u30a4\u30d9\u30f3\u30c8\u3092\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u4e2d\u5fc3\u306b
\ No newline at end of file
......@@ -33,7 +33,7 @@ THE SOFTWARE.
</st:documentation>
<d:taglib uri="local">
<d:tag name="computerCaption">
<a href="${rootURL}/${c.url}">${title}</a>
<a href="${rootURL}/${c.url}" class="model-link">${title}</a>
<j:if test="${c.offline}"> <st:nbsp/> (${%offline})</j:if>
<j:if test="${!c.acceptingTasks}"> <st:nbsp/> (${%suspended})</j:if>
</d:tag>
......@@ -57,7 +57,7 @@ THE SOFTWARE.
<td class="pane">
<j:choose>
<j:when test="${c.offline}">
<a href="${rootURL}/${c.url}">${%Offline}</a>
<a href="${rootURL}/${c.url}" class="model-link">${%Offline}</a>
</j:when>
<j:otherwise>
${%Idle}
......@@ -79,7 +79,7 @@ THE SOFTWARE.
</j:invokeStatic>
<j:choose>
<j:when test="${h.hasPermission(exeparent,exeparent.READ)}">
<a href="${rootURL}/${exeparent.url}">${exeparent.fullDisplayName}</a>&#160;<a href="${rootURL}/${exe.url}">#${exe.number}</a>
<a href="${rootURL}/${exeparent.url}" class="model-link">${exeparent.fullDisplayName}</a>&#160;<a href="${rootURL}/${exe.url}" class="model-link">#${exe.number}</a>
<t:buildProgressBar build="${exe}" executor="${executor}"/>
</j:when>
<j:otherwise>
......
......@@ -32,5 +32,5 @@ THE SOFTWARE.
</st:documentation>
<img src="${imagesURL}/16x16/${job.buildStatusUrl}" alt="${job.iconColor.description}" height="16" width="16"/>
<a href="${h.getRelativeLinkTo(job)}" tooltip="${job.name}">${job.displayName}</a>
<a href="${h.getRelativeLinkTo(job)}" class="model-link">${job.displayName}</a>
</j:jelly>
......@@ -30,7 +30,7 @@ THE SOFTWARE.
</st:documentation>
<j:choose>
<j:when test="${value!=null and value!=app}">
<a href="${rootURL}/computer/${value.nodeName}">${value.nodeName}</a>
<a href="${rootURL}/computer/${value.nodeName}" class="model-link">${value.nodeName}</a>
</j:when>
<j:otherwise>
${%master}
......
......@@ -36,7 +36,7 @@ THE SOFTWARE.
<j:set var="b" value="${it[property]}"/>
<j:if test="${b!=null}">
<li>
<a href="${property}/">${%format(title,b.number,b.timestampString)}</a>
<a href="${property}/" class="model-link">${%format(title,b.number,b.timestampString)}</a>
</li>
</j:if>
</j:jelly>
\ No newline at end of file
......@@ -22,4 +22,4 @@
Build\ after\ other\ projects\ are\ built=\u4ed6\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u30d3\u30eb\u30c9\u5f8c\u306b\u30d3\u30eb\u30c9
Projects\ names=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u540d
Multiple\ projects\ can\ be\ specified\ like\ 'abc,\ def'=\u8907\u6570\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u6307\u5b9a\u3059\u308b\u306b\u306fabc, def\u306e\u3088\u3046\u306b\u30ab\u30f3\u30de\u3067\u533a\u5207\u308a\u307e\u3059
\ No newline at end of file
Multiple\ projects\ can\ be\ specified\ like\ ''abc,\ def''=\u8907\u6570\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u6307\u5b9a\u3059\u308b\u306b\u306fabc, def\u306e\u3088\u3046\u306b\u30ab\u30f3\u30de\u3067\u533a\u5207\u308a\u307e\u3059
\ No newline at end of file
......@@ -48,7 +48,7 @@ THE SOFTWARE.
</div>
</td>
<td colspan="5" style="${indenter.getCss(job)}">
<a href="${rootURL}/${v.url}">
<a href="${rootURL}/${v.url}" class="model-link">
${v.viewName}
</a>
</td>
......
......@@ -62,7 +62,7 @@ THE SOFTWARE.
<j:set var="stuck" value="${item.isStuck()}"/>
<j:choose>
<j:when test="${h.hasPermission(item.task,item.task.READ)}">
<a href="${rootURL}/${item.task.url}">
<a href="${rootURL}/${item.task.url}" class="model-link">
${item.task.fullDisplayName}
</a>
<j:if test="${stuck}">
......
......@@ -20,9 +20,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Build\ Queue=\u30D3\u30EB\u30C9\u30AD\u30E5\u30FC
No\ builds\ in\ the\ queue.=\u306A\u3057
Build\ Queue=\u30d3\u30eb\u30c9\u30ad\u30e5\u30fc
No\ builds\ in\ the\ queue.=\u306a\u3057
Jenkins\ is\ going\ to\ shut\ down.\ No\ further\ builds\ will\ be\ performed.=\
Jenkins\u306F\u7D42\u4E86\u6E96\u5099\u4E2D\u306E\u305F\u3081\u30D3\u30EB\u30C9\u306F\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\u3002
cancel=\u30AD\u30E3\u30F3\u30BB\u30EB
Unknown\ Task=\u672A\u77E5\u306E\u30BF\u30B9\u30AF
Jenkins\u306f\u7d42\u4e86\u6e96\u5099\u4e2d\u306e\u305f\u3081\u30d3\u30eb\u30c9\u306f\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002
cancel=\u30ad\u30e3\u30f3\u30bb\u30eb
Unknown\ Task=\u672a\u77e5\u306e\u30bf\u30b9\u30af
WaitingSince={0} \u304b\u3089\u5f85\u6a5f\u4e2d
......@@ -43,10 +43,8 @@ THE SOFTWARE.
<!-- called to generate partial HTML. set up HTML headers and etc -->
<!-- copied from layout.jelly -->
<st:contentType value="text/html;charset=UTF-8" />
<j:set var="rootURL" value="${request.contextPath}" />
<j:new var="h" className="hudson.Functions" /><!-- instead of JSP functions -->
<j:set var="resURL" value="${rootURL}${h.resourcePath}" />
<j:set var="imagesURL" value="${rootURL}${h.resourcePath}/images" />
${h.initPageVariables(context)}
<j:set var="ajax" value="true"/>
<d:invokeBody/>
......
此差异由.gitattributes 抑制。
<!--
The MIT License
Copyright (c) 2012- 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.
-->
<?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>
Used inside &lt;l:layout> to render additional breadcrumb items.
<st:attribute name="href">
URL that the breadcrumb item links to. Can be omitted.
</st:attribute>
<st:attribute name="id">
If specified, this ID will be assigned to the LI element.
This is useful for programmatically adding the context menu
</st:attribute>
<st:attribute name="title" use="required">
Display name of the breadcrumb.
</st:attribute>
</st:documentation>
<j:if test="${mode=='breadcrumbs'}">
<li id="${attrs.id}">
<a href="${attrs.href}">
${attrs.title}
</a>
</li>
</j:if>
</j:jelly>
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册