| author | akozak | 2011-11-25 05:19:11 (EST) |
|---|---|---|
| committer | Winston Prakash | 2011-12-01 20:47:36 (EST) |
| commit | 144e1baca5729d3f069a42435fab86c7ae42c020 (patch) (side-by-side diff) | |
| tree | 593e5c8d6d0a05195c4f68ed5a516147d4aa282d | |
| parent | d2e1c7b6911c7ab38f4a4761212cc44249ffbc32 (diff) | |
| download | org.eclipse.hudson.core-144e1baca5729d3f069a42435fab86c7ae42c020.zip org.eclipse.hudson.core-144e1baca5729d3f069a42435fab86c7ae42c020.tar.gz org.eclipse.hudson.core-144e1baca5729d3f069a42435fab86c7ae42c020.tar.bz2 | |
Draft implementation of cascadable JobProperties. Improve highlighting for modified elements.
Signed-off-by: Winston Prakash <winston.prakash@gmail.com>
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"); } } |

