Package org.jboss.seam.wiki.core.action

Source Code of org.jboss.seam.wiki.core.action.NodeHome

/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.action;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Events;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.framework.EntityHome;
import org.jboss.seam.security.AuthorizationException;
import org.jboss.seam.security.Identity;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
import org.jboss.seam.wiki.core.model.*;
import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
import org.jboss.seam.wiki.util.WikiUtil;
import org.jboss.seam.wiki.preferences.Preferences;
import org.jboss.seam.international.StatusMessages;

import static org.jboss.seam.international.StatusMessage.Severity.WARN;
import static org.jboss.seam.international.StatusMessage.Severity.INFO;

import java.util.Date;
import java.util.List;

/**
* Superclass for all creating and editing documents, directories, files, etc.
*
* @author Christian Bauer
*/
public abstract class NodeHome<N extends WikiNode, P extends WikiNode> extends EntityHome<N> {

    // TODO: This is a performance optimization, our EM is always already joined (SMPC)
    protected void joinTransaction() {}

    /* -------------------------- Context Wiring ------------------------------ */

    @In
    private WikiNodeDAO wikiNodeDAO;
    @In
    private UserDAO userDAO;
    @In
    private WikiDirectory wikiRoot;
    @In
    protected User currentUser;
    @In
    protected List<Role.AccessLevel> accessLevelsList;

    public WikiNodeDAO getWikiNodeDAO() { return wikiNodeDAO; }
    public UserDAO getUserDAO() { return userDAO; }
    public WikiDirectory getWikiRoot() { return wikiRoot; }
    public User getCurrentUser() { return currentUser; }
    public List<Role.AccessLevel> getAccessLevelsList() { return accessLevelsList; }

    /* -------------------------- Request Wiring ------------------------------ */

    private Long parentNodeId;

    public Long getParentNodeId() {
        return parentNodeId;
    }
    public void setParentNodeId(Long parentNodeId) {
        this.parentNodeId = parentNodeId;
    }

    private P parentNode;
    public P getParentNode() {
        return parentNode;
    }
    public void setParentNode(P parentNode) {
        this.parentNode = parentNode;
    }

    public void setNodeId(Long o) {
        super.setId(o);
    }
    public Long getNodeId() {
        return (Long)super.getId();
    }

    /* -------------------------- Additional States ------------------------------ */

    private boolean editor = false;

    public boolean isEditor() { return editor; }

    public void initEditor(boolean visibleWorkspace) {
        getLog().debug("initializing editor workspace");
        this.editor = true;

        if (visibleWorkspace) {
            // Set workspace description of the current conversation
            String desc = getEditorWorkspaceDescription(getNodeId() == null);
            WikiPreferences prefs = Preferences.instance().get(WikiPreferences.class);
            if (desc != null && desc.length() > prefs.getWorkspaceSwitcherDescriptionLength()) {
                desc = desc.substring(0, prefs.getWorkspaceSwitcherDescriptionLength().intValue()) + "...";
            }
            Conversation.instance().setDescription(desc);
        }
    }

    public void initEditor() {
        initEditor(true);
    }

    /* -------------------------- Basic Overrides ------------------------------ */

    @Override
    protected String getPersistenceContextName() {
        return "restrictedEntityManager";
    }

    @Override
    public N find() {
        getLog().debug("finding an existing instance with id: " + getId());
        N foundNode = findInstance();
        if (foundNode == null) {
            handleNotFound();
            return null;
        }
        getLog().debug("found instance: " + foundNode);
        return isEditor() ? beforeNodeEditFound(afterNodeFound(foundNode)) : afterNodeFound(foundNode);
    }

    @Override
    protected N createInstance() {
        getLog().debug("creating a new instance");
        N newNode = super.createInstance();
        getLog().debug("created new instance: " + newNode);
        return isEditor() ? beforeNodeEditNew(afterNodeCreated(newNode)) : afterNodeCreated(newNode);
    }

    /* -------------------------- Basic Subclass Callbacks ------------------------------ */

    public N afterNodeCreated(N node) {

        if (parentNodeId == null)
            throw new InvalidWikiRequestException("Missing parentNodeId parameter");

        outjectCurrentLocation(node);

        return node;
    }

    public N beforeNodeEditNew(N node) {

        getLog().debug("loading parent node with id: " + parentNodeId);
        parentNode = findParentNode(parentNodeId);
        if (parentNode == null)
            throw new InvalidWikiRequestException("Could not find parent node with id: " + parentNodeId);
        getLog().debug("initalized with parent node: " + parentNode);

        // Check write access level of the parent node, if the user wants to create a new node
        if (!isPersistAllowed(node, parentNode))
            throw new AuthorizationException("You don't have permission for this operation");

        // Default to same access permissions as parent node
        node.setWriteAccessLevel(parentNode.getWriteAccessLevel());
        node.setReadAccessLevel(parentNode.getReadAccessLevel());
        writeAccessLevel = getAccessLevelsList().get(
            accessLevelsList.indexOf(
                new Role.AccessLevel(parentNode.getWriteAccessLevel())
            )
        );
        readAccessLevel = getAccessLevelsList().get(
            accessLevelsList.indexOf(
                new Role.AccessLevel(parentNode.getReadAccessLevel())
            )
        );

        return node;
    }

    public N afterNodeFound(N node) {

        getLog().debug("using parent of instance: " + node.getParent());
        if (node.getParent() != null) {  // Wiki Root doesn't have a parent
            parentNode = (P)node.getParent();
            parentNodeId = parentNode.getId();
        }

        outjectCurrentLocation(node);

        return node;
    }

    public N beforeNodeEditFound(N node) {

        // Check write access level of the node the user wants to edit
        if (!isUpdateAllowed(node, null))
            throw new AuthorizationException("You don't have permission for this operation");

        writeAccessLevel = getAccessLevelsList().get(
            accessLevelsList.indexOf(
                new Role.AccessLevel(node.getWriteAccessLevel())
            )
        );
        readAccessLevel = getAccessLevelsList().get(
            accessLevelsList.indexOf(
                new Role.AccessLevel(node.getReadAccessLevel())
            )
        );

        return node;
    }


    /* -------------------------- Custom CUD ------------------------------ */

    @Override
    public String persist() {
        checkPersistPermissions();

        if (!validateComponents(getPersistValidations())) return null;

        if (!preparePersist()) return null;

        getLog().trace("linking new node with its parent node: " + getParentNode());
        getInstance().setParent(getParentNode());

        // Creation metadata
        setCreatedMetadata();

        // Wiki name conversion
        setWikiName();

        // Set its area number (if subclass didn't already set it)
        if (getInstance().getAreaNumber() == null)
            getInstance().setAreaNumber(getInstance().getParent().getAreaNumber());

        // Validate
        if (!isValidModel()) return null;

        if (!beforePersist()) return null;

        getLog().debug("persisting node: " + getInstance());
        String outcome = super.persist();
        if (outcome != null) {
            Events.instance().raiseEvent("PreferenceEditor.flushAll");
            Events.instance().raiseEvent("Node.persisted", getInstance());
        }

        // Now set the message identifier, if nobody else did
        if (getInstance().getMessageId() == null && requiresMessageId()) {
            getInstance().setMessageId(
                // Use the identifier and the creation time, both quite unique and immutable
                WikiUtil.calculateMessageId(
                    getInstance().getId(),
                    String.valueOf(getInstance().getCreatedOn().getTime()))
            );
            // Need to flush again, to execute UPDATE
            getEntityManager().flush();
        }

        return outcome;
    }

    @Override
    public String update() {
        checkUpdatePermissions();

        if (!validateComponents(getUpdateValidations())) return null;

        if (!prepareUpdate()) return null;

        // Modification metadata
        setLastModifiedMetadata();

        // Wiki name conversion
        setWikiName();

        // Validate
        if (!isValidModel()) return null;

        if (!beforeUpdate()) return null;

        getLog().debug("updating node: " + getInstance());
        String outcome = super.update();
        if (outcome != null) {
            Events.instance().raiseEvent("PreferenceEditor.flushAll");
            Events.instance().raiseEvent("Node.updated", getInstance());
        }
        return outcome;
    }

    public boolean isRemovable() {
        getLog().debug("checking removability of current instance");
        return isManaged() &&
                getNodeRemover() != null &&
                getNodeRemover().isRemovable(getInstance());
    }

    @Override
    public String remove() {
        if (!isRemovable()) return null;
       
        checkRemovePermissions();

        getLog().debug("removing node: " + getInstance());
        getNodeRemover().removeDependencies(getInstance());
        String outcome = super.remove();
        if (outcome != null) {
            Events.instance().raiseEvent("Node.removed", getInstance());
        }
        return outcome;
    }

    public String remove(Long nodeId) {
        getLog().debug("requested node remove with id: " + nodeId);
        setNodeId(nodeId);
        initEditor(false);
        String outcome = remove();
        if (outcome != null) {
            Events.instance().raiseEvent("Node.removed", getInstance());
        }
        return outcome;
    }

    public String trash() {
        if (!isRemovable()) return null;

        checkRemovePermissions();

        getLog().debug("trashing node : " + getInstance());
        getNodeRemover().trash(getInstance());
        setLastModifiedMetadata();
        getEntityManager().flush();
        trashedMessage();

        Events.instance().raiseEvent("Node.removed", getInstance());
        return "removed";
    }

    /* -------------------------- Internal (Subclass) Methods ------------------------------ */

    public abstract Class<N> getEntityClass();

    protected abstract N findInstance();

    protected abstract P findParentNode(Long parentNodeId);

    protected void outjectCurrentLocation(WikiNode node) {
        if (isPageRootController()) {
            // Outjects current node or parent directory, e.g. for breadcrumb rendering
            Contexts.getPageContext().set("currentLocation", node);
        }
    }

    protected void setWikiName() {
        getLog().trace("setting wiki name of node");
        getInstance().setWikiname(WikiUtil.convertToWikiName(getInstance().getName()));
    }

    protected void setCreatedMetadata() {
        getLog().trace("setting created metadata");
        getInstance().setCreatedBy(getCurrentUser());
        getInstance().setCreatedOn(new Date());
    }

    protected void setLastModifiedMetadata() {
        getLog().trace("setting last modified metadata");
        getInstance().setLastModifiedBy(getCurrentUser());
        getInstance().setLastModifiedOn(new Date());
    }

    protected boolean isValidModel() {
        getLog().trace("validating model");
        if (getParentNode() == null) return true; // Special case, editing the wiki root

        // Unique wiki name
        if (getWikiNodeDAO().isUniqueWikiname(getParentNode().getAreaNumber(), getInstance())) {
            return true;
        } else {
            StatusMessages.instance().addToControlFromResourceBundleOrDefault(
                "name",
                WARN,
                "lacewiki.entity.DuplicateName",
                "This name is already used, please change it"
            );
            return false;
        }

    }

    protected void checkPersistPermissions() {
        getLog().debug("checking persist permissions");
        if (!isPersistAllowed(getInstance(), getParentNode()))
            throw new AuthorizationException("You don't have permission for this operation");
    }

    protected void checkUpdatePermissions() {
        getLog().debug("checking update permissions");
        if (!isUpdateAllowed(getInstance(), getParentNode()))
            throw new AuthorizationException("You don't have permission for this operation");
    }

    protected void checkRemovePermissions() {
        getLog().debug("checking remove permissions");
        if (!isRemoveAllowed(getInstance(), getParentNode()))
            throw new AuthorizationException("You don't have permission for this operation");
    }

    protected void trashedMessage() {
        StatusMessages.instance().addFromResourceBundleOrDefault(
                INFO,
                "lacewiki.msg.Node.Trashed",
                "'{0}' has been moved into the trash.",
                getInstance().getName()
        );
    }

    public boolean isPersistAllowed(N node, P parent) {
        return Identity.instance().hasPermission("Node", "create", parent);
    }

    public boolean isUpdateAllowed(N node, P parent) {
        return Identity.instance().hasPermission("Node", "edit", node);
    }

    public boolean isRemoveAllowed(N node, P parent) {
        return Identity.instance().hasPermission("Node", "edit", node);
    }

    protected boolean validateComponents(Validatable validatableComponents[]) {
        if (validatableComponents == null) return true;
        boolean allValid = true;
        for (Validatable validatableComponent : validatableComponents) {
            validatableComponent.validate();
            allValid = validatableComponent.isValid();
        }
        return allValid;
    }

    protected Validatable[] getUpdateValidations() {
        return null;
    }

    protected Validatable[] getPersistValidations() {
        return null;
    }

    protected boolean requiresMessageId() {
        return true;
    }

    /* -------------------------- Optional Subclass Callbacks ------------------------------ */

    protected boolean isPageRootController() { return true; }

    /**
     * Called before the superclass does its preparation;
     * @return boolean continue processing
     */
    protected boolean preparePersist() { return true; }

    /**
     * Called after superclass did its preparation right before the actual persist()
     * @return boolean continue processing
     */
    protected boolean beforePersist() { return true; }

    /**
     * Called before the superclass does its preparation;
     * @return boolean continue processing
     */
    protected boolean prepareUpdate() { return true; }

    /**
     * Called after superclass did its preparation right before the actual update()
     * @return boolean continue processing
     */
    protected boolean beforeUpdate() { return true; }

    /**
     * Called when a node is removed, obtains remover for execution of dependency deletion
     * before the node is finally removed.
     * @return NodeRemover instance
     */
    protected abstract NodeRemover getNodeRemover();

    /**
     * Description (i18n) of workspace switcher item.
     *
     * @param create true if editor is initialized to create an item, false if it's used to update an item.
     * @return String description of workspace switcher item or <tt>null</tt> if no workspace switcher item should be shown.
     */
    protected abstract String getEditorWorkspaceDescription(boolean create);

    /* -------------------------- Public Features ------------------------------ */

    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
    public void selectOwner(Long creatorId) {
        User newCreator = userDAO.findUser(creatorId);
        getInstance().setCreatedBy(newCreator);
    }

    private Role.AccessLevel writeAccessLevel;
    private Role.AccessLevel readAccessLevel;

    public Role.AccessLevel getWriteAccessLevel() {
        return writeAccessLevel;
    }

    public void setWriteAccessLevel(Role.AccessLevel writeAccessLevel) {
        if (!Identity.instance().hasPermission("Node", "changeAccessLevel", getInstance()) ) {
            throw new AuthorizationException("You don't have permission for this operation");
        }
        this.writeAccessLevel = writeAccessLevel;
        getInstance().setWriteAccessLevel(
            writeAccessLevel != null ? writeAccessLevel.getAccessLevel() : Role.ADMINROLE_ACCESSLEVEL
        );
    }

    public Role.AccessLevel getReadAccessLevel() {
        return readAccessLevel;
    }

    public void setReadAccessLevel(Role.AccessLevel readAccessLevel) {
        if (!Identity.instance().hasPermission("Node", "changeAccessLevel", getInstance()) ) {
            throw new AuthorizationException("You don't have permission for this operation");
        }
        this.readAccessLevel = readAccessLevel;
        getInstance().setReadAccessLevel(
            readAccessLevel != null ? readAccessLevel.getAccessLevel() : Role.ADMINROLE_ACCESSLEVEL
        );
    }

}
TOP

Related Classes of org.jboss.seam.wiki.core.action.NodeHome

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.