/* * 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.taxonomy.plugin; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.jcr.Node; import javax.jcr.RepositoryException; import org.apache.wicket.ResourceReference; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.html.CSSPackageResource; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.RefreshingView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; import org.hippoecm.frontend.dialog.AbstractDialog; import org.hippoecm.frontend.dialog.DialogLink; import org.hippoecm.frontend.dialog.IDialogFactory; import org.hippoecm.frontend.model.IModelReference; import org.hippoecm.frontend.plugin.IPluginContext; 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.service.IEditor.Mode; import org.hippoecm.frontend.service.render.RenderPlugin; import org.onehippo.taxonomy.api.Category; import org.onehippo.taxonomy.api.Taxonomy; import org.onehippo.taxonomy.plugin.api.TaxonomyHelper; import org.onehippo.taxonomy.plugin.model.Classification; import org.onehippo.taxonomy.plugin.model.ClassificationDao; import org.onehippo.taxonomy.plugin.model.ClassificationModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Plugin that edits the classification for a document. The storage implementation is delegated to a * {@link ClassificationDao}, so this plugin is unaware of any of the taxonomy node types. */ public class TaxonomyPickerPlugin extends RenderPlugin<Node> { public static final String HIPPOTRANSLATION_TRANSLATED = "hippotranslation:translated"; public static final String HIPPOTRANSLATION_LOCALE = "hippotranslation:locale"; @SuppressWarnings("unused") private final static String SVN_ID = "$Id: TaxonomyPickerPlugin.java 68792 2009-06-24 10:44:39Z fvlankvelt $"; private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(TaxonomyPickerPlugin.class); private class CategoryListView extends RefreshingView<String> { private static final long serialVersionUID = 1L; public CategoryListView(String id) { super(id); } @Override protected Iterator<IModel<String>> getItemModels() { if (dao == null) { List<IModel<String>> models = Collections.emptyList(); return models.iterator(); } Classification classification = dao.getClassification(TaxonomyPickerPlugin.this.getModelObject()); final Iterator<String> upstream = classification.getKeys().iterator(); return new Iterator<IModel<String>>() { @Override public boolean hasNext() { return upstream.hasNext(); } @Override public IModel<String> next() { return new Model<String>(upstream.next()); } @Override public void remove() { upstream.remove(); } }; } @Override protected void populateItem(Item<String> item) { Taxonomy taxonomy = getTaxonomy(); if (taxonomy != null) { Category category = taxonomy.getCategoryByKey(item.getModelObject()); if (category != null) { item.add(new Label("key", new Model<String>(TaxonomyHelper.getCategoryName(category, getPreferredLocale())))); // <HCT> item.add(new Label("path", new Model<String>(TaxonomyHelper.getCategoryPath(category)))); // </HCT> } else { item.add(new Label("key", new ResourceModel("invalid.taxonomy.key"))); // <HCT> item.add(new Label("path", new ResourceModel("invalid.taxonomy.key"))); // </HCT> } } else { item.add(new Label("key", new ResourceModel("invalid.taxonomy"))); // <HCT> item.add(new Label("path", new ResourceModel("invalid.taxonomy"))); // </HCT> } } } private class CategoryCompareView extends ListView<Change<String>> { private static final long serialVersionUID = 1L; public CategoryCompareView(String id, IModel<List<Change<String>>> changeModel) { super(id, changeModel); } @Override protected void populateItem(ListItem<Change<String>> item) { Taxonomy taxonomy = getTaxonomy(); Change<String> change = item.getModelObject(); Label label; if (taxonomy != null) { Category category = taxonomy.getCategoryByKey(change.getValue()); if (category != null) { item.add(label = new Label("key", new Model<String>(TaxonomyHelper.getCategoryName(category, getPreferredLocale())))); // <HCT> item.add(new Label("path", new Model<String>(TaxonomyHelper.getCategoryPath(category)))); // </HCT> } else { item.add(label = new Label("key", new ResourceModel("invalid.taxonomy.key"))); // <HCT> item.add(new Label("path", new ResourceModel("invalid.taxonomy.key"))); // </HCT> } } else { item.add(label = new Label("key", new ResourceModel("invalid.taxonomy"))); // <HCT> item.add(new Label("path", new ResourceModel("invalid.taxonomy"))); // </HCT> } switch (change.getType()) { case ADDED: label.add(new AttributeAppender("class", new Model<String>("hippo-diff-added"), " ")); break; case REMOVED: label.add(new AttributeAppender("class", new Model<String>("hippo-diff-removed"), " ")); break; } } } private ClassificationDao dao; public TaxonomyPickerPlugin(final IPluginContext context, final IPluginConfig config) { super(context, config); add(CSSPackageResource.getHeaderContribution(TaxonomyPickerPlugin.class, "res/style.css")); add(new Label("title", new ResourceModel("title"))); dao = context.getService(config.getString(ClassificationDao.SERVICE_ID), ClassificationDao.class); if (dao == null) { LOG.warn("No DAO found to retrieve classification"); } final Mode mode = Mode.fromString(config.getString("mode", "view")); if (dao != null && mode == Mode.EDIT) { add(new TaxonomyPickerPlugin.CategoryListView("keys")); final ClassificationModel model = new ClassificationModel(dao, getModel()); IDialogFactory dialogFactory = new IDialogFactory() { private static final long serialVersionUID = 1L; @Override public AbstractDialog<Classification> createDialog() { return createPickerDialog(model, getPreferredLocale()); } }; add(new DialogLink("edit", new ResourceModel("edit"), dialogFactory, getDialogService())).setEnabled( getTaxonomy() != null); } else if (dao != null && mode == Mode.COMPARE && config.containsKey("model.compareTo")) { IModel<List<Change<String>>> changesModel = new LoadableDetachableModel<List<Change<String>>>() { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") @Override protected List<Change<String>> load() { if (dao != null) { IModelReference<Node> baseRef = context.getService(config.getString("model.compareTo"), IModelReference.class); if (baseRef != null) { IModel<Node> baseModel = baseRef.getModel(); if (baseModel != null) { List<String> currentKeys = dao.getClassification(getModel().getObject()).getKeys(); List<String> baseKeys = dao.getClassification(baseModel.getObject()).getKeys(); return LCS.getChangeSet(baseKeys.toArray(new String[baseKeys.size()]), currentKeys .toArray(new String[currentKeys.size()])); } } } return Collections.emptyList(); } }; add(new CategoryCompareView("keys", changesModel)); add(new Label("edit", changesModel).setVisible(false)); } else { add(new TaxonomyPickerPlugin.CategoryListView("keys")); add(new Label("edit").setVisible(false)); } final IModel<CanonicalCategory> canonicalNameModel = new LoadableDetachableModel<CanonicalCategory>() { private static final long serialVersionUID = 3417187712118563862L; @Override protected CanonicalCategory load() { Taxonomy taxonomy = getTaxonomy(); if (taxonomy != null) { Classification classification = dao.getClassification(TaxonomyPickerPlugin.this.getModelObject()); return new CanonicalCategory(taxonomy, classification.getCanonical(), getPreferredLocale()); } else { return null; } } }; add(new Label("canon", new StringResourceModel("canonical", this, canonicalNameModel)) { private static final long serialVersionUID = 7479470942521630615L; @Override public boolean isVisible() { CanonicalCategory canonicalCategory = canonicalNameModel.getObject(); return canonicalCategory != null && canonicalCategory.getName() != null; } @Override protected void onDetach() { canonicalNameModel.detach(); super.onDetach(); } }); setOutputMarkupId(true); } @Override public void onModelChanged() { redraw(); } /** * Creates and returns taxonomy picker dialog instance. * <P> * If you want to provide a custom taxonomy picker plugin, you might want to * override this method. * </P> * * @param model * @return */ protected AbstractDialog<Classification> createPickerDialog(ClassificationModel model, String preferredLocale) { return new TaxonomyPickerDialog(getPluginContext(), getPluginConfig(), model, preferredLocale); } /** * Returns the translation locale of the document if exists. * Otherwise, returns the user's UI locale as a fallback. * * @return */ protected String getPreferredLocale() { Node node = getModel().getObject(); try { if (node.isNodeType(HIPPOTRANSLATION_TRANSLATED)) { if (node.hasProperty(HIPPOTRANSLATION_LOCALE)) { return node.getProperty(HIPPOTRANSLATION_LOCALE).getString(); } } } catch (RepositoryException e) { LOG.error("Failed to detect hippotranslation:locale to choose the preferred locale", e); } return getLocale().getLanguage(); } private Taxonomy getTaxonomy() { IPluginConfig config = getPluginConfig(); ITaxonomyService service = getPluginContext() .getService(config.getString(ITaxonomyService.SERVICE_ID, ITaxonomyService.class.getName()), ITaxonomyService.class); final String taxonomyName = config.getString(ITaxonomyService.TAXONOMY_NAME); if (taxonomyName == null || taxonomyName.isEmpty()) { LOG.info("No configured/chosen taxonomy name. Found '{}'", taxonomyName); return null; } return service.getTaxonomy(taxonomyName); } @Override public void renderHead(HtmlHeaderContainer container) { super.renderHead(container); container.getHeaderResponse(). renderCSSReference(new ResourceReference(TaxonomyPickerPlugin.class, "res/style.css")); } }