From e4e947081e75bd3bbb2f42c5e02ee9e9611175cd Mon Sep 17 00:00:00 2001 From: kohsuke Date: Thu, 9 Sep 2010 07:36:43 +0000 Subject: [PATCH] added a mechanism to refer to controls in the ancestor, not just the siblings. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@34581 71c3de6d-444a-0410-be80-ed276b4c234a --- core/src/main/java/hudson/RelativePath.java | 28 +++++++++++++ .../main/java/hudson/model/Descriptor.java | 9 ++++ .../main/webapp/scripts/hudson-behavior.js | 42 ++++++++++++------- 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/hudson/RelativePath.java diff --git a/core/src/main/java/hudson/RelativePath.java b/core/src/main/java/hudson/RelativePath.java new file mode 100644 index 0000000000..0beadad283 --- /dev/null +++ b/core/src/main/java/hudson/RelativePath.java @@ -0,0 +1,28 @@ +package hudson; + +import org.kohsuke.stapler.QueryParameter; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * Used in conjunction with {@link QueryParameter} to refer to + * nearby parameters that belong to different parents. + * + *

+ * Currently, "..", "../..", etc. are supported to indicate + * parameters that belong to the ancestors. + * + * @author Kohsuke Kawaguchi + * @since 1.376 + */ +@Documented +@Target(PARAMETER) +@Retention(RUNTIME) +public @interface RelativePath { + String value(); +} diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index eb8b75d29d..ab06614045 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -23,6 +23,7 @@ */ package hudson.model; +import hudson.RelativePath; import hudson.XmlFile; import hudson.BulkChange; import hudson.Util; @@ -300,6 +301,10 @@ public abstract class Descriptor> implements Saveable { if (name==null || name.length()==0) continue; // unknown parameter name. we'll report the error when the form is submitted. + RelativePath rp = p.annotation(RelativePath.class); + if (rp!=null) + name = rp.value()+'/'+name; + if (query.length()==0) query.append("+qs(this)"); if (name.equals("value")) { @@ -346,6 +351,10 @@ public abstract class Descriptor> implements Saveable { if (name==null || name.length()==0) continue; // unknown parameter name. we'll report the error when the form is submitted. + RelativePath rp = p.annotation(RelativePath.class); + if (rp!=null) + name = rp.value()+'/'+name; + depends.add(name); continue; } diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 1b26aa8041..ab71983443 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -138,8 +138,18 @@ var FormChecker = { /** * Find the sibling (in the sense of the structured form submission) form item of the given name, * and returns that DOM node. + * + * @param {HTMLElement} e + * @param {string} name + * Name of the control to find. Can include "../../" etc in the prefix. + * See @RelativePath. */ function findNearBy(e,name) { + while (name.startsWith("../")) { + name = name.substring(3); + e = findFormParent(e,null,true); + } + // does 'e' itself match the criteria? // as some plugins use the field name as a parameter value, instead of 'value' var p = findFormItem(e,name,function(e,filter) { @@ -148,20 +158,14 @@ function findNearBy(e,name) { }); if (p!=null) return p; - var owner = findFormParent(e,null); - - // handle "../foo" syntax - while (name.startsWith("../")) { - owner = findFormParent(owner,null); - name = name.substring(3); - } + var owner = findFormParent(e,null,true); p = findPreviousFormItem(e,name); - if (p!=null && findFormParent(p,null)==owner) + if (p!=null && findFormParent(p,null,true)==owner) return p; var n = findNextFormItem(e,name); - if (n!=null && findFormParent(n,null)==owner) + if (n!=null && findFormParent(n,null,true)==owner) return n; return null; // not found @@ -199,7 +203,7 @@ function qs(owner) { nearBy : function(name) { var e = findNearBy(owner,name); if (e==null) return this; // skip - return this.append(name+'='+toValue(e)); + return this.append(Path.tail(name)+'='+toValue(e)); }, addThis : function() { @@ -993,6 +997,14 @@ function applyTooltip(e,text) { e = null; // avoid memory leak } +var Path = { + tail : function(p) { + var idx = p.lastIndexOf("/"); + if (idx<0) return p; + return p.substring(idx+1); + } +}; + /** * Install change handlers based on the 'fillDependsOn' attribute. */ @@ -1016,7 +1028,7 @@ function refillOnChange(e,onChange) { return; } c.addEventListener("change",h,false); - deps.push({name:name,control:c}); + deps.push({name:Path.tail(name),control:c}); }); } h(); // initial fill @@ -1593,7 +1605,9 @@ function createSearchBox(searchURL) { * @return null * if the given element shouldn't be a part of the final submission. */ -function findFormParent(e,form) { +function findFormParent(e,form,static) { + static = static || false; + if (form==null) // caller can pass in null to have this method compute the owning form form = findAncestor(e,"FORM"); @@ -1606,12 +1620,12 @@ function findFormParent(e,form) { else e = e.parentNode; - if(e.getAttribute("field-disabled")!=null) + if(!static && e.getAttribute("field-disabled")!=null) return null; // this field shouldn't contribute to the final result var name = e.getAttribute("name"); if(name!=null) { - if(e.tagName=="INPUT" && !xor(e.checked,Element.hasClassName(e,"negative"))) + if(e.tagName=="INPUT" && !static && !xor(e.checked,Element.hasClassName(e,"negative"))) return null; // field is not active return e;