/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id$
*/
package org.nuxeo.ecm.webapp.clipboard;
import static org.jboss.seam.ScopeType.EVENT;
import static org.jboss.seam.ScopeType.SESSION;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.remoting.WebRemote;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.core.Events;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.international.LocaleSelector;
import org.jboss.seam.international.StatusMessage;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.CoreSession.CopyOption;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.LifeCycleConstants;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.ecm.core.io.download.DownloadService;
import org.nuxeo.ecm.core.schema.FacetNames;
import org.nuxeo.ecm.core.schema.SchemaManager;
import org.nuxeo.ecm.platform.actions.Action;
import org.nuxeo.ecm.platform.types.TypeManager;
import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
import org.nuxeo.ecm.platform.ui.web.api.WebActions;
import org.nuxeo.ecm.platform.ui.web.cache.SeamCacheHelper;
import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
import org.nuxeo.ecm.webapp.documentsLists.DocumentsListDescriptor;
import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
import org.nuxeo.ecm.webapp.helpers.EventManager;
import org.nuxeo.ecm.webapp.helpers.EventNames;
import org.nuxeo.runtime.api.Framework;
/**
* This is the action listener behind the copy/paste template that knows how to copy/paste the selected user data to the
* target action listener, and also create/remove the corresponding objects into the backend.
*
* @author <a href="mailto:rcaraghin@nuxeo.com">Razvan Caraghin</a>
*/
@Name("clipboardActions")
@Scope(SESSION)
public class ClipboardActionsBean implements ClipboardActions, Serializable {
private static final long serialVersionUID = -2407222456116573225L;
private static final Log log = LogFactory.getLog(ClipboardActionsBean.class);
@In(create = true, required = false)
protected FacesMessages facesMessages;
@In(create = true)
protected Map<String, String> messages;
@In(create = true, required = false)
protected transient CoreSession documentManager;
@In(create = true)
protected transient DocumentsListsManager documentsListsManager;
@In(create = true)
protected TypeManager typeManager;
@In(create = true)
protected NavigationContext navigationContext;
@In(create = true)
protected transient WebActions webActions; // it is serializable
@In(create = true)
protected transient LocaleSelector localeSelector;
@RequestParameter()
protected String workListDocId;
private String currentSelectedList;
private String previouslySelectedList;
private transient List<String> availableLists;
private transient List<DocumentsListDescriptor> descriptorsForAvailableLists;
private Boolean canEditSelectedDocs;
private transient Map<String, List<Action>> actionCache;
@Override
public void releaseClipboardableDocuments() {
}
@Override
public boolean isInitialized() {
return documentManager != null;
}
@Override
public void putSelectionInWorkList(Boolean forceAppend) {
canEditSelectedDocs = null;
if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) {
putSelectionInWorkList(
documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION), forceAppend);
autoSelectCurrentList(DocumentsListsManager.DEFAULT_WORKING_LIST);
} else {
log.debug("No selectable Documents in context to process copy on...");
}
log.debug("add to worklist processed...");
}
@Override
public void putSelectionInWorkList() {
putSelectionInWorkList(false);
}
@Override
public void putSelectionInDefaultWorkList() {
canEditSelectedDocs = null;
if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) {
List<DocumentModel> docsList = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
Object[] params = { docsList.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params);
documentsListsManager.addToWorkingList(DocumentsListsManager.DEFAULT_WORKING_LIST, docsList);
// auto select clipboard
autoSelectCurrentList(DocumentsListsManager.DEFAULT_WORKING_LIST);
} else {
log.debug("No selectable Documents in context to process copy on...");
}
log.debug("add to worklist processed...");
}
@Override
@WebRemote
public void putInClipboard(String docId) {
DocumentModel doc = documentManager.getDocument(new IdRef(docId));
documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, doc);
Object[] params = { 1 };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params);
autoSelectCurrentList(DocumentsListsManager.CLIPBOARD);
}
@Override
public void putSelectionInClipboard() {
canEditSelectedDocs = null;
if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) {
List<DocumentModel> docsList = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
Object[] params = { docsList.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params);
documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, docsList);
// auto select clipboard
autoSelectCurrentList(DocumentsListsManager.CLIPBOARD);
} else {
log.debug("No selectable Documents in context to process copy on...");
}
log.debug("add to worklist processed...");
}
@Override
public void putSelectionInWorkList(List<DocumentModel> docsList) {
putSelectionInWorkList(docsList, false);
}
@Override
public void putSelectionInWorkList(List<DocumentModel> docsList, Boolean forceAppend) {
canEditSelectedDocs = null;
if (null != docsList) {
Object[] params = { docsList.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_added_to_worklist_docs"), params);
// Add to the default working list
documentsListsManager.addToWorkingList(getCurrentSelectedListName(), docsList, forceAppend);
log.debug("Elements copied to clipboard...");
} else {
log.debug("No copiedDocs to process copy on...");
}
log.debug("add to worklist processed...");
}
@Override
@Deprecated
public void copySelection(List<DocumentModel> copiedDocs) {
if (null != copiedDocs) {
Object[] params = { copiedDocs.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params);
// clipboard.copy(copiedDocs);
// Reset + Add to clipboard list
documentsListsManager.resetWorkingList(DocumentsListsManager.CLIPBOARD);
documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, copiedDocs);
// Add to the default working list
documentsListsManager.addToWorkingList(copiedDocs);
log.debug("Elements copied to clipboard...");
} else {
log.debug("No copiedDocs to process copy on...");
}
log.debug("Copy processed...");
}
public boolean exists(DocumentRef ref) {
return ref != null && documentManager.exists(ref);
}
@Override
public String removeWorkListItem(DocumentRef ref) {
DocumentModel doc = null;
if (exists(ref)) {
doc = documentManager.getDocument(ref);
} else { // document was permanently deleted so let's use the one in the work list
List<DocumentModel> workingListDocs = documentsListsManager.getWorkingList(getCurrentSelectedListName());
for (DocumentModel wDoc : workingListDocs) {
if (wDoc.getRef().equals(ref)) {
doc = wDoc;
}
}
}
documentsListsManager.removeFromWorkingList(getCurrentSelectedListName(), doc);
return null;
}
@Override
public String clearWorkingList() {
documentsListsManager.resetWorkingList(getCurrentSelectedListName());
return null;
}
@Override
public String pasteDocumentList(String listName) {
return pasteDocumentList(documentsListsManager.getWorkingList(listName));
}
@Override
public String pasteDocumentListInside(String listName, String docId) {
return pasteDocumentListInside(documentsListsManager.getWorkingList(listName), docId);
}
@Override
public String pasteDocumentList(List<DocumentModel> docPaste) {
DocumentModel currentDocument = navigationContext.getCurrentDocument();
if (null != docPaste) {
List<DocumentModel> newDocs = recreateDocumentsWithNewParent(getParent(currentDocument), docPaste);
Object[] params = { newDocs.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_pasted_docs"), params);
EventManager.raiseEventsOnDocumentSelected(currentDocument);
Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument);
log.debug("Elements pasted and created into the backend...");
} else {
log.debug("No docPaste to process paste on...");
}
return null;
}
@Override
public String pasteDocumentListInside(List<DocumentModel> docPaste, String docId) {
DocumentModel targetDoc = documentManager.getDocument(new IdRef(docId));
if (null != docPaste) {
List<DocumentModel> newDocs = recreateDocumentsWithNewParent(targetDoc, docPaste);
Object[] params = { newDocs.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_pasted_docs"), params);
EventManager.raiseEventsOnDocumentSelected(targetDoc);
Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, targetDoc);
log.debug("Elements pasted and created into the backend...");
} else {
log.debug("No docPaste to process paste on...");
}
return null;
}
public List<DocumentModel> moveDocumentsToNewParent(DocumentModel destFolder, List<DocumentModel> docs) {
DocumentRef destFolderRef = destFolder.getRef();
boolean destinationIsDeleted = LifeCycleConstants.DELETED_STATE.equals(destFolder.getCurrentLifeCycleState());
List<DocumentModel> newDocs = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (DocumentModel docModel : docs) {
DocumentRef sourceFolderRef = docModel.getParentRef();
String sourceType = docModel.getType();
boolean canRemoveDoc = documentManager.hasPermission(sourceFolderRef, SecurityConstants.REMOVE_CHILDREN);
boolean canPasteInCurrentFolder = typeManager.isAllowedSubType(sourceType, destFolder.getType(),
navigationContext.getCurrentDocument());
boolean sameFolder = sourceFolderRef.equals(destFolderRef);
if (canRemoveDoc && canPasteInCurrentFolder && !sameFolder) {
if (destinationIsDeleted) {
if (checkDeletedState(docModel)) {
DocumentModel newDoc = documentManager.move(docModel.getRef(), destFolderRef, null);
setDeleteState(newDoc);
newDocs.add(newDoc);
} else {
addWarnMessage(sb, docModel);
}
} else {
DocumentModel newDoc = documentManager.move(docModel.getRef(), destFolderRef, null);
newDocs.add(newDoc);
}
}
}
documentManager.save();
if (sb.length() > 0) {
facesMessages.add(StatusMessage.Severity.WARN, sb.toString());
}
return newDocs;
}
public String moveDocumentList(String listName, String docId) {
List<DocumentModel> docs = documentsListsManager.getWorkingList(listName);
DocumentModel targetDoc = documentManager.getDocument(new IdRef(docId));
// Get all parent folders
Set<DocumentRef> parentRefs = new HashSet<>();
for (DocumentModel doc : docs) {
parentRefs.add(doc.getParentRef());
}
List<DocumentModel> newDocs = moveDocumentsToNewParent(targetDoc, docs);
documentsListsManager.resetWorkingList(listName);
Object[] params = { newDocs.size() };
facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_moved_docs"), params);
EventManager.raiseEventsOnDocumentSelected(targetDoc);
Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, targetDoc);
// Send event to all initial parents
for (DocumentRef docRef : parentRefs) {
Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, documentManager.getDocument(docRef));
}
log.debug("Elements moved and created into the backend...");
return null;
}
public String moveDocumentList(String listName) {
DocumentModel currentDocument = navigationContext.getCurrentDocument();
return moveDocumentList(listName, currentDocument.getId());
}
@Override
public String moveWorkingList() {
try {
moveDocumentList(getCurrentSelectedListName());
} catch (NuxeoException e) {
log.error("moveWorkingList failed" + e.getMessage(), e);
facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation"));
}
return null;
}
@Override
public String pasteWorkingList() {
try {
pasteDocumentList(getCurrentSelectedList());
} catch (NuxeoException e) {
log.error("pasteWorkingList failed" + e.getMessage(), e);
facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation"));
}
return null;
}
@Override
public String pasteClipboard() {
try {
pasteDocumentList(DocumentsListsManager.CLIPBOARD);
returnToPreviouslySelectedList();
} catch (NuxeoException e) {
log.error("pasteClipboard failed" + e.getMessage(), e);
facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation"));
}
return null;
}
@Override
@WebRemote
public String pasteClipboardInside(String docId) {
pasteDocumentListInside(DocumentsListsManager.CLIPBOARD, docId);
return null;
}
@Override
@WebRemote
public String moveClipboardInside(String docId) {
moveDocumentList(DocumentsListsManager.CLIPBOARD, docId);
return null;
}
/**
* Creates the documents in the backend under the target parent.
*/
protected List<DocumentModel> recreateDocumentsWithNewParent(DocumentModel parent, List<DocumentModel> documents) {
List<DocumentModel> newDocuments = new ArrayList<>();
if (null == parent || null == documents) {
log.error("Null params received, returning...");
return newDocuments;
}
List<DocumentModel> documentsToPast = new LinkedList<>();
// filter list on content type
for (DocumentModel doc : documents) {
if (typeManager.isAllowedSubType(doc.getType(), parent.getType(), navigationContext.getCurrentDocument())) {
documentsToPast.add(doc);
}
}
// copying proxy or document
boolean isPublishSpace = isPublishSpace(parent);
boolean destinationIsDeleted = LifeCycleConstants.DELETED_STATE.equals(parent.getCurrentLifeCycleState());
List<DocumentRef> docRefs = new ArrayList<>();
List<DocumentRef> proxyRefs = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (DocumentModel doc : documentsToPast) {
if (destinationIsDeleted && !checkDeletedState(doc)) {
addWarnMessage(sb, doc);
} else if (doc.isProxy() && !isPublishSpace) {
// in a non-publish space, we want to expand proxies into
// normal docs
proxyRefs.add(doc.getRef());
} else {
// copy as is
docRefs.add(doc.getRef());
}
}
if (!proxyRefs.isEmpty()) {
newDocuments.addAll(documentManager.copyProxyAsDocument(proxyRefs, parent.getRef(),
CopyOption.RESET_LIFE_CYCLE));
}
if (!docRefs.isEmpty()) {
newDocuments.addAll(documentManager.copy(docRefs, parent.getRef(), CopyOption.RESET_LIFE_CYCLE));
}
if (destinationIsDeleted) {
for (DocumentModel d : newDocuments) {
setDeleteState(d);
}
}
documentManager.save();
if (sb.length() > 0) {
facesMessages.add(StatusMessage.Severity.WARN, sb.toString());
}
return newDocuments;
}
protected boolean checkDeletedState(DocumentModel doc) {
if (LifeCycleConstants.DELETED_STATE.equals(doc.getCurrentLifeCycleState())) {
return true;
}
if (doc.getAllowedStateTransitions().contains(LifeCycleConstants.DELETE_TRANSITION)) {
return true;
}
return false;
}
protected void setDeleteState(DocumentModel doc) {
if (doc.getAllowedStateTransitions().contains(LifeCycleConstants.DELETE_TRANSITION)) {
doc.followTransition(LifeCycleConstants.DELETE_TRANSITION);
}
}
protected void addWarnMessage(StringBuilder sb, DocumentModel doc) {
if (sb.length() == 0) {
sb.append(messages.get("document_no_deleted_state"));
sb.append("'").append(doc.getTitle()).append("'");
} else {
sb.append(", '").append(doc.getTitle()).append("'");
}
}
/**
* Check if the container is a publish space. If this is not the case, a proxy copied to it will be recreated as a
* new document.
*/
protected boolean isPublishSpace(DocumentModel container) {
SchemaManager schemaManager = Framework.getService(SchemaManager.class);
Set<String> publishSpaces = schemaManager.getDocumentTypeNamesForFacet(FacetNames.PUBLISH_SPACE);
if (publishSpaces == null || publishSpaces.isEmpty()) {
publishSpaces = new HashSet<>();
}
return publishSpaces.contains(container.getType());
}
/**
* Gets the parent document under the paste should be performed.
* <p>
* Rules:
* <p>
* In general the currentDocument is the parent. Exceptions to this rule: when the currentDocument is a domain or
* null. If Domain then content root is the parent. If null is passed, then the JCR root is taken as parent.
*/
protected DocumentModel getParent(DocumentModel currentDocument) {
if (currentDocument.isFolder()) {
return currentDocument;
}
DocumentModelList parents = navigationContext.getCurrentPath();
for (int i = parents.size() - 1; i >= 0; i--) {
DocumentModel parent = parents.get(i);
if (parent.isFolder()) {
return parent;
}
}
return null;
}
@Override
@Factory(value = "isCurrentWorkListEmpty", scope = EVENT)
public boolean factoryForIsCurrentWorkListEmpty() {
return isWorkListEmpty();
}
@Override
public boolean isWorkListEmpty() {
return documentsListsManager.isWorkingListEmpty(getCurrentSelectedListName());
}
@Override
public String exportWorklistAsZip() {
return exportWorklistAsZip(documentsListsManager.getWorkingList(getCurrentSelectedListName()));
}
@Override
public String exportAllBlobsFromWorkingListAsZip() {
return exportWorklistAsZip();
}
@Override
public String exportMainBlobFromWorkingListAsZip() {
return exportWorklistAsZip();
}
@Override
public String exportWorklistAsZip(List<DocumentModel> documents) {
return exportWorklistAsZip(documents, true);
}
public String exportWorklistAsZip(DocumentModel document) {
return exportWorklistAsZip(Collections.singletonList(document), true);
}
/**
* Checks if copy action is available in the context of the current Document.
* <p>
* Condition: the list of selected documents is not empty.
*/
@Override
public boolean getCanCopy() {
if (navigationContext.getCurrentDocument() == null) {
return false;
}
return !documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
}
/**
* Checks if the Paste action is available in the context of the current Document. Conditions:
* <p>
* <ul>
* <li>list is not empty
* <li>user has the needed permissions on the current document
* <li>the content of the list can be added as children of the current document
* </ul>
*/
@Override
public boolean getCanPaste(String listName) {
DocumentModel currentDocument = navigationContext.getCurrentDocument();
if (documentsListsManager.isWorkingListEmpty(listName) || currentDocument == null) {
return false;
}
DocumentModel pasteTarget = getParent(navigationContext.getCurrentDocument());
if (pasteTarget == null) {
// parent may be unreachable (right inheritance blocked)
return false;
}
if (!documentManager.hasPermission(pasteTarget.getRef(), SecurityConstants.ADD_CHILDREN)) {
return false;
} else {
// filter on allowed content types
// see if at least one doc can be pasted
// String pasteTypeName = clipboard.getClipboardDocumentType();
List<String> pasteTypesName = documentsListsManager.getWorkingListTypes(listName);
for (String pasteTypeName : pasteTypesName) {
if (typeManager.isAllowedSubType(pasteTypeName, pasteTarget.getType(),
navigationContext.getCurrentDocument())) {
return true;
}
}
return false;
}
}
@Override
public boolean getCanPasteInside(String listName, DocumentModel document) {
if (documentsListsManager.isWorkingListEmpty(listName) || document == null) {
return false;
}
if (!documentManager.hasPermission(document.getRef(), SecurityConstants.ADD_CHILDREN)) {
return false;
} else {
// filter on allowed content types
// see if at least one doc can be pasted
// String pasteTypeName = clipboard.getClipboardDocumentType();
List<String> pasteTypesName = documentsListsManager.getWorkingListTypes(listName);
for (String pasteTypeName : pasteTypesName) {
if (typeManager.isAllowedSubType(pasteTypeName, document.getType(),
navigationContext.getCurrentDocument())) {
return true;
}
}
return false;
}
}
/**
* Checks if the Move action is available in the context of the document document. Conditions:
* <p>
* <ul>
* <li>list is not empty
* <li>user has the needed permissions on the document
* <li>an element in the list can be removed from its folder and added as child of the current document
* </ul>
*/
@Override
public boolean getCanMoveInside(String listName, DocumentModel document) {
if (documentsListsManager.isWorkingListEmpty(listName) || document == null) {
return false;
}
DocumentRef destFolderRef = document.getRef();
DocumentModel destFolder = document;
if (!documentManager.hasPermission(destFolderRef, SecurityConstants.ADD_CHILDREN)) {
return false;
} else {
// filter on allowed content types
// see if at least one doc can be removed and pasted
for (DocumentModel docModel : documentsListsManager.getWorkingList(listName)) {
// skip deleted documents
if (!exists(docModel.getRef())) {
continue;
}
DocumentRef sourceFolderRef = docModel.getParentRef();
String sourceType = docModel.getType();
boolean canRemoveDoc = documentManager.hasPermission(sourceFolderRef, SecurityConstants.REMOVE_CHILDREN);
boolean canPasteInCurrentFolder = typeManager.isAllowedSubType(sourceType, destFolder.getType(),
navigationContext.getCurrentDocument());
boolean sameFolder = sourceFolderRef.equals(destFolderRef);
if (canRemoveDoc && canPasteInCurrentFolder && !sameFolder) {
return true;
}
}
return false;
}
}
/**
* Checks if the Move action is available in the context of the current Document. Conditions:
* <p>
* <ul>
* <li>list is not empty
* <li>user has the needed permissions on the current document
* <li>an element in the list can be removed from its folder and added as child of the current document
* </ul>
*/
public boolean getCanMove(String listName) {
DocumentModel currentDocument = navigationContext.getCurrentDocument();
return getCanMoveInside(listName, currentDocument);
}
@Override
public boolean getCanPasteWorkList() {
return getCanPaste(getCurrentSelectedListName());
}
@Override
public boolean getCanMoveWorkingList() {
return getCanMove(getCurrentSelectedListName());
}
@Override
public boolean getCanPasteFromClipboard() {
return getCanPaste(DocumentsListsManager.CLIPBOARD);
}
@Override
public boolean getCanPasteFromClipboardInside(DocumentModel document) {
return getCanPasteInside(DocumentsListsManager.CLIPBOARD, document);
}
@Override
public boolean getCanMoveFromClipboardInside(DocumentModel document) {
return getCanMoveInside(DocumentsListsManager.CLIPBOARD, document);
}
@Override
public void setCurrentSelectedList(String listId) {
if (listId != null && !listId.equals(currentSelectedList)) {
currentSelectedList = listId;
canEditSelectedDocs = null;
}
}
@RequestParameter()
String listIdToSelect;
@Override
public void selectList() {
if (listIdToSelect != null) {
setCurrentSelectedList(listIdToSelect);
}
}
@Override
public List<DocumentModel> getCurrentSelectedList() {
return documentsListsManager.getWorkingList(getCurrentSelectedListName());
}
@Override
public String getCurrentSelectedListName() {
if (currentSelectedList == null) {
if (!getAvailableLists().isEmpty()) {
setCurrentSelectedList(availableLists.get(0));
}
}
return currentSelectedList;
}
@Override
public String getCurrentSelectedListTitle() {
String title = null;
String listName = getCurrentSelectedListName();
if (listName != null) {
DocumentsListDescriptor desc = documentsListsManager.getWorkingListDescriptor(listName);
if (desc != null) {
title = desc.getTitle();
}
}
return title;
}
@Override
public List<String> getAvailableLists() {
if (availableLists == null) {
availableLists = documentsListsManager.getWorkingListNamesForCategory("CLIPBOARD");
}
return availableLists;
}
@Override
public List<DocumentsListDescriptor> getDescriptorsForAvailableLists() {
if (descriptorsForAvailableLists == null) {
List<String> availableLists = getAvailableLists();
descriptorsForAvailableLists = new ArrayList<>();
for (String lName : availableLists) {
descriptorsForAvailableLists.add(documentsListsManager.getWorkingListDescriptor(lName));
}
}
return descriptorsForAvailableLists;
}
@Override
public List<Action> getActionsForCurrentList() {
String lstName = getCurrentSelectedListName();
if (isWorkListEmpty()) {
// we use cache here since this is a very common case ...
if (actionCache == null) {
actionCache = new HashMap<>();
}
if (!actionCache.containsKey(lstName)) {
actionCache.put(lstName, webActions.getActionsList(lstName + "_LIST"));
}
return actionCache.get(lstName);
} else {
return webActions.getActionsList(lstName + "_LIST");
}
}
@Override
public List<Action> getActionsForSelection() {
return webActions.getActionsList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION + "_LIST", false);
}
private void autoSelectCurrentList(String listName) {
previouslySelectedList = getCurrentSelectedListName();
setCurrentSelectedList(listName);
}
private void returnToPreviouslySelectedList() {
setCurrentSelectedList(previouslySelectedList);
}
@Override
public boolean getCanEditSelectedDocs() {
if (canEditSelectedDocs == null) {
if (getCurrentSelectedList().isEmpty()) {
canEditSelectedDocs = false;
} else {
final List<DocumentModel> selectedDocs = getCurrentSelectedList();
// check selected docs
canEditSelectedDocs = checkWritePerm(selectedDocs);
}
}
return canEditSelectedDocs;
}
@Override
@Deprecated
// no longer used by the user_clipboard.xhtml template
public boolean getCanEditListDocs(String listName) {
final List<DocumentModel> docs = documentsListsManager.getWorkingList(listName);
final boolean canEdit;
if (docs.isEmpty()) {
canEdit = false;
} else {
// check selected docs
canEdit = checkWritePerm(docs);
}
return canEdit;
}
private boolean checkWritePerm(List<DocumentModel> selectedDocs) {
for (DocumentModel documentModel : selectedDocs) {
boolean canWrite = documentManager.hasPermission(documentModel.getRef(), SecurityConstants.WRITE_PROPERTIES);
if (!canWrite) {
return false;
}
}
return true;
}
@Override
public boolean isCacheEnabled() {
if (!SeamCacheHelper.canUseSeamCache()) {
return false;
}
return isWorkListEmpty();
}
@Override
public String getCacheKey() {
return getCurrentSelectedListName() + "::" + localeSelector.getLocaleString();
}
@Override
public boolean isCacheEnabledForSelection() {
if (!SeamCacheHelper.canUseSeamCache()) {
return false;
}
return documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
}
@Override
public String exportWorklistAsZip(List<DocumentModel> documents, boolean exportAllBlobs) {
Blob blob = null;
try {
DownloadService downloadService = Framework.getService(DownloadService.class);
DocumentListZipExporter zipExporter = new DocumentListZipExporter();
blob = zipExporter.exportWorklistAsZip(documents, documentManager, exportAllBlobs);
if (blob == null) {
// empty zip file, do nothing
facesMessages.add(StatusMessage.Severity.INFO, messages.get("label.clipboard.emptyDocuments"));
return null;
}
blob.setMimeType("application/zip");
blob.setFilename("clipboard.zip");
String key = downloadService.storeBlobs(Collections.singletonList(blob));
String url = BaseURL.getBaseURL() + downloadService.getDownloadUrl(key);
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect(url);
return "";
} catch (IOException io) {
if (blob != null) {
blob.getFile().delete();
}
throw new NuxeoException("Error while redirecting for clipboard content", io);
}
}
}