Java tutorial
/* * Copyright 2014-2016 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.hippoecm.frontend.plugins.reviewedactions; import java.io.Serializable; import java.rmi.RemoteException; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.jcr.Node; import javax.jcr.RepositoryException; import org.apache.wicket.Component; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.util.string.Strings; import org.hippoecm.addon.workflow.DestinationDialog; import org.hippoecm.addon.workflow.StdWorkflow; import org.hippoecm.addon.workflow.WorkflowDescriptorModel; import org.hippoecm.frontend.dialog.ExceptionDialog; import org.hippoecm.frontend.dialog.IDialogService; import org.hippoecm.frontend.editor.workflow.CopyNameHelper; import org.hippoecm.frontend.editor.workflow.dialog.DeleteDialog; import org.hippoecm.frontend.editor.workflow.dialog.WhereUsedDialog; import org.hippoecm.frontend.model.JcrNodeModel; import org.hippoecm.frontend.model.NodeModelWrapper; import org.hippoecm.frontend.plugin.IPluginContext; import org.hippoecm.frontend.plugin.config.IPluginConfig; import org.hippoecm.frontend.plugins.reviewedactions.dialogs.HistoryDialog; import org.hippoecm.frontend.plugins.standards.icon.HippoIcon; import org.hippoecm.frontend.plugins.standardworkflow.RenameDocumentArguments; import org.hippoecm.frontend.plugins.standardworkflow.RenameDocumentDialog; import org.hippoecm.frontend.session.UserSession; import org.hippoecm.frontend.skin.Icon; import org.hippoecm.frontend.util.CodecUtils; import org.hippoecm.repository.api.Document; import org.hippoecm.repository.api.HippoNode; import org.hippoecm.repository.api.StringCodec; import org.hippoecm.repository.api.Workflow; import org.hippoecm.repository.api.WorkflowException; import org.hippoecm.repository.api.WorkflowManager; import org.hippoecm.repository.standardworkflow.DefaultWorkflow; import org.hippoecm.repository.standardworkflow.FolderWorkflow; import org.onehippo.repository.documentworkflow.DocumentWorkflow; import org.onehippo.repository.util.JcrConstants; public class DocumentWorkflowPlugin extends AbstractDocumentWorkflowPlugin { private static final String DEFAULT_FOLDERWORKFLOW_CATEGORY = "embedded"; private StdWorkflow deleteAction; private StdWorkflow requestDeleteAction; private StdWorkflow renameAction; private StdWorkflow copyAction; private StdWorkflow moveAction; private StdWorkflow whereUsedAction; private StdWorkflow historyAction; public DocumentWorkflowPlugin(final IPluginContext context, final IPluginConfig config) { super(context, config); add(renameAction = new StdWorkflow("rename", new StringResourceModel("rename-label", this, null), context, getModel()) { private RenameDocumentArguments renameDocumentArguments; @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.TYPE); } @Override protected IModel getTooltip() { if (isEnabled()) { return super.getTooltip(); } else { return new StringResourceModel("unavailable-tip", this, null); } } @Override protected IDialogService.Dialog createRequestDialog() { String locale = null; try { final HippoNode node = getModelNode(); locale = CodecUtils.getLocaleFromNodeAndAncestors(node); renameDocumentArguments = new RenameDocumentArguments(node.getDisplayName(), node.getName()); } catch (RepositoryException ex) { renameDocumentArguments = new RenameDocumentArguments(); } return new RenameDocumentDialog(renameDocumentArguments, new StringResourceModel("rename-title", DocumentWorkflowPlugin.this, null), this, CodecUtils.getNodeNameCodecModel(context, locale), this.getModel()); } private HippoNode getModelNode() throws RepositoryException { return (HippoNode) getModel().getNode(); } @Override protected String execute(Workflow wf) throws Exception { final String targetName = renameDocumentArguments.getTargetName(); final String uriName = renameDocumentArguments.getUriName(); if (Strings.isEmpty(targetName)) { throw new WorkflowException("No name given for document node"); } if (Strings.isEmpty(uriName)) { throw new WorkflowException("No URL name given for document node"); } final HippoNode node = getModelNode(); String nodeName = getNodeNameCodec(node).encode(uriName); String localName = getLocalizeCodec().encode(targetName); if (Strings.isEmpty(nodeName)) { throw new IllegalArgumentException("You need to enter a name"); } WorkflowManager manager = UserSession.get().getWorkflowManager(); DefaultWorkflow defaultWorkflow = (DefaultWorkflow) manager.getWorkflow("core", node); if (!((WorkflowDescriptorModel) getDefaultModel()).getNode().getName().equals(nodeName)) { ((DocumentWorkflow) wf).rename(nodeName); } if (!node.getDisplayName().equals(localName)) { defaultWorkflow.setDisplayName(localName); } return null; } }); add(copyAction = new StdWorkflow("copy", new StringResourceModel("copy-label", this, null), context, getModel()) { NodeModelWrapper destination = null; String name = null; @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.FILES); } @Override protected IDialogService.Dialog createRequestDialog() { destination = new NodeModelWrapper(getFolder()) { }; try { IModel<StringCodec> codec = new AbstractReadOnlyModel<StringCodec>() { @Override public StringCodec getObject() { try { return getNodeNameCodec(getModelNode(), getFolder().getNode()); } catch (RepositoryException e) { return getNodeNameCodec(getFolder().getNode()); } } }; final CopyNameHelper copyNameHelper = new CopyNameHelper(codec, new StringResourceModel("copyof", DocumentWorkflowPlugin.this, null).getString()); name = copyNameHelper.getCopyName(getModelNode().getDisplayName(), destination.getNodeModel().getNode()); } catch (RepositoryException ex) { return new ExceptionDialog(ex); } return new DestinationDialog( new StringResourceModel("copy-title", DocumentWorkflowPlugin.this, null), new StringResourceModel("copy-name", DocumentWorkflowPlugin.this, null), new PropertyModel<>(this, "name"), destination, getPluginContext(), getPluginConfig()) { { setOkEnabled(true); } @Override public void invokeWorkflow() throws Exception { copyAction.invokeWorkflow(); } @Override protected boolean checkPermissions() { return isWritePermissionGranted(destination.getChainedModel()); } @Override protected boolean checkFolderTypes() { return isDocumentAllowedInFolder(DocumentWorkflowPlugin.this.getModel(), destination.getChainedModel()); } }; } @Override protected String execute(Workflow wf) throws Exception { Node folder = destination != null ? destination.getNodeModel().getNode() : new JcrNodeModel("/").getNode(); Node document = getModel().getNode(); String nodeName = getNodeNameCodec(document, folder).encode(name); DocumentWorkflow workflow = (DocumentWorkflow) wf; workflow.copy(new Document(folder), nodeName); JcrNodeModel resultModel = new JcrNodeModel(folder.getPath() + "/" + nodeName); Node result = resultModel.getNode(); WorkflowManager manager = UserSession.get().getWorkflowManager(); DefaultWorkflow defaultWorkflow = (DefaultWorkflow) manager.getWorkflow("core", result.getNode(nodeName)); defaultWorkflow.setDisplayName(getLocalizeCodec().encode(name)); browseTo(resultModel); return null; } private HippoNode getModelNode() throws RepositoryException { return (HippoNode) getModel().getNode(); } }); add(moveAction = new StdWorkflow("move", new StringResourceModel("move-label", this, null), context, getModel()) { NodeModelWrapper destination; @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.MOVE_INTO); } @Override protected IModel getTooltip() { if (isEnabled()) { return super.getTooltip(); } else { return new StringResourceModel("unavailable-tip", this, null); } } @Override protected IDialogService.Dialog createRequestDialog() { destination = new NodeModelWrapper(getFolder()) { }; return new DestinationDialog( new StringResourceModel("move-title", DocumentWorkflowPlugin.this, null), null, null, destination, getPluginContext(), getPluginConfig()) { @Override public void invokeWorkflow() throws Exception { moveAction.invokeWorkflow(); } @Override protected boolean checkPermissions() { return isWritePermissionGranted(destination.getChainedModel()); } @Override protected boolean checkFolderTypes() { return isDocumentAllowedInFolder(DocumentWorkflowPlugin.this.getModel(), destination.getChainedModel()); } }; } @Override protected String execute(Workflow wf) throws Exception { Node document = getModel().getNode(); Node folder = destination != null ? destination.getNodeModel().getObject() : new JcrNodeModel("/").getNode(); String nodeName = document.getName(); DocumentWorkflow workflow = (DocumentWorkflow) wf; workflow.move(new Document(folder), nodeName); browseTo(new JcrNodeModel(folder.getPath() + "/" + nodeName)); return null; } }); add(deleteAction = new StdWorkflow("delete", new StringResourceModel("delete-label", this, null), context, getModel()) { @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.TIMES); } @Override protected IModel getTooltip() { if (isEnabled()) { return super.getTooltip(); } else { return new StringResourceModel("unavailable-tip", this, null); } } @Override protected IDialogService.Dialog createRequestDialog() { IModel<String> message = new StringResourceModel("delete-message", DocumentWorkflowPlugin.this, null, getDocumentName()); IModel<String> title = new StringResourceModel("delete-title", DocumentWorkflowPlugin.this, null, getDocumentName()); return new DeleteDialog(title, getModel(), message, this, getEditorManager()); } @Override protected String execute(Workflow wf) throws Exception { DocumentWorkflow workflow = (DocumentWorkflow) wf; workflow.delete(); return null; } }); add(requestDeleteAction = new StdWorkflow("requestDelete", new StringResourceModel("request-delete", this, null), context, getModel()) { @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.TIMES); } @Override protected IDialogService.Dialog createRequestDialog() { IModel<String> message = new StringResourceModel("delete-message", DocumentWorkflowPlugin.this, null, getDocumentName()); IModel<String> title = new StringResourceModel("delete-title", DocumentWorkflowPlugin.this, null, getDocumentName()); return new DeleteDialog(title, getModel(), message, this, getEditorManager()); } @Override protected String execute(Workflow wf) throws Exception { DocumentWorkflow workflow = (DocumentWorkflow) wf; workflow.requestDeletion(); return null; } }); add(whereUsedAction = new StdWorkflow("where-used", new StringResourceModel("where-used-label", this, null), context, getModel()) { @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.LINK); } @Override protected IDialogService.Dialog createRequestDialog() { WorkflowDescriptorModel wdm = getModel(); return new WhereUsedDialog(wdm, getEditorManager()); } @Override protected String execute(Workflow wf) throws Exception { return null; } }); add(historyAction = new StdWorkflow("history", new StringResourceModel("history-label", this, null), context, getModel()) { @Override public String getSubMenu() { return "document"; } @Override protected Component getIcon(final String id) { return HippoIcon.fromSprite(id, Icon.RESTORE); } @Override protected IDialogService.Dialog createRequestDialog() { WorkflowDescriptorModel wdm = getModel(); return new HistoryDialog(wdm, getEditorManager()); } @Override protected String execute(Workflow wf) throws Exception { return null; } }); Map<String, Serializable> info = getHints(); if (!info.containsKey("delete")) { hideOrDisable(info, "requestDelete", requestDeleteAction); } else { requestDeleteAction.setVisible(false); } hideOrDisable(info, "delete", deleteAction); hideOrDisable(info, "rename", renameAction); hideOrDisable(info, "move", moveAction); hideOrDisable(info, "copy", copyAction); hideOrDisable(info, "listVersions", historyAction); } /** * Helper method for fetching a StringCodec for a document. If document has a locale, use it to find a fitting * {@link StringCodec}, otherwise search the destination tree for a locale and use that to find a fitting * {@link StringCodec}. */ private StringCodec getNodeNameCodec(final Node document, final Node destination) { String locale = CodecUtils.getLocaleFromDocumentOrFolder(document, destination); return CodecUtils.getNodeNameCodec(getPluginContext(), locale); } private static boolean isWritePermissionGranted(IModel<Node> model) { Node node = model != null ? model.getObject() : null; if (node != null) { try { return UserSession.get().getJcrSession().hasPermission(node.getPath(), JcrConstants.JCR_WRITE); } catch (RepositoryException ignore) { } } return false; } private static boolean isDocumentAllowedInFolder(final WorkflowDescriptorModel documentModel, IModel<Node> destinationFolder) { try { final Node handle = documentModel.getNode(); if (handle.hasNode(handle.getName())) { final String documentType = handle.getNode(handle.getName()).getPrimaryNodeType().getName(); // get allowed folder types from hints() method on folder workflow final Workflow workflow = new WorkflowDescriptorModel(DEFAULT_FOLDERWORKFLOW_CATEGORY, destinationFolder.getObject()).getWorkflow(); if (workflow instanceof FolderWorkflow) { final Map<String, Set<String>> prototypes = (Map<String, Set<String>>) workflow.hints() .get("prototypes"); // squash all configured values into one set final Set<String> allowedTypes = new HashSet<>(); for (final String key : prototypes.keySet()) { allowedTypes.addAll(prototypes.get(key)); } log.debug("Document type {} {} allowed in folder {} by folderTypes {}", documentType, (allowedTypes.contains(documentType) ? "" : "NOT"), destinationFolder.getObject().getPath(), allowedTypes); return allowedTypes.contains(documentType); } else { log.info("Workflow by category {} on subject {} is not a FolderWorkflow but {}", DEFAULT_FOLDERWORKFLOW_CATEGORY, destinationFolder.getObject(), ((workflow == null) ? "null" : workflow.getClass().getName())); } } else { log.error("(Supposed) document handle {} does not have same-name subnode", handle.getPath()); } } catch (RepositoryException | RemoteException e) { log.error(e.getClass().getName() + " during check for workflow allowed in folder: " + e.getMessage()); } catch (WorkflowException we) { log.error(we.getClass().getName() + " during workflow execution", we); } // forbid workflow action if something's wrong return false; } }