Index: addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/DynamicMultiSelectPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/DynamicMultiSelectPlugin.java (revision 1346) +++ addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/DynamicMultiSelectPlugin.java (revision ) @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Set; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -31,6 +32,8 @@ import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.extensions.markup.html.form.palette.Palette; import org.apache.wicket.extensions.markup.html.form.palette.component.Recorder; +import org.apache.wicket.feedback.ContainerFeedbackMessageFilter; +import org.apache.wicket.feedback.IFeedbackMessageFilter; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.html.basic.Label; @@ -45,7 +48,9 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.resource.CssResourceReference; +import org.hippoecm.frontend.PluginRequestTarget; import org.hippoecm.frontend.editor.ITemplateEngine; +import org.hippoecm.frontend.editor.TemplateEngineException; import org.hippoecm.frontend.editor.plugins.field.FieldPluginHelper; import org.hippoecm.frontend.model.IModelReference; import org.hippoecm.frontend.model.JcrItemModel; @@ -55,11 +60,19 @@ import org.hippoecm.frontend.model.properties.JcrMultiPropertyValueModel; import org.hippoecm.frontend.model.properties.JcrPropertyModel; import org.hippoecm.frontend.plugin.IPluginContext; +import org.hippoecm.frontend.plugin.config.IClusterConfig; import org.hippoecm.frontend.plugin.config.IPluginConfig; import org.hippoecm.frontend.plugins.standards.diff.LCS; import org.hippoecm.frontend.plugins.standards.diff.LCS.Change; +import org.hippoecm.frontend.plugins.standards.list.resolvers.CssClassAppender; +import org.hippoecm.frontend.service.IEditor; import org.hippoecm.frontend.service.render.RenderPlugin; import org.hippoecm.frontend.types.IFieldDescriptor; +import org.hippoecm.frontend.validation.IValidationResult; +import org.hippoecm.frontend.validation.IValidationService; +import org.hippoecm.frontend.validation.ModelPath; +import org.hippoecm.frontend.validation.ModelPathElement; +import org.hippoecm.frontend.validation.Violation; import org.onehippo.forge.selection.frontend.model.ValueList; import org.onehippo.forge.selection.frontend.plugin.sorting.SortHelper; import org.onehippo.forge.selection.frontend.provider.IValueListProvider; @@ -99,16 +112,92 @@ private final SortHelper sortHelper = new SortHelper(); + protected IEditor.Mode mode; + + abstract static class ValidationFilter extends Model { + private static final long serialVersionUID = 1L; + + private boolean valid = true; + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + public abstract void onValidation(IValidationResult result); + + @Override + public String getObject() { + return valid ? "" : "invalid"; + } + } + + private ValidationFilter filter; + + @Override + public void render(final PluginRequestTarget target) { + + if (isActive()) { + if (IEditor.Mode.EDIT == mode && filter != null) { + IModel validationModel = helper.getValidationModel(); + if (validationModel != null && validationModel.getObject() != null) { + boolean valid = isFieldValid(validationModel.getObject()); + if (valid != filter.isValid()) { + filter.setValid(valid); + target.appendJavaScript("Wicket.$('" + getMarkupId() + "').setAttribute('class', '" + filter.getObject() + "');"); + } + } + } + } + super.render(target); + } + + protected ITemplateEngine getTemplateEngine() { + return getPluginContext() + .getService(getPluginConfig().getString(ITemplateEngine.ENGINE), ITemplateEngine.class); + } + /** * Constructor. */ public DynamicMultiSelectPlugin(IPluginContext context, IPluginConfig config) { super(context, config); + this.mode = IEditor.Mode.fromString(config.getString(ITemplateEngine.MODE, "view")); helper = new FieldPluginHelper(context, config); subscribe(); + + IModel validationModel = null; + if (IEditor.Mode.EDIT == mode) { + validationModel = helper.getValidationModel(); + } + + IFieldDescriptor field = helper.getField(); + if (field != null && !doesTemplateSupportValidation()) { + filter = new ValidationFilter() { + private static final long serialVersionUID = 1L; + + @Override + public void onValidation(IValidationResult validation) { + // nothing; is updated on render + } + + }; + if (validationModel != null && validationModel.getObject() != null) { + IValidationResult validationResult = validationModel.getObject(); + filter.setValid(isFieldValid(validationResult)); + } + + if (!field.isMultiple()) { + add(new CssClassAppender(filter)); + } + } + // use caption for backwards compatibility; i18n should use field name String captionKey = helper.getField() != null ? helper.getField().getName() : config.getString("caption"); add(new Label("name", new StringResourceModel(captionKey, this, null, config.getString("caption")))); @@ -152,7 +241,7 @@ } log.warn("The configuration node name '" + CONFIG_VALUELIST_OPTIONS + "' is deprecated. Rename it to '" - + CONFIG_CLUSTER_OPTIONS + "'. " + options.toString()); + + CONFIG_CLUSTER_OPTIONS + "'. " + options.toString()); } final Locale locale = SelectionUtils.getLocale(SelectionUtils.getNode(model)); @@ -180,6 +269,56 @@ } add(modeFragment); } + + + protected FieldPluginHelper getFieldHelper() { + return helper; + } + + /** + * Checks if a field has any violations attached to it. + * + * @param validation The IValidationResult that contains all violations that occurred for this editor + * @return true if there are no violations present or non of the validation belong to the current field + */ + private boolean isFieldValid(final IValidationResult validation) { + if (!validation.isValid()) { + IFieldDescriptor field = getFieldHelper().getField(); + if (field == null) { + return false; + } + for (Violation violation : validation.getViolations()) { + Set paths = violation.getDependentPaths(); + for (ModelPath path : paths) { + if (path.getElements().length > 0) { + ModelPathElement first = path.getElements()[0]; + if (first.getField().equals(field)) { + return false; + } + } + } + } + } + IFeedbackMessageFilter filter = new ContainerFeedbackMessageFilter(this); + return !getSession().getFeedbackMessages().hasMessage(filter); + } + + protected boolean doesTemplateSupportValidation() { + ITemplateEngine engine = getTemplateEngine(); + IFieldDescriptor field = helper.getField(); + if (field != null) { + try { + IClusterConfig template = engine.getTemplate(field.getTypeDescriptor(), mode); + return (template.getReferences().contains(IValidationService.VALIDATE_ID)); + } catch (TemplateEngineException e) { + return false; + } + } else { + return false; + } + } + + @Override public void renderHead(final IHeaderResponse response) { @@ -217,7 +356,7 @@ } protected Fragment populateCompareMode(final IPluginContext context, final IPluginConfig config, - final JcrMultiPropertyValueModel model, final ValueList valueList) { + final JcrMultiPropertyValueModel model, final ValueList valueList) { final Fragment modeFragment; modeFragment = new Fragment("mode", "view", this); @@ -254,7 +393,7 @@ } protected Fragment populateEditMode(final IPluginConfig config, final JcrMultiPropertyValueModel model, - final ValueList valueList, final IModel choicesModel) { + final ValueList valueList, final IModel choicesModel) { final Fragment modeFragment; modeFragment = new Fragment("mode", "edit", this); @@ -272,7 +411,7 @@ } protected Fragment addList(final IPluginConfig config, final JcrMultiPropertyValueModel model, - final ValueList valueList, final IModel choicesModel) { + final ValueList valueList, final IModel choicesModel) { final Fragment typeFragment; typeFragment = new Fragment("type", "edit-select", this); @@ -309,7 +448,7 @@ } protected Fragment addPalette(final IPluginConfig config, final JcrMultiPropertyValueModel model, - final ValueList valueList, final IModel choicesModel) { + final ValueList valueList, final IModel choicesModel) { final Fragment typeFragment; typeFragment = new Fragment("type", "edit-palette", this); @@ -366,7 +505,7 @@ } protected Fragment addCheckboxes(final JcrMultiPropertyValueModel model, final ValueList valueList, - final IModel choicesModel) { + final IModel choicesModel) { final Fragment typeFragment; typeFragment = new Fragment("type", "edit-checkboxes", this); \ No newline at end of file