info.magnolia.security.app.dialog.field.WorkspaceAccessFieldFactory.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.security.app.dialog.field.WorkspaceAccessFieldFactory.java

Source

/**
 * This file Copyright (c) 2013-2015 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.security.app.dialog.field;

import info.magnolia.cms.security.Permission;
import info.magnolia.i18nsystem.SimpleTranslator;
import info.magnolia.jcr.RuntimeRepositoryException;
import info.magnolia.jcr.util.NodeUtil;
import info.magnolia.objectfactory.ComponentProvider;
import info.magnolia.objectfactory.Components;
import info.magnolia.ui.api.app.ChooseDialogCallback;
import info.magnolia.ui.api.context.UiContext;
import info.magnolia.ui.contentapp.choosedialog.ChooseDialogComponentProviderUtil;
import info.magnolia.ui.contentapp.field.WorkbenchFieldDefinition;
import info.magnolia.ui.dialog.choosedialog.ChooseDialogPresenter;
import info.magnolia.ui.dialog.choosedialog.ChooseDialogView;
import info.magnolia.ui.dialog.definition.ConfiguredChooseDialogDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredJcrContentConnectorDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredNodeTypeDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.NodeTypeDefinition;
import info.magnolia.ui.vaadin.integration.jcr.AbstractJcrNodeAdapter;
import info.magnolia.ui.vaadin.integration.jcr.DefaultProperty;
import info.magnolia.ui.vaadin.integration.jcr.DefaultPropertyUtil;
import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeAdapter;
import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
import info.magnolia.ui.vaadin.integration.jcr.ModelConstants;
import info.magnolia.ui.workbench.column.definition.ColumnDefinition;
import info.magnolia.ui.workbench.column.definition.PropertyColumnDefinition;
import info.magnolia.ui.workbench.definition.ConfiguredWorkbenchDefinition;
import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
import info.magnolia.ui.workbench.definition.WorkbenchDefinition;
import info.magnolia.ui.workbench.tree.TreePresenterDefinition;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;

import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.Field;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

/**
 * Field builder for the workspace ACL field.  Adds data to the item in an intermediary format that is transformed to the
 * final format by {@link info.magnolia.security.app.dialog.action.SaveRoleDialogAction}.
 *
 * @param <D> definition type
 * @see WorkspaceAccessFieldDefinition
 * @see info.magnolia.security.app.dialog.action.SaveRoleDialogAction
 */
public class WorkspaceAccessFieldFactory<D extends WorkspaceAccessFieldDefinition>
        extends AbstractAccessFieldFactory<D> {

    private static final Logger log = LoggerFactory.getLogger(WorkspaceAccessFieldFactory.class);

    public static final String INTERMEDIARY_FORMAT_PROPERTY_NAME = "__intermediary_format";
    public static final String ACCESS_TYPE_PROPERTY_NAME = "accessType";

    private final UiContext uiContext;
    private final SimpleTranslator i18n;
    private final ComponentProvider componentProvider;
    private ChooseDialogPresenter workbenchChooseDialogPresenter;

    @Inject
    public WorkspaceAccessFieldFactory(D definition, Item relatedFieldItem, UiContext uiContext,
            ChooseDialogPresenter workbenchChooseDialogPresenter, SimpleTranslator i18n,
            ComponentProvider componentProvider) {
        super(definition, relatedFieldItem);
        this.uiContext = uiContext;
        this.workbenchChooseDialogPresenter = workbenchChooseDialogPresenter;
        this.i18n = i18n;
        this.componentProvider = componentProvider;
    }

    /**
     * @deprecated since 5.3.1. {@link ComponentProvider} has to be injected in order to create the choose-dialog specific component provider, with proper bindings for e.g. {@link info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector} or {@link info.magnolia.ui.imageprovider.ImageProvider}.
     */
    @Deprecated
    public WorkspaceAccessFieldFactory(D definition, Item relatedFieldItem, UiContext uiContext,
            ChooseDialogPresenter workbenchChooseDialogPresenter, SimpleTranslator i18n) {
        this(definition, relatedFieldItem, uiContext, workbenchChooseDialogPresenter, i18n,
                Components.getComponentProvider());
    }

    @Override
    protected Field<Object> createFieldComponent() {

        final String aclName = "acl_" + getFieldDefinition().getWorkspace();

        final VerticalLayout layout = new VerticalLayout();
        layout.setSpacing(true);

        try {

            final JcrNodeAdapter roleItem = (JcrNodeAdapter) item;

            final VerticalLayout aclLayout = new VerticalLayout();

            final Label emptyLabel = new Label(i18n.translate("security.workspace.field.noAccess"));

            // Since JcrNewNodeAdapter.getJcrItem() returns the parent node we need to skip this step because we don't want to inspect the parent node
            if (!(roleItem instanceof JcrNewNodeAdapter)) {
                Node roleNode = roleItem.getJcrItem();
                if (roleNode.hasNode(aclName)) {

                    final Node aclNode = roleNode.getNode(aclName);

                    AccessControlList acl = new AccessControlList();
                    acl.readEntries(aclNode);

                    AbstractJcrNodeAdapter aclItem = new JcrNodeAdapter(aclNode);
                    roleItem.addChild(aclItem);

                    aclItem.addItemProperty(INTERMEDIARY_FORMAT_PROPERTY_NAME,
                            new DefaultProperty<String>(String.class, "true"));

                    final Set<AccessControlList.Entry> uniqueEntries = new HashSet<AccessControlList.Entry>();
                    for (final Node aclEntryNode : NodeUtil.getNodes(aclNode)) {
                        AccessControlList.Entry entry = acl.getEntryByNode(aclEntryNode);
                        if (uniqueEntries.contains(entry)) {
                            continue;
                        }

                        uniqueEntries.add(entry);
                        long permissions = entry.getPermissions();
                        long accessType = entry.getAccessType();
                        String path = entry.getPath();

                        JcrNodeAdapter entryItem = new JcrNodeAdapter(aclEntryNode);
                        entryItem.addItemProperty(INTERMEDIARY_FORMAT_PROPERTY_NAME,
                                new DefaultProperty<String>(String.class, "true"));
                        final Property<Long> permissionsProperty = DefaultPropertyUtil
                                .newDefaultProperty(Long.class, null);
                        entryItem.addItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME, permissionsProperty);
                        final Property<Long> accessProperty = DefaultPropertyUtil.newDefaultProperty(Long.class,
                                null);
                        entryItem.addItemProperty(ACCESS_TYPE_PROPERTY_NAME, accessProperty);
                        final Property<String> pathProperty = DefaultPropertyUtil.newDefaultProperty(String.class,
                                null);
                        entryItem.addItemProperty(AccessControlList.PATH_PROPERTY_NAME, pathProperty);

                        permissionsProperty.setValue(permissions);
                        accessProperty.setValue(accessType);
                        pathProperty.setValue(path);

                        aclItem.addChild(entryItem);

                        Component ruleRow = createRuleRow(aclLayout, entryItem, emptyLabel);
                        aclLayout.addComponent(ruleRow);
                    }
                }
            }

            if (aclLayout.getComponentCount() == 0) {
                aclLayout.addComponent(emptyLabel);
            }

            final HorizontalLayout buttons = new HorizontalLayout();
            final Button addButton = new Button(i18n.translate("security.workspace.field.addButton"));
            addButton.addClickListener(new Button.ClickListener() {

                @Override
                public void buttonClick(Button.ClickEvent event) {
                    try {

                        AbstractJcrNodeAdapter aclItem = getOrAddAclItem(roleItem, aclName);
                        if (aclItem.getItemProperty(INTERMEDIARY_FORMAT_PROPERTY_NAME) == null) {
                            aclItem.addItemProperty(INTERMEDIARY_FORMAT_PROPERTY_NAME,
                                    new DefaultProperty<String>(String.class, "true"));
                        }

                        JcrNodeAdapter entryItem = addAclEntryItem(aclItem);
                        entryItem.addItemProperty(INTERMEDIARY_FORMAT_PROPERTY_NAME,
                                new DefaultProperty<String>(String.class, "true"));
                        entryItem.addItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME,
                                new DefaultProperty<Long>(Long.class, Permission.ALL));
                        entryItem.addItemProperty(ACCESS_TYPE_PROPERTY_NAME, new DefaultProperty<Long>(Long.class,
                                AccessControlList.ACCESS_TYPE_NODE_AND_CHILDREN));
                        entryItem.addItemProperty(AccessControlList.PATH_PROPERTY_NAME,
                                new DefaultProperty<String>(String.class, ""));

                        Component ruleRow = createRuleRow(aclLayout, entryItem, emptyLabel);
                        aclLayout.removeComponent(emptyLabel);
                        aclLayout.addComponent(ruleRow, aclLayout.getComponentCount() - 1);
                    } catch (RepositoryException e) {
                        throw new RuntimeRepositoryException(e);
                    }
                }
            });
            buttons.addComponent(addButton);
            aclLayout.addComponent(buttons);

            layout.addComponent(aclLayout);

        } catch (RepositoryException e) {
            throw new RuntimeRepositoryException(e);
        }

        return new CustomField<Object>() {

            @Override
            protected Component initContent() {
                return layout;
            }

            @Override
            public Class<?> getType() {
                return Object.class;
            }
        };
    }

    protected Component createRuleRow(final AbstractOrderedLayout parentContainer,
            final AbstractJcrNodeAdapter ruleItem, final Label emptyLabel) {

        final HorizontalLayout ruleLayout = new HorizontalLayout();
        ruleLayout.setSpacing(true);
        ruleLayout.setWidth("100%");

        NativeSelect accessRights = new NativeSelect();
        accessRights.setNullSelectionAllowed(false);
        accessRights.setImmediate(true);
        accessRights.setInvalidAllowed(false);
        accessRights.setNewItemsAllowed(false);
        accessRights.addItem(Permission.ALL);
        accessRights.setItemCaption(Permission.ALL, i18n.translate("security.workspace.field.readWrite"));
        accessRights.addItem(Permission.READ);
        accessRights.setItemCaption(Permission.READ, i18n.translate("security.workspace.field.readOnly"));
        accessRights.addItem(Permission.NONE);
        accessRights.setItemCaption(Permission.NONE, i18n.translate("security.workspace.field.denyAccess"));
        accessRights.setPropertyDataSource(ruleItem.getItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME));
        ruleLayout.addComponent(accessRights);

        NativeSelect accessType = new NativeSelect();
        accessType.setNullSelectionAllowed(false);
        accessType.setImmediate(true);
        accessType.setInvalidAllowed(false);
        accessType.setNewItemsAllowed(false);
        accessType.setWidth("150px");
        accessType.addItem(AccessControlList.ACCESS_TYPE_NODE);
        accessType.setItemCaption(AccessControlList.ACCESS_TYPE_NODE,
                i18n.translate("security.workspace.field.selected"));
        accessType.addItem(AccessControlList.ACCESS_TYPE_CHILDREN);
        accessType.setItemCaption(AccessControlList.ACCESS_TYPE_CHILDREN,
                i18n.translate("security.workspace.field.subnodes"));
        accessType.addItem(AccessControlList.ACCESS_TYPE_NODE_AND_CHILDREN);
        accessType.setItemCaption(AccessControlList.ACCESS_TYPE_NODE_AND_CHILDREN,
                i18n.translate("security.workspace.field.selectedSubnodes"));
        Property accessTypeProperty = ruleItem.getItemProperty(ACCESS_TYPE_PROPERTY_NAME);
        accessType.setPropertyDataSource(accessTypeProperty);
        ruleLayout.addComponent(accessType);

        final TextField path = new TextField();
        path.setWidth("100%");
        path.setPropertyDataSource(ruleItem.getItemProperty(AccessControlList.PATH_PROPERTY_NAME));
        ruleLayout.addComponent(path);
        ruleLayout.setExpandRatio(path, 1.0f);

        Button chooseButton = new Button(i18n.translate("security.workspace.field.choose"));
        chooseButton.addClickListener(new Button.ClickListener() {

            @Override
            public void buttonClick(Button.ClickEvent event) {
                openChooseDialog(path);
            }
        });
        ruleLayout.addComponent(chooseButton);

        Button deleteButton = new Button();
        deleteButton.setHtmlContentAllowed(true);
        deleteButton.setCaption("<span class=\"" + "icon-trash" + "\"></span>");
        deleteButton.addStyleName("inline");
        deleteButton.setDescription(i18n.translate("security.workspace.field.delete"));
        deleteButton.addClickListener(new Button.ClickListener() {

            @Override
            public void buttonClick(Button.ClickEvent event) {
                parentContainer.removeComponent(ruleLayout);
                ruleItem.getParent().removeChild(ruleItem);
                if (parentContainer.getComponentCount() == 1) {
                    parentContainer.addComponent(emptyLabel, 0);
                }
            }
        });
        ruleLayout.addComponent(deleteButton);

        return ruleLayout;
    }

    protected void openChooseDialog(final TextField textField) {
        final ConfiguredChooseDialogDefinition def = new ConfiguredChooseDialogDefinition();
        final ConfiguredJcrContentConnectorDefinition contentConnectorDefinition = new ConfiguredJcrContentConnectorDefinition();
        contentConnectorDefinition.setWorkspace(getFieldDefinition().getWorkspace());
        contentConnectorDefinition.setRootPath("/");
        contentConnectorDefinition.setDefaultOrder(ModelConstants.JCR_NAME);
        // node types
        contentConnectorDefinition.setNodeTypes(resolveNodeTypes());
        def.setContentConnector(contentConnectorDefinition);

        final WorkbenchDefinition wbDef = resolveWorkbenchDefinition();
        final WorkbenchFieldDefinition fieldDef = new WorkbenchFieldDefinition();
        fieldDef.setWorkbench(wbDef);
        def.setField(fieldDef);

        // create chooseDialogComponentProvider and get new instance of presenter from there
        ComponentProvider chooseDialogComponentProvider = ChooseDialogComponentProviderUtil
                .createChooseDialogComponentProvider(def, componentProvider);
        workbenchChooseDialogPresenter = chooseDialogComponentProvider.newInstance(def.getPresenterClass(),
                chooseDialogComponentProvider);

        // Define selected ItemId

        ChooseDialogView chooseDialogView = workbenchChooseDialogPresenter.start(new ChooseDialogCallback() {
            @Override
            public void onItemChosen(String actionName, Object value) {
                try {
                    if (value instanceof JcrItemId) {
                        JcrItemId jcrItemId = (JcrItemId) value;
                        textField.setValue(JcrItemUtil.getJcrItem(jcrItemId).getPath());
                    } else {
                        textField.setValue("/");
                    }
                } catch (RepositoryException e) {
                    log.error("Failed to read chosen node", e);
                }
            }

            @Override
            public void onCancel() {
            }
        }, def, uiContext, textField.getValue());
        chooseDialogView.setCaption(StringUtils.capitalize(getFieldDefinition().getWorkspace()));
    }

    protected WorkbenchDefinition resolveWorkbenchDefinition() {

        if (getFieldDefinition().getWorkbench() != null) {
            return getFieldDefinition().getWorkbench();
        }

        ConfiguredWorkbenchDefinition workbenchDefinition = new ConfiguredWorkbenchDefinition();
        workbenchDefinition.setDialogWorkbench(true);
        workbenchDefinition.setEditable(false);

        // content views
        ArrayList<ContentPresenterDefinition> contentViews = new ArrayList<ContentPresenterDefinition>();
        TreePresenterDefinition treeView = new TreePresenterDefinition();
        ArrayList<ColumnDefinition> columns = new ArrayList<ColumnDefinition>();
        PropertyColumnDefinition column = new PropertyColumnDefinition();
        column.setEditable(false);
        column.setDisplayInChooseDialog(true);
        column.setLabel(i18n.translate("security.workspace.field.nodeName"));
        column.setPropertyName(ModelConstants.JCR_NAME);
        column.setName(ModelConstants.JCR_NAME);
        columns.add(column);
        treeView.setColumns(columns);
        contentViews.add(treeView);
        workbenchDefinition.setContentViews(contentViews);

        return workbenchDefinition;
    }

    private List<NodeTypeDefinition> resolveNodeTypes() {

        if (getFieldDefinition().getNodeTypes() != null) {
            return getFieldDefinition().getNodeTypes();
        }

        ArrayList<NodeTypeDefinition> nodeTypes = new ArrayList<NodeTypeDefinition>();
        ConfiguredNodeTypeDefinition nodeType = new ConfiguredNodeTypeDefinition();
        nodeType.setName(JcrConstants.NT_BASE);
        nodeType.setIcon("icon-folder");
        nodeTypes.add(nodeType);

        return nodeTypes;
    }

}