/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
*/
package org.alfresco.web.bean.wcm;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.config.JNDIConstants;
import org.alfresco.model.ContentModel;
import org.alfresco.model.WCMAppModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.web.scripts.FileTypeImageUtils;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avmsync.AVMDifference;
import org.alfresco.service.cmr.avmsync.AVMSyncService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.springframework.extensions.surf.util.ISO8601DateFormat;
import org.alfresco.util.NameMatcher;
import org.alfresco.wcm.sandbox.SandboxFactory;
import org.alfresco.wcm.sandbox.SandboxService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.forms.Form;
import org.alfresco.web.forms.FormInstanceData;
import org.alfresco.web.forms.FormNotFoundException;
import org.alfresco.web.forms.FormsService;
import org.alfresco.web.forms.Rendition;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIListItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Submit items for WCM workflow dialog.
*
* @author Kevin Roast
*/
public class SubmitDialog extends BaseDialogBean
{
private static final long serialVersionUID = -2445905376358150000L;
public static final String PARAM_LOAD_SELECTED_NODES_FROM_BROWSE_BEAN = "loadSelectedNodesFromBrowseBean";
private static final String SPACE_ICON = "/images/icons/" + BrowseBean.SPACE_SMALL_DEFAULT + ".gif";
private static final String MSG_DELETED_ITEM = "avm_node_deleted";
private static final String MSG_ERR_WORKFLOW_CONFIG = "submit_workflow_config_error";
public static final String MSG_ERR_INVALID_LAUNCH_DATE = "msg_err_invalid_launch_date_on_submit";
public static final String MSG_ERR_INVALID_EXPIRATION_DATE = "msg_err_invalid_expiration_date_on_submit";
public static final String MSG_ERR_PATTERN_INVALID_EXPIRATION_DATE = "msg_err_pattern_invalid_expiration_date_on_submit";
private String comment;
private String label;
private String[] workflowSelectedValue;
private boolean enteringExpireDate = false;
private boolean loadSelectedNodesFromBrowseBean = false;
private boolean autoDeploy = false;
private Date defaultExpireDate;
private Date launchDate;
private List submitItems;
private List warningItems;
private HashSet workflows;
private Map expirationDates;
private List workflowItems;
private Map workflowParams;
protected AVMBrowseBean avmBrowseBean;
transient private AVMService avmService;
transient private WorkflowService workflowService;
transient private AVMSyncService avmSyncService;
transient private FormsService formsService;
transient private SandboxFactory sandboxFactory;
transient private SandboxService sandboxService;
transient private NameMatcher nameMatcher;
/** Current workflow for dialog context */
protected WorkflowConfiguration actionWorkflow = null;
private static final Log logger = LogFactory.getLog(SubmitDialog.class);
/**
* @param avmService The AVM Service to set.
*/
public void setAvmService(AVMService avmService)
{
this.avmService = avmService;
}
protected AVMService getAvmService()
{
if (avmService == null)
{
avmService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getAVMService();
}
return avmService;
}
/**
* @param avmSyncService The AVMSyncService to set.
*/
public void setAvmSyncService(AVMSyncService avmSyncService)
{
this.avmSyncService = avmSyncService;
}
protected AVMSyncService getAvmSyncService()
{
if (this.avmSyncService == null)
{
this.avmSyncService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getAVMSyncService();
}
return this.avmSyncService;
}
/**
* @param avmBrowseBean The AVM BrowseBean to set
*/
public void setAvmBrowseBean(AVMBrowseBean avmBrowseBean)
{
this.avmBrowseBean = avmBrowseBean;
}
/**
* @param workflowService The WorkflowService to set.
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
protected WorkflowService getWorkflowService()
{
if (workflowService == null)
{
workflowService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getWorkflowService();
}
return workflowService;
}
/**
* @param nameMatcher The nameMatcher to set.
*/
public void setNameMatcher(NameMatcher nameMatcher)
{
this.nameMatcher = nameMatcher;
}
/**
* @return nameMatcher
*/
protected NameMatcher getNameMatcher()
{
//check for null for cluster environment
if (nameMatcher == null)
{
nameMatcher = (NameMatcher) FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "globalPathExcluder");
}
return nameMatcher;
}
/**
* @param formsService The FormsService to set.
*/
public void setFormsService(final FormsService formsService)
{
this.formsService = formsService;
}
protected FormsService getFormsService()
{
if (formsService == null)
{
formsService = (FormsService) FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "FormsService");
}
return formsService;
}
public void setSandboxService(final SandboxService sandboxService)
{
this.sandboxService = sandboxService;
}
protected SandboxService getSandboxService()
{
if (sandboxService == null)
{
sandboxService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getSandboxService();
}
return sandboxService;
}
// TODO - refactor ... push down into sandbox service (submit to workflow)
public void setSandboxFactory(final SandboxFactory sandboxFactory)
{
this.sandboxFactory = sandboxFactory;
}
protected SandboxFactory getSandboxFactory()
{
if (sandboxFactory == null)
{
sandboxFactory = (SandboxFactory) FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "SandboxFactory");
}
return sandboxFactory;
}
/**
* @see org.alfresco.web.bean.dialog.BaseDialogBean#init(java.util.Map)
*/
@Override
public void init(Map parameters)
{
super.init(parameters);
this.comment = null;
this.label = null;
this.submitItems = null;
this.warningItems = null;
this.workflowItems = null;
this.workflows = new HashSet(4);
this.expirationDates = new HashMap(8);
this.defaultExpireDate = new Date();
this.workflowSelectedValue = null;
this.launchDate = null;
this.autoDeploy = false;
this.workflowParams = null;
// determine if the dialog has been started from a workflow
this.loadSelectedNodesFromBrowseBean = Boolean.valueOf(this.parameters.get(PARAM_LOAD_SELECTED_NODES_FROM_BROWSE_BEAN));
}
@Override
protected String finishImpl(FacesContext context, String outcome) throws Exception
{
// NOTE: This does not get called in this dialog as we have overridden finish()
return null;
}
@Override
public String finish()
{
// NOTE: We need to handle the transaction ourselves in this dialog as the store needs
// to be committed and the virtualisation server informed of the workflow
// sandbox BEFORE the workflow gets started. This is so that the link validation
// service can use the virtualisation server to produce the link report.
// The best solution would be for the link check node of the workflow to be
// asynchronous and to wait until the initiating transaction had committed
// before running but there's no support for this.
// We therefore need to use 2 transactions, one to create the workflow store
// (if necessary) and one to start the workflow
if (getSubmitItemsSize() == 0)
{
return null;
}
FacesContext context = FacesContext.getCurrentInstance();
String outcome = null;
// check the isFinished flag to stop the finish button
// being pressed multiple times
if (this.isFinished == false)
{
this.isFinished = true;
try
{
// note: always submit via workflow (always defaults to direct submit workflow)
//outcome = submitViaWorkflow(context);
String workflowName = null;
if ((this.workflowSelectedValue != null) && (this.workflowSelectedValue.length > 0))
{
// get the defaults from the workflow configuration attached to the selected workflow
workflowName = this.workflowSelectedValue[0];
for (FormWorkflowWrapper wrapper : this.workflows)
{
if (wrapper.name.equals(workflowName))
{
this.workflowParams = wrapper.params;
}
}
if (this.workflowParams == null)
{
// create error msg for display in dialog - the user must configure the workflow params
Utils.addErrorMessage(Application.getMessage(context, MSG_ERR_WORKFLOW_CONFIG));
return outcome;
}
}
final String finalWorkflowName = workflowName;
List items = getSubmitItems();
final List relativePaths = new ArrayList(items.size());
for (ItemWrapper wrapper : items)
{
relativePaths.add(AVMUtil.getStoreRelativePath(wrapper.getDescriptor().getPath()));
}
Date currentDate = new Date();
if (launchDate !=null)
{
if (launchDate.before(currentDate))
{
Utils.addErrorMessage(Application.getMessage(context, MSG_ERR_INVALID_LAUNCH_DATE));
return null;
}
}
if ((submitItems != null) && (!submitItems.isEmpty()) && (expirationDates != null) && (!expirationDates.isEmpty()))
{
StringBuilder errorMessage = new StringBuilder();
byte errFlag = 0;
for (ItemWrapper wrapper : submitItems)
{
String key = wrapper.descriptor.getPath();
Date expiritionDate = expirationDates.get(key);
if (expiritionDate != null)
{
if (launchDate != null)
{
if (expiritionDate.before(launchDate))
{
errFlag = 1;
errorMessage.append(wrapper.descriptor.getName()).append(", ");
}
}
else
{
if (expiritionDate.before(currentDate))
{
errorMessage.append(wrapper.descriptor.getName()).append(", ");
errFlag = 2;
}
}
}
}
if (errorMessage.length()>0)
{
errorMessage.delete(errorMessage.length()-2, errorMessage.length());
}
switch (errFlag)
{
case 1:
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(context, MSG_ERR_PATTERN_INVALID_EXPIRATION_DATE), errorMessage.toString()));
return null;
}
case 2:
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(context, MSG_ERR_INVALID_EXPIRATION_DATE), errorMessage.toString()));
return null;
}
}
}
final String sbStoreId = this.avmBrowseBean.getSandbox();
String submitLabel = this.label;
String submitComment = this.comment;
// crop to maximum length
if (submitLabel != null && submitLabel.length() > 255)
{
submitLabel = submitLabel.substring(0, 255);
}
if (submitComment != null && submitComment.length() > 255)
{
submitComment = submitComment.substring(0, 255);
}
final String finalSubmitLabel = submitLabel;
final String finalSubmitComment = submitComment;
// note: always submit via workflow (if no workflow selected, then defaults to direct submit workflow)
// This nees to run with higher rights to push into the work flow store ....
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork