001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 * $Id$ 020 */ 021package org.nuxeo.ecm.webapp.clipboard; 022 023import static org.jboss.seam.ScopeType.EVENT; 024import static org.jboss.seam.ScopeType.SESSION; 025 026import java.io.File; 027import java.io.IOException; 028import java.io.Serializable; 029import java.util.ArrayList; 030import java.util.Collections; 031import java.util.HashMap; 032import java.util.HashSet; 033import java.util.LinkedList; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037 038import javax.faces.context.FacesContext; 039import javax.servlet.http.HttpServletRequest; 040 041import org.apache.commons.logging.Log; 042import org.apache.commons.logging.LogFactory; 043import org.jboss.seam.annotations.Factory; 044import org.jboss.seam.annotations.In; 045import org.jboss.seam.annotations.Name; 046import org.jboss.seam.annotations.Scope; 047import org.jboss.seam.annotations.remoting.WebRemote; 048import org.jboss.seam.annotations.web.RequestParameter; 049import org.jboss.seam.core.Events; 050import org.jboss.seam.faces.FacesMessages; 051import org.jboss.seam.international.LocaleSelector; 052import org.jboss.seam.international.StatusMessage; 053import org.nuxeo.ecm.core.api.CoreSession; 054import org.nuxeo.ecm.core.api.CoreSession.CopyOption; 055import org.nuxeo.ecm.core.api.DocumentModel; 056import org.nuxeo.ecm.core.api.DocumentModelList; 057import org.nuxeo.ecm.core.api.DocumentRef; 058import org.nuxeo.ecm.core.api.IdRef; 059import org.nuxeo.ecm.core.api.LifeCycleConstants; 060import org.nuxeo.ecm.core.api.NuxeoException; 061import org.nuxeo.ecm.core.api.security.SecurityConstants; 062import org.nuxeo.ecm.core.io.download.DownloadService; 063import org.nuxeo.ecm.core.schema.FacetNames; 064import org.nuxeo.ecm.core.schema.SchemaManager; 065import org.nuxeo.ecm.platform.actions.Action; 066import org.nuxeo.ecm.platform.types.TypeManager; 067import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 068import org.nuxeo.ecm.platform.ui.web.api.WebActions; 069import org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants; 070import org.nuxeo.ecm.platform.ui.web.cache.SeamCacheHelper; 071import org.nuxeo.ecm.platform.ui.web.tag.fn.Functions; 072import org.nuxeo.ecm.platform.ui.web.util.BaseURL; 073import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 074import org.nuxeo.ecm.webapp.documentsLists.DocumentsListDescriptor; 075import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 076import org.nuxeo.ecm.webapp.helpers.EventManager; 077import org.nuxeo.ecm.webapp.helpers.EventNames; 078import org.nuxeo.runtime.api.Framework; 079 080/** 081 * This is the action listener behind the copy/paste template that knows how to copy/paste the selected user data to the 082 * target action listener, and also create/remove the corresponding objects into the backend. 083 * 084 * @author <a href="mailto:rcaraghin@nuxeo.com">Razvan Caraghin</a> 085 */ 086@Name("clipboardActions") 087@Scope(SESSION) 088public class ClipboardActionsBean implements ClipboardActions, Serializable { 089 090 private static final long serialVersionUID = -2407222456116573225L; 091 092 private static final Log log = LogFactory.getLog(ClipboardActionsBean.class); 093 094 @In(create = true, required = false) 095 protected FacesMessages facesMessages; 096 097 @In(create = true) 098 protected Map<String, String> messages; 099 100 @In(create = true, required = false) 101 protected transient CoreSession documentManager; 102 103 @In(create = true) 104 protected transient DocumentsListsManager documentsListsManager; 105 106 @In(create = true) 107 protected TypeManager typeManager; 108 109 @In(create = true) 110 protected NavigationContext navigationContext; 111 112 @In(create = true) 113 protected transient WebActions webActions; // it is serializable 114 115 @In(create = true) 116 protected transient LocaleSelector localeSelector; 117 118 @RequestParameter() 119 protected String workListDocId; 120 121 private String currentSelectedList; 122 123 private String previouslySelectedList; 124 125 private transient List<String> availableLists; 126 127 private transient List<DocumentsListDescriptor> descriptorsForAvailableLists; 128 129 private Boolean canEditSelectedDocs; 130 131 private transient Map<String, List<Action>> actionCache; 132 133 public void releaseClipboardableDocuments() { 134 } 135 136 public boolean isInitialized() { 137 return documentManager != null; 138 } 139 140 public void putSelectionInWorkList(Boolean forceAppend) { 141 canEditSelectedDocs = null; 142 if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) { 143 putSelectionInWorkList( 144 documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION), forceAppend); 145 autoSelectCurrentList(DocumentsListsManager.DEFAULT_WORKING_LIST); 146 } else { 147 log.debug("No selectable Documents in context to process copy on..."); 148 } 149 log.debug("add to worklist processed..."); 150 } 151 152 public void putSelectionInWorkList() { 153 putSelectionInWorkList(false); 154 } 155 156 public void putSelectionInDefaultWorkList() { 157 canEditSelectedDocs = null; 158 if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) { 159 List<DocumentModel> docsList = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 160 Object[] params = { docsList.size() }; 161 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params); 162 documentsListsManager.addToWorkingList(DocumentsListsManager.DEFAULT_WORKING_LIST, docsList); 163 164 // auto select clipboard 165 autoSelectCurrentList(DocumentsListsManager.DEFAULT_WORKING_LIST); 166 167 } else { 168 log.debug("No selectable Documents in context to process copy on..."); 169 } 170 log.debug("add to worklist processed..."); 171 } 172 173 @WebRemote 174 public void putInClipboard(String docId) { 175 DocumentModel doc = documentManager.getDocument(new IdRef(docId)); 176 documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, doc); 177 Object[] params = { 1 }; 178 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params); 179 180 autoSelectCurrentList(DocumentsListsManager.CLIPBOARD); 181 } 182 183 public void putSelectionInClipboard() { 184 canEditSelectedDocs = null; 185 if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) { 186 List<DocumentModel> docsList = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 187 Object[] params = { docsList.size() }; 188 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params); 189 190 documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, docsList); 191 192 // auto select clipboard 193 autoSelectCurrentList(DocumentsListsManager.CLIPBOARD); 194 195 } else { 196 log.debug("No selectable Documents in context to process copy on..."); 197 } 198 log.debug("add to worklist processed..."); 199 } 200 201 public void putSelectionInWorkList(List<DocumentModel> docsList) { 202 putSelectionInWorkList(docsList, false); 203 } 204 205 public void putSelectionInWorkList(List<DocumentModel> docsList, Boolean forceAppend) { 206 canEditSelectedDocs = null; 207 if (null != docsList) { 208 Object[] params = { docsList.size() }; 209 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_added_to_worklist_docs"), params); 210 211 // Add to the default working list 212 documentsListsManager.addToWorkingList(getCurrentSelectedListName(), docsList, forceAppend); 213 log.debug("Elements copied to clipboard..."); 214 215 } else { 216 log.debug("No copiedDocs to process copy on..."); 217 } 218 219 log.debug("add to worklist processed..."); 220 } 221 222 @Deprecated 223 public void copySelection(List<DocumentModel> copiedDocs) { 224 if (null != copiedDocs) { 225 Object[] params = { copiedDocs.size() }; 226 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_copied_docs"), params); 227 228 // clipboard.copy(copiedDocs); 229 230 // Reset + Add to clipboard list 231 documentsListsManager.resetWorkingList(DocumentsListsManager.CLIPBOARD); 232 documentsListsManager.addToWorkingList(DocumentsListsManager.CLIPBOARD, copiedDocs); 233 234 // Add to the default working list 235 documentsListsManager.addToWorkingList(copiedDocs); 236 log.debug("Elements copied to clipboard..."); 237 238 } else { 239 log.debug("No copiedDocs to process copy on..."); 240 } 241 242 log.debug("Copy processed..."); 243 } 244 245 public boolean exists(DocumentRef ref) { 246 return ref != null && documentManager.exists(ref); 247 } 248 249 public String removeWorkListItem(DocumentRef ref) { 250 DocumentModel doc = null; 251 if (exists(ref)) { 252 doc = documentManager.getDocument(ref); 253 } else { // document was permanently deleted so let's use the one in the work list 254 List<DocumentModel> workingListDocs = documentsListsManager.getWorkingList(getCurrentSelectedListName()); 255 for (DocumentModel wDoc : workingListDocs) { 256 if (wDoc.getRef().equals(ref)) { 257 doc = wDoc; 258 } 259 } 260 } 261 documentsListsManager.removeFromWorkingList(getCurrentSelectedListName(), doc); 262 return null; 263 } 264 265 public String clearWorkingList() { 266 documentsListsManager.resetWorkingList(getCurrentSelectedListName()); 267 return null; 268 } 269 270 public String pasteDocumentList(String listName) { 271 return pasteDocumentList(documentsListsManager.getWorkingList(listName)); 272 } 273 274 public String pasteDocumentListInside(String listName, String docId) { 275 return pasteDocumentListInside(documentsListsManager.getWorkingList(listName), docId); 276 } 277 278 public String pasteDocumentList(List<DocumentModel> docPaste) { 279 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 280 if (null != docPaste) { 281 List<DocumentModel> newDocs = recreateDocumentsWithNewParent(getParent(currentDocument), docPaste); 282 283 Object[] params = { newDocs.size() }; 284 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_pasted_docs"), params); 285 286 EventManager.raiseEventsOnDocumentSelected(currentDocument); 287 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument); 288 289 log.debug("Elements pasted and created into the backend..."); 290 } else { 291 log.debug("No docPaste to process paste on..."); 292 } 293 294 return null; 295 } 296 297 public String pasteDocumentListInside(List<DocumentModel> docPaste, String docId) { 298 DocumentModel targetDoc = documentManager.getDocument(new IdRef(docId)); 299 if (null != docPaste) { 300 List<DocumentModel> newDocs = recreateDocumentsWithNewParent(targetDoc, docPaste); 301 302 Object[] params = { newDocs.size() }; 303 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_pasted_docs"), params); 304 305 EventManager.raiseEventsOnDocumentSelected(targetDoc); 306 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, targetDoc); 307 308 log.debug("Elements pasted and created into the backend..."); 309 } else { 310 log.debug("No docPaste to process paste on..."); 311 } 312 313 return null; 314 } 315 316 public List<DocumentModel> moveDocumentsToNewParent(DocumentModel destFolder, List<DocumentModel> docs) { 317 DocumentRef destFolderRef = destFolder.getRef(); 318 boolean destinationIsDeleted = LifeCycleConstants.DELETED_STATE.equals(destFolder.getCurrentLifeCycleState()); 319 List<DocumentModel> newDocs = new ArrayList<>(); 320 StringBuilder sb = new StringBuilder(); 321 for (DocumentModel docModel : docs) { 322 DocumentRef sourceFolderRef = docModel.getParentRef(); 323 324 String sourceType = docModel.getType(); 325 boolean canRemoveDoc = documentManager.hasPermission(sourceFolderRef, SecurityConstants.REMOVE_CHILDREN); 326 boolean canPasteInCurrentFolder = typeManager.isAllowedSubType(sourceType, destFolder.getType(), 327 navigationContext.getCurrentDocument()); 328 boolean sameFolder = sourceFolderRef.equals(destFolderRef); 329 if (canRemoveDoc && canPasteInCurrentFolder && !sameFolder) { 330 if (destinationIsDeleted) { 331 if (checkDeletedState(docModel)) { 332 DocumentModel newDoc = documentManager.move(docModel.getRef(), destFolderRef, null); 333 setDeleteState(newDoc); 334 newDocs.add(newDoc); 335 } else { 336 addWarnMessage(sb, docModel); 337 } 338 } else { 339 DocumentModel newDoc = documentManager.move(docModel.getRef(), destFolderRef, null); 340 newDocs.add(newDoc); 341 } 342 } 343 } 344 documentManager.save(); 345 346 if (sb.length() > 0) { 347 facesMessages.add(StatusMessage.Severity.WARN, sb.toString()); 348 } 349 return newDocs; 350 } 351 352 public String moveDocumentList(String listName, String docId) { 353 List<DocumentModel> docs = documentsListsManager.getWorkingList(listName); 354 DocumentModel targetDoc = documentManager.getDocument(new IdRef(docId)); 355 // Get all parent folders 356 Set<DocumentRef> parentRefs = new HashSet<>(); 357 for (DocumentModel doc : docs) { 358 parentRefs.add(doc.getParentRef()); 359 } 360 361 List<DocumentModel> newDocs = moveDocumentsToNewParent(targetDoc, docs); 362 363 documentsListsManager.resetWorkingList(listName); 364 365 Object[] params = { newDocs.size() }; 366 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_moved_docs"), params); 367 368 EventManager.raiseEventsOnDocumentSelected(targetDoc); 369 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, targetDoc); 370 371 // Send event to all initial parents 372 for (DocumentRef docRef : parentRefs) { 373 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, documentManager.getDocument(docRef)); 374 } 375 376 log.debug("Elements moved and created into the backend..."); 377 378 return null; 379 } 380 381 public String moveDocumentList(String listName) { 382 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 383 return moveDocumentList(listName, currentDocument.getId()); 384 } 385 386 public String moveWorkingList() { 387 try { 388 moveDocumentList(getCurrentSelectedListName()); 389 } catch (NuxeoException e) { 390 log.error("moveWorkingList failed" + e.getMessage(), e); 391 facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation")); 392 } 393 return null; 394 } 395 396 public String pasteWorkingList() { 397 try { 398 pasteDocumentList(getCurrentSelectedList()); 399 } catch (NuxeoException e) { 400 log.error("pasteWorkingList failed" + e.getMessage(), e); 401 facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation")); 402 } 403 return null; 404 } 405 406 public String pasteClipboard() { 407 try { 408 pasteDocumentList(DocumentsListsManager.CLIPBOARD); 409 returnToPreviouslySelectedList(); 410 } catch (NuxeoException e) { 411 log.error("pasteClipboard failed" + e.getMessage(), e); 412 facesMessages.add(StatusMessage.Severity.WARN, messages.get("invalid_operation")); 413 414 } 415 return null; 416 } 417 418 @WebRemote 419 public String pasteClipboardInside(String docId) { 420 pasteDocumentListInside(DocumentsListsManager.CLIPBOARD, docId); 421 return null; 422 } 423 424 @WebRemote 425 public String moveClipboardInside(String docId) { 426 moveDocumentList(DocumentsListsManager.CLIPBOARD, docId); 427 return null; 428 } 429 430 /** 431 * Creates the documents in the backend under the target parent. 432 */ 433 protected List<DocumentModel> recreateDocumentsWithNewParent(DocumentModel parent, List<DocumentModel> documents) { 434 435 List<DocumentModel> newDocuments = new ArrayList<>(); 436 437 if (null == parent || null == documents) { 438 log.error("Null params received, returning..."); 439 return newDocuments; 440 } 441 442 List<DocumentModel> documentsToPast = new LinkedList<>(); 443 444 // filter list on content type 445 for (DocumentModel doc : documents) { 446 if (typeManager.isAllowedSubType(doc.getType(), parent.getType(), navigationContext.getCurrentDocument())) { 447 documentsToPast.add(doc); 448 } 449 } 450 451 // copying proxy or document 452 boolean isPublishSpace = isPublishSpace(parent); 453 boolean destinationIsDeleted = LifeCycleConstants.DELETED_STATE.equals(parent.getCurrentLifeCycleState()); 454 List<DocumentRef> docRefs = new ArrayList<>(); 455 List<DocumentRef> proxyRefs = new ArrayList<>(); 456 StringBuilder sb = new StringBuilder(); 457 for (DocumentModel doc : documentsToPast) { 458 if (destinationIsDeleted && !checkDeletedState(doc)) { 459 addWarnMessage(sb, doc); 460 } else if (doc.isProxy() && !isPublishSpace) { 461 // in a non-publish space, we want to expand proxies into 462 // normal docs 463 proxyRefs.add(doc.getRef()); 464 } else { 465 // copy as is 466 docRefs.add(doc.getRef()); 467 } 468 } 469 if (!proxyRefs.isEmpty()) { 470 newDocuments.addAll(documentManager.copyProxyAsDocument(proxyRefs, parent.getRef(), 471 CopyOption.RESET_LIFE_CYCLE)); 472 } 473 if (!docRefs.isEmpty()) { 474 newDocuments.addAll(documentManager.copy(docRefs, parent.getRef(), CopyOption.RESET_LIFE_CYCLE)); 475 } 476 if (destinationIsDeleted) { 477 for (DocumentModel d : newDocuments) { 478 setDeleteState(d); 479 } 480 } 481 documentManager.save(); 482 if (sb.length() > 0) { 483 facesMessages.add(StatusMessage.Severity.WARN, sb.toString()); 484 } 485 return newDocuments; 486 } 487 488 protected boolean checkDeletedState(DocumentModel doc) { 489 if (LifeCycleConstants.DELETED_STATE.equals(doc.getCurrentLifeCycleState())) { 490 return true; 491 } 492 if (doc.getAllowedStateTransitions().contains(LifeCycleConstants.DELETE_TRANSITION)) { 493 return true; 494 } 495 return false; 496 } 497 498 protected void setDeleteState(DocumentModel doc) { 499 if (doc.getAllowedStateTransitions().contains(LifeCycleConstants.DELETE_TRANSITION)) { 500 doc.followTransition(LifeCycleConstants.DELETE_TRANSITION); 501 } 502 } 503 504 protected void addWarnMessage(StringBuilder sb, DocumentModel doc) { 505 if (sb.length() == 0) { 506 sb.append(messages.get("document_no_deleted_state")); 507 sb.append("'").append(doc.getTitle()).append("'"); 508 } else { 509 sb.append(", '").append(doc.getTitle()).append("'"); 510 } 511 } 512 513 /** 514 * Check if the container is a publish space. If this is not the case, a proxy copied to it will be recreated as a 515 * new document. 516 */ 517 protected boolean isPublishSpace(DocumentModel container) { 518 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 519 Set<String> publishSpaces = schemaManager.getDocumentTypeNamesForFacet(FacetNames.PUBLISH_SPACE); 520 if (publishSpaces == null || publishSpaces.isEmpty()) { 521 publishSpaces = new HashSet<>(); 522 } 523 return publishSpaces.contains(container.getType()); 524 } 525 526 /** 527 * Gets the parent document under the paste should be performed. 528 * <p> 529 * Rules: 530 * <p> 531 * In general the currentDocument is the parent. Exceptions to this rule: when the currentDocument is a domain or 532 * null. If Domain then content root is the parent. If null is passed, then the JCR root is taken as parent. 533 */ 534 protected DocumentModel getParent(DocumentModel currentDocument) { 535 536 if (currentDocument.isFolder()) { 537 return currentDocument; 538 } 539 540 DocumentModelList parents = navigationContext.getCurrentPath(); 541 for (int i = parents.size() - 1; i >= 0; i--) { 542 DocumentModel parent = parents.get(i); 543 if (parent.isFolder()) { 544 return parent; 545 } 546 } 547 548 return null; 549 } 550 551 @Factory(value = "isCurrentWorkListEmpty", scope = EVENT) 552 public boolean factoryForIsCurrentWorkListEmpty() { 553 return isWorkListEmpty(); 554 } 555 556 public boolean isWorkListEmpty() { 557 return documentsListsManager.isWorkingListEmpty(getCurrentSelectedListName()); 558 } 559 560 public String exportWorklistAsZip() { 561 return exportWorklistAsZip(documentsListsManager.getWorkingList(getCurrentSelectedListName())); 562 } 563 564 public String exportAllBlobsFromWorkingListAsZip() { 565 return exportWorklistAsZip(); 566 } 567 568 public String exportMainBlobFromWorkingListAsZip() { 569 return exportWorklistAsZip(); 570 } 571 572 public String exportWorklistAsZip(List<DocumentModel> documents) { 573 return exportWorklistAsZip(documents, true); 574 } 575 576 public String exportWorklistAsZip(DocumentModel document) { 577 return exportWorklistAsZip(Collections.singletonList(document), true); 578 } 579 580 /** 581 * Checks if copy action is available in the context of the current Document. 582 * <p> 583 * Condition: the list of selected documents is not empty. 584 */ 585 public boolean getCanCopy() { 586 if (navigationContext.getCurrentDocument() == null) { 587 return false; 588 } 589 return !documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 590 } 591 592 /** 593 * Checks if the Paste action is available in the context of the current Document. Conditions: 594 * <p> 595 * <ul> 596 * <li>list is not empty 597 * <li>user has the needed permissions on the current document 598 * <li>the content of the list can be added as children of the current document 599 * </ul> 600 */ 601 public boolean getCanPaste(String listName) { 602 603 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 604 605 if (documentsListsManager.isWorkingListEmpty(listName) || currentDocument == null) { 606 return false; 607 } 608 609 DocumentModel pasteTarget = getParent(navigationContext.getCurrentDocument()); 610 if (pasteTarget == null) { 611 // parent may be unreachable (right inheritance blocked) 612 return false; 613 } 614 if (!documentManager.hasPermission(pasteTarget.getRef(), SecurityConstants.ADD_CHILDREN)) { 615 return false; 616 } else { 617 // filter on allowed content types 618 // see if at least one doc can be pasted 619 // String pasteTypeName = clipboard.getClipboardDocumentType(); 620 List<String> pasteTypesName = documentsListsManager.getWorkingListTypes(listName); 621 for (String pasteTypeName : pasteTypesName) { 622 if (typeManager.isAllowedSubType(pasteTypeName, pasteTarget.getType(), 623 navigationContext.getCurrentDocument())) { 624 return true; 625 } 626 } 627 return false; 628 } 629 } 630 631 public boolean getCanPasteInside(String listName, DocumentModel document) { 632 if (documentsListsManager.isWorkingListEmpty(listName) || document == null) { 633 return false; 634 } 635 636 if (!documentManager.hasPermission(document.getRef(), SecurityConstants.ADD_CHILDREN)) { 637 return false; 638 } else { 639 // filter on allowed content types 640 // see if at least one doc can be pasted 641 // String pasteTypeName = clipboard.getClipboardDocumentType(); 642 List<String> pasteTypesName = documentsListsManager.getWorkingListTypes(listName); 643 for (String pasteTypeName : pasteTypesName) { 644 if (typeManager.isAllowedSubType(pasteTypeName, document.getType(), 645 navigationContext.getCurrentDocument())) { 646 return true; 647 } 648 } 649 return false; 650 } 651 } 652 653 /** 654 * Checks if the Move action is available in the context of the document document. Conditions: 655 * <p> 656 * <ul> 657 * <li>list is not empty 658 * <li>user has the needed permissions on the document 659 * <li>an element in the list can be removed from its folder and added as child of the current document 660 * </ul> 661 */ 662 public boolean getCanMoveInside(String listName, DocumentModel document) { 663 if (documentsListsManager.isWorkingListEmpty(listName) || document == null) { 664 return false; 665 } 666 DocumentRef destFolderRef = document.getRef(); 667 DocumentModel destFolder = document; 668 if (!documentManager.hasPermission(destFolderRef, SecurityConstants.ADD_CHILDREN)) { 669 return false; 670 } else { 671 // filter on allowed content types 672 // see if at least one doc can be removed and pasted 673 for (DocumentModel docModel : documentsListsManager.getWorkingList(listName)) { 674 // skip deleted documents 675 if (!exists(docModel.getRef())) { 676 continue; 677 } 678 DocumentRef sourceFolderRef = docModel.getParentRef(); 679 String sourceType = docModel.getType(); 680 boolean canRemoveDoc = documentManager.hasPermission(sourceFolderRef, SecurityConstants.REMOVE_CHILDREN); 681 boolean canPasteInCurrentFolder = typeManager.isAllowedSubType(sourceType, destFolder.getType(), 682 navigationContext.getCurrentDocument()); 683 boolean sameFolder = sourceFolderRef.equals(destFolderRef); 684 if (canRemoveDoc && canPasteInCurrentFolder && !sameFolder) { 685 return true; 686 } 687 } 688 return false; 689 } 690 } 691 692 /** 693 * Checks if the Move action is available in the context of the current Document. Conditions: 694 * <p> 695 * <ul> 696 * <li>list is not empty 697 * <li>user has the needed permissions on the current document 698 * <li>an element in the list can be removed from its folder and added as child of the current document 699 * </ul> 700 */ 701 public boolean getCanMove(String listName) { 702 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 703 return getCanMoveInside(listName, currentDocument); 704 } 705 706 public boolean getCanPasteWorkList() { 707 return getCanPaste(getCurrentSelectedListName()); 708 } 709 710 public boolean getCanMoveWorkingList() { 711 return getCanMove(getCurrentSelectedListName()); 712 } 713 714 public boolean getCanPasteFromClipboard() { 715 return getCanPaste(DocumentsListsManager.CLIPBOARD); 716 } 717 718 public boolean getCanPasteFromClipboardInside(DocumentModel document) { 719 return getCanPasteInside(DocumentsListsManager.CLIPBOARD, document); 720 } 721 722 public boolean getCanMoveFromClipboardInside(DocumentModel document) { 723 return getCanMoveInside(DocumentsListsManager.CLIPBOARD, document); 724 } 725 726 public void setCurrentSelectedList(String listId) { 727 if (listId != null && !listId.equals(currentSelectedList)) { 728 currentSelectedList = listId; 729 canEditSelectedDocs = null; 730 } 731 } 732 733 @RequestParameter() 734 String listIdToSelect; 735 736 public void selectList() { 737 if (listIdToSelect != null) { 738 setCurrentSelectedList(listIdToSelect); 739 } 740 } 741 742 public List<DocumentModel> getCurrentSelectedList() { 743 return documentsListsManager.getWorkingList(getCurrentSelectedListName()); 744 } 745 746 public String getCurrentSelectedListName() { 747 if (currentSelectedList == null) { 748 if (!getAvailableLists().isEmpty()) { 749 setCurrentSelectedList(availableLists.get(0)); 750 } 751 } 752 return currentSelectedList; 753 } 754 755 public String getCurrentSelectedListTitle() { 756 String title = null; 757 String listName = getCurrentSelectedListName(); 758 if (listName != null) { 759 DocumentsListDescriptor desc = documentsListsManager.getWorkingListDescriptor(listName); 760 if (desc != null) { 761 title = desc.getTitle(); 762 } 763 } 764 return title; 765 } 766 767 public List<String> getAvailableLists() { 768 if (availableLists == null) { 769 availableLists = documentsListsManager.getWorkingListNamesForCategory("CLIPBOARD"); 770 } 771 return availableLists; 772 } 773 774 public List<DocumentsListDescriptor> getDescriptorsForAvailableLists() { 775 if (descriptorsForAvailableLists == null) { 776 List<String> availableLists = getAvailableLists(); 777 descriptorsForAvailableLists = new ArrayList<>(); 778 for (String lName : availableLists) { 779 descriptorsForAvailableLists.add(documentsListsManager.getWorkingListDescriptor(lName)); 780 } 781 } 782 return descriptorsForAvailableLists; 783 } 784 785 public List<Action> getActionsForCurrentList() { 786 String lstName = getCurrentSelectedListName(); 787 if (isWorkListEmpty()) { 788 // we use cache here since this is a very common case ... 789 if (actionCache == null) { 790 actionCache = new HashMap<>(); 791 } 792 if (!actionCache.containsKey(lstName)) { 793 actionCache.put(lstName, webActions.getActionsList(lstName + "_LIST")); 794 } 795 return actionCache.get(lstName); 796 } else { 797 return webActions.getActionsList(lstName + "_LIST"); 798 } 799 } 800 801 public List<Action> getActionsForSelection() { 802 return webActions.getActionsList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION + "_LIST", false); 803 } 804 805 private void autoSelectCurrentList(String listName) { 806 previouslySelectedList = getCurrentSelectedListName(); 807 setCurrentSelectedList(listName); 808 } 809 810 private void returnToPreviouslySelectedList() { 811 setCurrentSelectedList(previouslySelectedList); 812 } 813 814 public boolean getCanEditSelectedDocs() { 815 if (canEditSelectedDocs == null) { 816 if (getCurrentSelectedList().isEmpty()) { 817 canEditSelectedDocs = false; 818 } else { 819 final List<DocumentModel> selectedDocs = getCurrentSelectedList(); 820 821 // check selected docs 822 canEditSelectedDocs = checkWritePerm(selectedDocs); 823 } 824 } 825 return canEditSelectedDocs; 826 } 827 828 @Deprecated 829 // no longer used by the user_clipboard.xhtml template 830 public boolean getCanEditListDocs(String listName) { 831 final List<DocumentModel> docs = documentsListsManager.getWorkingList(listName); 832 833 final boolean canEdit; 834 if (docs.isEmpty()) { 835 canEdit = false; 836 } else { 837 // check selected docs 838 canEdit = checkWritePerm(docs); 839 } 840 return canEdit; 841 } 842 843 private boolean checkWritePerm(List<DocumentModel> selectedDocs) { 844 for (DocumentModel documentModel : selectedDocs) { 845 boolean canWrite = documentManager.hasPermission(documentModel.getRef(), SecurityConstants.WRITE_PROPERTIES); 846 if (!canWrite) { 847 return false; 848 } 849 } 850 return true; 851 } 852 853 public boolean isCacheEnabled() { 854 if (!SeamCacheHelper.canUseSeamCache()) { 855 return false; 856 } 857 return isWorkListEmpty(); 858 } 859 860 public String getCacheKey() { 861 return getCurrentSelectedListName() + "::" + localeSelector.getLocaleString(); 862 } 863 864 public boolean isCacheEnabledForSelection() { 865 if (!SeamCacheHelper.canUseSeamCache()) { 866 return false; 867 } 868 return documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 869 } 870 871 @Override 872 public String exportWorklistAsZip(List<DocumentModel> documents, boolean exportAllBlobs) { 873 try { 874 FacesContext context = FacesContext.getCurrentInstance(); 875 DocumentListZipExporter zipExporter = new DocumentListZipExporter(); 876 File tmpFile = zipExporter.exportWorklistAsZip(documents, documentManager, exportAllBlobs); 877 if (tmpFile == null) { 878 // empty zip file, do nothing 879 facesMessages.add(StatusMessage.Severity.INFO, messages.get("label.clipboard.emptyDocuments")); 880 return null; 881 } else { 882 if (tmpFile.length() > Functions.getBigFileSizeLimit()) { 883 HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); 884 request.setAttribute(NXAuthConstants.DISABLE_REDIRECT_REQUEST_KEY, true); 885 String zipDownloadURL = BaseURL.getBaseURL(request); 886 zipDownloadURL += DownloadService.NXBIGZIPFILE + "/"; 887 zipDownloadURL += tmpFile.getName(); 888 try { 889 context.getExternalContext().redirect(zipDownloadURL); 890 } catch (IOException e) { 891 log.error("Error while redirecting for big file downloader", e); 892 } 893 } else { 894 ComponentUtils.downloadFile(tmpFile, "clipboard.zip", "clipboardZip"); 895 tmpFile.delete(); 896 } 897 898 return ""; 899 } 900 } catch (IOException io) { 901 throw new NuxeoException(io); 902 } 903 } 904}