/*
* #%L
* Alfresco Repository WAR Community
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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 .
* #L%
*/
package org.alfresco.web.bean.coci;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.repo.web.scripts.FileTypeImageUtils;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.FileUploadBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.ReportedException;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Kevin Roast
*/
public class CheckinCheckoutDialog extends BaseDialogBean
{
// ------------------------------------------------------------------------------
// Private data
private static final long serialVersionUID = -4898900470274506760L;
private static Log logger = LogFactory.getLog(CheckinCheckoutDialog.class);
/* /** I18N messages */
public static final String MSG_ERROR_CHECKIN = "error_checkin";
public static final String MSG_ERROR_CANCELCHECKOUT = "error_cancel_checkout";
public static final String MSG_ERROR_UPDATE = "error_update";
public static final String MSG_ERROR_CHECKOUT = "error_checkout";
private final static String MSG_LEFT_QUOTE = "left_qoute";
private final static String MSG_RIGHT_QUOTE = "right_quote";
public static final String FILE = "file";
protected CCProperties property;
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param property the property to set
*/
public void setProperty(CCProperties property)
{
this.property = property;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @return Returns the BrowseBean.
*/
public BrowseBean getBrowseBean()
{
return this.browseBean;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
public boolean getFinishButtonDisabled()
{
return false;
}
public String getFinishButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "check_in");
}
public String getContainerTitle()
{
FacesContext fc = FacesContext.getCurrentInstance();
return Application.getMessage(fc, "check_in") + " " +
Application.getMessage(fc, MSG_LEFT_QUOTE)+ this.property.getDocument().getName() + Application.getMessage(fc, MSG_RIGHT_QUOTE);
}
/**
* Determines whether the document being checked in has
* the versionable aspect applied
*
* @return true if the versionable aspect is applied
*/
public boolean isVersionable()
{
return property.getDocument().hasAspect(ContentModel.ASPECT_VERSIONABLE);
}
/**
* @return Returns the message to display when a file has been uploaded
*/
public String getFileUploadSuccessMsg()
{
String msg = Application.getMessage(FacesContext.getCurrentInstance(), "file_upload_success");
return MessageFormat.format(msg, new Object[] {Utils.encode(getFileName())});
}
/**
* @return Returns the name of the file
*/
public String getFileName()
{
// try and retrieve the file and filename from the file upload bean
// representing the file we previously uploaded.
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
property.setFile(fileBean.getFile());
property.setFileName(fileBean.getFileName());
}
return property.getFileName();
}
/**
* @param fileName The name of the file
*/
public void setFileName(String fileName)
{
property.setFileName(fileName);
// we also need to keep the file upload bean in sync
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
fileBean.setFileName(property.getFileName());
}
}
/**
* Clear the uploaded form, clearing the specific Upload component by Id
*/
protected void clearUpload(final String id)
{
// remove the file upload bean from the session
final FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)
ctx.getExternalContext().getSessionMap().get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
fileBean.setFile(null);
fileBean.setFileName(null);
}
}
/**
* Action handler called when the user wishes to remove an uploaded file
*/
public String removeUploadedFile()
{
this.clearUpload(CheckinCheckoutDialog.FILE);
property.setFileName(null) ;
property.setFile(null);
return null;
}
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Action event called by all actions that need to setup a Content Document context on the
* CheckinCheckoutDialog before an action page/wizard is called. The context will be a Node in
* setDocument() which can be retrieved on action pages via getDocument().
*
* @param event ActionEvent
*/
public void setupContentAction(ActionEvent event)
{
UIActionLink link = (UIActionLink) event.getComponent();
Map params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
setupContentDocument(id);
}
else
{
property.setDocument(null);
}
resetState();
}
public void setupWorkflowContentAction(ActionEvent event)
{
// do the common processing
setupContentAction(event);
// retrieve the id of the task
UIActionLink link = (UIActionLink)event.getComponent();
Map params = link.getParameterMap();
property.setWorkflowTaskId(params.get("taskId"));
property.setWorkflowAction(true);
if (logger.isDebugEnabled())
logger.debug("Setup for workflow package action for task id: " + property.getWorkflowTaskId());
}
/**
* Setup a content document node context
*
* @param id GUID of the node to setup as the content document context
* @return The Node
*/
protected Node setupContentDocument(String id)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current document to: " + id);
Node node = null;
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
node = new Node(ref);
// create content URL to the content download servlet with ID and expected filename
// the myfile part will be ignored by the servlet but gives the browser a hint
String url = DownloadContentServlet.generateDownloadURL(ref, node.getName());
node.getProperties().put("url", url);
node.getProperties().put("workingCopy", node.hasAspect(ContentModel.ASPECT_WORKING_COPY));
node.getProperties().put("fileType32", FileTypeImageUtils.getFileTypeImage(node.getName(), false));
// remember the document
property.setDocument(node);
// refresh the UI, calling this method now is fine as it basically makes sure certain
// beans clear the state - so when we finish here other beans will have been reset
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
throw new AbortProcessingException("Invalid node reference");
}
return node;
}
/**
* Action handler called to calculate which editing screen to display based on the mimetype
* of a document. If appropriate, the in-line editing screen will be shown.
*/
public void editFile(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map params = link.getParameterMap();
String id = params.get("id");
try
{
if (id != null && id.length() != 0)
{
boolean editingInline = false;
Node node = setupContentDocument(id);
if (node.hasAspect(ApplicationModel.ASPECT_INLINEEDITABLE) &&
node.getProperties().get(ApplicationModel.PROP_EDITINLINE) != null &&
((Boolean)node.getProperties().get(ApplicationModel.PROP_EDITINLINE)).booleanValue() == true)
{
// retrieve the content reader for this node
ContentReader reader = property.getContentService().getReader(node.getNodeRef(), ContentModel.PROP_CONTENT);
if (reader != null)
{
editingInline = true;
String mimetype = reader.getMimetype();
// calculate which editor screen to display
if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(mimetype) ||
MimetypeMap.MIMETYPE_XML.equals(mimetype) ||
MimetypeMap.MIMETYPE_TEXT_CSS.equals(mimetype) ||
MimetypeMap.MIMETYPE_JAVASCRIPT.equals(mimetype))
{
// make content available to the text editing screen
property.setEditorOutput(reader.getContentString());
// navigate to appropriate screen
FacesContext fc = FacesContext.getCurrentInstance();
this.navigator.setupDispatchContext(node);
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:editTextInline");
}
else
{
// make content available to the html editing screen
property.setDocumentContent(reader.getContentString());
property.setEditorOutput(null);
// navigate to appropriate screen
FacesContext fc = FacesContext.getCurrentInstance();
this.navigator.setupDispatchContext(node);
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:editHtmlInline");
}
}
}
if (editingInline == false)
{
// normal downloadable document
FacesContext fc = FacesContext.getCurrentInstance();
this.navigator.setupDispatchContext(node);
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:editFile");
}
}
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
/**
* Action handler called to set the content of a node from an inline editing page.
*/
public String editInline(FacesContext context, String outcome)
{
final Node node = property.getDocument();
if (node != null)
{
try
{
if (logger.isDebugEnabled())
logger.debug("Trying to update content node Id: " + node.getId());
// get an updating writer that we can use to modify the content on the current node
ContentWriter writer = property.getContentService().getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
writer.putContent(property.getEditorOutput());
// clean up and clear action context
resetState();
property.setDocument(null);
property.setDocumentContent(null);
property.setEditorOutput(null);
}
catch (Throwable err)
{
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_UPDATE) + err.getMessage());
outcome = null;
ReportedException.throwIfNecessary(err);
}
}
else
{
logger.warn("WARNING: editInlineOK called without a current Document!");
}
return outcome;
}
/**
* Action to undo the checkout of a document just checked out from the checkout screen.
*/
public String undoCheckout()
{
String outcome = null;
Node node = property.getWorkingDocument();
if (node != null)
{
try
{
// try to cancel checkout of the working copy
this.property.getVersionOperationsService().cancelCheckout(node.getNodeRef());
resetState();
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
}
catch (Throwable err)
{
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_CANCELCHECKOUT) + err.getMessage(), err);
ReportedException.throwIfNecessary(err);
}
}
else
{
logger.warn("WARNING: undoCheckout called without a current WorkingDocument!");
}
return outcome;
}
/**
* Action called upon completion of the Check In file page
*/
public String checkinFileOK(final FacesContext context, String outcome)
{
// NOTE: for checkin the document node _is_ the working document!
final Node node = property.getDocument();
if (node != null && (property.getCopyLocation().equals(CCProperties.COPYLOCATION_CURRENT) || (this.getFileName() != null && !this.getFileName().equals(""))))
{
try
{
RetryingTransactionHelper txnHelper = Repository.getRetryingTransactionHelper(FacesContext.getCurrentInstance());
RetryingTransactionCallback