From 99b11d0df29ba6428a64486a8335a8f55a2c3d27 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 5 Jun 2012 17:20:41 -0700 Subject: [PATCH] added UI sample fo contextual form field validation --- .../FormFieldValidationWithContext.java | 127 ++++++++++++++++++ .../City/config.groovy | 7 + .../State/config.groovy | 19 +++ .../index.groovy | 22 +++ .../index.properties | 9 ++ 5 files changed, 184 insertions(+) create mode 100644 ui-samples-plugin/src/main/java/jenkins/plugins/ui_samples/FormFieldValidationWithContext.java create mode 100644 ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/City/config.groovy create mode 100644 ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/State/config.groovy create mode 100644 ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.groovy create mode 100644 ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.properties diff --git a/ui-samples-plugin/src/main/java/jenkins/plugins/ui_samples/FormFieldValidationWithContext.java b/ui-samples-plugin/src/main/java/jenkins/plugins/ui_samples/FormFieldValidationWithContext.java new file mode 100644 index 0000000000..68f6ec0690 --- /dev/null +++ b/ui-samples-plugin/src/main/java/jenkins/plugins/ui_samples/FormFieldValidationWithContext.java @@ -0,0 +1,127 @@ +package jenkins.plugins.ui_samples; + +import hudson.Extension; +import hudson.RelativePath; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Descriptor; +import hudson.util.FormValidation; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * How to access values of the nearby input fields when you do form field validation. + * + * @author Kohsuke Kawaguchi + */ +@Extension +public class FormFieldValidationWithContext extends UISample { + private List states = new ArrayList(Arrays.asList( + new State("California",new City("Sacramento"), Arrays.asList(new City("San Francisco"),new City("Los Angeles"))), + new State("New York",new City("New York"), Arrays.asList(new City("Albany"),new City("Ithaca"))) + )); + + public FormFieldValidationWithContext() { + } + + @DataBoundConstructor + public FormFieldValidationWithContext(List states) { + this.states = states; + } + + @Override + public String getDescription() { + return "How to access values of the nearby input fields when you do form field validation"; + } + + public List getStates() { + return states; + } + + @Override + public List getSourceFiles() { + List r = super.getSourceFiles(); + r.add(new SourceFile("City/config.groovy")); + r.add(new SourceFile("State/config.groovy")); + return r; + } + + public static class State extends AbstractDescribableImpl { + /* + I'm lazy and just exposing fields as opposed to getter/setter. + Jenkins doesn't care and works correctly either way. + */ + public String name; + public City capital; + public List cities; + + @DataBoundConstructor + public State(String name, City capital, List cities) { + this.name = name; + this.capital = capital; + this.cities = cities; + } + + @Extension + public static class DescriptorImpl extends Descriptor { + @Override + public String getDisplayName() { + return ""; + } + + public FormValidation doCheckName(@QueryParameter String value, + @RelativePath("capital") @QueryParameter String name) { + /* + @RelativePath("capital") @QueryParameter + ... is short for + @RelativePath("capital") @QueryParameter("name") + ... and thus can be thought of "capital/name" + + so this matches the current city name entered as the capital of this state + */ + + return FormValidation.ok("Are you sure " + name + " is a capital of " + value + "?"); + } + } + } + + public static class City extends AbstractDescribableImpl { + public String name; + + @DataBoundConstructor + public City(String name) { + this.name = name; + } + + @Extension + public static class DescriptorImpl extends Descriptor { + @Override + public String getDisplayName() { + return ""; + } + + public FormValidation doCheckName(@QueryParameter String value, + @RelativePath("..") @QueryParameter String name) { + /* + @RelativePath("..") @QueryParameter + ... is short for + @RelativePath("..") @QueryParameter("name") + ... and thus can be thought of "../name" + + in the UI, fields for city is wrapped inside those of state, so "../name" binds + to the name field in the state. + */ + + if (name==null || value==null || value.contains(name)) return FormValidation.ok(); + return FormValidation.warning("City name doesn't contain "+name); + } + } + } + + @Extension + public static class DescriptorImpl extends UISampleDescriptor { + } +} diff --git a/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/City/config.groovy b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/City/config.groovy new file mode 100644 index 0000000000..b7dd63efdc --- /dev/null +++ b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/City/config.groovy @@ -0,0 +1,7 @@ +package jenkins.plugins.ui_samples.FormFieldValidationWithContext.City; + +def f = namespace(lib.FormTagLib) + +f.entry(title:"City Name", field:"name") { + f.textbox() +} diff --git a/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/State/config.groovy b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/State/config.groovy new file mode 100644 index 0000000000..53fdcd9ef9 --- /dev/null +++ b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/State/config.groovy @@ -0,0 +1,19 @@ +package jenkins.plugins.ui_samples.FormFieldValidationWithContext.State; + +def f = namespace(lib.FormTagLib) + +f.entry(title:"State Name", field:"name") { + f.textbox() +} + +f.nested { + table { + f.section(title:"Capital city") { + f.property(field:"capital") + } + + f.entry(title:"Other cities") { + f.repeatableProperty(field:"cities") + } + } +} diff --git a/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.groovy b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.groovy new file mode 100644 index 0000000000..ef37b97a2f --- /dev/null +++ b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.groovy @@ -0,0 +1,22 @@ +package jenkins.plugins.ui_samples.FormFieldValidationWithContext; + +import lib.JenkinsTagLib +import lib.FormTagLib + +def f=namespace(FormTagLib.class) + +t=namespace(JenkinsTagLib.class) + +namespace("/lib/samples").sample(title:_("Context-sensitive form validation")) { + p { + raw(_("blurb.context")) + raw(_("blurb.otheruse")) + raw(_("blurb.contrived")) + } + + f.form { + f.entry(title:"States") { + f.repeatableProperty(field:"states") + } + } +} diff --git a/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.properties b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.properties new file mode 100644 index 0000000000..16568baed4 --- /dev/null +++ b/ui-samples-plugin/src/main/resources/jenkins/plugins/ui_samples/FormFieldValidationWithContext/index.properties @@ -0,0 +1,9 @@ +blurb.context=\ + Form field validation can access values of the nearby input controls, which is useful for performing \ + complex context sensitive form validation. +blurb.otheruse=\ + The same technique can be also used for auto-completion, populating combobox/listbox, and so on. +blurb.contrived=\ + The example below is bit contrived, but all the input elements are named 'name' (for city name and state name), \ + and we use @RelativePath so that the validation of the state name refers to the capital name, \ + and the validation of the city name refers to the state name. \ No newline at end of file -- GitLab