diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index b8e78113e6..948680a04e 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -1,28 +1,43 @@ package hudson; import hudson.maven.ExecutedMojo; -import hudson.model.*; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.Descriptor; +import hudson.model.Hudson; +import hudson.model.Item; +import hudson.model.ItemGroup; +import hudson.model.Items; +import hudson.model.Job; +import hudson.model.JobPropertyDescriptor; +import hudson.model.ModelObject; +import hudson.model.Node; +import hudson.model.Project; +import hudson.model.Run; +import hudson.model.TopLevelItem; +import hudson.model.View; import hudson.search.SearchableModelObject; +import hudson.security.AccessControlled; +import hudson.security.AuthorizationStrategy; +import hudson.security.Permission; +import hudson.security.SecurityRealm; +import hudson.slaves.ComputerLauncher; +import hudson.slaves.RetentionStrategy; import hudson.tasks.BuildStep; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildWrapper; import hudson.tasks.BuildWrappers; import hudson.tasks.Builder; import hudson.tasks.Publisher; -import hudson.security.SecurityRealm; -import hudson.security.AuthorizationStrategy; -import hudson.security.Permission; import hudson.util.Area; -import hudson.slaves.ComputerLauncher; -import hudson.slaves.RetentionStrategy; +import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; +import org.apache.commons.jelly.JellyContext; import org.apache.commons.jexl.parser.ASTSizeFunction; import org.apache.commons.jexl.util.Introspector; -import org.apache.commons.jelly.JellyContext; import org.kohsuke.stapler.Ancestor; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -425,7 +440,15 @@ public class Functions { } public static void checkPermission(Permission permission) throws IOException, ServletException { - Hudson.getInstance().getACL().checkPermission(permission); + if (permission != null) { + Hudson.getInstance().getACL().checkPermission(permission); + } + } + + public static void checkPermission(AccessControlled object, Permission permission) throws IOException, ServletException { + if (permission != null) { + object.checkPermission(permission); + } } /** @@ -438,6 +461,10 @@ public class Functions { return permission==null || Hudson.getInstance().getACL().hasPermission(permission); } + public static boolean hasPermission(AccessControlled object, Permission permission) throws IOException, ServletException { + return permission==null || object.hasPermission(permission); + } + public static void adminCheck(StaplerRequest req, StaplerResponse rsp, Object required, Permission permission) throws IOException, ServletException { // this is legacy --- all views should be eventually converted to // the permission based model. diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index 8a9af12144..930e9e7b15 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -3,6 +3,7 @@ package hudson.model; import hudson.XmlFile; import hudson.Util; import hudson.Functions; +import hudson.security.AccessControlled; import hudson.security.Permission; import hudson.security.ACL; import org.kohsuke.stapler.export.Exported; @@ -27,7 +28,7 @@ import javax.servlet.ServletException; // Item doesn't necessarily have to be Actionable, but // Java doesn't let multiple inheritance. @ExportedBean -public abstract class AbstractItem extends Actionable implements Item, HttpDeletable { +public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled { /** * Project name. */ @@ -164,8 +165,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet * Returns the {@link ACL} for this object. */ public ACL getACL() { - // TODO: this object should have its own ACL - return Hudson.getInstance().getACL(); + return Hudson.getInstance().getAuthorizationStrategy().getACL(this); } /** diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 1b2940fdaf..2c95f7b106 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -616,7 +616,7 @@ public abstract class AbstractProject

,R extends A } Launcher launcher = workspace != null ? workspace.createLauncher(listener) : null; - LOGGER.info("Polling SCM changes of "+ getName()); + LOGGER.fine("Polling SCM changes of "+ getName()); return scm.pollChanges(this, launcher, workspace, listener ); } catch (AbortException e) { listener.fatalError(Messages.AbstractProject_Aborted()); diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 7d0621bf9b..a187028f73 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -7,25 +7,32 @@ import hudson.slaves.RetentionStrategy; import hudson.node_monitors.NodeMonitor; import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; +import hudson.security.ACL; +import hudson.security.AccessControlled; +import hudson.security.Permission; +import hudson.security.PermissionGroup; import hudson.tasks.BuildWrapper; import hudson.tasks.Publisher; import hudson.util.DaemonThreadFactory; import hudson.util.RemotingDiagnostics; import hudson.util.RunList; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import javax.servlet.ServletException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.LogRecord; + +import javax.servlet.ServletException; + +import org.jvnet.localizer.Localizable; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; @@ -53,8 +60,9 @@ import org.kohsuke.stapler.export.ExportedBean; * @author Kohsuke Kawaguchi */ @ExportedBean -public abstract class Computer extends AbstractModelObject { - private final CopyOnWriteArrayList executors = new CopyOnWriteArrayList(); +public abstract class Computer extends AbstractModelObject implements AccessControlled { + + private final CopyOnWriteArrayList executors = new CopyOnWriteArrayList(); private int numExecutors; @@ -74,6 +82,18 @@ public abstract class Computer extends AbstractModelObject { setNode(node); } + public ACL getACL() { + return Hudson.getInstance().getAuthorizationStrategy().getACL(this); + } + + public void checkPermission(Permission permission) { + getACL().checkPermission(permission); + } + + public boolean hasPermission(Permission permission) { + return getACL().hasPermission(permission); + } + /** * Gets the channel that can be used to run a program on this computer. * @@ -385,7 +405,7 @@ public abstract class Computer extends AbstractModelObject { } public void doToggleOffline( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + checkPermission(Hudson.ADMINISTER); setTemporarilyOffline(!temporarilyOffline); rsp.forwardToPreviousPage(req); @@ -400,7 +420,7 @@ public abstract class Computer extends AbstractModelObject { */ public void doDumpExportTable( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { // this is a debug probe and may expose sensitive information - Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + checkPermission(Hudson.ADMINISTER); rsp.setContentType("text/plain"); rsp.setCharacterEncoding("UTF-8"); @@ -412,7 +432,7 @@ public abstract class Computer extends AbstractModelObject { public void doScript( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { // ability to run arbitrary script is dangerous, // so tie it to the admin access - Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + checkPermission(Hudson.ADMINISTER); String text = req.getParameter("script"); if(text!=null) { diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index fd5d4d07d8..b9d6bd0a13 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -245,6 +245,8 @@ public final class Hudson extends View implements ItemGroup, Node, */ private List views; // can't initialize it eagerly for backward compatibility + private transient MyView myView = new MyView(this); + private transient final FingerprintMap fingerprintMap = new FingerprintMap(); /** @@ -702,9 +704,11 @@ public final class Hudson extends View implements ItemGroup, Node, return v; } } - if(this.getViewName().equals(name)) + if (this.getViewName().equals(name)) { return this; - else + } else if (myView.getViewName().equals(name)) { + return myView; + } else return null; } @@ -715,12 +719,13 @@ public final class Hudson extends View implements ItemGroup, Node, public synchronized View[] getViews() { if(views==null) views = new ArrayList(); - View[] r = new View[views.size()+1]; + View[] r = new View[views.size()+2]; views.toArray(r); - // sort Views and put "all" at the very beginning - r[r.length-1] = r[0]; - Arrays.sort(r,1,r.length, View.SORTER); - r[0] = this; + r[r.length-2] = r[0]; + r[r.length-1] = r[1]; + Arrays.sort(r,2,r.length, View.SORTER); + r[0] = myView; + r[1] = this; return r; } diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index 28eb40b7a2..d0a323bc6e 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -61,7 +61,7 @@ public class Label implements Comparable

diff --git a/core/src/main/java/hudson/security/ProjectBasedAuthorizationStrategy.java b/core/src/main/java/hudson/security/ProjectBasedAuthorizationStrategy.java new file mode 100644 index 0000000000..28175796f4 --- /dev/null +++ b/core/src/main/java/hudson/security/ProjectBasedAuthorizationStrategy.java @@ -0,0 +1,89 @@ +package hudson.security; + +import hudson.model.AbstractProject; +import hudson.model.Computer; +import hudson.model.Descriptor; +import hudson.model.Hudson; +import hudson.model.Item; +import hudson.model.View; +import hudson.util.FormFieldValidator; + +import java.util.Collections; +import java.util.List; + +import net.sf.json.JSONObject; + +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.GrantedAuthorityImpl; +import org.acegisecurity.acls.sid.GrantedAuthoritySid; +import org.kohsuke.stapler.StaplerRequest; + +/** + * {@link AuthorizationStrategy} that grants control based on owned permissions. + */ +// TODO: ask Tom to explain what this class is. +public class ProjectBasedAuthorizationStrategy extends AuthorizationStrategy { + @Override + public ACL getRootACL() { + return ROOT_ACL; + } + + @Override + public ACL getACL(AbstractProject project) { + SparseACL acl = new SparseACL(ROOT_ACL); + acl.add(new GrantedAuthoritySid(createItemAuthority(project)), Permission.FULL_CONTROL, true); + return acl; + } + + @Override + public ACL getACL(Computer computer) { + SparseACL acl = new SparseACL(ROOT_ACL); + acl.add(ACL.ANONYMOUS, Hudson.ADMINISTER, false); + acl.add(ACL.EVERYONE, Hudson.ADMINISTER, true); + return acl; + } + + @Override + public ACL getACL(View view) { + SparseACL acl = new SparseACL(ROOT_ACL); + acl.add(ACL.ANONYMOUS, Permission.CREATE, false); + acl.add(ACL.EVERYONE, Permission.CREATE, true); + return acl; + } + + public List getGroups() { + return Collections.emptyList(); + } + + public static final SparseACL ROOT_ACL = new SparseACL(null); + + static { + ROOT_ACL.add(new GrantedAuthoritySid("admin"), Permission.FULL_CONTROL, true); + ROOT_ACL.add(ACL.ANONYMOUS,FormFieldValidator.CHECK,false); + ROOT_ACL.add(ACL.EVERYONE,FormFieldValidator.CHECK,true); + ROOT_ACL.add(ACL.EVERYONE,Permission.READ,true); + ROOT_ACL.add(ACL.EVERYONE,Permission.FULL_CONTROL,false); + } + + public GrantedAuthority createItemAuthority(Item item) { + return new GrantedAuthorityImpl(item.getFullName()); + } + + public Descriptor getDescriptor() { + return DESCRIPTOR; + } + + public static final Descriptor DESCRIPTOR = new Descriptor(ProjectBasedAuthorizationStrategy.class) { + public String getDisplayName() { + return "Project Based Access Control"; + } + + public AuthorizationStrategy newInstance(StaplerRequest req, JSONObject formData) throws FormException { + return new ProjectBasedAuthorizationStrategy(); + } + + public String getHelpFile() { + return "/help/security/full-control-once-logged-in.html"; + } + }; +} diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 464b991021..1d8cb18ae6 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -180,7 +180,7 @@ public final class SlaveComputer extends Computer { } public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + checkPermission(Hudson.ADMINISTER); disconnect(); rsp.sendRedirect("."); } diff --git a/core/src/main/java/hudson/tasks/UserNameResolver.java b/core/src/main/java/hudson/tasks/UserNameResolver.java new file mode 100644 index 0000000000..c8bca84da5 --- /dev/null +++ b/core/src/main/java/hudson/tasks/UserNameResolver.java @@ -0,0 +1,72 @@ +package hudson.tasks; + +import hudson.ExtensionPoint; +import hudson.Plugin; +import hudson.scm.SCM; +import hudson.scm.CVSSCM; +import hudson.scm.SubversionSCM; +import hudson.model.User; +import hudson.model.AbstractProject; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Finds full name off the user when none is specified. + * + *

+ * This is an extension point of Hudson. Plugins tha contribute new implementation + * of this class must register it to {@link #LIST}. The typical way to do this is: + * + *

+ * class PluginImpl extends {@link Plugin} {
+ *   public void start() {
+ *     ...
+ *     UserNameResolver.LIST.add(new UserNameResolver());
+ *   }
+ * }
+ * 
+ * + * @author Kohsuke Kawaguchi + * @sine 1.192 + */ +public abstract class UserNameResolver implements ExtensionPoint { + + /** + * Finds full name of the given user. + * + *

+ * This method is called when a {@link User} without explicitly name is used. + * + *

+ * When multiple resolvers are installed, they are consulted in order and + * the search will be over when a name is found by someoene. + * + *

+ * Since {@link UserNameResolver} is singleton, this method can be invoked concurrently + * from multiple threads. + * + * @return + * null if the inference failed. + */ + public abstract String findNameFor(User u); + + public static String resolve(User u) { + for (UserNameResolver r : LIST) { + String name = r.findNameFor(u); + if(name!=null) return name; + } + + return null; + } + + /** + * All registered {@link UserNameResolver} implementations. + */ + public static final List LIST = new CopyOnWriteArrayList(); + +} diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java index cd5752f341..ae6f1b82bc 100644 --- a/core/src/main/java/hudson/util/FormFieldValidator.java +++ b/core/src/main/java/hudson/util/FormFieldValidator.java @@ -1,22 +1,25 @@ package hudson.util; +import static hudson.Util.fixEmpty; +import hudson.EnvVars; import hudson.FilePath; import hudson.Util; -import hudson.EnvVars; -import static hudson.Util.fixEmpty; import hudson.model.AbstractProject; import hudson.model.Hudson; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import hudson.security.Permission; -import javax.servlet.ServletException; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; -import java.net.HttpURLConnection; + +import javax.servlet.ServletException; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; /** * Base class that provides the framework for doing on-the-fly form field validation. @@ -28,29 +31,35 @@ import java.net.HttpURLConnection; * @author Kohsuke Kawaguchi */ public abstract class FormFieldValidator { - protected final StaplerRequest request; + + public static final Permission CHECK = new Permission(Hudson.PERMISSIONS, "check", Permission.FULL_CONTROL); + + protected final StaplerRequest request; protected final StaplerResponse response; - private final boolean isAdminOnly; + private final Permission permission; + /** - * * @param adminOnly * Pass true to only let admin users to run the check. This is necessary * for security reason, so that unauthenticated user cannot obtain sensitive * information or run a process that may have side-effect. */ protected FormFieldValidator(StaplerRequest request, StaplerResponse response, boolean adminOnly) { + this(request, response, CHECK); + } + + protected FormFieldValidator(StaplerRequest request, StaplerResponse response, Permission permission) { this.request = request; this.response = response; - isAdminOnly = adminOnly; + this.permission = permission; } /** * Runs the validation code. */ public final void process() throws IOException, ServletException { - if(isAdminOnly && !Hudson.adminCheck(request,response)) - return; // failed check + Hudson.getInstance().checkPermission(permission); check(); } diff --git a/core/src/main/java/hudson/util/PluginServletFilter.java b/core/src/main/java/hudson/util/PluginServletFilter.java new file mode 100644 index 0000000000..2ef98d6e9b --- /dev/null +++ b/core/src/main/java/hudson/util/PluginServletFilter.java @@ -0,0 +1,51 @@ +package hudson.util; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +/** + * Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins + * + */ +public class PluginServletFilter implements Filter { + + public static CopyOnWriteList LIST = new CopyOnWriteList(); + + public PluginServletFilter() { + } + + public void init(FilterConfig filterConfig) throws ServletException { + for (Filter f : LIST) + f.init(filterConfig); + } + + public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain) throws IOException, ServletException { + new FilterChain() { + private int position=0; + // capture the array for thread-safety + private final Filter[] filters = LIST.toArray(new Filter[0]); + + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if(position==filters.length) { + // reached to the end + chain.doFilter(request,response); + } else { + // call next + filters[position++].doFilter(request,response,this); + } + } + }.doFilter(request,response); + } + + public void destroy() { + for (Filter f : LIST) + f.destroy(); + } +} diff --git a/core/src/main/resources/hudson/matrix/MatrixProject/index.jelly b/core/src/main/resources/hudson/matrix/MatrixProject/index.jelly index 45bb90c0d8..ff63b311e2 100644 --- a/core/src/main/resources/hudson/matrix/MatrixProject/index.jelly +++ b/core/src/main/resources/hudson/matrix/MatrixProject/index.jelly @@ -3,7 +3,7 @@

${%Project} ${it.name}

- + diff --git a/core/src/main/resources/hudson/maven/MavenModuleSet/index.jelly b/core/src/main/resources/hudson/maven/MavenModuleSet/index.jelly index e0ae6b45e8..4d47128a20 100644 --- a/core/src/main/resources/hudson/maven/MavenModuleSet/index.jelly +++ b/core/src/main/resources/hudson/maven/MavenModuleSet/index.jelly @@ -3,7 +3,7 @@

${it.pronoun} ${it.displayName}

- + diff --git a/core/src/main/resources/hudson/model/AbstractBuild/index.jelly b/core/src/main/resources/hudson/model/AbstractBuild/index.jelly index 9a9822871c..171ad099b8 100644 --- a/core/src/main/resources/hudson/model/AbstractBuild/index.jelly +++ b/core/src/main/resources/hudson/model/AbstractBuild/index.jelly @@ -22,7 +22,7 @@
- +
diff --git a/core/src/main/resources/hudson/model/ComputerSet/index.jelly b/core/src/main/resources/hudson/model/ComputerSet/index.jelly index 49bdc5e09d..91198dc13c 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/index.jelly +++ b/core/src/main/resources/hudson/model/ComputerSet/index.jelly @@ -2,7 +2,7 @@ Entrance to the configuration page --> - + diff --git a/core/src/main/resources/hudson/model/Hudson/configure.jelly b/core/src/main/resources/hudson/model/Hudson/configure.jelly index f282fd3424..b63b3d1047 100644 --- a/core/src/main/resources/hudson/model/Hudson/configure.jelly +++ b/core/src/main/resources/hudson/model/Hudson/configure.jelly @@ -2,7 +2,7 @@ Config page --> - + diff --git a/core/src/main/resources/hudson/model/Hudson/log.jelly b/core/src/main/resources/hudson/model/Hudson/log.jelly index 04df3e4fa9..4edbd88b1d 100644 --- a/core/src/main/resources/hudson/model/Hudson/log.jelly +++ b/core/src/main/resources/hudson/model/Hudson/log.jelly @@ -2,7 +2,7 @@ Log view --> - +

${%Hudson Log}

diff --git a/core/src/main/resources/hudson/model/Hudson/managePlugins.jelly b/core/src/main/resources/hudson/model/Hudson/managePlugins.jelly index e004d34be7..15c1b269f1 100644 --- a/core/src/main/resources/hudson/model/Hudson/managePlugins.jelly +++ b/core/src/main/resources/hudson/model/Hudson/managePlugins.jelly @@ -2,7 +2,7 @@ Config page --> - + diff --git a/core/src/main/resources/hudson/model/Hudson/systemInfo.jelly b/core/src/main/resources/hudson/model/Hudson/systemInfo.jelly index 5453685702..e9882b9817 100644 --- a/core/src/main/resources/hudson/model/Hudson/systemInfo.jelly +++ b/core/src/main/resources/hudson/model/Hudson/systemInfo.jelly @@ -2,15 +2,13 @@ Various system information for diagnostics --> - + -

${%System Properties}

${%Environment Variables}

-
diff --git a/core/src/main/resources/hudson/model/Hudson/threadDump.jelly b/core/src/main/resources/hudson/model/Hudson/threadDump.jelly index 03ee6d27a4..3d02c7436f 100644 --- a/core/src/main/resources/hudson/model/Hudson/threadDump.jelly +++ b/core/src/main/resources/hudson/model/Hudson/threadDump.jelly @@ -2,7 +2,7 @@ Produces stack dump of all threads by using JMX. --> - + diff --git a/core/src/main/resources/hudson/model/Job/index.jelly b/core/src/main/resources/hudson/model/Job/index.jelly index bbe7069575..99b1617035 100644 --- a/core/src/main/resources/hudson/model/Job/index.jelly +++ b/core/src/main/resources/hudson/model/Job/index.jelly @@ -3,7 +3,7 @@

${it.pronoun} ${it.displayName}

- +
diff --git a/core/src/main/resources/hudson/model/MyView/noJob.jelly b/core/src/main/resources/hudson/model/MyView/noJob.jelly new file mode 100644 index 0000000000..858a75f423 --- /dev/null +++ b/core/src/main/resources/hudson/model/MyView/noJob.jelly @@ -0,0 +1,3 @@ +
+ This view has no jobs. +
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/View/index.jelly b/core/src/main/resources/hudson/model/View/index.jelly index 870f5bfdc1..3875be1ee9 100644 --- a/core/src/main/resources/hudson/model/View/index.jelly +++ b/core/src/main/resources/hudson/model/View/index.jelly @@ -5,7 +5,7 @@
- +
@@ -20,7 +20,7 @@ - + diff --git a/core/src/main/resources/hudson/scm/SCM/project-changes.jelly b/core/src/main/resources/hudson/scm/SCM/project-changes.jelly index d358265de3..9fbd183c8f 100644 --- a/core/src/main/resources/hudson/scm/SCM/project-changes.jelly +++ b/core/src/main/resources/hudson/scm/SCM/project-changes.jelly @@ -23,7 +23,7 @@
    -
  1. +
  2. ${c.msgAnnotated} — diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly index d309c2d876..0a43c91e03 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly @@ -8,7 +8,7 @@
- +

launch agent diff --git a/core/src/main/resources/lib/hudson/editableDescription.jelly b/core/src/main/resources/lib/hudson/editableDescription.jelly index 1b2f100920..b5f8be6941 100644 --- a/core/src/main/resources/lib/hudson/editableDescription.jelly +++ b/core/src/main/resources/lib/hudson/editableDescription.jelly @@ -9,7 +9,7 @@ ${it.description} - +

-
+
\ No newline at end of file diff --git a/core/src/main/resources/lib/layout/hasPermission.jelly b/core/src/main/resources/lib/layout/hasPermission.jelly new file mode 100644 index 0000000000..84f7452e9c --- /dev/null +++ b/core/src/main/resources/lib/layout/hasPermission.jelly @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly index c2f863f0b5..645f63934e 100644 --- a/core/src/main/resources/lib/layout/layout.jelly +++ b/core/src/main/resources/lib/layout/layout.jelly @@ -31,8 +31,7 @@ - - ${h.adminCheck(request,response,secured,permission)} + ${h.checkPermission(it,permission)} ${h.appendIfNotNull(title, ' [Hudson]', 'Hudson')} diff --git a/core/src/main/resources/lib/layout/task.jelly b/core/src/main/resources/lib/layout/task.jelly index 6e3b3d000c..63fd96f447 100644 --- a/core/src/main/resources/lib/layout/task.jelly +++ b/core/src/main/resources/lib/layout/task.jelly @@ -8,7 +8,7 @@ permission: permission object. If specified, the link will be displayed only if you have a permission (optional) --> - +