All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.workflow.CmsExtendedWorkflowManager Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 18.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.workflow;

import org.opencms.ade.publish.CmsPublishService;
import org.opencms.ade.publish.I_CmsVirtualProject;
import org.opencms.ade.publish.shared.CmsPublishOptions;
import org.opencms.ade.publish.shared.CmsPublishResource;
import org.opencms.ade.publish.shared.CmsWorkflow;
import org.opencms.ade.publish.shared.CmsWorkflowAction;
import org.opencms.ade.publish.shared.CmsWorkflowResponse;
import org.opencms.db.CmsResourceState;
import org.opencms.file.CmsGroup;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsUser;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.lock.CmsLock;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.publish.CmsPublishEventAdapter;
import org.opencms.publish.CmsPublishJobEnqueued;
import org.opencms.publish.CmsPublishJobRunning;
import org.opencms.security.CmsPermissionSet;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.logging.Log;

/**
 * The default workflow manager implementation, which supports 2 basic actions, Release and Publish.
 */
public class CmsExtendedWorkflowManager extends CmsDefaultWorkflowManager {

    /** The release workflow action. */
    public static final String ACTION_RELEASE = "release";

    /** The parameter which points to the XML content used for notifications. */
    public static final String PARAM_NOTIFICATION_CONTENT = "notificationContent";

    /** The key for the configurable workflow project manager group. */
    public static final String PARAM_WORKFLOW_PROJECT_MANAGER_GROUP = "workflowProjectManagerGroup";

    /** The key for the configurable workflow project user group. */
    public static final String PARAM_WORKFLOW_PROJECT_USER_GROUP = "workflowProjectUserGroup";

    /** The key for the 'release' workflow. */
    public static final String WORKFLOW_RELEASE = "WORKFLOW_RELEASE";

    /** The logger instance for this class. */
    private static final Log LOG = CmsLog.getLog(CmsExtendedWorkflowManager.class);

    /**
     * @see org.opencms.workflow.CmsDefaultWorkflowManager#createFormatter(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflow, org.opencms.ade.publish.shared.CmsPublishOptions)
     */
    @Override
    public I_CmsPublishResourceFormatter createFormatter(
        CmsObject cms,
        CmsWorkflow workflow,
        CmsPublishOptions options) {

        String workflowKey = workflow.getId();
        boolean release = WORKFLOW_RELEASE.equals(workflowKey);
        CmsExtendedPublishResourceFormatter formatter = new CmsExtendedPublishResourceFormatter(cms);
        formatter.setRelease(release);
        return formatter;
    }

    /**
     * @see org.opencms.workflow.CmsDefaultWorkflowManager#executeAction(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflowAction, org.opencms.ade.publish.shared.CmsPublishOptions, java.util.List)
     */
    @Override
    public CmsWorkflowResponse executeAction(
        CmsObject userCms,
        CmsWorkflowAction action,
        CmsPublishOptions options,
        List resources) throws CmsException {

        if (LOG.isInfoEnabled()) {
            LOG.info(
                "workflow action: "
                    + userCms.getRequestContext().getCurrentUser().getName()
                    + " "
                    + action.getAction());
            List resourceNames = new ArrayList();
            for (CmsResource resource : resources) {
                resourceNames.add(resource.getRootPath());
            }
            LOG.info("Resources: " + CmsStringUtil.listAsString(resourceNames, ","));
        }
        try {

            String actionKey = action.getAction();
            if (ACTION_RELEASE.equals(actionKey)) {
                return actionRelease(userCms, resources);
            } else {
                return super.executeAction(userCms, action, options, resources);
            }
        } catch (CmsException e) {
            LOG.error("workflow action failed");
            LOG.error(e.getLocalizedMessage(), e);
            throw e;
        }
    }

    /**
     * @see org.opencms.workflow.CmsDefaultWorkflowManager#getRealOrVirtualProject(org.opencms.util.CmsUUID)
     */
    @Override
    public I_CmsVirtualProject getRealOrVirtualProject(CmsUUID projectId) {

        I_CmsVirtualProject result = m_virtualProjects.get(projectId);
        if (result == null) {
            result = new CmsExtendedRealProjectWrapper(projectId);
        }
        return result;
    }

    /**
     * Gets the name of the group which should be used as the 'manager' group for newly created workflow projects.

* * @return a group name */ public String getWorkflowProjectManagerGroup() { return getParameter(PARAM_WORKFLOW_PROJECT_MANAGER_GROUP, OpenCms.getDefaultUsers().getGroupAdministrators()); } /** * Gets the name of the group which should be used as the 'user' group for newly created workflow projects.

* * @return a group name */ public String getWorkflowProjectUserGroup() { return getParameter(PARAM_WORKFLOW_PROJECT_USER_GROUP, OpenCms.getDefaultUsers().getGroupAdministrators()); } /** * @see org.opencms.workflow.CmsDefaultWorkflowManager#getWorkflowResources(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflow, org.opencms.ade.publish.shared.CmsPublishOptions, boolean) */ @Override public CmsWorkflowResources getWorkflowResources( CmsObject cms, CmsWorkflow workflow, CmsPublishOptions options, boolean canOverrideWorkflow) { String workflowKey = workflow.getId(); String overrideId = null; if (WORKFLOW_RELEASE.equals(workflowKey)) { List result = super.getWorkflowResources( cms, workflow, options, canOverrideWorkflow).getWorkflowResources(); if (canOverrideWorkflow) { boolean override = false; for (CmsResource permCheckResource : result) { try { boolean canPublish = cms.hasPermissions( permCheckResource, CmsPermissionSet.ACCESS_DIRECT_PUBLISH); if (canPublish) { override = true; } } catch (Exception e) { LOG.error( "Can't check permissions for " + permCheckResource.getRootPath() + ":" + e.getLocalizedMessage(), e); } if (override) { List resources = getWorkflowResources( cms, getWorkflows(cms).get(CmsDefaultWorkflowManager.WORKFLOW_PUBLISH), options, false).getWorkflowResources(); result = resources; overrideId = WORKFLOW_PUBLISH; } } } CmsWorkflowResources realResult = new CmsWorkflowResources(result, getWorkflows(cms).get(overrideId)); return realResult; } else { CmsWorkflowResources realResult = super.getWorkflowResources(cms, workflow, options, canOverrideWorkflow); return realResult; } } /** * @see org.opencms.workflow.I_CmsWorkflowManager#getWorkflows(org.opencms.file.CmsObject) */ @Override public Map getWorkflows(CmsObject cms) { Map parentWorkflows = super.getWorkflows(cms); Map result = new LinkedHashMap(); String releaseLabel = getLabel(cms, Messages.GUI_WORKFLOW_ACTION_RELEASE_0); CmsWorkflowAction release = new CmsWorkflowAction(ACTION_RELEASE, releaseLabel, true); List actions = new ArrayList(); actions.add(release); CmsWorkflow releaseWorkflow = new CmsWorkflow(WORKFLOW_RELEASE, releaseLabel, actions); try { boolean isProjectManager = isProjectManager(cms); // make release action always available, but make it the default if the user // isn't a project manager. if (isProjectManager) { result.putAll(parentWorkflows); result.put(WORKFLOW_RELEASE, releaseWorkflow); } else { result.put(WORKFLOW_RELEASE, releaseWorkflow); result.putAll(parentWorkflows); } } catch (CmsException e) { result = parentWorkflows; } return result; } /** * @see org.opencms.workflow.A_CmsWorkflowManager#initialize(org.opencms.file.CmsObject) */ @Override public void initialize(CmsObject adminCms) { super.initialize(adminCms); OpenCms.getPublishManager().addPublishListener(new CmsPublishEventAdapter() { @Override public void onFinish(CmsPublishJobRunning publishJob) { CmsExtendedWorkflowManager.this.onFinishPublishJob(publishJob); } /** * @see org.opencms.publish.CmsPublishEventAdapter#onStart(org.opencms.publish.CmsPublishJobEnqueued) */ @Override public void onStart(CmsPublishJobEnqueued publishJob) { CmsExtendedWorkflowManager.this.onStartPublishJob(publishJob); } }); } /** * Implementation of the 'release' workflow action.

* * @param userCms the current user's CMS context * @param resources the resources which should be released * * @return the workflow response for this action * * @throws CmsException if something goes wrong */ protected CmsWorkflowResponse actionRelease(CmsObject userCms, List resources) throws CmsException { checkNewParentsInList(userCms, resources); String projectName = generateProjectName(userCms); String projectDescription = generateProjectDescription(userCms); CmsObject offlineAdminCms = OpenCms.initCmsObject(m_adminCms); offlineAdminCms.getRequestContext().setCurrentProject(userCms.getRequestContext().getCurrentProject()); String managerGroup = getWorkflowProjectManagerGroup(); String userGroup = getWorkflowProjectUserGroup(); CmsProject workflowProject = m_adminCms.createProject( projectName, projectDescription, userGroup, managerGroup, CmsProject.PROJECT_TYPE_WORKFLOW); CmsObject newProjectCms = OpenCms.initCmsObject(offlineAdminCms); newProjectCms.getRequestContext().setCurrentProject(workflowProject); newProjectCms.getRequestContext().setSiteRoot(""); newProjectCms.copyResourceToProject("/"); CmsUser admin = offlineAdminCms.getRequestContext().getCurrentUser(); clearLocks(userCms.getRequestContext().getCurrentProject(), resources); for (CmsResource resource : resources) { CmsLock lock = offlineAdminCms.getLock(resource); if (lock.isUnlocked()) { offlineAdminCms.lockResource(resource); } else if (!lock.isOwnedBy(admin)) { offlineAdminCms.changeLock(resource); } offlineAdminCms.writeProjectLastModified(resource, workflowProject); offlineAdminCms.unlockResource(resource); } for (CmsUser user : getNotificationMailRecipients()) { sendNotification(userCms, user, workflowProject, resources); } return new CmsWorkflowResponse( true, "", new ArrayList(), new ArrayList(), workflowProject.getUuid()); } /** * Checks that the parent folders of new resources which are released are either not new or are also released.

* * @param userCms the user CMS context * @param resources the resources to check * * @throws CmsException if the check fails */ protected void checkNewParentsInList(CmsObject userCms, List resources) throws CmsException { Map resourcesByPath = new HashMap(); CmsObject rootCms = OpenCms.initCmsObject(m_adminCms); rootCms.getRequestContext().setCurrentProject(userCms.getRequestContext().getCurrentProject()); rootCms.getRequestContext().setSiteRoot(""); for (CmsResource resource : resources) { resourcesByPath.put(resource.getRootPath(), resource); } for (CmsResource resource : resources) { if (resource.getState().isNew()) { String parentPath = CmsResource.getParentFolder(resource.getRootPath()); CmsResource parent = resourcesByPath.get(parentPath); if (parent == null) { parent = rootCms.readResource(parentPath); if (parent.getState().isNew()) { throw new CmsNewParentNotInWorkflowException( Messages.get().container( Messages.ERR_NEW_PARENT_NOT_IN_WORKFLOW_1, resource.getRootPath())); } } } } } /** * Cleans up empty workflow projects.

* * @param projects the workflow projects to clean up * * @throws CmsException if something goes wrong */ protected void cleanupEmptyWorkflowProjects(List projects) throws CmsException { if (projects == null) { projects = OpenCms.getOrgUnitManager().getAllManageableProjects(m_adminCms, "", true); } for (CmsProject project : projects) { if (project.isWorkflowProject()) { if (isProjectEmpty(project)) { m_adminCms.deleteProject(project.getUuid()); } } } } /** * Removes a project if there are no longer any resources which have been last modified in that project.

* * @param project the project * @throws CmsException if something goes wrong */ protected void cleanupProjectIfEmpty(CmsProject project) throws CmsException { if ((project.getType().getMode() == CmsProject.PROJECT_TYPE_WORKFLOW.getMode()) && isProjectEmpty(project)) { LOG.info("Removing project " + project.getName() + " because it is an empty workflow project."); m_adminCms.deleteProject(project.getUuid()); } } /** * Ensures that the resources to be released are unlocked.

* * @param project the project in which to operate * @param resources the resources for which the locks should be removed * * @throws CmsException if something goes wrong */ protected void clearLocks(CmsProject project, List resources) throws CmsException { CmsObject rootCms = OpenCms.initCmsObject(m_adminCms); rootCms.getRequestContext().setCurrentProject(project); rootCms.getRequestContext().setSiteRoot(""); for (CmsResource resource : resources) { CmsLock lock = rootCms.getLock(resource); if (lock.isUnlocked()) { continue; } String currentPath = resource.getRootPath(); while (lock.isInherited()) { currentPath = CmsResource.getParentFolder(currentPath); lock = rootCms.getLock(currentPath); } rootCms.changeLock(currentPath); rootCms.unlockResource(currentPath); } } /** * Helper method to check whether a project exists.

* * @param projectName the project name * * @return true if the project exists */ protected boolean existsProject(String projectName) { try { m_adminCms.readProject(projectName); return true; } catch (CmsException e) { return false; } } /** * Generates the description for a new workflow project based on the user for whom it is created.

* * @param userCms the user's current CMS context * * @return the workflow project description */ protected String generateProjectDescription(CmsObject userCms) { CmsUser user = userCms.getRequestContext().getCurrentUser(); OpenCms.getLocaleManager(); Locale locale = CmsLocaleManager.getDefaultLocale(); long time = System.currentTimeMillis(); Date date = new Date(time); DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale); String dateString = format.format(date); String result = Messages.get().getBundle(locale).key( Messages.GUI_WORKFLOW_PROJECT_DESCRIPTION_2, user.getName(), dateString); return result; } /** * Generates the name for a new workflow project based on the user for whom it is created.

* * @param userCms the user's current CMS context * * @return the workflow project name */ protected String generateProjectName(CmsObject userCms) { String projectName = generateProjectName(userCms, true); if (existsProject(projectName)) { projectName = generateProjectName(userCms, false); } return projectName; } /** * Generates the name for a new workflow project based on the user for whom it is created.

* * @param userCms the user's current CMS context * @param shortTime if true, the short time format will be used, else the medium time format * * @return the workflow project name */ protected String generateProjectName(CmsObject userCms, boolean shortTime) { CmsUser user = userCms.getRequestContext().getCurrentUser(); long time = System.currentTimeMillis(); Date date = new Date(time); OpenCms.getLocaleManager(); Locale locale = CmsLocaleManager.getDefaultLocale(); DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale); DateFormat timeFormat = DateFormat.getTimeInstance(shortTime ? DateFormat.SHORT : DateFormat.MEDIUM, locale); String dateStr = dateFormat.format(date) + " " + timeFormat.format(date); String username = user.getName(); String result = Messages.get().getBundle(locale).key(Messages.GUI_WORKFLOW_PROJECT_NAME_2, username, dateStr); result = result.replaceAll("/", "|"); return result; } /** * Gets the list of recipients for the notifications.

* * @return the list of users which should be notified when resources are released */ protected List getNotificationMailRecipients() { String group = getWorkflowProjectManagerGroup(); CmsObject cms = m_adminCms; try { List users = cms.getUsersOfGroup(group); return users; } catch (CmsException e) { LOG.error(e.getLocalizedMessage(), e); return new ArrayList(); } } /** * Gets the resource notification content path.

* * @return the resource notification content path */ protected String getNotificationResource() { String result = getParameter( PARAM_NOTIFICATION_CONTENT, "/system/workplace/admin/notification/workflow-notification"); return result; } /** * Helper method for generating the workflow response which should be sent when publishing the resources would break relations.

* * @param userCms the user's CMS context * @param publishResources the resources whose links would be broken * * @return the workflow response */ protected CmsWorkflowResponse getPublishBrokenRelationsResponse( CmsObject userCms, List publishResources) { List actions = new ArrayList(); String forcePublishLabel = Messages.get().getBundle(getLocale(userCms)).key( Messages.GUI_WORKFLOW_ACTION_FORCE_PUBLISH_0); CmsWorkflowAction forcePublish = new CmsWorkflowAction(ACTION_FORCE_PUBLISH, forcePublishLabel, true, true); actions.add(forcePublish); return new CmsWorkflowResponse( false, Messages.get().getBundle(getLocale(userCms)).key(Messages.GUI_BROKEN_LINKS_0), publishResources, actions, null); } /** * Gets the workflow response which should be sent when the resources have successfully been published.

* * @return the successful workflow response */ protected CmsWorkflowResponse getSuccessResponse() { return new CmsWorkflowResponse( true, "", new ArrayList(), new ArrayList(), null); } /** * Checks whether there are resources which have last been modified in a given project.

* * @param project the project which should be checked * @return true if there are no resources which have been last modified inside the project * * @throws CmsException if something goes wrong */ protected boolean isProjectEmpty(CmsProject project) throws CmsException { List resources = m_adminCms.readProjectView(project.getUuid(), CmsResourceState.STATE_KEEP); return resources.isEmpty(); } /** * Checks whether the user for a given CMS context can manage workflow projects.

* * @param userCms the user CMS Context * @return true if this user can manage workflow projects * * @throws CmsException if something goes wrong */ protected boolean isProjectManager(CmsObject userCms) throws CmsException { CmsGroup managerGroup = m_adminCms.readGroup(getWorkflowProjectManagerGroup()); List groups = m_adminCms.getGroupsOfUser( userCms.getRequestContext().getCurrentUser().getName(), false); return groups.contains(managerGroup); } /** * Handles finished publish jobs by removing projects of resources in the publish job if they are empty workflow projects.

* * @param publishJob the finished published job */ protected void onFinishPublishJob(CmsPublishJobRunning publishJob) { try { cleanupEmptyWorkflowProjects(null); } catch (CmsException e) { LOG.error(e.getLocalizedMessage(), e); } } /** * This is called when a publish job is started.

* * @param publishJob the publish job being started */ protected void onStartPublishJob(CmsPublishJobEnqueued publishJob) { // do nothing } /** * Sends the notification for released resources.

* * @param userCms the user's CMS context * @param recipient the OpenCms user to whom the notification should be sent * @param workflowProject the workflow project which * @param resources the resources which have been affected by a workflow action */ protected void sendNotification( CmsObject userCms, CmsUser recipient, CmsProject workflowProject, List resources) { try { String linkHref = OpenCms.getLinkManager().getServerLink( userCms, "/system/workplace/commons/publish.jsp?" + CmsPublishService.PARAM_PUBLISH_PROJECT_ID + "=" + workflowProject.getUuid() + "&" + CmsPublishService.PARAM_CONFIRM + "=true"); CmsWorkflowNotification notification = new CmsWorkflowNotification( m_adminCms, userCms, recipient, getNotificationResource(), workflowProject, resources, linkHref); notification.send(); } catch (Throwable e) { LOG.error(e.getLocalizedMessage(), e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy