Index: addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/DynamicDropdownPlugin.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/DynamicDropdownPlugin.java (revision 852) +++ addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/DynamicDropdownPlugin.java (revision ) @@ -15,8 +15,11 @@ */ package org.onehippo.forge.selection.frontend.plugin; +import org.apache.commons.lang.StringUtils; import org.apache.wicket.AttributeModifier; +import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; +import org.apache.wicket.Page; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; import org.apache.wicket.extensions.markup.html.form.select.IOptionRenderer; @@ -48,30 +51,27 @@ import org.slf4j.LoggerFactory; import javax.jcr.RepositoryException; + import java.io.Serializable; import java.util.*; /** - * A dynamic dropdown plugin, which is backed by a ValueListProvider service - * that provides a ValueList object. + * A dynamic dropdown plugin, which is backed by a ValueListProvider service that provides a ValueList object. *

- * The plugin configuration can be provided with a valuelist.provider - * property, defaulting to "service.valuelist.default", which is the name of the - * DocumentValueListProvider. + * The plugin configuration can be provided with a valuelist.provider property, defaulting to + * "service.valuelist.default", which is the name of the DocumentValueListProvider. *

- * The default DocumentValueListProvider reads a document of the type - * 'selection:valuelist', which contains key label pairs used to display values - * and labels in the dropdown. + * The default DocumentValueListProvider reads a document of the type 'selection:valuelist', which contains key label + * pairs used to display values and labels in the dropdown. *

- * The plugin configuration must then be provided with a source - * property, which can either be a valid UUID of a handle or the path to the - * document based on the JCR root. + * The plugin configuration must then be provided with a source property, which can either be a valid UUID + * of a handle or the path to the document based on the JCR root. *

- * List items that have a group name set, will be grouped together using OPTGROUP elements. Groups can be mixed - * with regular option elements. Groups and options without a group are shown in the order they are encountered - * in the {@link org.onehippo.forge.selection.frontend.model.ValueList} used. + * List items that have a group name set, will be grouped together using OPTGROUP elements. Groups can be mixed with + * regular option elements. Groups and options without a group are shown in the order they are encountered in the + * {@link org.onehippo.forge.selection.frontend.model.ValueList} used. */ -public class DynamicDropdownPlugin extends RenderPlugin { +public class DynamicDropdownPlugin extends RenderPlugin implements AjaxUpdatesAware { private static final String SERVICE_VALUELIST_DEFAULT = "service.valuelist.default"; @@ -81,7 +81,7 @@ private final SortHelper sortHelper = new SortHelper(); @SuppressWarnings("unchecked") - public DynamicDropdownPlugin(IPluginContext context, IPluginConfig config) { + public DynamicDropdownPlugin(final IPluginContext context, final IPluginConfig config) { super(context, config); // check the cnd for multiple, this is not supported (NB: check doesn't work for relaxed cnd) @@ -89,8 +89,8 @@ final boolean multiple = getValueModel().getJcrPropertymodel().getProperty().getDefinition().isMultiple(); if (multiple) { throw new IllegalStateException(this.getClass().getName() + " does not support fields that are multiple, " + - "please use " + DynamicMultiSelectPlugin.class.getName() + " for that." + - " Field name is " + getValueModel().getJcrPropertymodel().getProperty().getDefinition().getName() + "."); + "please use " + DynamicMultiSelectPlugin.class.getName() + " for that." + + " Field name is " + getValueModel().getJcrPropertymodel().getProperty().getDefinition().getName() + "."); } } catch (RepositoryException e) { throw new InstantiationError("Error instantiating " + this.getClass().getName() + ": " + e.getMessage()); @@ -98,117 +98,134 @@ add(CSSPackageResource.getHeaderContribution(DynamicDropdownPlugin.class, "DynamicDropdownPlugin.css")); - final String mode = config.getString(ITemplateEngine.MODE); - - // configured provider, or the default - String providerName = config.getString(Config.VALUELIST_PROVIDER, SERVICE_VALUELIST_DEFAULT); - IValueListProvider selectedProvider = context.getService(providerName, IValueListProvider.class); - - if (selectedProvider == null && !SERVICE_VALUELIST_DEFAULT.equals(providerName)) { - selectedProvider = context.getService(SERVICE_VALUELIST_DEFAULT, IValueListProvider.class); + final ValueList valueList = getValueList(config.getString(Config.SOURCE)); + if(valueList == null) { + return; } - if (selectedProvider == null) { - log.warn("DynamicDropdownPlugin: value list provider can not be found by name '{}'", - config.getString(Config.VALUELIST_PROVIDER)); + final String mode = config.getString(ITemplateEngine.MODE); + List selectItems = getInternalDropdownModel(valueList); + if ("edit".equals(mode)) { + add(getSelect(config, valueList, selectItems)); - DropDownChoice dummyChoice = new DropDownChoice("selectDropdown"); + Label valueLabel = new Label("selectLabel"); + valueLabel.setVisibilityAllowed(false); + add(valueLabel); + } else { + DropDownChoice dummyChoice = new DropDownChoice("selectDropdown"); dummyChoice.setVisibilityAllowed(false); add(dummyChoice); - Label valueLabel = new Label("selectLabel", new Model("-")); - add(valueLabel); - return; + + Label label = null; + if ("compare".equals(mode)) { + if (config.containsKey("model.compareTo")) { + IModelReference baseRef = context.getService(config.getString("model.compareTo"), + IModelReference.class); + if (baseRef != null) { + IModel baseModel = baseRef.getModel(); + if (baseModel == null) { + log.info("base model service provides null model"); + baseModel = new Model(null); - } + } + IModel baseLabel = new ValueLabelModel(valueList, baseModel); + IModel curLabel = new ValueLabelModel(valueList, getValueModel()); + label = (Label) new Label("selectLabel", new TextDiffModel(baseLabel, curLabel)) + .setEscapeModelStrings(false); + } else { + log.warn("opened in compare mode, but no base model service is available"); + } + } else { + log.warn("opened in compare mode, but no base model was configured"); + } + } + if (label == null) { + label = new Label("selectLabel", new ValueLabelModel(valueList, getModel())); + } + add(label); + } + } - final Locale locale = SelectionUtils.getLocale(SelectionUtils.getNode(getModel())); - final ValueList valueList = selectedProvider.getValueList(config.getString(Config.SOURCE), locale); - - sortHelper.sort(valueList, config); - - List selectItems = getInternalDropdownModel(valueList); - if ("edit".equals(mode)) { + private Select getSelect(final IPluginConfig config, final ValueList valueList, final List selectItems) { - final IOptionRenderer optionRenderer = new IOptionRenderer() { - private static final long serialVersionUID = 1L; + final IOptionRenderer optionRenderer = new IOptionRenderer() { + private static final long serialVersionUID = 1L; - public String getDisplayValue(String option) { - return valueList.getListItemByKey(option).getLabel(); - } + public String getDisplayValue(String option) { + return valueList.getListItemByKey(option).getLabel(); + } - public IModel getModel(String option) { - return new Model(option); - } - }; - Select select = new Select("selectDropdown", getValueModel()); - RepeatingView optionsListView = new RepeatingView("selectDropdownItem"); + public IModel getModel(String option) { + return new Model(option); + } + }; + Select select = new Select("selectDropdown", getValueModel()); + RepeatingView optionsListView = new RepeatingView("selectDropdownItem"); - int i = 0; - for (InternalSelectItem selectItem : selectItems) { - if (selectItem instanceof InternalSelectOptionGroup) { - InternalSelectOptionGroup optionGroup = (InternalSelectOptionGroup) selectItem; - Fragment optionGroupFragment = new OptionGroupFragment("dropdownListViewItem-" + i++, - "selectOptionGroup", optionGroup, optionRenderer); - optionGroupFragment.setRenderBodyOnly(true); - optionsListView.add(optionGroupFragment); - } else { - InternalSelectOption option = (InternalSelectOption) selectItem; - OptionFragment optionFragment = new OptionFragment("dropdownListViewItem-" + i++, "selectOption", - option); - optionFragment.setRenderBodyOnly(true); - optionsListView.add(optionFragment); - } - } - select.add(optionsListView); + int i = 0; + for (InternalSelectItem selectItem : selectItems) { + if (selectItem instanceof InternalSelectOptionGroup) { + InternalSelectOptionGroup optionGroup = (InternalSelectOptionGroup) selectItem; + Fragment optionGroupFragment = new OptionGroupFragment("dropdownListViewItem-" + i++, + "selectOptionGroup", optionGroup, optionRenderer); + optionGroupFragment.setRenderBodyOnly(true); + optionsListView.add(optionGroupFragment); + } else { + InternalSelectOption option = (InternalSelectOption) selectItem; + OptionFragment optionFragment = new OptionFragment("dropdownListViewItem-" + i++, "selectOption", + option); + optionFragment.setRenderBodyOnly(true); + optionsListView.add(optionFragment); + } + } + select.add(optionsListView); - select.add(new OnChangeAjaxBehavior() { - private static final long serialVersionUID = 1L; + select.add(new OnChangeAjaxBehavior() { + private static final long serialVersionUID = 1L; - @Override - protected void onUpdate(AjaxRequestTarget target) { + @Override + protected void onUpdate(AjaxRequestTarget target) { + final String broadcastEventId = config.getString(Config.BROADCAST_EVENT_ID); + if (StringUtils.isNotEmpty(broadcastEventId)) { + log.debug("Broadcasting event {}", broadcastEventId); + System.out.println("Broadcasting event: " + broadcastEventId); + broadcastEvent(target, getPage(), broadcastEventId, getModelObject()); } + } - }); + }); - add(select); + return select; + } - Label valueLabel = new Label("selectLabel"); - valueLabel.setVisibilityAllowed(false); - add(valueLabel); - } else { - DropDownChoice dummyChoice = new DropDownChoice("selectDropdown"); + private ValueList getValueList(String path) { + // configured provider, or the default + String providerName = getPluginConfig().getString(Config.VALUELIST_PROVIDER, SERVICE_VALUELIST_DEFAULT); + IValueListProvider selectedProvider = getPluginContext().getService(providerName, IValueListProvider.class); + + if (selectedProvider == null && !SERVICE_VALUELIST_DEFAULT.equals(providerName)) { + selectedProvider = getPluginContext().getService(SERVICE_VALUELIST_DEFAULT, IValueListProvider.class); + } + + if (selectedProvider == null) { + log.warn("DynamicDropdownPlugin: value list provider can not be found by name '{}'", + getPluginConfig().getString(Config.VALUELIST_PROVIDER)); + + DropDownChoice dummyChoice = new DropDownChoice("selectDropdown"); dummyChoice.setVisibilityAllowed(false); add(dummyChoice); + Label valueLabel = new Label("selectLabel", new Model("-")); + add(valueLabel); + return null; + } - Label label = null; - if ("compare".equals(mode)) { - if (config.containsKey("model.compareTo")) { - IModelReference baseRef = context.getService(config.getString("model.compareTo"), - IModelReference.class); - if (baseRef != null) { - IModel baseModel = baseRef.getModel(); - if (baseModel == null) { - log.info("base model service provides null model"); - baseModel = new Model(null); + final Locale locale = SelectionUtils.getLocale(SelectionUtils.getNode(getModel())); + final ValueList valueList = selectedProvider.getValueList(path, locale); + sortHelper.sort(valueList, getPluginConfig()); + + return valueList; - } + } - IModel baseLabel = new ValueLabelModel(valueList, baseModel); - IModel curLabel = new ValueLabelModel(valueList, getValueModel()); - label = (Label) new Label("selectLabel", new TextDiffModel(baseLabel, curLabel)) - .setEscapeModelStrings(false); - } else { - log.warn("opened in compare mode, but no base model service is available"); - } - } else { - log.warn("opened in compare mode, but no base model was configured"); - } - } - if (label == null) { - label = new Label("selectLabel", new ValueLabelModel(valueList, getModel())); - } - add(label); - } - } private List getInternalDropdownModel(ValueList valueList) { List selectItems = new ArrayList(valueList.size()); selectItems.add(new InternalSelectOption("", - new StringResourceModel("choose.one", DynamicDropdownPlugin.this, null).getString())); + new StringResourceModel("choose.one", DynamicDropdownPlugin.this, null).getString())); Map groupsSoFar = new HashMap(); for (ListItem listItem : valueList) { if (isGroupItem(listItem)) { @@ -234,6 +251,36 @@ return (JcrPropertyValueModel) getModel(); } + @Override + public void onAjaxUpdate(AjaxRequestTarget target, final String eventId, final Object message) { + final String receiveEventId = getPluginConfig().getString(Config.RECEIVE_EVENT_ID); + if (StringUtils.equals(receiveEventId, eventId)) { + log.info("Received message: " + message); + System.out.println("Received eventId: " + eventId + " message: " + message); + // TODO: change source valuelist + + String valueListName = "/content/documents/valuelists/" + message; + final ValueList valueList = getValueList(valueListName); + + List selectItems = getInternalDropdownModel(valueList); + replace(getSelect(getPluginConfig(), valueList, selectItems)); + DynamicDropdownPlugin.this.redraw(); + } + } + + protected void broadcastEvent(final AjaxRequestTarget target, final Page page, final String eventId, final Object message) { + page.visitChildren(new Component.IVisitor() { + @Override + public Object component(final Component component) { + if(component instanceof AjaxUpdatesAware) { + ((AjaxUpdatesAware) component).onAjaxUpdate(target, eventId, message); + } + return null; + } + }); + } + + private abstract static class InternalSelectItem { private String label; @@ -285,15 +332,15 @@ private static final long serialVersionUID = 1L; public OptionGroupFragment(String id, String markupId, InternalSelectOptionGroup optionGroup, - IOptionRenderer optionRenderer) { + IOptionRenderer optionRenderer) { super(id, markupId, DynamicDropdownPlugin.this); // a container is bound to the optgroup element, so we can set the label on the optgroup element MarkupContainer container = new WebMarkupContainer("optionGroup"); container.add(new AttributeModifier("label", new StringResourceModel(optionGroup.getLabel(), this, null, - optionGroup.getLabel()))); + optionGroup.getLabel()))); // the SelectOptions widget is a repeating view. This widget will thus replace the tag to which it is bound SelectOptions optionGroupCmpt = new SelectOptions("optionGroupItems", optionGroup - .getOptions(), optionRenderer); + .getOptions(), optionRenderer); container.add(optionGroupCmpt); add(container); } @@ -302,8 +349,8 @@ /** * Fragment containing an {@link org.apache.wicket.extensions.markup.html.form.select.SelectOption}. The fragment is - * a direct child of a select tag. We need this fragment to add a label to a {@link org.apache.wicket.extensions.markup.html.form.select.SelectOption} - * widget. + * a direct child of a select tag. We need this fragment to add a label to a {@link + * org.apache.wicket.extensions.markup.html.form.select.SelectOption} widget. */ private class OptionFragment extends Fragment { private static final long serialVersionUID = 1L; @@ -311,7 +358,7 @@ public OptionFragment(String id, String markupId, InternalSelectOption selectOption) { super(id, markupId, DynamicDropdownPlugin.this); SelectOption wicketSelectOption = new SelectOption("option", new Model(selectOption - .getValue())); + .getValue())); wicketSelectOption.add(new Label("optionLabel", selectOption.getLabel())); add(wicketSelectOption); } Index: addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/AjaxUpdatesAware.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/AjaxUpdatesAware.java (revision ) +++ addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/AjaxUpdatesAware.java (revision ) @@ -0,0 +1,27 @@ +/* + * Copyright 2009-2013 Hippo B.V. (http://www.onehippo.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onehippo.forge.selection.frontend.plugin; + +import org.apache.wicket.ajax.AjaxRequestTarget; + +/** + * @version $Id: $ + */ +public interface AjaxUpdatesAware { + + void onAjaxUpdate(AjaxRequestTarget target, String eventId, Object message); + +} Index: addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/Config.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/Config.java (revision 852) +++ addon-cms/src/main/java/org/onehippo/forge/selection/frontend/plugin/Config.java (revision ) @@ -25,4 +25,6 @@ public final static String SORT_COMPARATOR = "sortComparator"; public final static String SORT_ORDER = "sortOrder"; public final static String SORT_BY = "sortBy"; + public final static String BROADCAST_EVENT_ID = "broadcastEventId"; + public final static String RECEIVE_EVENT_ID = "receiveEventId"; }