/** * 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.brixcms.plugin.site.page.admin; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.util.lang.Objects; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.brixcms.jcr.wrapper.BrixNode; import org.brixcms.plugin.site.page.AbstractContainer; import org.brixcms.plugin.site.page.global.GlobalContainerNode; import org.brixcms.web.BrixFeedbackPanel; import org.brixcms.web.generic.BrixGenericPanel; import org.brixcms.web.util.DisabledClassAppender; import com.inmethod.grid.IDataSource; import com.inmethod.grid.IGridColumn; import com.inmethod.grid.SizeUnit; import com.inmethod.grid.column.CheckBoxColumn; import com.inmethod.grid.column.PropertyColumn; import com.inmethod.grid.column.editable.EditablePropertyColumn; import com.inmethod.grid.column.editable.SubmitCancelColumn; import com.inmethod.grid.datagrid.DataGrid; import com.inmethod.grid.datagrid.DefaultDataGrid; public class VariablesPanel extends BrixGenericPanel<BrixNode> { private AjaxLink<?> delete; public VariablesPanel(String id, IModel<BrixNode> model) { super(id, model); List<IGridColumn<DataSource, Entry, String>> columns = new ArrayList<IGridColumn<DataSource, Entry, String>>(); columns.add(new CheckBoxColumn<DataSource, Entry, String>("checkbox")); columns.add(new PropertyColumn<DataSource, Entry, String, String>(new ResourceModel("key"), "key")); columns.add(new EditablePropertyColumn<DataSource, Entry, String, String>(new ResourceModel("value"), "value") { @Override protected void addValidators(FormComponent<String> component) { component.setRequired(true); } }); columns.add(new SubmitCancelColumn<DataSource, Entry, String>("submitcancel", new ResourceModel("edit")) { @Override protected void onError(AjaxRequestTarget target, IModel<Entry> rowModel, WebMarkupContainer rowComponent) { target.addChildren(VariablesPanel.this, FeedbackPanel.class); } @Override protected void onSubmitted(AjaxRequestTarget target, IModel<Entry> rowModel, WebMarkupContainer rowComponent) { target.addChildren(VariablesPanel.this, FeedbackPanel.class); super.onSubmitted(target, rowModel, rowComponent); } }); final DataGrid<DataSource, Entry, String> grid = new DefaultDataGrid<DataSource, Entry, String>("grid", Model.of(new DataSource()), columns) { @Override public void onItemSelectionChanged(IModel<Entry> item, boolean newValue) { Optional<AjaxRequestTarget> target = getRequestCycle().find(AjaxRequestTarget.class); if (target.isPresent()) { target.get().add(delete); } super.onItemSelectionChanged(item, newValue); } }; add(grid); grid.setSelectToEdit(false); grid.setClickRowToSelect(true); grid.setContentHeight(17, SizeUnit.EM); add(delete = new AjaxLink<Void>("deleteSelected") { @Override public void onClick(AjaxRequestTarget target) { AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); for (IModel<Entry> m : grid.getSelectedItems()) { Entry e = m.getObject(); node.setVariableValue(e.getKey(), null); } node.save(); grid.markAllItemsDirty(); grid.update(); grid.resetSelectedItems(); target.add(this); } @Override public boolean isEnabled() { return grid.getSelectedItems().isEmpty() == false; } }); delete.add(new DisabledClassAppender()); delete.setOutputMarkupId(true); add(new InsertForm("form") { @Override protected void onItemAdded() { grid.markAllItemsDirty(); grid.update(); } }); add(new BrixFeedbackPanel("feedback").setOutputMarkupId(true)); } private class DataSource implements IDataSource<Entry>, Serializable { public IModel<Entry> model(Entry object) { return Model.of(object); } public void query(IQuery query, IQueryResult<Entry> result) { AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); List<Entry> res = new ArrayList<Entry>(); for (String s : node.getSavedVariableKeys()) { res.add(new Entry(s)); } Collections.sort(res, new Comparator<Entry>() { public int compare(Entry o1, Entry o2) { return o1.getKey().compareTo(o2.getKey()); } }); int total = res.size(); if (total > query.getFrom()) { res = res.subList((int) query.getFrom(), total); } result.setItems(res.iterator()); result.setTotalCount(total); } public void detach() { } } private class Entry implements Serializable { private final String key; public Entry(String key) { this.key = key; } public String getKey() { return key; } public String getValue() { AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); return node.getVariableValue(key, false); } public void setValue(String value) { AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); node.setVariableValue(key, value); node.save(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Entry == false) { return false; } Entry that = (Entry) obj; return Objects.equal(key, that.key); } @Override public int hashCode() { return Objects.hashCode(key); } } private abstract class InsertForm extends Form<Void> { public InsertForm(String id) { super(id); IModel<List<? extends String>> choicesModel = new LoadableDetachableModel<List<? extends String>>() { @Override protected List<? extends String> load() { List<String> result = new ArrayList<String>(); AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); result.addAll(node.getVariableKeys()); return result; } }; final TextField<String> tf; add(tf = new TextField<String>("key", new PropertyModel<String>(this, "key"))); tf.setRequired(true); tf.setOutputMarkupId(true); final DropDownChoice<String> keySuggestions; add(keySuggestions = new DropDownChoice<String>("keySuggestions", new Model<String>(), choicesModel) { @Override public boolean isVisible() { return VariablesPanel.this.getModelObject() instanceof GlobalContainerNode == false; } }); keySuggestions.setNullValid(true); keySuggestions.add(new AjaxFormComponentUpdatingBehavior("change") { @Override protected void onUpdate(AjaxRequestTarget target) { tf.setModelObject(keySuggestions.getModelObject()); keySuggestions.setModelObject(null); target.add(tf); target.add(keySuggestions); target.focusComponent(tf); } }); add(new TextField<String>("value", new PropertyModel<String>(this, "value")).setRequired(true)); add(new AjaxButton("submit") { @Override protected void onSubmit(AjaxRequestTarget target) { AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); node.setVariableValue(key, value); node.save(); onItemAdded(); key = null; value = null; target.add(InsertForm.this); target.addChildren(findParent(VariablesPanel.class), FeedbackPanel.class); } @Override protected void onError(AjaxRequestTarget target) { target.addChildren(findParent(VariablesPanel.class), FeedbackPanel.class); } }); tf.add(new IValidator<String>() { public void validate(IValidatable<String> validatable) { String key = validatable.getValue(); AbstractContainer node = (AbstractContainer) VariablesPanel.this.getModelObject(); if (key.contains("/") || key.contains(":")) { report(validatable, "keyValidator.invalidKey", key); } else if (node.getVariableValue(key, false) != null) { report(validatable, "keyValidator.duplicateKey", key); } } private void report(IValidatable<String> validatable, String messageKey, String key) { validatable.error(new ValidationError().addKey(messageKey).setVariable("key", key)); } }); } private String key; private String value; abstract protected void onItemAdded(); } }