aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakozak2011-11-25 05:19:11 (EST)
committerWinston Prakash2011-12-01 20:47:36 (EST)
commit9889a61bc3b11d682bb6a6c10788f2a466f89643 (patch)
treeb70d7ccb5b1cd990a82dd50eed5ccae2e11f6f44
parent131628bf145b0b6133549a4b5ea3f53a8c6a2a6e (diff)
downloadorg.eclipse.hudson.core-9889a61bc3b11d682bb6a6c10788f2a466f89643.zip
org.eclipse.hudson.core-9889a61bc3b11d682bb6a6c10788f2a466f89643.tar.gz
org.eclipse.hudson.core-9889a61bc3b11d682bb6a6c10788f2a466f89643.tar.bz2
Draft implementation of cascadable JobProperties. Improve highlighting for modified elements.
Signed-off-by: Winston Prakash <winston.prakash@gmail.com>
-rw-r--r--hudson-core/src/main/java/hudson/model/Items.java1
-rw-r--r--hudson-core/src/main/java/hudson/model/Job.java196
-rw-r--r--hudson-core/src/main/java/hudson/util/CascadingUtil.java21
-rw-r--r--hudson-core/src/main/resources/hudson/model/Job/configure.jelly67
-rw-r--r--hudson-core/src/main/resources/lib/form/rowSet.jelly13
-rw-r--r--hudson-core/src/main/resources/lib/hudson/cascadingDescriptorList.jelly1
-rw-r--r--hudson-core/src/test/java/hudson/model/LegacyProjectTest.java4
-rw-r--r--hudson-core/src/test/java/hudson/util/CascadingUtilTest.java10
-rw-r--r--hudson-war/src/main/webapp/css/style.css4
-rw-r--r--hudson-war/src/main/webapp/scripts/cascading.js6
-rw-r--r--hudson-war/src/main/webapp/scripts/hudson-behavior.js25
11 files changed, 270 insertions, 78 deletions
diff --git a/hudson-core/src/main/java/hudson/model/Items.java b/hudson-core/src/main/java/hudson/model/Items.java
index 39fa2eb..081aabe 100644
--- a/hudson-core/src/main/java/hudson/model/Items.java
+++ b/hudson-core/src/main/java/hudson/model/Items.java
@@ -155,6 +155,7 @@ public class Items {
XSTREAM.alias("copy-write-list-property", CopyOnWriteListProjectProperty.class);
XSTREAM.alias("axis-list-property", AxisListProjectProperty.class);
XSTREAM.alias("describable-list-property", DescribableListProjectProperty.class);
+ XSTREAM.aliasField("cascading-job-properties", Job.class, "cascadingJobProperties");
XSTREAM.aliasField("project-properties", Job.class, "jobProperties");
XSTREAM.alias("appointed-node-property", AppointedNode.class);
}
diff --git a/hudson-core/src/main/java/hudson/model/Job.java b/hudson-core/src/main/java/hudson/model/Job.java
index 542b43e..eef18db 100644
--- a/hudson-core/src/main/java/hudson/model/Job.java
+++ b/hudson-core/src/main/java/hudson/model/Job.java
@@ -88,6 +88,7 @@ import net.sf.json.JSONObject;
import org.eclipse.hudson.api.model.IJob;
import org.eclipse.hudson.api.model.IProjectProperty;
+import org.eclipse.hudson.api.model.project.property.BaseProjectProperty;
import org.eclipse.hudson.api.model.project.property.ExternalProjectProperty;
import org.jvnet.localizer.Localizable;
import org.kohsuke.args4j.Argument;
@@ -185,6 +186,14 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
private Set<String> cascadingChildrenNames = new CopyOnWriteArraySet<String>();
/**
+ * Set contains json-save names of cascadable {@link JobProperty} classes. Intended to be used for cascading support
+ * of external hudson plugins, that extends {@link JobProperty} class.
+ * See {@link #properties} field description
+ * @since 2.2.0
+ */
+ private Set<String> cascadingJobProperties = new CopyOnWriteArraySet<String>();
+
+ /**
* Selected cascadingProject for this job.
*/
protected transient JobT cascadingProject;
@@ -425,7 +434,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
property.setJob(this);
}
convertLogRotatorProperty();
- convertJobProperty();
+ convertJobProperties();
}
void convertLogRotatorProperty() {
@@ -435,11 +444,40 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
}
}
- void convertJobProperty() {
- if (null != properties && null == getProperty(PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)) {
- setParameterDefinitionProperties(properties);
+ void convertJobProperties() {
+ if (null != properties && null == cascadingJobProperties) {
+ cascadingJobProperties = new CopyOnWriteArraySet<String>();
+ convertCascadingJobProperties(properties);
+ }
+ }
+
+ /**
+ * Adds cascading JobProperty.
+ *
+ * @param cascadingJobProperty BaseProjectProperty wrapper for JobProperty.
+ */
+ private void addCascadingJobProperty(BaseProjectProperty projectProperty) {
+ if (null != projectProperty) {
+ cascadingJobProperties.add(projectProperty.getKey());
}
}
+
+ /**
+ * Adds cascading JobProperty.
+ *
+ * @param cascadingJobPropertyKey key of cascading JobProperty.
+ */
+ private void removeCascadingJobProperty(String cascadingJobPropertyKey) {
+ if (null != cascadingJobPropertyKey) {
+ IProjectProperty projectProperty = CascadingUtil.getProjectProperty(this, cascadingJobPropertyKey);
+ if (null != projectProperty) {
+ projectProperty.resetValue();
+ }
+ cascadingJobProperties.remove(cascadingJobPropertyKey);
+ }
+ }
+
+
/**
* Initialize project properties if null.
*/
@@ -626,19 +664,74 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
public boolean supportsLogRotator() {
return true;
}
-
- public void setParameterDefinitionProperties(CopyOnWriteList<JobProperty<? super JobT>> properties) {
+ /**
+ * Method converts JobProperties to cascading values.
+ * <p/>
+ * If property is {@link AuthorizationMatrixProperty} - it will be skipped.
+ * If property is {@link ParametersDefinitionProperty} - it will be added to list of parameterDefinition properties.
+ * All the rest properties will be converted to {@link BaseProjectProperty} classes and added
+ * to cascadingJobProperties set.
+ *
+ * @param properties list of {@link JobProperty}
+ */
+ @SuppressWarnings("unchecked")
+ private void convertCascadingJobProperties(CopyOnWriteList<JobProperty<? super JobT>> properties) {
CopyOnWriteList parameterDefinitionProperties = new CopyOnWriteList();
for (JobProperty property : properties) {
+ if (property instanceof AuthorizationMatrixProperty) {
+ continue;
+ }
if (property instanceof ParametersDefinitionProperty) {
parameterDefinitionProperties.add(property);
+ continue;
}
+ BaseProjectProperty projectProperty = CascadingUtil.getBaseProjectProperty(this,
+ property.getDescriptor().getJsonSafeClassName());
+ addCascadingJobProperty(projectProperty);
}
+ if (null == getProperty(PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)) {
+ setParameterDefinitionProperties(parameterDefinitionProperties);
+ }
+ }
+
+ /**
+ * @return list of cascading {@link JobProperty} instances. Includes {@link ParametersDefinitionProperty} and
+ * children of {@link JobProperty} from external plugins.
+ */
+ @SuppressWarnings("unchecked")
+ private CopyOnWriteList getCascadingJobProperties() {
+ CopyOnWriteList result = new CopyOnWriteList();
+ CopyOnWriteList<ParametersDefinitionProperty> definitionProperties = getParameterDefinitionProperties();
+ if (null != cascadingJobProperties && !cascadingJobProperties.isEmpty()) {
+ for (String key : cascadingJobProperties) {
+ IProjectProperty projectProperty = CascadingUtil.getProjectProperty(this, key);
+ Object value = projectProperty.getValue();
+ if (null != value) {
+ result.add(value);
+ }
+ }
+ }
+ if (null != definitionProperties && !definitionProperties.isEmpty()) {
+ result.addAll(definitionProperties.getView());
+ }
+ return result;
+ }
+
+ /**
+ * Sets list of {@link ParametersDefinitionProperty}. Supports cascading functionality.
+ *
+ * @param properties properties to set.
+ */
+ private void setParameterDefinitionProperties(CopyOnWriteList<ParametersDefinitionProperty> properties) {
CascadingUtil.setParameterDefinitionProperties(this, PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME,
- parameterDefinitionProperties);
+ properties);
}
- public CopyOnWriteList<ParametersDefinitionProperty> getParameterDefinitionProperties() {
+ /**
+ * @return list of {@link ParametersDefinitionProperty}. Supports cascading functionality.
+ */
+ @SuppressWarnings("unchecked")
+ private CopyOnWriteList<ParametersDefinitionProperty> getParameterDefinitionProperties() {
return CascadingUtil.getCopyOnWriteListProjectProperty(this, PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
.getValue();
}
@@ -678,17 +771,23 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
*
* @since 1.188
*/
+ @SuppressWarnings("unchecked")
public void addProperty(JobProperty<? super JobT> jobProp) throws IOException {
- ((JobProperty) jobProp).setOwner(this);
- if (((JobProperty) jobProp) instanceof ParametersDefinitionProperty) {
+ JobProperty jobProperty = (JobProperty) jobProp;
+ jobProperty.setOwner(this);
+ if (jobProperty instanceof AuthorizationMatrixProperty) {
+ properties.add(jobProp);
+ } else if (jobProperty instanceof ParametersDefinitionProperty) {
CopyOnWriteList list = CascadingUtil.getCopyOnWriteListProjectProperty(this,
- PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
- .getOriginalValue();
+ PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
if (null != list) {
list.add(jobProp);
}
} else {
- properties.add(jobProp);
+ BaseProjectProperty projectProperty = CascadingUtil.getBaseProjectProperty(this,
+ jobProperty.getDescriptor().getJsonSafeClassName());
+ projectProperty.setValue(jobProperty);
+ addCascadingJobProperty(projectProperty);
}
save();
}
@@ -698,16 +797,19 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
*
* @since 1.279
*/
+ @SuppressWarnings("unchecked")
public void removeProperty(JobProperty<? super JobT> jobProp) throws IOException {
- if (((JobProperty) jobProp) instanceof ParametersDefinitionProperty) {
+ JobProperty jobProperty = (JobProperty) jobProp;
+ if (jobProperty instanceof AuthorizationMatrixProperty) {
+ properties.remove(jobProp);
+ } else if (jobProperty instanceof ParametersDefinitionProperty) {
CopyOnWriteList list = CascadingUtil.getCopyOnWriteListProjectProperty(this,
- PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
- .getOriginalValue();
+ PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
if (null != list) {
list.remove(jobProp);
}
} else {
- properties.remove(jobProp);
+ removeCascadingJobProperty(jobProperty.getDescriptor().getJsonSafeClassName());
}
save();
}
@@ -718,13 +820,16 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
* @return The property that was just removed.
* @since 1.279
*/
+ @SuppressWarnings("unchecked")
public <T extends JobProperty> T removeProperty(Class<T> clazz) throws IOException {
CopyOnWriteList<JobProperty<? super JobT>> sourceProperties;
if (clazz.equals(ParametersDefinitionProperty.class)) {
sourceProperties = CascadingUtil.getCopyOnWriteListProjectProperty(this,
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
- } else {
+ } else if (clazz.equals(AuthorizationMatrixProperty.class)) {
sourceProperties = properties;
+ } else {
+ sourceProperties = getCascadingJobProperties();
}
if (null != sourceProperties) {
for (JobProperty<? super JobT> p : sourceProperties) {
@@ -747,14 +852,16 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
/**
* List of all {@link JobProperty} exposed primarily for the remoting API.
- * @since 1.282
+ * List contains cascadable {@link JobProperty} if any.
+ * @since 2.2.0
*/
@Exported(name = "property", inline = true)
+ @SuppressWarnings("unchecked")
public List<JobProperty<? super JobT>> getAllProperties() {
- CopyOnWriteList<ParametersDefinitionProperty> definitionProperties = getParameterDefinitionProperties();
+ CopyOnWriteList cascadingJobProperties = getCascadingJobProperties();
List<JobProperty<? super JobT>> result = properties.getView();
- if (null != definitionProperties) {
- result = Collections.unmodifiableList(ListUtils.union(result, definitionProperties.getView()));
+ if (null != cascadingJobProperties && !cascadingJobProperties.isEmpty()) {
+ result = Collections.unmodifiableList(ListUtils.union(result, cascadingJobProperties.getView()));
}
return result;
}
@@ -762,14 +869,16 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
/**
* Gets the specific property, or null if the propert is not configured for
* this job.
+ * Supports cascading properties
+ * @since 2.2.0
*/
+ @SuppressWarnings("unchecked")
public <T extends JobProperty> T getProperty(Class<T> clazz) {
CopyOnWriteList<JobProperty<? super JobT>> sourceProperties;
- if (clazz.equals(ParametersDefinitionProperty.class)) {
- sourceProperties = CascadingUtil.getCopyOnWriteListProjectProperty(this,
- PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
- } else {
+ if (clazz.equals(AuthorizationMatrixProperty.class)) {
sourceProperties = properties;
+ } else {
+ sourceProperties = getCascadingJobProperties();
}
if (null != sourceProperties) {
for (JobProperty p : sourceProperties) {
@@ -1369,19 +1478,30 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
properties.clear();
CopyOnWriteList parameterDefinitionProperties = new CopyOnWriteList();
int i = 0;
- for (JobPropertyDescriptor d : JobPropertyDescriptor
- .getPropertyDescriptors(Job.this.getClass())) {
- String name = "jobProperty" + (i++);
- JSONObject config = json.getJSONObject(name);
- JobProperty prop = d.newInstance(req, config);
-
- if (prop instanceof ParametersDefinitionProperty) {
- prop.setOwner(this);
- parameterDefinitionProperties.add(prop);
- } else if (null != prop) {
- prop.setOwner(this);
- properties.add(prop);
+ for (JobPropertyDescriptor d : JobPropertyDescriptor.getPropertyDescriptors(Job.this.getClass())) {
+ if (!CascadingUtil.isCascadableJobProperty(d)) {
+ String name = "jobProperty" + i;
+ JSONObject config = json.getJSONObject(name);
+ JobProperty prop = d.newInstance(req, config);
+ if (null != prop) {
+ prop.setOwner(this);
+ if (prop instanceof AuthorizationMatrixProperty) {
+ properties.add(prop);
+ } else if (prop instanceof ParametersDefinitionProperty) {
+ parameterDefinitionProperties.add(prop);
+ }
+ }
+ } else {
+ BaseProjectProperty property = CascadingUtil.getBaseProjectProperty(this,
+ d.getJsonSafeClassName());
+ JobProperty prop = d.newInstance(req, json.getJSONObject(d.getJsonSafeClassName()));
+ if (null != prop) {
+ prop.setOwner(this);
+ }
+ property.setValue(prop);
+ addCascadingJobProperty(property);
}
+ i++;
}
setParameterDefinitionProperties(parameterDefinitionProperties);
LogRotator logRotator = null;
diff --git a/hudson-core/src/main/java/hudson/util/CascadingUtil.java b/hudson-core/src/main/java/hudson/util/CascadingUtil.java
index e98949e..87ac8a2 100644
--- a/hudson-core/src/main/java/hudson/util/CascadingUtil.java
+++ b/hudson-core/src/main/java/hudson/util/CascadingUtil.java
@@ -21,8 +21,10 @@ import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.Job;
+import hudson.model.JobPropertyDescriptor;
import hudson.model.ParameterDefinition;
import hudson.model.ParametersDefinitionProperty;
+import hudson.security.AuthorizationMatrixProperty;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import java.io.IOException;
@@ -509,4 +511,21 @@ public class CascadingUtil {
}
}
}
-} \ No newline at end of file
+
+ /**
+ * Checks whether JobProperty supports cascading.
+ * Method skips {@link AuthorizationMatrixProperty} and {@link ParametersDefinitionProperty} classes.
+ * {@link AuthorizationMatrixProperty} doesn't support cascading for now.
+ * As for {@link ParametersDefinitionProperty} single instance doesn't support cascading, so, classes are
+ * grouped into list of {@link ParametersDefinitionProperty} and whole list could be inherited or overridden.
+
+ * * @param d property descriptor.
+ * @return true - if JobProperty could be used for cascading, false - otherwise.
+ * @see #setParameterDefinitionProperties(hudson.model.Job, String, CopyOnWriteList)
+ * @see hudson.model.Job#getParameterDefinitionProperties()
+ */
+ public static boolean isCascadableJobProperty(JobPropertyDescriptor d) {
+ return !(d instanceof AuthorizationMatrixProperty.DescriptorImpl
+ || d instanceof ParametersDefinitionProperty.DescriptorImpl);
+ }
+}
diff --git a/hudson-core/src/main/resources/hudson/model/Job/configure.jelly b/hudson-core/src/main/resources/hudson/model/Job/configure.jelly
index fa0ec2b..7086a56 100644
--- a/hudson-core/src/main/resources/hudson/model/Job/configure.jelly
+++ b/hudson-core/src/main/resources/hudson/model/Job/configure.jelly
@@ -61,29 +61,48 @@
</f:optionalBlock>
</j:if>
- <!-- job property configurations -->
- <j:set var="instances" value="${it.properties}" />
- <j:forEach var="d" items="${h.getJobPropertyDescriptors(it.getClass())}" varStatus="loop">
- <j:scope>
- <j:set var="descriptor" value="${d}" />
- <j:set var="instance" value="${instances[d]}" />
+ <!-- job property configurations -->
+ <j:set var="instances" value="${it.properties}" />
+ <j:forEach var="d" items="${h.getJobPropertyDescriptors(it.getClass())}" varStatus="loop">
+ <j:scope>
+ <j:set var="descriptor" value="${d}"/>
+ <j:set var="instance" value="${instances[d]}"/>
+ <j:set var="rowSetName" value="${'jobProperty'+loop.index}"/>
+ <j:set var="propertyOverridden" value="false"/>
+ <j:if test="${cu.isCascadableJobProperty(descriptor)}">
+ <j:set var="rowSetName" value="${descriptor.jsonSafeClassName}"/>
+ <j:set var="cascadingProperty" value="${cu.getBaseProjectProperty(it, descriptor.jsonSafeClassName)}"/>
+ <j:set var="instance" value="${cascadingProperty.value}"/>
+ <j:set var="propertyOverridden" value="${cascadingProperty.isOverridden()}"/>
+ </j:if>
+ <f:rowSet name="${rowSetName}" isPropertyOverridden='${propertyOverridden}'>
+ <j:if test="${propertyOverridden}">
+ <tr>
+ <td>
+ <div class="optional-reset">
+ <a href="#" class="reset-button"
+ resetURL="${jobUrl}/resetProjectProperty?propertyName=${cascadingProperty.key}">
+ <img src="${imagesURL}/16x16/undo.png"/>
+ </a>
+ </div>
+ </td>
+ </tr>
+ </j:if>
+ <st:include from="${d}" page="${d.configPage}" optional="true"/>
+ </f:rowSet>
+ </j:scope>
+ </j:forEach>
- <f:rowSet name="jobProperty${loop.index}">
- <st:include from="${d}" page="${d.configPage}" optional="true"/>
- </f:rowSet>
- </j:scope>
- </j:forEach>
+ <!-- additional entries from derived classes -->
+ <st:include page="configure-entries.jelly" />
- <!-- additional entries from derived classes -->
- <st:include page="configure-entries.jelly" />
-
- <j:if test="${h.hasPermission(it,it.CONFIGURE)}">
- <f:block>
- <!--<input type="button" name="StructureTest" value="Test" onclick="buildFormTree(this.form)" />-->
- <f:submit value="${%Save}" />
- </f:block>
- </j:if>
- </f:form>
- </l:main-panel>
- </l:layout>
-</j:jelly>
+ <j:if test="${h.hasPermission(it,it.CONFIGURE)}">
+ <f:block>
+ <!--<input type="button" name="StructureTest" value="Test" onclick="buildFormTree(this.form)" />-->
+ <f:submit value="${%Save}" />
+ </f:block>
+ </j:if>
+ </f:form>
+ </l:main-panel>
+ </l:layout>
+ </j:jelly>
diff --git a/hudson-core/src/main/resources/lib/form/rowSet.jelly b/hudson-core/src/main/resources/lib/form/rowSet.jelly
index cf14826..1672808 100644
--- a/hudson-core/src/main/resources/lib/form/rowSet.jelly
+++ b/hudson-core/src/main/resources/lib/form/rowSet.jelly
@@ -1,6 +1,6 @@
<!-- **************************************************************************
#
-# Copyright (c) 2004-2009 Oracle Corporation.
+# Copyright (c) 2004-2011 Oracle Corporation.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
@@ -9,12 +9,11 @@
#
# Contributors:
#
-# Kohsuke Kawaguchi
+# Kohsuke Kawaguchi, Nikita Levyankov
#
#
#************************************************************************** -->
-
<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:documentation>
Adds @nameRef to all table rows inside this tag, so that when the form is submitted,
@@ -27,15 +26,21 @@
<st:attribute name="ref">
id of the thing that serves as the group head, if that's available separately
</st:attribute>
+ <st:attribute name="isPropertyOverridden">
+ if present and true - value is overridden
+ </st:attribute>
</st:documentation>
+ <j:if test="${attrs.isPropertyOverridden==null}">
+ <j:set target="${attrs}" property="isPropertyOverridden" value="false"/>
+ </j:if>
<j:choose>
<j:when test="${attrs.ref==null and attrs.name==null}">
<!-- noop -->
<d:invokeBody />
</j:when>
<j:otherwise>
- <tr ref="${attrs.ref}" class="row-set-start" style="display:none" name="${attrs.name}" />
+ <tr ref="${attrs.ref}" class="row-set-start ${attrs.isPropertyOverridden? 'modified': 'original'}" style="display:none" name="${attrs.name}" />
<d:invokeBody />
<tr class="row-set-end" />
</j:otherwise>
diff --git a/hudson-core/src/main/resources/lib/hudson/cascadingDescriptorList.jelly b/hudson-core/src/main/resources/lib/hudson/cascadingDescriptorList.jelly
index 3fce557..1ece3f1 100644
--- a/hudson-core/src/main/resources/lib/hudson/cascadingDescriptorList.jelly
+++ b/hudson-core/src/main/resources/lib/hudson/cascadingDescriptorList.jelly
@@ -47,6 +47,7 @@
isPropertyOverridden="${instanceProperty.isOverridden()}">
<st:include from="${descriptor}" page="${descriptor.configPage}" optional="true"/>
</f:optionalBlock>
+ <tr class="descriptor-separator"><td colspan="4"/></tr>
</j:forEach>
</f:section>
</j:if>
diff --git a/hudson-core/src/test/java/hudson/model/LegacyProjectTest.java b/hudson-core/src/test/java/hudson/model/LegacyProjectTest.java
index e53aace..93a0b38 100644
--- a/hudson-core/src/test/java/hudson/model/LegacyProjectTest.java
+++ b/hudson-core/src/test/java/hudson/model/LegacyProjectTest.java
@@ -333,7 +333,7 @@ public class LegacyProjectTest {
* @throws java.io.IOException if any
*/
@Test
- public void testConvertLegacyNodePropertes() throws IOException {
+ public void testConvertLegacyNodeProperties() throws IOException {
AbstractProject project = (AbstractProject) Items.getConfigFile(config).read();
project.setAllowSave(false);
project.initProjectProperties();
@@ -350,7 +350,7 @@ public class LegacyProjectTest {
project.setAllowSave(false);
project.initProjectProperties();
assertNull(project.getProperty(Job.PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME));
- project.convertJobProperty();
+ project.convertJobProperties();
//Properties should be initialized
assertNotNull(project.properties);
CopyOnWriteListProjectProperty property = (CopyOnWriteListProjectProperty) project.getProperty(
diff --git a/hudson-core/src/test/java/hudson/util/CascadingUtilTest.java b/hudson-core/src/test/java/hudson/util/CascadingUtilTest.java
index 7c988aa..c6aee81 100644
--- a/hudson-core/src/test/java/hudson/util/CascadingUtilTest.java
+++ b/hudson-core/src/test/java/hudson/util/CascadingUtilTest.java
@@ -19,6 +19,8 @@ import hudson.model.FreeStyleProject;
import hudson.model.FreeStyleProjectMock;
import hudson.model.Hudson;
import hudson.model.Job;
+import hudson.model.ParametersDefinitionProperty;
+import hudson.security.AuthorizationMatrixProperty;
import hudson.tasks.JavadocArchiver;
import hudson.tasks.Publisher;
import java.util.ArrayList;
@@ -342,4 +344,12 @@ public class CascadingUtilTest {
assertNotNull(CascadingUtil.getExternalProjectProperty(job, javadocArchiverKey).getValue());
verifyAll();
}
+
+ /**
+ * Verify {@link CascadingUtil#isCascadableJobProperty(hudson.model.JobPropertyDescriptor)} method.
+ */
+ public void testIsCascadableJobProperty() {
+ assertFalse(CascadingUtil.isCascadableJobProperty(new AuthorizationMatrixProperty.DescriptorImpl()));
+ assertFalse(CascadingUtil.isCascadableJobProperty(new ParametersDefinitionProperty.DescriptorImpl()));
+ }
}
diff --git a/hudson-war/src/main/webapp/css/style.css b/hudson-war/src/main/webapp/css/style.css
index dfa890d..3277bef 100644
--- a/hudson-war/src/main/webapp/css/style.css
+++ b/hudson-war/src/main/webapp/css/style.css
@@ -1025,6 +1025,10 @@ tr.modified > td.setting-leftspace {
margin-top: -10px;
}
+.descriptor-separator {
+ height: 2px;
+}
+
/* ======================= Job Configuration table ========================== */
table.configure {
border-spacing: 0px;
diff --git a/hudson-war/src/main/webapp/scripts/cascading.js b/hudson-war/src/main/webapp/scripts/cascading.js
index 6857ef9..e7876f1 100644
--- a/hudson-war/src/main/webapp/scripts/cascading.js
+++ b/hudson-war/src/main/webapp/scripts/cascading.js
@@ -72,8 +72,10 @@ function onProjectPropertyChanged() {
});
}
};
- jQuery('form[name=config] input, form[name=config] .setting-input').live("change", modify);
- jQuery('form[name=config] button').live("click", modify);
+ jQuery("form[action=configSubmit] input[type=checkbox]").live('click', modify);
+ jQuery("form[action=configSubmit] input[type!=checkbox]").live('change', modify);
+ jQuery("form[action=configSubmit] .setting-input").live('change', modify);
+ jQuery("form[action=configSubmit] button").live('click', modify);
}
jQuery(document).ready(function(){
diff --git a/hudson-war/src/main/webapp/scripts/hudson-behavior.js b/hudson-war/src/main/webapp/scripts/hudson-behavior.js
index fb1c4e6..8342636 100644
--- a/hudson-war/src/main/webapp/scripts/hudson-behavior.js
+++ b/hudson-war/src/main/webapp/scripts/hudson-behavior.js
@@ -527,8 +527,8 @@ var hudsonRules = {
if(nameRef!=null && row.getAttribute("nameref")==null){
row.setAttribute("nameref",nameRef); // to handle inner rowSets, don't override existing values
}
- if(Element.hasClassName(link,"modified")){
- Element.addClassName(row,"modified");
+ if(Element.hasClassName(tr,"modified")){
+ addModifiedClass(row);
}
tr.parentNode.insertBefore(row, tr.nextSibling);
}
@@ -881,11 +881,6 @@ var hudsonRules = {
g.updateSingleButton(r,s,e);
};
applyNameRef(s,e,r.id);
- if (Element.hasClassName(r.parentNode,'modified')){
- jQuery('[nameref='+ r.id +']').children().addClass('modified');
- //required for the advanced sections (hudson generates content inside div).
- jQuery('[nameref='+ r.id +'] div.advancedLink').addClass('modified');
- }
g.buttons.push(u);
// apply the initial visibility
@@ -1076,10 +1071,26 @@ function replaceDescription() {
function applyNameRef(s,e,id) {
$(id).groupingNode = true;
// s contains the node itself
+ var modified = Element.hasClassName(s, "modified") || Element.hasClassName(s.firstChild, "modified");
for(var x=s.nextSibling; x!=e; x=x.nextSibling) {
// to handle nested <f:rowSet> correctly, don't overwrite the existing value
if(x.getAttribute("nameRef")==null)
x.setAttribute("nameRef",id);
+ if (modified) {
+ addModifiedClass(x);
+ }
+ }
+}
+
+// Remove 'original' class from elements and its children and
+// add 'modified' class to it.
+function addModifiedClass(element) {
+ Element.removeClassName(element, "original");
+ Element.addClassName(element, "modified");
+ var elements = Element.childElements(element)
+ for (var key = 0; key < elements.size(); key++) {
+ Element.removeClassName(elements[key], "original");
+ Element.addClassName(elements[key], "modified");
}
}