提交 a49dcb95 编写于 作者: D Daniel Beck
......@@ -39,7 +39,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.258</stapler.version>
<stapler.version>1.259</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
<groovy.version>2.4.12</groovy.version>
</properties>
......@@ -75,7 +75,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>version-number</artifactId>
<version>1.6</version>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci</groupId>
......@@ -600,6 +600,11 @@ THE SOFTWARE.
<artifactId>spotbugs-annotations</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
......
......@@ -46,4 +46,10 @@ public class AboutJenkins extends ManagementLink {
public Permission getRequiredPermission() {
return Jenkins.SYSTEM_READ;
}
@Nonnull
@Override
public Category getCategory() {
return Category.STATUS;
}
}
......@@ -46,7 +46,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import net.sf.json.JSONException;
import org.kohsuke.stapler.Stapler;
import net.sf.json.JSONObject;
......@@ -132,10 +134,14 @@ public class DescriptorExtensionList<T extends Describable<T>, D extends Descrip
}
/**
* Creates a new instance of a {@link Describable}
* from the structured form submission data posted
* by a radio button group.
* Creates a new instance of a {@link Describable} from the structured form submission data posted by a radio button group.
* @param config Submitted configuration for Radio List
* @return New instance.
* {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter}
* @throws FormException Data submission error
* @since 1.312
*/
@CheckForNull
public T newInstanceFromRadioList(JSONObject config) throws FormException {
if(config.isNullObject())
return null; // none was selected
......@@ -143,8 +149,21 @@ public class DescriptorExtensionList<T extends Describable<T>, D extends Descrip
return get(idx).newInstance(Stapler.getCurrentRequest(),config);
}
public T newInstanceFromRadioList(JSONObject parent, String name) throws FormException {
return newInstanceFromRadioList(parent.getJSONObject(name));
/**
* Creates a new instance of a {@link Describable} from the structured form submission data posted by a radio list.
* @since 1.312
* @param name Name of the form field
* @return Created instance.
* {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter}
* @throws FormException Data submission error
*/
@CheckForNull
public T newInstanceFromRadioList(@Nonnull JSONObject parent, @Nonnull String name) throws FormException {
try {
return newInstanceFromRadioList(parent.getJSONObject(name));
} catch (JSONException ex) {
throw new FormException(ex, name);
}
}
/**
......
......@@ -391,7 +391,7 @@ public class EnvVars extends TreeMap<String,String> {
/**
* Add entire map but filter null values out.
* @since TODO
* @since 2.214
*/
public void putAllNonNull(Map<String, String> map) {
map.forEach(this::putIfNotNull);
......
......@@ -309,7 +309,7 @@ public class ExtensionList<T> extends AbstractList<T> implements OnMaster {
private List<ExtensionComponent<T>> ensureLoaded() {
if(extensions!=null)
return extensions; // already loaded
if (jenkins.getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0)
if (jenkins == null || jenkins.getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED) < 0)
return legacyInstances; // can't perform the auto discovery until all plugins are loaded, so just make the legacy instances visible
synchronized (getLoadLock()) {
......
......@@ -27,6 +27,8 @@ package hudson;
import hudson.model.Slave;
import hudson.security.*;
import java.text.SimpleDateFormat;
import java.util.function.Predicate;
import jenkins.util.SystemProperties;
import hudson.cli.CLICommand;
......@@ -219,10 +221,36 @@ public class Functions {
return Util.XS_DATETIME_FORMATTER.format(cal.getTime());
}
@Restricted(NoExternalUse.class)
public static String iso8601DateTime(Date date) {
return Util.XS_DATETIME_FORMATTER.format(date);
}
/**
* Returns a localized string for the specified date, not including time.
* @param date
* @return
*/
@Restricted(NoExternalUse.class)
public static String localDate(Date date) {
return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(date);
}
public static String rfc822Date(Calendar cal) {
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
}
/**
* Returns a human-readable string describing the time difference between now and the specified date.
*
* @param date
* @return
*/
@Restricted(NoExternalUse.class)
public static String getTimeSpanString(Date date) {
return Util.getTimeSpanString(Math.abs(date.getTime() - new Date().getTime()));
}
/**
* During Jenkins start-up, before {@link InitMilestone#PLUGINS_STARTED} the extensions lists will be empty
* and they are not guaranteed to be fully populated until after {@link InitMilestone#EXTENSIONS_AUGMENTED},
......@@ -498,7 +526,7 @@ public class Functions {
/**
* Returns true if and only if the UI refresh is enabled.
*
* @since TODO
* @since 2.222
*/
@Restricted(DoNotUse.class)
public static boolean isUiRefreshEnabled() {
......@@ -1071,7 +1099,7 @@ public class Functions {
*
* @param predicate
* Filter the descriptors based on this predicate
* @since TODO
* @since 2.222
*/
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigByDescriptor(Predicate<Descriptor> predicate) {
ExtensionList<Descriptor> exts = ExtensionList.lookup(Descriptor.class);
......@@ -1120,7 +1148,7 @@ public class Functions {
/**
* Descriptors shown in the global configuration form to users with {@link Jenkins#SYSTEM_READ} permission.
*
* @since TODO
* @since 2.222
*/
@Restricted(NoExternalUse.class)
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigUnclassifiedReadable() {
......@@ -1134,7 +1162,7 @@ public class Functions {
* @throws AccessDeniedException
* if the user doesn't have the permission.
*
* @since TODO
* @since 2.222
*/
public static void checkAnyPermission(AccessControlled ac, Permission[] permissions) {
if (permissions == null || permissions.length == 0) {
......@@ -1144,6 +1172,31 @@ public class Functions {
ac.checkAnyPermission(permissions);
}
/**
* This version is so that the 'checkAnyPermission' on {@code layout.jelly}
* degrades gracefully if "it" is not an {@link AccessControlled} object.
* Otherwise it will perform no check and that problem is hard to notice.
*/
public static void checkAnyPermission(Object object, Permission[] permissions) throws IOException, ServletException {
if (permissions == null || permissions.length == 0) {
return;
}
if (object instanceof AccessControlled)
checkAnyPermission((AccessControlled) object, permissions);
else {
List<Ancestor> ancs = Stapler.getCurrentRequest().getAncestors();
for(Ancestor anc : Iterators.reverse(ancs)) {
Object o = anc.getObject();
if (o instanceof AccessControlled) {
checkAnyPermission((AccessControlled) o, permissions);
return;
}
}
checkAnyPermission(Jenkins.get(), permissions);
}
}
private static class Tag implements Comparable<Tag> {
double ordinal;
String hierarchy;
......
......@@ -126,6 +126,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
......@@ -2245,6 +2246,33 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return this;
}
@Restricted(DoNotUse.class) // Used from table.jelly
public boolean isMetaLabel(String label) {
return "adopt-this-plugin".equals(label);
}
@Restricted(DoNotUse.class) // Used from table.jelly
public boolean hasAdoptThisPluginLabel(UpdateSite.Plugin plugin) {
final String[] categories = plugin.categories;
if (categories == null) {
return false;
}
return Arrays.asList(categories).contains("adopt-this-plugin");
}
@Restricted(DoNotUse.class) // Used from table.jelly
public boolean hasAdoptThisPluginLabel(PluginWrapper plugin) {
final UpdateSite.Plugin pluginMeta = Jenkins.get().getUpdateCenter().getPlugin(plugin.getShortName());
if (pluginMeta == null) {
return false;
}
final String[] categories = pluginMeta.categories;
if (categories == null) {
return false;
}
return Arrays.asList(categories).contains("adopt-this-plugin");
}
/**
* Escape hatch for StaplerProxy-based access control
*/
......
......@@ -36,6 +36,7 @@ import hudson.util.VersionNumber;
import io.jenkins.lib.versionnumber.JavaSpecificationVersion;
import jenkins.YesNoMaybe;
import jenkins.model.Jenkins;
import jenkins.security.UpdateSiteWarningsMonitor;
import jenkins.util.java.JavaUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
......@@ -1263,6 +1264,10 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
return HttpResponses.redirectViaContextPath("/pluginManager/installed"); // send back to plugin manager
}
@Restricted(DoNotUse.class) // Jelly
public List<UpdateSite.Warning> getActiveWarnings() {
return ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(this, Collections.emptyList());
}
private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName());
......
......@@ -103,7 +103,7 @@ public final class MemoryUsageMonitor extends PeriodicWork {
* Generates the memory usage statistics graph.
*/
public TrendChart doGraph(@QueryParameter String type) throws IOException {
Jenkins.get().checkPermission(Jenkins.SYSTEM_READ);
Jenkins.get().checkAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE);
return MultiStageTimeSeries.createTrendChart(TimeScale.parse(type),used,max);
}
}
......
......@@ -440,6 +440,12 @@ public class OldDataMonitor extends AdministrativeMonitor {
@Extension @Symbol("oldData")
public static class ManagementLinkImpl extends ManagementLink {
@Nonnull
@Override
public Category getCategory() {
return Category.TROUBLESHOOTING;
}
@Override
public String getIconFileName() {
return "document.png";
......
......@@ -89,14 +89,14 @@ public enum InitMilestone implements Milestone {
/**
* By this milestone, all the system configurations are loaded from file system
* @since TODO
* @since 2.220
*/
SYSTEM_CONFIG_LOADED("System config loaded"),
/**
* By this milestone, the system configuration is adapted just in case any plugin (CasC might be an example) needs
* to update configuration files
* @since TODO
* @since 2.220
*/
SYSTEM_CONFIG_ADAPTED("System config adapted"),
......@@ -107,7 +107,7 @@ public enum InitMilestone implements Milestone {
/**
* By this milestone, any job configuration is adapted or updated just in case any plugin needs to update former/old configurations or init scripts
* @since TODO
* @since 2.220
*/
JOB_CONFIG_ADAPTED("Configuration for all jobs updated"),
......
......@@ -3,13 +3,23 @@ package hudson.init.impl;
import hudson.init.Initializer;
import jenkins.model.Jenkins;
import jenkins.telemetry.impl.java11.MissingClassTelemetry;
import org.kohsuke.MetaInfServices;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.compression.CompressionFilter;
import javax.annotation.CheckForNull;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.EOFException;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -23,22 +33,7 @@ public class InstallUncaughtExceptionHandler {
@Initializer
public static void init(final Jenkins j) throws IOException {
CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> {
if (rsp.isCommitted()) {
LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, null, e);
return;
}
req.setAttribute("javax.servlet.error.exception",e);
try {
// If we have an exception, let's see if it's related with missing classes on Java 11. We reach
// here with a ClassNotFoundException in an action, for example. Setting the report here is the only
// way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass
MissingClassTelemetry.reportExceptionInside(e);
WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops");
} catch (ServletException | IOException x) {
if (!Stapler.isSocketException(x)) {
throw x;
}
}
handleException(j, e, req, rsp, 500);
});
try {
Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler());
......@@ -54,6 +49,46 @@ public class InstallUncaughtExceptionHandler {
}
}
private static void handleException(Jenkins j, Throwable e, HttpServletRequest req, HttpServletResponse rsp, int code) throws IOException, ServletException {
if (rsp.isCommitted()) {
LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, null, e);
return;
}
String id = UUID.randomUUID().toString();
LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, "Caught unhandled exception with ID " + id, e);
req.setAttribute("jenkins.exception.id", id);
req.setAttribute("javax.servlet.error.exception",e);
rsp.setStatus(code);
try {
// If we have an exception, let's see if it's related with missing classes on Java 11. We reach
// here with a ClassNotFoundException in an action, for example. Setting the report here is the only
// way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass
MissingClassTelemetry.reportExceptionInside(e);
WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops");
} catch (ServletException | IOException x) {
if (!Stapler.isSocketException(x)) {
throw x;
}
}
}
@Restricted(NoExternalUse.class)
@MetaInfServices
public static class ErrorCustomizer implements HttpResponses.ErrorCustomizer {
@CheckForNull
@Override
public HttpResponses.HttpResponseException handleError(int code, Throwable cause) {
if (Jenkins.getInstanceOrNull() == null) {
return null;
}
return new HttpResponses.HttpResponseException(cause) {
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
handleException(Jenkins.get(), cause, req, rsp, code);
}
};
}
}
private static boolean isEOFException(Throwable e) {
if (e == null) {
return false;
......
......@@ -53,6 +53,7 @@ import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.types.FileSet;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
......@@ -100,6 +101,13 @@ public class WindowsInstallerLink extends ManagementLink {
return Messages.WindowsInstallerLink_Description();
}
@Nonnull
@Override
public Category getCategory() {
return Category.CONFIGURATION;
}
/**
* Is the installation successful?
*/
......
......@@ -673,7 +673,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
public void delete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
try {
doDoDelete(req,rsp);
delete();
rsp.setStatus(204);
} catch (InterruptedException e) {
// TODO: allow this in Stapler
throw new ServletException(e);
......
......@@ -1383,7 +1383,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* Retrieve the RSS feed for the last build for each project executed in this computer.
* Only the information from {@link AbstractProject} is displayed since there isn't a proper API to gather
* information about the node where the builds are executed for other sorts of projects such as Pipeline
* @since TODO
* @since 2.215
*/
@Restricted(DoNotUse.class)
public void doRssLatest( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
......
......@@ -839,7 +839,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
* Override to return something different if appropriate. The only currently supported alternative return value is {@link Jenkins#MANAGE}.
*
* @return Permission required to globally configure this descriptor.
* @since TODO
* @since 2.222
*/
public @Nonnull
Permission getRequiredGlobalConfigPagePermission() {
......
......@@ -857,7 +857,7 @@ public class Executor extends Thread implements ModelObject {
* @param runExtId
* if not null, the externalizable id ({@link Run#getExternalizableId()})
* of the build the user expects to interrupt
* @since TODO
* @since 2.209
*/
@RequirePOST
@Restricted(NoExternalUse.class)
......
......@@ -1307,7 +1307,7 @@ public class Fingerprint implements ModelObject, Saveable {
/**
* Returns a facet that blocks the deletion of the fingerprint.
* Returns null if no such facet.
* @since TODO
* @since 2.223
*/
public @CheckForNull FingerprintFacet getFacetBlockingDeletion() {
for (FingerprintFacet facet : facets) {
......
......@@ -62,7 +62,7 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
/**
* Gets all the items in this collection in a read-only view
* that matches supplied Predicate
* @since TODO
* @since 2.221
*/
default Collection<T> getItems(Predicate<T> pred) {
return getItemsStream(pred)
......@@ -71,7 +71,7 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
/**
* Gets a read-only stream of all the items in this collection
* @since TODO
* @since 2.221
*/
default Stream<T> getItemsStream() {
return getItems().stream();
......@@ -80,7 +80,7 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
/**
* Gets a read-only stream of all the items in this collection
* that matches supplied Predicate
* @since TODO
* @since 2.221
*/
default Stream<T> getItemsStream(Predicate<T> pred) {
return getItemsStream().filter(pred);
......@@ -132,7 +132,7 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
/**
* Similar to {@link #getAllItems(Class)} with additional predicate filtering
* @since TODO
* @since 2.221
*/
default <T extends Item> List<T> getAllItems(Class<T> type, Predicate<T> pred) {
return Items.getAllItems(this, type, pred);
......@@ -150,7 +150,7 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
/**
* Gets all the {@link Item}s unordered, lazily and recursively in the {@link ItemGroup} tree
* and filter them by the given type and given predicate
* @since TODO
* @since 2.221
*/
default <T extends Item> Iterable<T> allItems(Class<T> type, Predicate<T> pred) {
return Items.allItems(this, type, pred);
......
......@@ -413,7 +413,7 @@ public class Items {
* @param pred Predicate condition to filter items
* @return List of items matching given criteria
*
* @since TODO
* @since 2.221
*/
public static <T extends Item> List<T> getAllItems(final ItemGroup root, Class<T> type, Predicate<T> pred) {
List<T> r = new ArrayList<>();
......@@ -466,7 +466,7 @@ public class Items {
* @param <T> the type.
* @param <T> the predicate.
* @return An {@link Iterable} for all items.
* @since TODO
* @since 2.221
*/
public static <T extends Item> Iterable<T> allItems(ItemGroup root, Class<T> type, Predicate<T> pred) {
return allItems(Jenkins.getAuthentication(), root, type, pred);
......@@ -502,7 +502,7 @@ public class Items {
* @param <T> the type.
* @param pred the predicate.
* @return An {@link Iterable} for all items.
* @since TODO
* @since 2.221
*/
public static <T extends Item> Iterable<T> allItems(Authentication authentication, ItemGroup root, Class<T> type, Predicate<T> pred) {
return new AllItemsIterable<>(root, authentication, type, pred);
......
......@@ -31,6 +31,12 @@ import hudson.security.Permission;
import jenkins.model.Jenkins;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.localizer.Localizable;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.annotation.CheckForNull;
......@@ -131,4 +137,82 @@ public abstract class ManagementLink implements ExtensionPoint, Action {
public boolean getRequiresPOST() {
return false;
}
/**
* Name of the category for this management link. Exists so that plugins with core dependency pre-dating the version
* when this was introduced can define a category. Plugins with newer core dependency override {@link #getCategory()} instead.
*
* @return name of the desired category, one of the enum values of {@link Category}, e.g. {@code STATUS}.
* @since 2.226
*/
@Restricted(NoExternalUse.class) // TODO I don't think this works
protected @Nonnull String getCategoryName() {
return "UNCATEGORIZED";
}
/**
* Category for management link, uses {@code String} so it can be done with core dependency pre-dating the version this feature was added.
*
* @return An enum value of {@link Category}.
* @since 2.226
*/
public @Nonnull Category getCategory() {
try {
return Category.valueOf(getCategoryName());
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, "invalid category {0} for class {1}", new Object[]{getCategoryName() , this.getClass().getName()});
return Category.UNCATEGORIZED;
}
}
/**
* Categories supported by this version of core.
*
* @since 2.226
*/
public enum Category {
/**
* Configuration pages that don't fit into a more specific section.
*/
CONFIGURATION(Messages._ManagementLink_Category_CONFIGURATION()),
/**
* Security related options. Useful for plugins providing security related {@code ManagementLink}s (e.g. security realms).
* Use {@link Category#STATUS} instead if the feature is informational.
*/
SECURITY(Messages._ManagementLink_Category_SECURITY()),
/**
* Status information about the Jenkins instance, such as log messages, load statistics, or general information.
*/
STATUS(Messages._ManagementLink_Category_STATUS()),
/**
* Troubleshooting utilities. This overlaps some with status information, but the difference is that status
* always applies, while troubleshooting only matters when things go wrong.
*/
TROUBLESHOOTING(Messages._ManagementLink_Category_TROUBLESHOOTING()),
/**
* Tools are specifically tools for administrators, such as the Jenkins CLI and Script Console, as well as specific stand-alone administrative features ({@link jenkins.management.ShutdownLink}, {@link jenkins.management.ReloadLink}).
* This has nothing to do with build tools or tool installers.
*/
TOOLS(Messages._ManagementLink_Category_TOOLS()),
/**
* Anything that doesn't fit into any of the other categories. Expected to be necessary only very rarely.
*/
MISC(Messages._ManagementLink_Category_MISC()),
/**
* The default category for uncategorized items. Do not explicitly specify this category for your {@code ManagementLink}.
*/
UNCATEGORIZED(Messages._ManagementLink_Category_UNCATEGORIZED());
private Localizable label;
Category(Localizable label) {
this.label = label;
}
public @Nonnull String getLabel() {
return label.toString();
}
}
private static final Logger LOGGER = Logger.getLogger(ManagementLink.class.getName());
}
......@@ -81,7 +81,7 @@ public final class RSS {
* URL of the model object that owns this feed. Relative to the context root.
* @param runList
* Entries to be listed in the RSS feed.
* @since TODO
* @since 2.215
*/
public static void rss(StaplerRequest req, StaplerResponse rsp, String title, String url, RunList runList) throws IOException, ServletException {
rss(req, rsp, title, url, runList, null);
......@@ -98,7 +98,7 @@ public final class RSS {
* Entries to be listed in the RSS feed.
* @param feedAdapter
* Controls how to render entries to RSS.
* @since TODO
* @since 2.215
*/
public static void rss(StaplerRequest req, StaplerResponse rsp, String title, String url, RunList runList, FeedAdapter<Run> feedAdapter) throws IOException, ServletException {
final FeedAdapter<Run> feedAdapter_ = feedAdapter == null ? Run.FEED_ADAPTER : feedAdapter;
......
......@@ -162,7 +162,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* This is applied to the raw, unformatted description. Especially complex formatting
* like hyperlinks can result in much less text being shown than this might imply.
* Negative values will disable truncation, {@code 0} will enforce empty strings.
* @since TODO
* @since 2.223
*/
private static /* non-final for Groovy */ int TRUNCATED_DESCRIPTION_LIMIT = SystemProperties.getInteger("historyWidget.descriptionLimit", 100);
......@@ -992,7 +992,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* @return a list with the builds (youngest build first).
* May be smaller than 'numberOfBuilds' or even empty
* if not enough builds satisfying the threshold have been found. Never null.
* @since TODO
* @since 2.202
*/
protected @Nonnull List<RunT> getBuildsOverThreshold(int numberOfBuilds, @Nonnull Result threshold) {
List<RunT> builds = new ArrayList<>(numberOfBuilds);
......
......@@ -989,7 +989,9 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
/**
* Returns a list of plugins that should be shown in the "available" tab, grouped by category.
* A plugin with multiple categories will appear multiple times in the list.
* @deprecated
*/
@Deprecated
public PluginEntry[] getCategorizedAvailables() {
TreeSet<PluginEntry> entries = new TreeSet<>();
for (Plugin p : getAvailables()) {
......@@ -1002,7 +1004,8 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
return entries.toArray(new PluginEntry[0]);
}
private static String getCategoryDisplayName(String category) {
@Restricted(NoExternalUse.class) // Jelly only
public static String getCategoryDisplayName(String category) {
if (category==null)
return Messages.UpdateCenter_PluginCategory_misc();
try {
......@@ -1011,7 +1014,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
return Messages.UpdateCenter_PluginCategory_unrecognized(category);
return category;
}
}
......@@ -2454,6 +2457,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
@Deprecated
public static final class PluginEntry implements Comparable<PluginEntry> {
public Plugin plugin;
public String category;
......
......@@ -43,8 +43,10 @@ import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
......@@ -67,6 +69,7 @@ import io.jenkins.lib.versionnumber.JavaSpecificationVersion;
import jenkins.model.Jenkins;
import jenkins.plugins.DetachedPluginsUtil;
import jenkins.security.UpdateSiteWarningsConfiguration;
import jenkins.security.UpdateSiteWarningsMonitor;
import jenkins.util.JSONSignatureValidator;
import jenkins.util.SystemProperties;
import jenkins.util.java.JavaUtils;
......@@ -165,7 +168,7 @@ public class UpdateSite {
* does not exist, or is otherwise due for update.
* Accepted formats are JSONP or HTML with {@code postMessage}, not raw JSON.
* @return null if no updates are necessary, or the future result
* @since TODO
* @since 2.222
*/
public @CheckForNull Future<FormValidation> updateDirectly() {
return updateDirectly(DownloadService.signatureCheck);
......@@ -196,7 +199,7 @@ public class UpdateSite {
/**
* Forces an update of the data file from the configured URL, irrespective of the last time the data was retrieved.
* @return A {@code FormValidation} indicating the if the update metadata was successfully downloaded from the configured update site
* @since TODO
* @since 2.222
* @throws IOException if there was an error downloading or saving the file.
*/
public @Nonnull FormValidation updateDirectlyNow() throws IOException {
......@@ -1018,6 +1021,13 @@ public class UpdateSite {
*/
private Set<Plugin> incompatibleParentPlugins;
/**
* Date when this plugin was released.
* @since 2.224
*/
@Exported
public final Date releaseTimestamp;
@DataBoundConstructor
public Plugin(String sourceId, JSONObject o) {
super(sourceId, o, UpdateSite.this.url);
......@@ -1027,6 +1037,16 @@ public class UpdateSite {
this.compatibleSinceVersion = Util.intern(get(o,"compatibleSinceVersion"));
this.minimumJavaVersion = Util.intern(get(o, "minimumJavaVersion"));
this.requiredCore = Util.intern(get(o,"requiredCore"));
final String releaseTimestamp = get(o, "releaseTimestamp");
Date date = null;
if (releaseTimestamp != null) {
try {
date = Date.from(Instant.parse(releaseTimestamp));
} catch (Exception ex) {
LOGGER.log(Level.FINE, "Failed to parse releaseTimestamp for " + title + " from " + sourceId, ex);
}
}
this.releaseTimestamp = date;
this.categories = o.has("labels") ? internInPlace((String[])o.getJSONArray("labels").toArray(EMPTY_STRING_ARRAY)) : null;
JSONArray ja = o.getJSONArray("dependencies");
int depCount = (int)(ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL)).count());
......@@ -1276,10 +1296,36 @@ public class UpdateSite {
return getDependenciesIncompatibleWithInstalledVersion(cache).isEmpty();
}
/**
* Returns true if and only if this update addressed a currently active security vulnerability.
*
* @return true if and only if this update addressed a currently active security vulnerability.
*/
@Restricted(NoExternalUse.class) // Jelly
public boolean fixesSecurityVulnerabilities() {
final PluginWrapper installed = getInstalled();
if (installed == null) {
return false;
}
boolean allWarningsStillApply = true;
for (Warning warning : ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(installed, Collections.emptyList())) {
boolean thisWarningApplies = false;
for (WarningVersionRange range : warning.versionRanges) {
if (range.includes(new VersionNumber(version))) {
thisWarningApplies = true;
}
}
if (!thisWarningApplies) {
allWarningsStillApply = false;
}
}
return !allWarningsStillApply;
}
/**
* Get the list of incompatible dependencies (if there are any, as determined by isNeededDependenciesCompatibleWithInstalledVersion)
*
* @since TODO
* @since 2.203
*/
@Restricted(NoExternalUse.class) // table.jelly
@SuppressWarnings("unchecked")
......
......@@ -27,6 +27,7 @@ import hudson.PluginWrapper;
import hudson.Util;
import hudson.Extension;
import hudson.node_monitors.ArchitectureMonitor.DescriptorImpl;
import hudson.security.Permission;
import hudson.util.Secret;
import static java.util.concurrent.TimeUnit.DAYS;
......@@ -35,6 +36,7 @@ import net.sf.json.JSONObject;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.kohsuke.stapler.StaplerRequest;
import javax.annotation.Nonnull;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
......@@ -192,6 +194,12 @@ public class UsageStatistics extends PageDecorator implements PersistentDescript
}
}
@Nonnull
@Override
public Permission getRequiredGlobalConfigPagePermission() {
return Jenkins.MANAGE;
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
try {
......
......@@ -89,7 +89,7 @@ public abstract class ACL {
* @throws IllegalArgumentException
* if no permissions are provided
*
* @since TODO
* @since 2.222
*/
public final void checkAnyPermission(@Nonnull Permission... permissions) {
if (permissions.length == 0) {
......
......@@ -51,7 +51,7 @@ public interface AccessControlled {
* Convenient short-cut for {@code getACL().checkAnyPermission(permission)}
* @see ACL#checkAnyPermission(Permission...)
*
* @since TODO
* @since 2.222
*/
default void checkAnyPermission(@Nonnull Permission... permission) throws AccessDeniedException {
getACL().checkAnyPermission(permission);
......@@ -68,7 +68,7 @@ public interface AccessControlled {
* Convenient short-cut for {@code getACL().hasAnyPermission(permission)}
* @see ACL#hasAnyPermission(Permission...)
*
* @since TODO
* @since 2.222
*/
default boolean hasAnyPermission(@Nonnull Permission... permission) {
return getACL().hasAnyPermission(permission);
......
......@@ -41,6 +41,7 @@ import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import jenkins.model.GlobalConfigurationCategory;
......@@ -93,6 +94,12 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
return Jenkins.get().isDisableRememberMe();
}
@Nonnull
@Override
public Category getCategory() {
return Category.SECURITY;
}
@POST
public synchronized void doConfigure(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
// for compatibility reasons, the actual value is stored in Jenkins
......
......@@ -251,7 +251,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
private User _doCreateAccount(StaplerRequest req, StaplerResponse rsp, String formView) throws ServletException, IOException {
if(!allowsSignup())
throw HttpResponses.error(SC_UNAUTHORIZED,new Exception("User sign up is prohibited"));
throw HttpResponses.errorWithoutStack(SC_UNAUTHORIZED, "User sign up is prohibited");
boolean firstUser = !hasSomeUser();
User u = createAccount(req, rsp, enableCaptcha, formView);
......@@ -771,6 +771,12 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
public String getDescription() {
return Messages.HudsonPrivateSecurityRealm_ManageUserLinks_Description();
}
@Nonnull
@Override
public Category getCategory() {
return Category.SECURITY;
}
}
/**
......
......@@ -148,14 +148,14 @@ public class JNLPLauncher extends ComputerLauncher {
}
/**
* @since TODO
* @since 2.216
*/
public boolean isWebSocket() {
return webSocket;
}
/**
* @since TODO
* @since 2.216
*/
@DataBoundSetter
public void setWebSocket(boolean webSocket) {
......
......@@ -157,10 +157,12 @@ public abstract class DownloadFromUrlInstaller extends ToolInstaller {
if (toolInstallerList != null) {
ToolInstallerEntry[] entryList = toolInstallerList.list;
ToolInstallerEntry sampleEntry = entryList[0];
if (sampleEntry != null) {
if (sampleEntry.id != null && sampleEntry.name != null && sampleEntry.url != null) {
return true;
if (entryList != null) {
ToolInstallerEntry sampleEntry = entryList[0];
if (sampleEntry != null) {
if (sampleEntry.id != null && sampleEntry.name != null && sampleEntry.url != null) {
return true;
}
}
}
}
......
......@@ -52,7 +52,7 @@ public abstract class SafeTimerTask extends TimerTask {
/**
* Lambda-friendly means of creating a task.
* @since TODO
* @since 2.216
*/
public static SafeTimerTask of(ExceptionRunnable r) {
return new SafeTimerTask() {
......@@ -64,7 +64,7 @@ public abstract class SafeTimerTask extends TimerTask {
}
/**
* @see #of
* @since TODO
* @since 2.216
*/
@FunctionalInterface
public interface ExceptionRunnable {
......
......@@ -29,6 +29,7 @@ import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Descriptor.FormException;
import jenkins.model.Jenkins;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.Stapler;
......@@ -150,7 +151,8 @@ public final class DescriptorList<T extends Describable<T>> extends AbstractList
* from the structured form submission data posted
* by a radio button group.
* @param config Submitted configuration for Radio List
* @return new instance or {@code null} if none was selected in the radio list
* @return New instance.
* {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter}
* @throws FormException Data submission error
*/
@CheckForNull
......@@ -167,12 +169,17 @@ public final class DescriptorList<T extends Describable<T>> extends AbstractList
* by a radio button group.
* @param parent JSON, which contains the configuration entry for the radio list
* @param name Name of the configuration entry for the radio list
* @return new instance or {@code null} if none was selected in the radio list
* @return New instance.
* {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter}
* @throws FormException Data submission error
*/
@CheckForNull
public T newInstanceFromRadioList(JSONObject parent, String name) throws FormException {
return newInstanceFromRadioList(parent.getJSONObject(name));
try {
return newInstanceFromRadioList(parent.getJSONObject(name));
} catch (JSONException ex) {
throw new FormException(ex, name);
}
}
/**
......
......@@ -62,4 +62,10 @@ public class CliLink extends ManagementLink {
public String getUrlName() {
return "cli";
}
@Nonnull
@Override
public Category getCategory() {
return Category.TOOLS;
}
}
......@@ -62,4 +62,10 @@ public class ConfigureLink extends ManagementLink {
public String getUrlName() {
return "configure";
}
@Nonnull
@Override
public Category getCategory() {
return Category.CONFIGURATION;
}
}
......@@ -30,6 +30,8 @@ import hudson.security.Permission;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import javax.annotation.Nonnull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -59,4 +61,10 @@ public class ConsoleLink extends ManagementLink {
public Permission getRequiredPermission() {
return Jenkins.ADMINISTER;
}
@Nonnull
@Override
public Category getCategory() {
return Category.TOOLS;
}
}
......@@ -27,7 +27,6 @@ package jenkins.management;
import hudson.Extension;
import hudson.model.ManagementLink;
import hudson.security.Permission;
import jenkins.management.Messages;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
......@@ -63,4 +62,10 @@ public class NodesLink extends ManagementLink {
public String getUrlName() {
return "computer";
}
@Nonnull
@Override
public Category getCategory() {
return Category.CONFIGURATION;
}
}
......@@ -28,6 +28,8 @@ import hudson.Extension;
import hudson.model.ManagementLink;
import org.jenkinsci.Symbol;
import javax.annotation.Nonnull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -52,4 +54,10 @@ public class PluginsLink extends ManagementLink {
public String getUrlName() {
return "pluginManager";
}
@Nonnull
@Override
public Category getCategory() {
return Category.CONFIGURATION;
}
}
......@@ -28,6 +28,8 @@ import hudson.Extension;
import hudson.model.ManagementLink;
import org.jenkinsci.Symbol;
import javax.annotation.Nonnull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -61,4 +63,10 @@ public class ReloadLink extends ManagementLink {
public boolean getRequiresPOST() {
return true;
}
@Nonnull
@Override
public Category getCategory() {
return Category.TOOLS;
}
}
......@@ -29,6 +29,8 @@ import hudson.model.ManagementLink;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import javax.annotation.Nonnull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -58,4 +60,10 @@ public class ShutdownLink extends ManagementLink {
public boolean getRequiresPOST() {
return true;
}
@Nonnull
@Override
public Category getCategory() {
return Category.TOOLS;
}
}
......@@ -62,4 +62,10 @@ public class StatisticsLink extends ManagementLink {
public String getUrlName() {
return "load-statistics";
}
@Nonnull
@Override
public Category getCategory() {
return Category.STATUS;
}
}
......@@ -31,8 +31,6 @@ import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import javax.annotation.CheckForNull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -56,11 +54,18 @@ public class SystemInfoLink extends ManagementLink {
@Nonnull
@Override
public Permission getRequiredPermission() {
return Jenkins.SYSTEM_READ;
//This link is displayed to any user with permission to access the management menu
return Jenkins.READ;
}
@Override
public String getUrlName() {
return "systemInfo";
}
@Nonnull
@Override
public Category getCategory() {
return Category.STATUS;
}
}
......@@ -28,6 +28,8 @@ import hudson.Extension;
import hudson.model.ManagementLink;
import org.jenkinsci.Symbol;
import javax.annotation.Nonnull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
......@@ -52,4 +54,10 @@ public class SystemLogLink extends ManagementLink {
public String getUrlName() {
return "log";
}
@Nonnull
@Override
public Category getCategory() {
return Category.STATUS;
}
}
......@@ -106,7 +106,7 @@ public abstract class FingerprintFacet implements ExtensionPoint {
/**
* Returns whether Fingerprint deletion has been blocked by this Facet.
* Returns false by default. Override the default to block the deletion of the associated Fingerprint.
* @since TODO
* @since 2.223
*/
public boolean isFingerprintDeletionBlocked() {
return false;
......
......@@ -1061,7 +1061,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Retrieve the proxy configuration.
*
* @return the proxy configuration
* @since TODO
* @since 2.205
*/
@CheckForNull
public ProxyConfiguration getProxy() {
......@@ -1072,7 +1072,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Set the proxy configuration.
*
* @param proxy the proxy to set
* @since TODO
* @since 2.205
*/
public void setProxy(@CheckForNull ProxyConfiguration proxy) {
this.proxy = proxy;
......@@ -1373,10 +1373,32 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
return updateCenter;
}
/**
* If usage statistics has been disabled
*
* @since 2.226
*/
@CheckForNull
public Boolean isNoUsageStatistics() {
return noUsageStatistics;
}
/**
* If usage statistics are being collected
*
* @return {@code true} if usage statistics should be collected.
* Defaults to {@code true} when {@link #noUsageStatistics} is not set.
* @since 2.226
*/
public boolean isUsageStatisticsCollected() {
return noUsageStatistics==null || !noUsageStatistics;
}
/**
* Sets the noUsageStatistics flag
*
* @since 2.226
*/
public void setNoUsageStatistics(Boolean noUsageStatistics) throws IOException {
this.noUsageStatistics = noUsageStatistics;
save();
......@@ -1772,7 +1794,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Gets just the immediate children of {@link Jenkins} based on supplied predicate.
*
* @see #getAllItems(Class)
* @since TODO
* @since 2.221
*/
public List<TopLevelItem> getItems(Predicate<TopLevelItem> pred) {
List<TopLevelItem> viewableItems = new ArrayList<>();
......@@ -2582,7 +2604,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
return securityRealm;
}
public void setSecurityRealm(SecurityRealm securityRealm) {
/**
* Sets a security realm.
* @param securityRealm Security realm to set.
* If {@code null}, {@link SecurityRealm#NO_AUTHENTICATION} will be set.
*/
public void setSecurityRealm(@CheckForNull SecurityRealm securityRealm) {
if(securityRealm==null)
securityRealm= SecurityRealm.NO_AUTHENTICATION;
this.useSecurity = true;
......@@ -2612,7 +2639,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
saveQuietly();
}
public void setAuthorizationStrategy(AuthorizationStrategy a) {
/**
* Sets a new authorization strategy.
* @param a Authorization strategy to set.
* If {@code null}, {@link AuthorizationStrategy#UNSECURED} will be set
*/
public void setAuthorizationStrategy(@CheckForNull AuthorizationStrategy a) {
if (a == null)
a = AuthorizationStrategy.UNSECURED;
useSecurity = true;
......@@ -4705,6 +4737,22 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
public List<ManagementLink> getManagementLinks() {
return ManagementLink.all();
}
// for Jelly
@Restricted(NoExternalUse.class)
public Map<ManagementLink.Category, List<ManagementLink>> getCategorizedManagementLinks() {
Map<ManagementLink.Category, List<ManagementLink>> byCategory = new TreeMap<>();
for (ManagementLink link : ManagementLink.all()) {
if (link.getIconFileName() == null) {
continue;
}
if (!Jenkins.get().hasPermission(link.getRequiredPermission())) {
continue;
}
byCategory.computeIfAbsent(link.getCategory(), c -> new ArrayList<>()).add(link);
}
return byCategory;
}
/**
* If set, a currently active setup wizard - e.g. installation
......@@ -5147,6 +5195,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
}
@Restricted(NoExternalUse.class)
public boolean shouldShowStackTrace() {
// Used by oops.jelly
return Boolean.getBoolean(Jenkins.class.getName() + ".SHOW_STACK_TRACE");
}
/**
* Hash of {@link #VERSION}.
*/
......@@ -5233,6 +5287,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".workspacesDir";
/**
* Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created.
*/
......@@ -5255,7 +5310,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* <p>This permission is disabled by default and support for it considered experimental.
* Administrators can set the system property {@code jenkins.security.ManagePermission} to enable it.</p>
*
* @since TODO
* @since 2.222
*/
@Restricted(Beta.class)
public static final Permission MANAGE = new Permission(PERMISSIONS, "Manage",
......
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright (c) 2020 CloudBees, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -41,6 +42,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -122,8 +126,7 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
catch (AuthenticationException | AccessDeniedException ex) {
handleException(request, response, chain, ex);
} catch (ServletException ex) {
if (ex.getRootCause() instanceof AuthenticationException
|| ex.getRootCause() instanceof AccessDeniedException) {
if (ex.getRootCause() instanceof AuthenticationException || ex.getRootCause() instanceof AccessDeniedException) {
handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
}
else {
......@@ -235,4 +238,5 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
public void destroy() {
}
}
\ No newline at end of file
}
......@@ -145,7 +145,7 @@ public abstract class Telemetry implements ExtensionPoint {
* Returns true iff we're in the time period during which this is supposed to collect data.
* @return true iff we're in the time period during which this is supposed to collect data
*
* @since TODO
* @since 2.202
*/
public boolean isActivePeriod() {
LocalDate now = LocalDate.now();
......
......@@ -106,7 +106,8 @@ public class MissingClassTelemetry extends Telemetry {
//hundreds when a job is created
{"org.codehaus.groovy.control.ClassNodeResolver", "tryAsLoaderClassOrScript"},
{"org.kohsuke.stapler.RequestImpl$TypePair", "convertJSON"},
{"net.bull.javamelody.FilterContext", "isMojarraAvailable"} // JENKINS-60725
{"net.bull.javamelody.FilterContext", "isMojarraAvailable"}, // JENKINS-60725
{"hudson.remoting.RemoteClassLoader$ClassLoaderProxy", "fetch3"} // JENKINS-61521
};
......
......@@ -39,6 +39,7 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.verb.POST;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.logging.Level;
......@@ -73,6 +74,12 @@ public class GlobalToolConfiguration extends ManagementLink {
return Jenkins.ADMINISTER;
}
@Nonnull
@Override
public Category getCategory() {
return Category.CONFIGURATION;
}
@POST
public synchronized void doConfigure(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, Descriptor.FormException {
boolean result = configure(req, req.getSubmittedForm());
......
......@@ -39,7 +39,7 @@ import org.kohsuke.accmod.restrictions.Beta;
/**
* One WebSocket connection.
* @see WebSockets
* @since TODO
* @since 2.216
*/
@Restricted(Beta.class)
public abstract class WebSocketSession {
......
......@@ -41,7 +41,7 @@ import org.kohsuke.stapler.Stapler;
/**
* Support for serving WebSocket responses.
* @since TODO
* @since 2.216
*/
@Restricted(Beta.class)
@Extension
......
......@@ -4,7 +4,24 @@
width: 15em;
}
time {
white-space: nowrap;
}
#filter-container {
margin: 1em;
text-align: right;
}
.plugin-manager__categories {
margin-top: 0.25em;
}
.plugin-manager__category-label {
display: inline-block;
border: 1px solid #ccc;
background-color: #eee;
border-radius: 4px;
font-size: 0.75rem;
padding: 0 0.5rem;
margin: 0 0.25rem 0.25rem 0;
}
......@@ -7,27 +7,6 @@ function checkPluginsWithoutWarnings() {
}
}
}
function showhideCategories(hdr,on) {
var table = hdr.parentNode.parentNode.parentNode,
newDisplay = on ? '' : 'none',
nameList = new Array(), id;
for (var i = 1; i < table.rows.length; i++) {
if (on || table.rows[i].cells.length == 1)
table.rows[i].style.display = newDisplay;
else {
// Hide duplicate rows for a plugin:version when not viewing by-category
id = table.rows[i].cells[1].getAttribute('data-id');
if (nameList[id] == 1) table.rows[i].style.display = 'none';
nameList[id] = 1;
}
}
}
function showhideCategory(col) {
var row = col.parentNode.nextSibling;
var newDisplay = row && row.style.display == 'none' ? '' : 'none';
for (; row && row.cells.length > 1; row = row.nextSibling)
row.style.display = newDisplay;
}
Behaviour.specify("#filter-box", '_table', 0, function(e) {
function applyFilter() {
......
......@@ -26,75 +26,83 @@ THE SOFTWARE.
List of available new plugins
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout title="${%Update Center}" permission="${app.ADMINISTER}">
<st:include page="sidepanel.jelly"/>
<l:main-panel>
<local:tabBar page="advanced" xmlns:local="/hudson/PluginManager" />
<div class="pane-frame">
<table id="pluginsAdv" class="pane" style="margin-top:0; border-top:none">
<tr style="border-top:none; white-space: normal">
<td>
<h1>${%HTTP Proxy Configuration}</h1>
<f:form method="post" action="proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${it.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</j:scope>
<f:block>
<f:submit />
</f:block>
</f:form>
<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 title="${%Update Center}" permission="${app.ADMINISTER}">
<st:include page="sidepanel.jelly"/>
<l:main-panel>
<local:tabBar page="advanced" xmlns:local="/hudson/PluginManager"/>
<div class="pane-frame">
<div id="pluginsAdv" class="pane" style="margin-top:0; border-top:none">
<section>
<h2>${%HTTP Proxy Configuration}</h2>
<f:form method="post" action="proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${it.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}"/>
</j:scope>
<f:block>
<f:submit/>
</f:block>
</f:form>
</section>
<h1>${%Upload Plugin}</h1>
<f:form method="post" action="uploadPlugin" name="uploadPlugin" enctype="multipart/form-data">
<f:block>
<div style="margin-bottom: 1em;">
${%uploadtext}
</div>
</f:block>
<f:block>
<!-- @size is for other browsers, @style is for IE -->
${%File}: <input type="file" name="name" class="setting-input" style="width:80%" size='40' />
</f:block>
<f:block>
<f:submit value="${%Upload}" />
</f:block>
</f:form>
<section>
<h2>${%Upload Plugin}</h2>
<f:form method="post" action="uploadPlugin" name="uploadPlugin" enctype="multipart/form-data">
<f:block>
<div style="margin-bottom: 1em;">
${%uploadtext}
</div>
</f:block>
<f:block>
<!-- @size is for other browsers, @style is for IE -->
${%File}:
<input type="file" name="name" class="setting-input" style="width:80%" size='40'/>
</f:block>
<f:block>
<f:submit value="${%Upload}"/>
</f:block>
</f:form>
</section>
<h1>${%Update Site}</h1>
<f:form method="post" action="siteConfigure" name="siteConfigure">
<f:entry title="${%URL}" >
<f:textbox name="site" value="${app.updateCenter.getSite(app.updateCenter.ID_DEFAULT).url}" />
</f:entry>
<f:block>
<f:submit />
</f:block>
</f:form>
<j:set var="hasNonDefault" value="${false}"/>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<j:set var="hasNonDefault" value="${true}"/>
</j:if>
</j:forEach>
<j:if test="${hasNonDefault}">
<h2>${%Other Sites}</h2>
<ul>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<li>${site.url}</li>
<section>
<h2>${%Update Site}</h2>
<f:form method="post" action="siteConfigure" name="siteConfigure">
<f:entry title="${%URL}">
<f:textbox name="site"
value="${app.updateCenter.getSite(app.updateCenter.ID_DEFAULT).url}"/>
</f:entry>
<f:block>
<f:submit/>
</f:block>
</f:form>
<j:set var="hasNonDefault" value="${false}"/>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<j:set var="hasNonDefault" value="${true}"/>
</j:if>
</j:forEach>
</section>
<j:if test="${hasNonDefault}">
<section>
<h2 class="h4">${%Other Sites}</h2>
<ul>
<j:forEach var="site" items="${app.updateCenter.sites}">
<j:if test="${site.id != app.updateCenter.ID_DEFAULT}">
<li>${site.url}</li>
</j:if>
</j:forEach>
</ul>
</section>
</j:if>
</j:forEach>
</ul>
</j:if>
</td>
</tr>
</table>
</div>
<div align="right" style="margin-top:1em">
<st:include page="check.jelly"/>
</div>
</l:main-panel>
</l:layout>
</div>
</div>
<div align="right" style="margin-top:1em">
<st:include page="check.jelly"/>
</div>
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:table page="available" list="${app.updateCenter.categorizedAvailables}" xmlns:local="/hudson/PluginManager" />
<local:table page="available" list="${app.updateCenter.availables}" xmlns:local="/hudson/PluginManager" />
</j:jelly>
......@@ -102,6 +102,20 @@ THE SOFTWARE.
</j:otherwise>
</j:choose>
</div>
<j:if test="${!p.getActiveWarnings().isEmpty()}">
<div class="alert alert-danger">${%securityWarning}
<ul>
<j:forEach var="warning" items="${p.getActiveWarnings()}">
<li><a href="${warning.url}" target="_blank">${warning.message}</a></li>
</j:forEach>
</ul>
</div>
</j:if>
<j:if test="${it.hasAdoptThisPluginLabel(p)}">
<div class="alert alert-warning">
${%adoptThisPlugin}
</div>
</j:if>
</td>
<td class="center pane" style="white-space:normal">
<a href="plugin/${p.shortName}/thirdPartyLicenses">
......@@ -152,16 +166,22 @@ THE SOFTWARE.
</tr>
</j:forEach>
<!-- failed ones -->
<j:forEach var="p" items="${it.pluginManager.failedPlugins}">
<j:forEach var="p" items="${it.failedPlugins}">
<tr class="hoverback">
<td class="pane" />
<td class="center pane enable">
<input type="checkbox" disabled="disabled"/>
</td>
<td class="pane">
<h4 class="error">Failed : ${p.name}</h4>
<div style="padding-left: 1em">
<pre>${p.exceptionString}</pre>
<div>
${p.name}
</div>
<div class="alert alert-danger">
<pre>${p.cause.message}</pre>
</div>
</td>
<td class="pane" />
<td class="pane" />
<td class="pane" />
</tr>
</j:forEach>
</j:otherwise>
......
......@@ -24,3 +24,8 @@ requires.restart=This Jenkins instance requires a restart. Changing the state of
detached-disable=This plugin may not be safe to disable
detached-uninstall=This plugin may not be safe to uninstall
detached-possible-dependents=Its functionality was at one point moved out of Jenkins core, and another plugin with a core dependency predating the split may be relying on it implicitly.
securityWarning=\
Warning: The currently installed plugin version may not be safe to use. Please review the following security notices:
adoptThisPlugin=\
<strong>This plugin is up for adoption!</strong> We are looking for new maintainers. \
Visit our <a href="https://jenkins.io/doc/developer/plugin-governance/adopt-a-plugin/" target="_blank">Adopt a Plugin</a> initiative for more information.
......@@ -30,22 +30,8 @@ THE SOFTWARE.
page: page name to be passed to local:tabBar
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j: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="${%Update Center}" permission="${app.ADMINISTER}">
<l:header>
<script type="text/javascript">
<![CDATA[
function flip(item) {
var nodes = document.getElementsByName(item.name);
for (var i = 0; i < nodes.length; ++i) {
if(nodes[i].nodeName == "INPUT") {
nodes[i].checked = item.checked;
}
}
}
]]>
</script>
</l:header>
<st:include page="sidepanel.jelly"/>
<l:main-panel>
<st:adjunct includes="hudson.PluginManager._table"/>
......@@ -62,42 +48,42 @@ THE SOFTWARE.
<table id="plugins" class="sortable pane bigtable stripped-odd">
<tr>
<th initialSortDir="${isUpdates?null:'down'}"
tooltip="${%Check to install the plugin}${isUpdates?'':'&lt;br/&gt;'+'%Click this heading to sort by category'}"
width="32" onclick="showhideCategories(this,1)">${%Install}</th>
<th initialSortDir="${isUpdates?'down':null}"
onclick="showhideCategories(this,0)">${%Name}</th>
<th onclick="showhideCategories(this,0)">${%Version}</th>
tooltip="${%Check to install the plugin}"
width="32">${%Install}</th>
<th initialSortDir="${isUpdates?'down':null}">${%Name}</th>
<th>${%Version}</th>
<th>${%Released}</th>
<j:if test="${isUpdates}"><th>${%Installed}</th></j:if>
</tr>
<j:choose>
<j:when test="${!empty(list)}">
<j:set var="lastCat" value="" />
<j:set var="cache" value="${it.createCache()}"/>
<j:forEach var="p" items="${list}">
<j:if test="${!isUpdates}">
<j:set var="thisCat" value="${p.category}" />
<j:set var="p" value="${p.plugin}" />
<j:if test="${thisCat != lastCat}">
<tr class="plugin-category" name="${thisCat}">
<td class="pane" colspan="4" onclick="showhideCategory(this)">${thisCat}</td>
</tr>
</j:if>
<j:set var="lastCat" value="${thisCat}" />
</j:if>
<j:set var="installJob" value="${isUpdates ? app.updateCenter.getJob(p) : null}" />
<j:set var="installedOk"
value="${installJob.status.success and installJob.plugin.version==p.version}" />
<tr class="${installJob!=null ? 'already-upgraded' : ''} plugin" name="${p.displayName}">
<td class="pane" align="center" data="${thisCat}_">
<td class="pane" align="center">
<input type="checkbox" name="plugin.${p.name}.${p.sourceId}"
checked="${installedOk ? 'checked' : null}"
disabled="${installedOk ? 'disabled' : null}" onClick="flip(this);"
disabled="${installedOk ? 'disabled' : null}"
data-compat-warning="${!p.isCompatible(cache)}" />
</td>
<td class="pane" data="${h.xmlEscape(p.displayName)}" data-id="${h.xmlEscape(p.name+':'+p.version)}">
<div>
<a href="${p.wiki}" target="_blank"><st:out value="${p.displayName}"/></a>
</div>
<j:if test="${!p.categories.isEmpty()}">
<div class="plugin-manager__categories">
<j:forEach var="label" items="${p.categories}">
<j:if test="${!it.isMetaLabel(label)}">
<span class="plugin-manager__category-label">
${app.updateCenter.getCategoryDisplayName(label)}
</span>
</j:if>
</j:forEach>
</div>
</j:if>
<j:if test="${p.excerpt!=null}">
<div class="excerpt"><j:out value="${p.excerpt}" /></div>
</j:if>
......@@ -119,6 +105,9 @@ THE SOFTWARE.
<j:if test="${p.isForNewerJava()}">
<div class="alert alert-danger">${%javaWarning(p.minimumJavaVersion)}</div>
</j:if>
<j:if test="${p.fixesSecurityVulnerabilities()}">
<div class="alert alert-warning">${%Applying this update will address security vulnerabilities in the currently installed version.}</div>
</j:if>
<j:if test="${!p.isNeededDependenciesCompatibleWithInstalledVersion(cache)}">
<div class="alert alert-danger">${%depCompatWarning}
<br/>${%parentDepCompatWarning}
......@@ -144,8 +133,20 @@ THE SOFTWARE.
</ul>
</div>
</j:if>
<j:if test="${it.hasAdoptThisPluginLabel(p)}">
<div class="alert alert-warning">
${%adoptThisPlugin}
</div>
</j:if>
</td>
<td class="pane"><st:out value="${p.version}" /></td>
<td class="pane" data="${p.releaseTimestamp.time}">
<j:if test="${p.releaseTimestamp != null}">
<time datetime="${h.iso8601DateTime(p.releaseTimestamp)}" tooltip="${h.localDate(p.releaseTimestamp)}">
${%ago(h.getTimeSpanString(p.releaseTimestamp))}
</time>
</j:if>
</td>
<j:if test="${isUpdates}">
<td class="pane">
<j:choose><j:when test="${p.installed.active}">
......@@ -162,7 +163,7 @@ THE SOFTWARE.
</j:when>
<j:otherwise>
<tr>
<td colspan="4" align="center">
<td colspan="5" align="center">
<div style="padding:1em">
${%No updates}
</div>
......
......@@ -45,4 +45,7 @@ depJavaWarning=\
and in turn loading this plugin will fail.
securityWarning=\
Warning: This plugin version may not be safe to use. Please review the following security notices:
ago={0} ago
adoptThisPlugin=\
<strong>This plugin is up for adoption!</strong> We are looking for new maintainers. \
Visit our <a href="https://jenkins.io/doc/developer/plugin-governance/adopt-a-plugin/" target="_blank">Adopt a Plugin</a> initiative for more information.
......@@ -21,21 +21,44 @@ 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:t="/lib/hudson" xmlns:f="/lib/form">
<st:statusCode value="503" /><!-- SERVICE NOT AVAILABLE -->
<l:layout permission="${app.ADMINISTER}">
<st:include it="${app}" page="sidepanel.jelly" />
<l:main-panel>
<h1 style="margin-top:4em">
${%Please wait while Jenkins is restarting}<span id="progress">...</span>
</h1>
<p style="color:gray;">
${%blurb}
</p>
<script>applySafeRedirector('${rootURL}/')</script>
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:x="jelly:xml">
<j:new var="h" className="hudson.Functions" />
<st:statusCode value="503" /><!-- SERVICE NOT AVAILABLE -->
<st:header name="Expires" value="0" />
<st:header name="Cache-Control" value="no-cache,no-store,must-revalidate" />
<!-- response contentType header -->
<st:contentType value="text/html;charset=UTF-8" />
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html" />
<html lang="${request.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" resURL="${resURL}">
<title>${%Restarting Jenkins}</title>
<!-- we do not want bots on this page -->
<meta name="ROBOTS" content="NOFOLLOW" />
<!-- mobile friendly layout -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="${resURL}/css/simple-page.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/simple-page.theme.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/loading.css" type="text/css" />
</head>
<body>
<div class="simple-page" role="main">
<div class="modal signup">
<div class="signupIntroDefault">
<div class="logo" />
<h1 class="loading">
${%Please wait while Jenkins is restarting}
<span>.</span><span>.</span><span>.</span>
</h1>
<p>
${%blurb}
</p>
</div>
</div>
</div>
<script src="${resURL}/scripts/loading.js" type="text/javascript" />
</body>
</html>
</j:jelly>
......@@ -23,7 +23,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form">
<l:layout permission="${app.ADMINISTER}" title="${%Install as Windows Service}">
<st:include it="${app}" page="sidepanel.jelly" />
<l:main-panel>
......
......@@ -33,6 +33,6 @@ THE SOFTWARE.
</p>
<h2>Delete a job</h2>
<p>
To programmatically delete this job, do HTTP POST to <a href="../doDelete">this URL</a>.
To programmatically delete this job, do HTTP DELETE to <a href="../">this URL</a>.
</p>
</j:jelly>
......@@ -31,7 +31,7 @@ THE SOFTWARE.
<l:layout type="one-column" permission="${it.EXTENDED_READ}" title="${%Config(it.displayName)}">
<j:choose>
<j:when test="${app.hasPermission(it.CONFIGURE)}">
<j:when test="${it.hasPermission(it.CONFIGURE)}">
<j:set var="readOnlyMode" value="false" />
</j:when>
<j:otherwise>
......
......@@ -404,3 +404,11 @@ User.IllegalFullname="{0}" is prohibited as a full name for security reasons.
TimeZoneProperty.DisplayName=User Defined Time Zone
TimeZoneProperty.DisplayDefaultTimeZone=Default
ManagementLink.Category.CONFIGURATION=System Configuration
ManagementLink.Category.SECURITY=Security
ManagementLink.Category.STATUS=Status Information
ManagementLink.Category.TROUBLESHOOTING=Troubleshooting
ManagementLink.Category.TOOLS=Tools and Actions
ManagementLink.Category.MISC=Other
ManagementLink.Category.UNCATEGORIZED=Uncategorized
......@@ -38,7 +38,7 @@ THE SOFTWARE.
<form method="post" action="createItem" name="createItem" id="createItem">
<div class="header">
<div class="add-item-name">
<label for="name">${%ItemName.label}</label>
<label for="name" class="h3">${%ItemName.label}</label>
<input name="name" id="name" data-valid="false" type="text" tabindex="0" />
<div class="input-help">&#187; ${%ItemName.help}</div>
<div id="itemname-required" class="input-validation-message input-message-disabled">&#187; ${%ItemName.validation.required}</div>
......
......@@ -21,26 +21,24 @@ 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:x="jelly:xml" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:x="jelly:xml">
<j:new var="h" className="hudson.Functions" />
<st:statusCode value="503"/><!-- SERVICE NOT AVAILABLE -->
<st:setHeader name="Expires" value="0"/>
<st:setHeader name="Cache-Control" value="no-cache,no-store,must-revalidate"/>
<st:statusCode value="503" /><!-- SERVICE NOT AVAILABLE -->
<st:header name="Expires" value="0" />
<st:header name="Cache-Control" value="no-cache,no-store,must-revalidate" />
<!-- response contentType header -->
<st:contentType value="text/html;charset=UTF-8"/>
<st:contentType value="text/html;charset=UTF-8" />
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html"/>
<x:doctype name="html" />
<html lang="${request.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" resURL="${resURL}">
<title>${%Starting Jenkins}</title>
<!-- we do not want bots on this page -->
<meta name="ROBOTS" content="NOFOLLOW"/>
<meta name="ROBOTS" content="NOFOLLOW" />
<!-- mobile friendly layout -->
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="${resURL}/css/simple-page.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/simple-page.theme.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/loading.css" type="text/css" />
......@@ -49,7 +47,7 @@ THE SOFTWARE.
<div class="simple-page" role="main">
<div class="modal signup">
<div class="signupIntroDefault">
<div class="logo"></div>
<div class="logo" />
<h1 class="loading">
${%Please wait while Jenkins is getting ready to work}
<span>.</span><span>.</span><span>.</span>
......@@ -60,8 +58,7 @@ THE SOFTWARE.
</div>
</div>
</div>
<script src="${resURL}/scripts/loading.js" type="text/javascript"></script>
<script>safeRedirector(window.location.href);</script>
<script src="${resURL}/scripts/loading.js" type="text/javascript" />
</body>
</html>
</j:jelly>
......@@ -21,26 +21,24 @@ 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:x="jelly:xml" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:x="jelly:xml">
<j:new var="h" className="hudson.Functions" />
<st:statusCode value="503"/><!-- SERVICE NOT AVAILABLE -->
<st:setHeader name="Expires" value="0"/>
<st:setHeader name="Cache-Control" value="no-cache,no-store,must-revalidate"/>
<st:statusCode value="503" /><!-- SERVICE NOT AVAILABLE -->
<st:header name="Expires" value="0" />
<st:header name="Cache-Control" value="no-cache,no-store,must-revalidate" />
<!-- response contentType header -->
<st:contentType value="text/html;charset=UTF-8"/>
<st:contentType value="text/html;charset=UTF-8" />
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html"/>
<x:doctype name="html" />
<html lang="${request.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" resURL="${resURL}">
<title>${%Restarting Jenkins}</title>
<!-- we do not want bots on this page -->
<meta name="ROBOTS" content="NOFOLLOW"/>
<meta name="ROBOTS" content="NOFOLLOW" />
<!-- mobile friendly layout -->
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="${resURL}/css/simple-page.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/simple-page.theme.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/loading.css" type="text/css" />
......@@ -49,7 +47,7 @@ THE SOFTWARE.
<div class="simple-page" role="main">
<div class="modal signup">
<div class="signupIntroDefault">
<div class="logo"></div>
<div class="logo" />
<h1 class="loading">
${%Please wait while Jenkins is restarting}
<span>.</span><span>.</span><span>.</span>
......@@ -60,8 +58,7 @@ THE SOFTWARE.
</div>
</div>
</div>
<script src="${resURL}/scripts/loading.js" type="text/javascript"></script>
<script>safeRedirector(window.location.href);</script>
<script src="${resURL}/scripts/loading.js" type="text/javascript" />
</body>
</html>
</j:jelly>
......@@ -26,7 +26,6 @@ THE SOFTWARE.
<l:layout norefresh="true" type="full-screen" title="${it.displayName}">
<l:main-panel>
<link rel="stylesheet" href="${resURL}/css/google-fonts/roboto/css/roboto.css" type="text/css" />
<style type="text/css">
#configure-instance {
padding: 20px 100px 20px 100px;
......@@ -38,7 +37,6 @@ THE SOFTWARE.
}
#configure-instance h1 {
font-family: 'roboto', sans-serif;
font-size: 48px;
margin-top: 30px;
font-weight: 500;
......
......@@ -3,9 +3,7 @@
<l:layout type="full-screen" title="${it.displayName}">
<l:main-panel>
<link rel="stylesheet" href="${resURL}/css/google-fonts/roboto/css/roboto.css" type="text/css" />
<style type="text/css">
#create-admin-user {
padding: 20px 100px 20px 100px;
margin: 8px;
......@@ -16,7 +14,6 @@
}
#create-admin-user h1 {
font-family: 'roboto', sans-serif;
font-size: 48px;
line-height: 48px;
margin-top: 30px;
......
......@@ -28,7 +28,6 @@ THE SOFTWARE.
<div id="visible-am-container">
<a id="visible-am-button" href="#" onclick="toggleVisibleAmList(event)" title="${%tooltip(it.activeAdministrativeMonitorsCount)}">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-social-symbol.svg#ic_notifications_24px" />
<span class="hidden-xs hidden-sm">${%monitors(it.activeAdministrativeMonitorsCount)}</span>
<span class="am-monitor__count">
${it.activeAdministrativeMonitorsCount}
</span>
......
tooltip=There are {0} active administrative monitors.
monitors={0,choice,0#monitors|1#monitor|1<monitors}
......@@ -22,4 +22,3 @@
Manage\ Jenkins=Jenkins verwalten
tooltip={0,choice,0#Keine Administrator-Warnungen sind|1#{0} Administrator-Warnung ist|1<{0} Administrator-Warnungen sind} aktiv.
monitors={0,choice,0#Warnungen|1#Warnung|1<Warnungen}
......@@ -14,8 +14,8 @@
height: 20px;
min-width: 20px;
color:#F94B4E;
background-color: #fff;
color:#fff;
background-color: #dc3545;
font-weight: bold;
border-radius: 4px;
......
......@@ -40,12 +40,8 @@ l.layout(norefresh:true, permission:app.ADMINISTER, title:my.displayName) {
}
st.adjunct(includes: "lib.form.confirm")
} else {
p {
_("There are no cloud implementations for dynamically allocated agents installed.")
a(href: rootURL + "/pluginManager/available") {
_("Go to plugin manager.")
}
}
p(_("There are no cloud implementations for dynamically allocated agents installed. "))
a(href: rootURL + "/pluginManager/available", _("Go to plugin manager."))
}
}
}
......@@ -29,7 +29,6 @@ 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 title="${%Manage Jenkins}" permissions="${app.MANAGE_AND_SYSTEM_READ}">
<l:header>
<link rel="stylesheet" href="${resURL}/bootstrap/css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/css/font-awesome/css/font-awesome.min.css" />
</l:header>
......@@ -47,49 +46,55 @@ THE SOFTWARE.
<st:include page="downgrade.jelly" />
</div>
<j:forEach var="m" items="${it.managementLinks}">
<l:hasPermission permission="${m.requiredPermission}">
<j:set var="icon" value="${m.iconClassName != null ? m.iconClassName : m.iconFileName}" />
<j:if test="${icon!=null}">
<div class="manage-option">
<j:set var="iconUrl" value="${icon.startsWith('/') ? resURL+icon : imagesURL + '/48x48/' + icon}" />
<j:set var="alt" value="${icon.replaceAll('\\d*\\.[^.]+$', '')}"/>
${taskTags!=null and attrs.contextMenu!='false' ? taskTags.add(m.urlName, iconUrl, m.displayName, m.requiresPOST, m.requiresConfirmation) : null}
<j:choose>
<j:when test="${m.requiresConfirmation}">
<l:confirmationLink href="${m.urlName}" post="${m.requiresPOST}" message="${%are.you.sure(m.displayName)}">
<img class="icon" src="${iconUrl}" />
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</l:confirmationLink>
</j:when>
<j:when test="${m.requiresPOST}">
<f:link href="${m.urlName}" post="${m.requiresPOST}">
<img class="icon" src="${iconUrl}" alt="[${%alt}]"/>
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</f:link>
</j:when>
<j:otherwise>
<a href="${m.urlName}" title="${m.displayName}">
<img class="icon" src="${iconUrl}" alt="manage-option"/>
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</a>
</j:otherwise>
</j:choose>
</div>
</j:if>
</l:hasPermission>
<j:forEach var="category" items="${it.categorizedManagementLinks.entrySet()}">
<section class="manage-page__category">
<h2>${category.key.label}</h2>
<div class="manage-page__row">
<j:forEach var="m" items="${category.value}">
<j:set var="icon" value="${m.iconClassName != null ? m.iconClassName : m.iconFileName}" />
<j:if test="${icon!=null}">
<div class="manage-option manage-page__column">
<j:set var="iconUrl" value="${icon.startsWith('/') ? resURL+icon : imagesURL + '/48x48/' + icon}" />
<j:set var="alt" value="${icon.replaceAll('\\d*\\.[^.]+$', '')}"/>
${taskTags!=null and attrs.contextMenu!='false' ? taskTags.add(m.urlName, iconUrl, m.displayName, m.requiresPOST, m.requiresConfirmation) : null}
<j:choose>
<j:when test="${m.requiresConfirmation}">
<l:confirmationLink href="${m.urlName}" post="${m.requiresPOST}" message="${%are.you.sure(m.displayName)}">
<img class="icon" src="${iconUrl}" />
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</l:confirmationLink>
</j:when>
<j:when test="${m.requiresPOST}">
<f:link href="${m.urlName}" post="${m.requiresPOST}">
<img class="icon" src="${iconUrl}" alt="[${%alt}]"/>
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</f:link>
</j:when>
<j:otherwise>
<a href="${m.urlName}" title="${m.displayName}">
<img class="icon" src="${iconUrl}" alt="manage-option"/>
<dl>
<dt>${m.displayName}</dt>
<dd><j:out value="${m.description}"/></dd>
<dd><st:include it="${m}" page="info.jelly" optional="true"/></dd>
</dl>
</a>
</j:otherwise>
</j:choose>
</div>
</j:if>
</j:forEach>
</div>
</section>
</j:forEach>
</l:main-panel>
......
......@@ -27,32 +27,22 @@ THE SOFTWARE.
<!--
This is the page designated by web.xml and UncaughtExceptionHandler to process an exception thrown by us.
-->
<st:statusCode value="500" />
<l:layout title="Jenkins">
<st:statusCode value="${response.getStatus()}" />
<l:layout title="Jenkins" type="one-column">
<l:header />
<l:side-panel>
<l:task href="https://jenkins.io/" icon="icon-next icon-md" title="${%Jenkins project}"/>
<l:task href="https://jenkins.io/redirect/report-an-issue" icon="icon-gear2 icon-md" title="${%Bug tracker}"/>
<l:task href="https://jenkins.io/redirect/mailing-lists" icon="icon-search icon-md" title="${%Mailing Lists}"/>
<l:task href="https://twitter.com/jenkinsci" icon="icon-user icon-md" title="${%Twitter: @jenkinsci}"/>
</l:side-panel>
<l:main-panel>
<h1 style="text-align: center">
<img src="${imagesURL}/rage.png" height="179" width="154"/> <span style="font-size:50px"><st:nbsp/>${%Oops!}</span>
</h1>
<div id="error-description">
<p>
${%problemHappened}
${%checkJIRA}
${%vote}
${%pleaseReport}
${%stackTracePlease}
${%checkML}
</p>
<h2>${%Stack trace}</h2>
<pre style="margin:2em; clear:both">${h.printThrowable(request.getAttribute('javax.servlet.error.exception'))}</pre>
<h1 style="text-align: center">
<img src="${imagesURL}/rage.png" height="179" width="154"/> <span style="font-size:50px"><st:nbsp/>${%Oops!}</span>
</h1>
<div id="error-description">
<h2 style="text-align: center">${%problemHappened}</h2>
<p style="text-align: center">Logging ID=${request.getAttribute('jenkins.exception.id')}</p>
</div>
<j:if test="${app.shouldShowStackTrace()}">
<p>${%checkJIRA} ${%vote} ${%pleaseReport} ${%stackTracePlease} ${%checkML}</p>
<h2>${%Stack trace}</h2>
<pre style="margin:2em; clear:both">${h.printThrowable(request.getAttribute('javax.servlet.error.exception'))}</pre>
</j:if>
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
</j:jelly>
......@@ -27,13 +27,17 @@ 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">
<l:layout permission="${app.SYSTEM_READ}" title="${%System Information}">
<l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%System Information}">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<h1>${%System Properties}</h1>
<t:propertyTable items="${h.systemProperties}" />
<h1>${%Environment Variables}</h1>
<t:propertyTable items="${h.envVars}" />
<l:hasPermission permission="${app.SYSTEM_READ}">
<h1>${%System Properties}</h1>
<t:propertyTable items="${h.systemProperties}" />
</l:hasPermission>
<l:hasPermission permission="${app.SYSTEM_READ}">
<h1>${%Environment Variables}</h1>
<t:propertyTable items="${h.envVars}" />
</l:hasPermission>
<h1>${%Plugins}</h1>
<table class="pane sortable bigtable">
<j:choose>
......
# The MIT License
#
#
# Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -52,3 +52,5 @@ ParameterizedJobMixIn.build_now=Lancer un build
BlockedBecauseOfBuildInProgress.shortDescription=Le build #{0} est d\u00e9j\u00e0 en cours {1}
BlockedBecauseOfBuildInProgress.ETA=\ (fin pr\u00e9vue \u00e0 : {0})
BuildDiscarderProperty.displayName=Supprimer les anciens builds
NewViewLink.NewView=Cr\u00E9er une Vue
......@@ -34,7 +34,7 @@
.token-list .token-list-item {
min-height: inherit;
padding: 8px 10px;
font-size: 13px;
font-size: 0.875rem;
line-height: 26px;
}
.token-list .token-list-item.legacy-token {
......@@ -56,7 +56,7 @@
}
.token-list .token-list-item .token-creation{
margin-left: 5px;
font-size: 90%;
font-size: 0.75rem;
}
.token-list .token-list-item .token-creation.age-ok{
color: #6d7680;
......@@ -74,11 +74,11 @@
float: right;
}
.token-list .token-list-item .token-use-counter{
font-size: 90%;
font-size: 0.75rem;
color: #6d7680;
}
.token-list .token-list-item .no-statistics{
font-size: 90%;
font-size: 0.75rem;
color: #6d7680;
}
.token-list .token-list-item .token-revoke{
......@@ -105,8 +105,8 @@
width: 40%;
display: none;
font-family: monospace;
font-size: 14px;
margin-right: 2px;
font-size: 1em;
}
.token-list .token-list-new-item .new-token-value.visible{
display: inline-block;
......
......@@ -47,3 +47,4 @@ ResourceDomainConfiguration.IOException=Failed to connect: {0}
ResourceDomainConfiguration.Invalid=Not a valid URL.
ResourceDomainConfiguration.SameAsJenkinsRoot=Cannot use the same host name for both Jenkins root URL and resource root URL.
ResourceDomainConfiguration.SameAsCurrent=You are currently accessing Jenkins through a URL similar to the proposed resource root URL. Saving this URL might remove your access to Jenkins.
......@@ -23,8 +23,8 @@
pluginTitle = {0} {1}
coreTitle = Jenkins {0} core and libraries
blurb = Warnings have been published for the following currently installed components.
more = Additional warnings are hidden due to the current security configuration
blurb = Warnings have been published for the following currently installed components:
more = Additional warnings are hidden due to the current security configuration.
pluginManager.link = Go to plugin manager
......
......@@ -40,10 +40,15 @@ Behaviour.specify("INPUT.apply-button", 'apply', 0, function (e) {
// otherwise this is possibly an error from the server, so we need to render the whole content.
var doc = target.contentDocument || target.contentWindow.document;
var error = doc.getElementById('error-description');
var r = YAHOO.util.Dom.getClientRegion();
var contentHeight = r.height/5;
var contentWidth = r.width/2;
if (!error) {
// fallback if it's not a regular error dialog from oops.jelly: use the entire body
error = Element('div', {id: 'error-description'});
error.appendChild(doc.getElementsByTagName('body')[0]);
contentHeight = r.height*3/4;
contentWidth = r.width*3/4;
}
if (oldError = $('error-description')) {
......@@ -52,11 +57,8 @@ Behaviour.specify("INPUT.apply-button", 'apply', 0, function (e) {
}
$(containerId).appendChild(error);
var r = YAHOO.util.Dom.getClientRegion();
var contentHeight = r.height*3/4;
var dialogStyleHeight = contentHeight+40;
var contentWidth = r.width*3/4;
var dialogStyleWidth = contentWidth+20;
$(containerId).style.height = contentHeight+"px";
......
......@@ -36,9 +36,14 @@ THE SOFTWARE.
The name of the enum to set as default value for the first configuration.
</st:attribute>
</st:documentation>
<select class="setting-input" name="${field}">
<j:forEach var="it" items="${descriptor.getPropertyType(instance,field).enumConstants}">
<f:option value="${it.name()}" selected="${instance != null ? it==instance[field] : it.name()==default}">
<j:if test="${attrs.field==null}">
<j:set target="${attrs}" property="field" value="${entry.field}" />
</j:if>
<select class="setting-input" name="${attrs.field}">
<j:forEach var="it" items="${descriptor.getPropertyType(instance,attrs.field).getEnumConstants()}">
<f:option value="${it.name()}" selected="${instance[attrs.field]!=null ? it==instance[attrs.field] : it.name()==attrs.default}">
<d:invokeBody />
</f:option>
</j:forEach>
......
......@@ -98,12 +98,12 @@ THE SOFTWARE.
<j:if test="${value != null &amp;&amp; !empty(value.toString()) &amp;&amp; (value.toString().codePointAt(0) == 10 || value.toString().codePointAt(0) == 13)}"><j:whitespace>&#10;</j:whitespace></j:if>
<st:out value="${value}" />
</textarea>
<j:if test="${customizedFields != null and attrs.field != null and value != default}">
<j:mute>${customizedFields.add(name)}</j:mute>
</j:if>
<!-- resize handle -->
<div class="textarea-handle"/>
</f:possibleReadOnlyField>
<j:if test="${customizedFields != null and attrs.field != null and value != default}">
<j:mute>${customizedFields.add(name)}</j:mute>
</j:if>
<!-- resize handle -->
<div class="textarea-handle"/>
<j:if test="${attrs.previewEndpoint!=null}">
<div class="textarea-preview-container">
<j:if test="${attrs.previewEndpoint == '/markupFormatter/previewDescription'}">
......
......@@ -18,19 +18,10 @@
min-height: 2.5rem;
font-size: 1rem;
line-height: 1.5;
font-family: Roboto, Helvetica, Arial, sans-serif;
padding: 0 1.25rem;
background-color: #F8F8F8;
}
/*
Revert the font change to the options menu.
It's used all over the application and having Roboto may make it clash with other UI elements
*/
#breadcrumb-menu {
font-family: Helvetica, Arial, sans-serif;
}
#breadcrumbs {
list-style-type: none;
margin: 0;
......
......@@ -28,7 +28,7 @@ var breadcrumbs = (function() {
}
Event.observe(window,"load",function(){
menu = new YAHOO.widget.Menu("breadcrumb-menu", {position:"dynamic", hidedelay:1000, zIndex:2001});
menu = new YAHOO.widget.Menu("breadcrumb-menu", {position:"dynamic", hidedelay:1000, zIndex:2001, scrollincrement: 2});
});
......
......@@ -159,7 +159,6 @@ THE SOFTWARE.
<link rel="stylesheet" href="${resURL}/scripts/yui/menu/assets/skins/sam/menu.css" type="text/css" />
<!--link rel="stylesheet" href="${resURL}/scripts/yui/editor/assets/skins/sam/editor.css" type="text/css" /-->
<link rel="stylesheet" href="${resURL}/css/google-fonts/roboto/css/roboto.css" type="text/css" />
<link rel="stylesheet" href="${resURL}/jsbundles/ui-refresh-overrides.css" type="text/css" />
<l:hasPermission permission="${app.READ}">
......
......@@ -47,8 +47,6 @@
<!-- search box -->
<j:set var="searchURL" value="${h.searchURL}"/>
<form action="${searchURL}" method="get" style="position:relative;" class="no-json" name="search" role="search">
<!-- this div determines the minimum width -->
<div id="search-box-minWidth"/>
<!-- this div is used to calculate the width of the text box -->
<div id="search-box-sizer"/>
<div id="searchform">
......
......@@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci</groupId>
<artifactId>jenkins</artifactId>
<version>1.52</version>
<version>1.54</version>
<relativePath />
</parent>
......@@ -76,7 +76,7 @@ THE SOFTWARE.
</issueManagement>
<properties>
<revision>2.224</revision>
<revision>2.228</revision>
<changelist>-SNAPSHOT</changelist>
<!-- *.html files are in UTF-8, and *.properties are in iso-8859-1, so this configuration is actually incorrect,
......@@ -665,6 +665,12 @@ THE SOFTWARE.
<!-- TODO: https://issues.jenkins-ci.org/browse/JENKINS-53788 (JDK11 issue on CI) -->
<doclint>none</doclint>
<maven.javadoc.skip>true</maven.javadoc.skip>
<!-- release is needed for reliable cross compilation. It is supported
starting JDK 9, but as the only earlier version we support is 8, it would
be either compiled by javac from 8 or one from a newer compiler that
correctly handle the release flag. This will replace both source and target
once we drop support for JDK 8 that only recognize source and target. -->
<maven.compiler.release>${java.level}</maven.compiler.release>
</properties>
<activation>
<jdk>11</jdk>
......
......@@ -70,7 +70,7 @@ THE SOFTWARE.
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>2.61</version>
<version>2.62</version>
<scope>test</scope>
<exclusions>
<exclusion>
......
......@@ -55,7 +55,7 @@ public class ManagementLinkTest {
for (int i=0; ; i++) {
HtmlPage page = wc.goTo("manage");
List<?> anchors = DomNodeUtil.selectNodes(page, "//div[@class='manage-option']/a[not(@onclick)]");
List<?> anchors = DomNodeUtil.selectNodes(page, "//div[contains(@class,'manage-option')]/a[not(@onclick)]");
assertTrue(anchors.size()>=8);
if (i==anchors.size()) return; // done
......
......@@ -24,8 +24,10 @@
package hudson.security;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import hudson.model.FreeStyleProject;
import hudson.model.Item;
import hudson.model.UnprotectedRootAction;
import hudson.model.User;
import java.util.Collection;
import java.util.Collections;
......@@ -39,6 +41,9 @@ import org.junit.rules.ExpectedException;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.MockAuthorizationStrategy;
import org.jvnet.hudson.test.TestExtension;
import javax.annotation.CheckForNull;
public class ACLTest {
......@@ -121,6 +126,29 @@ public class ACLTest {
r.jenkins.getACL().checkAnyPermission();
}
@Test
@Issue("JENKINS-61465")
public void checkAnyPermissionOnNonAccessControlled() throws Exception {
expectedException = ExpectedException.none();
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy()
.grant(Jenkins.READ).everywhere().toEveryone());
JenkinsRule.WebClient wc = r.createWebClient();
try {
wc.goTo("either");
fail();
} catch (FailingHttpStatusCodeException ex) {
assertEquals(403, ex.getStatusCode());
}
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy()
.grant(Jenkins.ADMINISTER).everywhere().toEveryone());
wc.goTo("either"); // expected to work
}
private static class DoNotBotherMe extends AuthorizationStrategy {
@Override
......@@ -140,4 +168,26 @@ public class ACLTest {
}
@TestExtension
public static class EitherPermission implements UnprotectedRootAction {
@CheckForNull
@Override
public String getIconFileName() {
return null;
}
@CheckForNull
@Override
public String getDisplayName() {
return null;
}
@CheckForNull
@Override
public String getUrlName() {
return "either";
}
}
}
......@@ -22,7 +22,6 @@ import hudson.cli.DisablePluginCommand;
import hudson.model.Descriptor;
import hudson.model.MyView;
import hudson.model.View;
import hudson.model.labels.LabelAtom;
import hudson.tasks.Shell;
import static org.hamcrest.Matchers.containsString;
......@@ -30,7 +29,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
......@@ -179,6 +178,21 @@ public class JenkinsManagePermissionTest {
updated.getWebResponse(), hasResponseCode(HttpURLConnection.HTTP_OK));
}
@Issue("JENKINS-61457")
@Test
public void managePermissionCanChangeUsageStatistics() throws Exception {
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy()
.grant(Jenkins.MANAGE, Jenkins.READ).everywhere().toEveryone());
boolean previousValue = j.jenkins.isUsageStatisticsCollected();
HtmlForm form = j.createWebClient().goTo("configure").getFormByName("config");
form.getInputByName("_.usageStatisticsCollected").setChecked(!previousValue);
j.submit(form);
assertThat("Can set UsageStatistics", j.jenkins.isUsageStatisticsCollected(), not(previousValue));
}
private String getShell() {
Descriptor descriptorByName = j.getInstance().getDescriptorByName("hudson.tasks.Shell");
return ((Shell.DescriptorImpl) descriptorByName).getShell();
......
/*
* The MIT License
*
* Copyright (c) 2020, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.model.FreeStyleProject;
import hudson.model.ItemGroup;
import hudson.model.TopLevelItemDescriptor;
import hudson.model.User;
import jenkins.model.Jenkins;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.MockAuthorizationStrategy;
import org.xml.sax.SAXException;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
public class StackTraceSuppressionTest {
@Rule
public JenkinsRule j = new JenkinsRule();
@Before
public void setup() {
clearProperties();
}
@After
public void teardown() {
clearProperties();
}
private void clearProperties() {
System.clearProperty("jenkins.model.Jenkins.SHOW_STACK_TRACE");
}
@Test
public void authenticationManageException() throws Exception {
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.READ).everywhere().to("alice"));
User alice = User.getById("alice", true);
JenkinsRule.WebClient wc = j.createWebClient();
wc.login(alice.getId());
wc.setThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.goTo("manage");
String content = page.getWebResponse().getContentAsString();
assertThat(content, containsString(alice.getId() + " is missing a permission"));
assertThat(content, not(containsString("Caused by")));
}
@Test
public void nonexistentAdjunct() throws Exception {
/* This test belongs in Stapler but it's easy to put it together here.
This test is based upon Stapler throwing an exception for this broken request.
If Stapler is improved to better handle this error, this test may erroneously fail. */
String relativePath = "adjuncts/40331c1bldu3i%3b//'%3b//\"%3b//%25>%3f>uezm3<script>alert(1)</script>foo/org/kohsuke/stapler/jquery/jquery.full.js";
String detailString = "AdjunctManager.doDynamic";
checkSuppressedStack(relativePath, detailString);
}
@Test
public void nonexistentAdjunctShowsTrace() throws Exception {
/* This test belongs in Stapler but it's easy to put it together here.
This test is based upon Stapler throwing an exception for this broken request.
If Stapler is improved to better handle this error, this test may erroneously fail. */
String relativePath = "adjuncts/40331c1bldu3i%3b//'%3b//\"%3b//%25>%3f>uezm3<script>alert(1)</script>foo/org/kohsuke/stapler/jquery/jquery.full.js";
String detailString = "AdjunctManager.doDynamic";
checkDisplayedStackTrace(relativePath, detailString);
}
@Test
public void exception() throws Exception {
/* This test is based upon an incomplete / incorrect project implementation
throwing an uncaught exception.
If Jenkins is improved to better handle this error, this test may erroneously fail. */
FreeStyleProject projectError = createBrokenProject();
String relativePath = "job/" + projectError.getName() + "/configure";
String detailString = "JellyTagException";
checkSuppressedStack(relativePath, detailString);
}
@Test
public void exceptionShowsTrace() throws Exception {
/* This test is based upon an incomplete / incorrect project implementation
throwing an uncaught exception.
If Jenkins is improved to better handle this error, this test may erroneously fail. */
FreeStyleProject projectError = createBrokenProject();
String relativePath = "job/" + projectError.getName() + "/configure";
String detailString = "JellyTagException";
checkDisplayedStackTrace(relativePath, detailString);
}
@Test
public void exceptionEndpoint() throws Exception {
/* This test is based upon a testing endpoint that really shouldn't exist in production code.
If Jenkins is improved to eliminate this endpoint, this test may erroneously fail. */
String relativePath = "exception";
String detailString = "Jenkins.doException";
checkSuppressedStack(relativePath, detailString);
}
@Test
public void exceptionEndpointShowsTrace() throws Exception {
/* This test is based upon a testing endpoint that really shouldn't exist in production code.
If Jenkins is improved to eliminate this endpoint, this test may erroneously fail. */
String relativePath = "exception";
String detailString = "Jenkins.doException";
checkDisplayedStackTrace(relativePath, detailString);
}
private FreeStyleProject createBrokenProject() throws IOException {
TopLevelItemDescriptor descriptor = new TopLevelItemDescriptor(FreeStyleProject.class) {
@Override
public FreeStyleProject newInstance(ItemGroup parent, String name) {
return new FreeStyleProject(parent, name) {
@Override
public void save() {
//do not need save
}
};
}
};
return (FreeStyleProject) j.jenkins.createProject(descriptor, "throw-error");
}
private void checBaseResponseContent(String content) {
assertThat(content, containsString("A problem occurred while processing the request."));
assertThat(content, containsString("Logging ID="));
assertThat(content, containsString("Oops!"));
}
private void checkSuppressedStack(String relativePath, String detailString) throws IOException, SAXException {
JenkinsRule.WebClient wc = j.createWebClient();
wc.setThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.goTo(relativePath);
String content = page.getWebResponse().getContentAsString();
checBaseResponseContent(content);
assertThat(content, not(containsString(detailString)));
}
private void checkDisplayedStackTrace(String relativePath, String detailString) throws IOException, SAXException {
System.setProperty("jenkins.model.Jenkins.SHOW_STACK_TRACE", "true");
JenkinsRule.WebClient wc = j.createWebClient();
wc.setThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.goTo(relativePath);
String content = page.getWebResponse().getContentAsString();
checBaseResponseContent(content);
assertThat(content, containsString("Stack trace"));
assertThat(content, containsString(detailString));
}
}
package lib.form;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSelect;
import hudson.Extension;
import hudson.model.BallColor;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.InvisibleAction;
import hudson.model.RootAction;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.StaplerRequest;
import static org.junit.Assert.assertEquals;
public class EnumTest {
@Rule
public JenkinsRule rule = new JenkinsRule();
@Test
public void testSelectionNoDefault() throws Exception {
HtmlForm form = getForm("noDefault");
HtmlSelect select;
select = form.getSelectByName("enum1");
assertEquals(BallColor.values().length, select.getOptionSize());
assertEquals(BallColor.YELLOW.name(), select.getDefaultValue());
select = form.getSelectByName("enum2");
assertEquals(BallColor.values().length, select.getOptionSize());
assertEquals(BallColor.values()[0].name(), select.getDefaultValue());
}
@Test
public void testSelectionWithDefault() throws Exception {
HtmlForm form = getForm("withDefault");
HtmlSelect select;
select = form.getSelectByName("enum1");
assertEquals(BallColor.YELLOW.name(), select.getDefaultValue());
select = form.getSelectByName("enum2");
assertEquals(BallColor.BLUE.name(), select.getDefaultValue());
}
private HtmlForm getForm(String viewName) throws Exception {
rule.jenkins.setCrumbIssuer(null);
HtmlPage page = rule.createWebClient().goTo("test/" + viewName);
return page.getFormByName("config");
}
@TestExtension
public static class Form extends InvisibleAction implements RootAction, Describable<EnumTest.Form> {
public BallColor enum1 = BallColor.YELLOW;
public BallColor enum2 = null;
public void doSubmitForm(StaplerRequest req) throws Exception {
JSONObject json = req.getSubmittedForm();
System.out.println(json);
}
public Form.DescriptorImpl getDescriptor() {
return Jenkins.get().getDescriptorByType(Form.DescriptorImpl.class);
}
@Override
public String getUrlName() {
return "test";
}
@Extension
public static final class DescriptorImpl extends Descriptor<EnumTest.Form> {}
}
}
<!--
The MIT License
Copyright (c) 2011, CloudBees, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!-- 3rd party license acknowledgements and -->
<?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:s="/lib/form">
<l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" type="one-column" title="Hello">
<l:main-panel>
<div class="container-fluid">
<div class="row">
<div class="col-md-24">
<h1>Hello, world!</h1>
</div>
</div>
</div>
</l:main-panel>
</l:layout>
</j:jelly>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form">
<l:layout title="Testing enum without default attribute">
<l:main-panel>
<f:form method="post" name="config" action="submitForm">
<j:set var="instance" value="${it}"/>
<j:set var="descriptor" value="${it.descriptor}"/>
<f:entry field="enum1">
<f:enum/>
</f:entry>
<f:entry field="enum2">
<f:enum/>
</f:entry>
<f:entry>
<f:submit value="submit"/>
</f:entry>
</f:form>
</l:main-panel>
</l:layout>
</j:jelly>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form">
<l:layout title="Testing enum without default attribute">
<l:main-panel>
<f:form method="post" name="config" action="submitForm">
<j:set var="instance" value="${it}"/>
<j:set var="descriptor" value="${it.descriptor}"/>
<f:entry field="enum1">
<f:enum default="BLUE"/>
</f:entry>
<f:entry field="enum2">
<f:enum default="BLUE"/>
</f:entry>
<f:entry>
<f:submit value="submit"/>
</f:entry>
</f:form>
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -3,7 +3,9 @@
# This script is a developer tool, to be used by maintainers
# to update '@since TODO' entries with actual Jenkins release versions.
set -euo pipefail
set -o errexit
set -o nounset
set -o pipefail
me="$( basename "$0" )"
......@@ -19,7 +21,7 @@ do
lineSha=$( git blame --porcelain -L "$line,$line" "$file" | head -1 | cut -d ' ' -f 1 )
echo -e "\tfirst sha: $lineSha"
firstTag=$( git tag --sort=creatordate --contains "$lineSha" | head -1 )
firstTag=$( git tag --sort=creatordate --contains "$lineSha" 'jenkins-*' | head -1 )
if [[ -n $firstTag ]]; then
echo -e "\tfirst tag was $firstTag"
......@@ -28,6 +30,6 @@ do
sed -i.bak "$sedExpr" "$file"
rm -f "$file.bak"
else
echo -e "\tNot updating file, no tag found. Normal if the associated PR/commit is not merged and released yet"
echo -e "\tNot updating file, no tag found. Normal if the associated PR/commit is not merged and released yet; otherwise make sure to fetch tags from jenkinsci/jenkins"
fi
done
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册