diff --git a/core/src/main/java/hudson/RelativePath.java b/core/src/main/java/hudson/RelativePath.java index 0beadad283..c5f562db3b 100644 --- a/core/src/main/java/hudson/RelativePath.java +++ b/core/src/main/java/hudson/RelativePath.java @@ -14,8 +14,23 @@ import static java.lang.annotation.RetentionPolicy.*; * nearby parameters that belong to different parents. * *
- * Currently, "..", "../..", etc. are supported to indicate - * parameters that belong to the ancestors. + * ".." refers + * to values in the parent object, and "foo" refers to the child + * object of the current object named "foo". They can be combined + * with '/' like path, such as "../foo/bar", "../..", and etc. + * + *
+ * A good way to think about this is the file system structure. + * {@code @RelativePath} is like the dirname, and {@code QueryParameter} + * is like the basename. Together they form a relative path. + * And because of the structured form submissions, + * form elements are organized in a tree structure of JSON objects, + * which is akin to directories and files. + * + *
+ * The relative path then points from the current input element
+ * (for which you are doing form validation, for example) to the target
+ * input element that you want to obtain the value.
*
* @author Kohsuke Kawaguchi
* @since 1.376
diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js
index c274df4eb7..834f3fe64e 100644
--- a/war/src/main/webapp/scripts/hudson-behavior.js
+++ b/war/src/main/webapp/scripts/hudson-behavior.js
@@ -144,6 +144,9 @@ var FormChecker = {
* @param {string} name
* Name of the control to find. Can include "../../" etc in the prefix.
* See @RelativePath.
+ *
+ * We assume that the name is normalized and doesn't contain any redundant component.
+ * That is, ".." can only appear as prefix, and "foo/../bar" is not OK (because it can be reduced to "bar")
*/
function findNearBy(e,name) {
while (name.startsWith("../")) {
@@ -151,21 +154,33 @@ function findNearBy(e,name) {
e = findFormParent(e,null,true);
}
+ // name="foo/bar/zot" -> prefixes=["bar","foo"] & name="zot"
+ var prefixes = name.split("/");
+ name = prefixes.pop();
+ prefixes = prefixes.reverse();
+
// 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) {
- if (filter(e)) return e;
- return null;
+ return filter(e) ? e : null;
});
- if (p!=null) return p;
+ if (p!=null && prefixes.length==0) return p;
var owner = findFormParent(e,null,true);
- function locate(iterator,e) {
+ function locate(iterator,e) {// keep finding elements until we find the good match
while (true) {
e = iterator(e,name);
if (e==null) return null;
- if (findFormParent(e,null,true)==owner)
+
+ // make sure this candidate element 'e' is in the right point in the hierarchy
+ var p = e;
+ for (var i=0; i