001/* 002 * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Mariana Cedica 016 * 017 * $Id$ 018 */ 019package org.nuxeo.ecm.platform.routing.web; 020 021import static org.jboss.seam.ScopeType.CONVERSATION; 022import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_SELECTION; 023 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030 031import javax.faces.convert.Converter; 032 033import org.apache.commons.lang.StringUtils; 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.jboss.seam.ScopeType; 037import org.jboss.seam.annotations.Factory; 038import org.jboss.seam.annotations.In; 039import org.jboss.seam.annotations.Install; 040import org.jboss.seam.annotations.Name; 041import org.jboss.seam.annotations.Observer; 042import org.jboss.seam.annotations.Scope; 043import org.jboss.seam.annotations.web.RequestParameter; 044import org.jboss.seam.contexts.Contexts; 045import org.jboss.seam.core.Events; 046import org.jboss.seam.faces.FacesMessages; 047import org.jboss.seam.international.StatusMessage; 048import org.nuxeo.common.collections.ScopedMap; 049import org.nuxeo.ecm.core.api.CoreSession; 050import org.nuxeo.ecm.core.api.DocumentModel; 051import org.nuxeo.ecm.core.api.DocumentModelList; 052import org.nuxeo.ecm.core.api.DocumentRef; 053import org.nuxeo.ecm.core.api.IdRef; 054import org.nuxeo.ecm.core.api.NuxeoPrincipal; 055import org.nuxeo.ecm.core.api.PathRef; 056import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 057import org.nuxeo.ecm.core.api.event.CoreEventConstants; 058import org.nuxeo.ecm.platform.routing.api.DocumentRoute; 059import org.nuxeo.ecm.platform.routing.api.DocumentRouteElement; 060import org.nuxeo.ecm.platform.routing.api.DocumentRouteTableElement; 061import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants; 062import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants.ExecutionTypeValues; 063import org.nuxeo.ecm.platform.routing.api.DocumentRoutingService; 064import org.nuxeo.ecm.platform.routing.api.LockableDocumentRoute; 065import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteAlredayLockedException; 066import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteNotLockedException; 067import org.nuxeo.ecm.platform.routing.core.api.DocumentRoutingEngineService; 068import org.nuxeo.ecm.platform.routing.core.impl.GraphRoute; 069import org.nuxeo.ecm.platform.task.Task; 070import org.nuxeo.ecm.platform.task.TaskEventNames; 071import org.nuxeo.ecm.platform.task.TaskService; 072import org.nuxeo.ecm.platform.types.TypeManager; 073import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 074import org.nuxeo.ecm.platform.ui.web.api.WebActions; 075import org.nuxeo.ecm.webapp.action.TypesTool; 076import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 077import org.nuxeo.ecm.webapp.edit.lock.LockActions; 078import org.nuxeo.ecm.webapp.helpers.EventManager; 079import org.nuxeo.ecm.webapp.helpers.EventNames; 080import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor; 081import org.nuxeo.runtime.api.Framework; 082 083/** 084 * Actions for current document route 085 * 086 * @author Mariana Cedica 087 */ 088@Scope(CONVERSATION) 089@Name("routingActions") 090@Install(precedence = Install.FRAMEWORK) 091public class DocumentRoutingActionsBean implements Serializable { 092 private static final long serialVersionUID = 1L; 093 094 private static final Log log = LogFactory.getLog(DocumentRoutingActionsBean.class); 095 096 public static final String SOURCE_DOC_NAME = "source_doc_name"; 097 098 public static final String ROUTE_DOCUMENT_REF = "route_doc_ref"; 099 100 @In(required = true, create = true) 101 protected NavigationContext navigationContext; 102 103 @In(create = true, required = false) 104 protected CoreSession documentManager; 105 106 @In(create = true, required = false) 107 protected FacesMessages facesMessages; 108 109 @In(create = true) 110 protected WebActions webActions; 111 112 @In(create = true) 113 protected LockActions lockActions; 114 115 @In(create = true, required = false) 116 protected TypesTool typesTool; 117 118 @In(create = true) 119 protected ResourcesAccessor resourcesAccessor; 120 121 @In(create = true) 122 protected TypeManager typeManager; 123 124 @In(create = true) 125 protected EventManager eventManager; 126 127 @In(required = true, create = true) 128 protected NuxeoPrincipal currentUser; 129 130 @In(create = true) 131 protected List<DocumentModel> relatedRoutes; 132 133 @In(create = true) 134 protected RelatedRouteActionBean relatedRouteAction; 135 136 @In(create = true) 137 protected DocumentsListsManager documentsListsManager; 138 139 @RequestParameter("stepId") 140 protected String stepId; 141 142 protected String relatedRouteModelDocumentId; 143 144 protected String docWithAttachedRouteId; 145 146 protected String hiddenSourceDocId; 147 148 protected String hiddenDocOrder; 149 150 enum StepOrder { 151 before, in, after 152 } 153 154 public DocumentRoutingService getDocumentRoutingService() { 155 return Framework.getService(DocumentRoutingService.class); 156 } 157 158 @Observer(value = { EventNames.DOCUMENT_CHANGED, EventNames.DOCUMENT_SELECTION_CHANGED }) 159 public void resetRelatedRouteDocumentId() { 160 relatedRouteModelDocumentId = null; 161 } 162 163 public boolean isRoutable() { 164 return getDocumentRoutingService().isRoutable(navigationContext.getCurrentDocument()); 165 } 166 167 public String startRoute() { 168 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 169 DocumentRoute currentRoute = currentDocument.getAdapter(DocumentRoute.class); 170 if (currentRoute == null) { 171 log.warn("Current document is not a workflow model"); 172 facesMessages.add(StatusMessage.Severity.ERROR, 173 resourcesAccessor.getMessages().get("label.document.routing.no.workflow")); 174 return null; 175 } 176 getDocumentRoutingService().createNewInstance(currentDocument.getName(), currentRoute.getAttachedDocuments(), 177 documentManager, true); 178 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument); 179 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_NEW_STARTED); 180 webActions.resetTabList(); 181 return null; 182 } 183 184 /** 185 * Gets the first related route. 186 * <p> 187 * When called on an actual route or route element, the route is returned. 188 * <p> 189 * When called on a regular document, the routing service is queried to get the routes which have the current 190 * document attached. 191 * <p> 192 * When dealing with a regular document, this is DEPRECATED as several graph routes may be related to the current 193 * document (for instance in the case of sub-workflows). Use {@link #getRelatedRoutes} instead. 194 */ 195 public DocumentRoute getRelatedRoute() { 196 // try to see if actually the current document is a route 197 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 198 199 if (currentDocument.hasFacet(DocumentRoutingConstants.DOCUMENT_ROUTE_DOCUMENT_FACET)) { 200 docWithAttachedRouteId = null; 201 return currentDocument.getAdapter(DocumentRoute.class); 202 } 203 // try to see if the current document is a routeElement 204 DocumentRouteElement relatedRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 205 if (relatedRouteElement != null) { 206 docWithAttachedRouteId = null; 207 return relatedRouteElement.getDocumentRoute(documentManager); 208 } 209 // else we must be in a document attached to a route 210 List<DocumentRoute> routes = getRelatedRoutes(); 211 if (routes.isEmpty()) { 212 return null; 213 } 214 docWithAttachedRouteId = currentDocument.getId(); 215 return routes.get(0); 216 } 217 218 /** 219 * Gets the list of routes related to the current document, by querying the routing service. 220 * 221 * @return the list of routes (may be empty) 222 * @since 5.7.2 223 */ 224 public List<DocumentRoute> getRelatedRoutes() { 225 queryForRelatedRoutes(); 226 List<DocumentRoute> routes = new ArrayList<DocumentRoute>(relatedRoutes.size()); 227 for (DocumentModel doc : relatedRoutes) { 228 routes.add(doc.getAdapter(DocumentRoute.class)); 229 } 230 return routes; 231 } 232 233 protected void queryForRelatedRoutes() { 234 if (relatedRoutes == null) { 235 relatedRoutes = relatedRouteAction.findRelatedRoute(); 236 } 237 } 238 239 /** 240 * Cancels the first workflow found on the current document 241 */ 242 public String cancelRoute() { 243 List<DocumentRoute> routes = getRelatedRoutes(); 244 if (routes.size() == 0) { 245 log.error("No workflow to cancel"); 246 return null; 247 } 248 DocumentRoute route = routes.get(0); 249 Framework.getLocalService(DocumentRoutingEngineService.class).cancel(route, documentManager); 250 // force computing of tabs 251 webActions.resetTabList(); 252 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_CANCELED); 253 Contexts.removeFromAllContexts("relatedRoutes"); 254 documentManager.save(); 255 return navigationContext.navigateToDocument(navigationContext.getCurrentDocument()); 256 } 257 258 public void saveRouteAsNewInstance() { 259 getDocumentRoutingService().saveRouteAsNewModel(getRelatedRoute(), documentManager); 260 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 261 facesMessages.add(StatusMessage.Severity.INFO, 262 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.route_duplicated")); 263 } 264 265 public void saveSelectedRouteAsNewInstance() { 266 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION); 267 if (!docs.isEmpty()) { 268 DocumentRoute route; 269 for (DocumentModel doc : docs) { 270 route = doc.getAdapter(DocumentRoute.class); 271 if (route != null) { 272 getDocumentRoutingService().saveRouteAsNewModel(route, documentManager); 273 } 274 } 275 } 276 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 277 facesMessages.add(StatusMessage.Severity.INFO, 278 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.selected_route_duplicated")); 279 } 280 281 public boolean getCanDuplicateRouteInstance() { 282 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION); 283 if (docs.isEmpty()) { 284 return false; 285 } 286 for (DocumentModel doc : docs) { 287 if (!doc.hasFacet(DocumentRoutingConstants.DOCUMENT_ROUTE_DOCUMENT_FACET)) { 288 return false; 289 } 290 } 291 return true; 292 } 293 294 public String validateRouteModel() { 295 DocumentRoute currentRouteModel = getRelatedRoute(); 296 try { 297 getDocumentRoutingService().validateRouteModel(currentRouteModel, documentManager); 298 } catch (DocumentRouteNotLockedException e) { 299 facesMessages.add(StatusMessage.Severity.WARN, 300 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 301 return null; 302 } 303 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentRouteModel.getDocument()); 304 getDocumentRoutingService().unlockDocumentRouteUnrestrictedSession(currentRouteModel, documentManager); 305 return null; 306 } 307 308 /** 309 * @deprecated since 5.9.2 - Use only routes of type 'graph' 310 */ 311 @Deprecated 312 protected List<DocumentRouteTableElement> computeRouteElements() { 313 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 314 DocumentRoute currentRoute = currentDocument.getAdapter(DocumentRoute.class); 315 return getElements(currentRoute); 316 } 317 318 /** 319 * @deprecated since 5.9.2 - Use only routes of type 'graph' 320 */ 321 @Deprecated 322 protected List<DocumentRouteTableElement> computeRelatedRouteElements() { 323 if (relatedRoutes.isEmpty()) { 324 return new ArrayList<DocumentRouteTableElement>(); 325 } 326 DocumentModel relatedRouteDocumentModel = documentManager.getDocument(new IdRef(relatedRoutes.get(0).getId())); 327 DocumentRoute currentRoute = relatedRouteDocumentModel.getAdapter(DocumentRoute.class); 328 return getElements(currentRoute); 329 } 330 331 /** 332 * @deprecated since 5.9.2 - Use only routes of type 'graph' 333 */ 334 @Deprecated 335 protected List<DocumentRouteTableElement> getElements(DocumentRoute currentRoute) { 336 return getDocumentRoutingService().getRouteElements(currentRoute, documentManager); 337 } 338 339 /** 340 * Check if the related route to this case is started (ready or running) or no 341 */ 342 public boolean hasRelatedRoute() { 343 queryForRelatedRoutes(); 344 return !relatedRoutes.isEmpty(); 345 } 346 347 public String startRouteRelatedToCurrentDocument() { 348 DocumentRoute route = getRelatedRoute(); 349 // check relatedRoutedoc id 350 if (!StringUtils.isEmpty(relatedRouteModelDocumentId)) { 351 DocumentModel model = documentManager.getDocument(new IdRef(relatedRouteModelDocumentId)); 352 route = model.getAdapter(DocumentRoute.class); 353 } 354 if (route == null) { 355 facesMessages.add(StatusMessage.Severity.WARN, 356 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.no.valid.route")); 357 return null; 358 } 359 360 List<String> documentIds = new ArrayList<String>(); 361 documentIds.add(navigationContext.getCurrentDocument().getId()); 362 route.setAttachedDocuments(documentIds); 363 getDocumentRoutingService().createNewInstance(route.getDocument().getName(), documentIds, documentManager, true); 364 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_NEW_STARTED); 365 webActions.resetTabList(); 366 return null; 367 } 368 369 /** 370 * returns true if the routeStarted on the current Document is editable (is Ready ) 371 */ 372 public boolean routeRelatedToCurrentDocumentIsRunning() { 373 DocumentRoute route = getRelatedRoute(); 374 if (route == null) { 375 return false; 376 } 377 return route.isRunning(); 378 } 379 380 public String getTypeDescription(DocumentRouteTableElement localizable) { 381 return depthFormatter(localizable.getDepth(), localizable.getElement().getDocument().getType()); 382 } 383 384 private String depthFormatter(int depth, String type) { 385 StringBuilder depthFormatter = new StringBuilder(); 386 for (int i = 0; i < depth - 1; i++) { 387 depthFormatter.append("__"); 388 } 389 depthFormatter.append(type); 390 return depthFormatter.toString(); 391 } 392 393 public Converter getDocumentModelConverter() { 394 return new DocumentModelConvertor(documentManager); 395 } 396 397 public boolean isStep(DocumentModel doc) { 398 return (doc.hasFacet(DocumentRoutingConstants.ROUTE_STEP_FACET)); 399 } 400 401 public boolean currentRouteModelIsDraft() { 402 DocumentModel relatedRouteModel = navigationContext.getCurrentDocument(); 403 DocumentRoute routeModel = relatedRouteModel.getAdapter(DocumentRoute.class); 404 if (routeModel == null) { 405 return false; 406 } 407 return routeModel.isDraft(); 408 } 409 410 @Deprecated 411 // @deprecated since 5.9.2 - Use only routes of type 'graph' 412 public String removeStep() { 413 boolean alreadyLockedByCurrentUser = false; 414 DocumentRoute routeModel = getRelatedRoute(); 415 if (getDocumentRoutingService().isLockedByCurrentUser(routeModel, documentManager)) { 416 alreadyLockedByCurrentUser = true; 417 } else { 418 if (lockRoute(routeModel) == null) { 419 return null; 420 } 421 } 422 if (StringUtils.isEmpty(stepId)) { 423 return null; 424 } 425 DocumentRef docRef = new IdRef(stepId); 426 DocumentModel stepToDelete = documentManager.getDocument(docRef); 427 try { 428 getDocumentRoutingService().removeRouteElement(stepToDelete.getAdapter(DocumentRouteElement.class), 429 documentManager); 430 } catch (DocumentRouteNotLockedException e) { 431 facesMessages.add(StatusMessage.Severity.WARN, 432 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 433 return null; 434 } 435 Contexts.removeFromAllContexts("relatedRoutes"); 436 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, routeModel.getDocument()); 437 // Release the lock only when currentUser had locked it before 438 // entering this method. 439 if (!alreadyLockedByCurrentUser) { 440 getDocumentRoutingService().unlockDocumentRoute(routeModel, documentManager); 441 } 442 return null; 443 } 444 445 /** 446 * Returns true if the givenDoc is a step that can be edited 447 * 448 * @deprecated since 5.9.2 - Use only routes of type 'graph' 449 */ 450 @Deprecated 451 public boolean isEditableStep(DocumentModel stepDoc) { 452 DocumentRouteElement stepElement = stepDoc.getAdapter(DocumentRouteElement.class); 453 // if fork, is not simple editable step 454 if (stepDoc.hasFacet("Folderish")) { 455 return false; 456 } 457 return stepElement.isModifiable(); 458 } 459 460 /** 461 * Returns true if the givenDoc is an routeElement that can be edited 462 * 463 * @deprecated since 5.9.2 - Use only routes of type 'graph' 464 */ 465 @Deprecated 466 public boolean isEditableRouteElement(DocumentModel stepDoc) { 467 DocumentRouteElement stepElement = stepDoc.getAdapter(DocumentRouteElement.class); 468 return stepElement.isModifiable(); 469 } 470 471 @Factory(value = "currentRouteLockedByCurrentUser", scope = ScopeType.EVENT) 472 public boolean isCurrentRouteLockedByCurrentUser() { 473 return getDocumentRoutingService().isLockedByCurrentUser(getRelatedRoute(), documentManager); 474 } 475 476 public boolean isCurrentRouteLocked() { 477 LockableDocumentRoute lockableRoute = getRelatedRoute().getDocument().getAdapter(LockableDocumentRoute.class); 478 return lockableRoute.isLocked(documentManager); 479 } 480 481 public boolean canUnlockRoute() { 482 return Boolean.TRUE.equals(lockActions.getCanUnlockDoc(getRelatedRoute().getDocument())); 483 } 484 485 public boolean canLockRoute() { 486 return Boolean.TRUE.equals(lockActions.getCanLockDoc(getRelatedRoute().getDocument())); 487 } 488 489 public Map<String, Serializable> getCurrentRouteLockDetails() { 490 return lockActions.getLockDetails(getRelatedRoute().getDocument()); 491 } 492 493 public String lockCurrentRoute() { 494 DocumentRoute docRouteElement = getRelatedRoute(); 495 return lockRoute(docRouteElement); 496 } 497 498 protected String lockRoute(DocumentRoute docRouteElement) { 499 try { 500 getDocumentRoutingService().lockDocumentRoute(docRouteElement.getDocumentRoute(documentManager), 501 documentManager); 502 } catch (DocumentRouteAlredayLockedException e) { 503 facesMessages.add(StatusMessage.Severity.WARN, 504 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.already.locked")); 505 return null; 506 } 507 return null; 508 } 509 510 public String unlockCurrentRoute() { 511 DocumentRoute route = getRelatedRoute(); 512 getDocumentRoutingService().unlockDocumentRoute(route, documentManager); 513 return null; 514 } 515 516 public boolean isEmptyFork(DocumentModel forkDoc) { 517 return forkDoc.hasFacet("Folderish") && !documentManager.hasChildren(forkDoc.getRef()); 518 } 519 520 public String editStep() { 521 if (StringUtils.isEmpty(stepId)) { 522 return null; 523 } 524 DocumentRef stepRef = new IdRef(stepId); 525 DocumentModel currentDoc = navigationContext.getCurrentDocument(); 526 if (currentDoc.getAdapter(DocumentRoute.class) == null) { 527 setDocWithAttachedRouteId(currentDoc.getId()); 528 } 529 return navigationContext.navigateToDocument(documentManager.getDocument(stepRef), "edit"); 530 } 531 532 public String updateRouteElement() { 533 boolean alreadyLockedByCurrentUser = false; 534 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 535 DocumentRouteElement docRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 536 DocumentRoute route = docRouteElement.getDocumentRoute(documentManager); 537 if (getDocumentRoutingService().isLockedByCurrentUser(route, documentManager)) { 538 alreadyLockedByCurrentUser = true; 539 } else { 540 if (lockRoute(route) == null) { 541 return null; 542 } 543 } 544 try { 545 getDocumentRoutingService().updateRouteElement(docRouteElement, documentManager); 546 } catch (DocumentRouteNotLockedException e) { 547 facesMessages.add(StatusMessage.Severity.WARN, 548 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.already.locked")); 549 return null; 550 } 551 navigationContext.invalidateCurrentDocument(); 552 facesMessages.add(StatusMessage.Severity.INFO, resourcesAccessor.getMessages().get("document_modified"), 553 resourcesAccessor.getMessages().get(currentDocument.getType())); 554 EventManager.raiseEventsOnDocumentChange(currentDocument); 555 // Release the lock only when currentUser had locked it before 556 // entering this method. 557 if (!alreadyLockedByCurrentUser) { 558 getDocumentRoutingService().unlockDocumentRoute(route, documentManager); 559 } 560 if (docWithAttachedRouteId == null) { 561 return webActions.setCurrentTabAndNavigate(docRouteElement.getDocumentRoute(documentManager).getDocument(), 562 "TAB_DOCUMENT_ROUTE_ELEMENTS"); 563 } 564 565 setRelatedRouteWhenNavigateBackToCase(); 566 return webActions.setCurrentTabAndNavigate(documentManager.getDocument(new IdRef(docWithAttachedRouteId)), 567 "TAB_CASE_MANAGEMENT_VIEW_RELATED_ROUTE"); 568 } 569 570 public String goBackToRoute() { 571 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 572 DocumentRouteElement docRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 573 return webActions.setCurrentTabAndNavigate(docRouteElement.getDocumentRoute(documentManager).getDocument(), 574 "TAB_DOCUMENT_ROUTE_ELEMENTS"); 575 } 576 577 /** 578 * @deprecated since 5.9.2 - Use only routes of type 'graph' 579 */ 580 @Deprecated 581 public String createRouteElement(String typeName) { 582 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 583 DocumentRef routeRef = currentDocument.getRef(); 584 DocumentRef sourceDocRef = new IdRef(hiddenSourceDocId); 585 DocumentModel sourceDoc = documentManager.getDocument(sourceDocRef); 586 String sourceDocName = null; 587 String parentPath = null; 588 if (StepOrder.in.toString().equals(hiddenDocOrder)) { 589 parentPath = sourceDoc.getPathAsString(); 590 } else { 591 DocumentModel parentDoc = documentManager.getParentDocument(sourceDocRef); 592 parentPath = parentDoc.getPathAsString(); 593 if (StepOrder.before.toString().equals(hiddenDocOrder)) { 594 sourceDocName = sourceDoc.getName(); 595 } else { 596 DocumentModelList orderedChilds = getDocumentRoutingService().getOrderedRouteElement(parentDoc.getId(), 597 documentManager); 598 int selectedDocumentIndex = orderedChilds.indexOf(sourceDoc); 599 int nextIndex = selectedDocumentIndex + 1; 600 if (nextIndex >= orderedChilds.size()) { 601 sourceDocName = null; 602 } else { 603 sourceDocName = orderedChilds.get(nextIndex).getName(); 604 } 605 } 606 } 607 org.nuxeo.ecm.platform.types.Type docType = typeManager.getType(typeName); 608 // we cannot use typesTool as intermediary since the DataModel callback 609 // will alter whatever type we set 610 typesTool.setSelectedType(docType); 611 DocumentModel changeableDocument = documentManager.createDocumentModel(typeName); 612 ScopedMap context = changeableDocument.getContextData(); 613 context.put(CoreEventConstants.PARENT_PATH, parentPath); 614 context.put(SOURCE_DOC_NAME, sourceDocName); 615 context.put(ROUTE_DOCUMENT_REF, routeRef); 616 navigationContext.setChangeableDocument(changeableDocument); 617 return "create_route_element"; 618 } 619 620 /** 621 * Moves the step in the parent container in the specified direction. If the step is in a parallel container, it 622 * can't be moved. A step can't be moved before a step already done or running. Assumed that the route is already 623 * locked to have this action available, so no check is done. 624 * 625 * @deprecated since 5.9.2 - Use only routes of type 'graph' 626 */ 627 @Deprecated 628 public String moveRouteElement(String direction) { 629 if (StringUtils.isEmpty(stepId)) { 630 return null; 631 } 632 DocumentModel routeElementDocToMove = documentManager.getDocument(new IdRef(stepId)); 633 DocumentModel parentDoc = documentManager.getDocument(routeElementDocToMove.getParentRef()); 634 ExecutionTypeValues executionType = ExecutionTypeValues.valueOf((String) parentDoc.getPropertyValue(DocumentRoutingConstants.EXECUTION_TYPE_PROPERTY_NAME)); 635 if (!DocumentRoutingConstants.ExecutionTypeValues.serial.equals(executionType)) { 636 facesMessages.add( 637 StatusMessage.Severity.WARN, 638 resourcesAccessor.getMessages().get( 639 "feedback.casemanagement.document.route.cant.move.steps.in.parallel.container")); 640 return null; 641 } 642 DocumentModelList orderedChilds = getDocumentRoutingService().getOrderedRouteElement(parentDoc.getId(), 643 documentManager); 644 int selectedDocumentIndex = orderedChilds.indexOf(routeElementDocToMove); 645 if (DocumentRoutingWebConstants.MOVE_STEP_UP.equals(direction)) { 646 if (selectedDocumentIndex == 0) { 647 facesMessages.add( 648 StatusMessage.Severity.WARN, 649 resourcesAccessor.getMessages().get( 650 "feedback.casemanagement.document.route.already.first.step.in.container")); 651 return null; 652 } 653 routeElementDocToMove.getAdapter(DocumentRouteElement.class); 654 DocumentModel stepMoveBefore = orderedChilds.get(selectedDocumentIndex - 1); 655 DocumentRouteElement stepElementMoveBefore = stepMoveBefore.getAdapter(DocumentRouteElement.class); 656 if (stepElementMoveBefore.isRunning()) { 657 facesMessages.add( 658 StatusMessage.Severity.WARN, 659 resourcesAccessor.getMessages().get( 660 "feedback.casemanagement.document.route.cant.move.step.before.already.running.step")); 661 return null; 662 } 663 if (!stepElementMoveBefore.isModifiable()) { 664 facesMessages.add( 665 StatusMessage.Severity.WARN, 666 resourcesAccessor.getMessages().get( 667 "feedback.casemanagement.document.route.cant.move.step.after.no.modifiable.step")); 668 return null; 669 } 670 documentManager.orderBefore(parentDoc.getRef(), routeElementDocToMove.getName(), stepMoveBefore.getName()); 671 } 672 if (DocumentRoutingWebConstants.MOVE_STEP_DOWN.equals(direction)) { 673 if (selectedDocumentIndex == orderedChilds.size() - 1) { 674 facesMessages.add( 675 StatusMessage.Severity.WARN, 676 resourcesAccessor.getMessages().get( 677 "feedback.casemanagement.document.already.last.step.in.container")); 678 return null; 679 } 680 routeElementDocToMove.getAdapter(DocumentRouteElement.class); 681 DocumentModel stepMoveAfter = orderedChilds.get(selectedDocumentIndex + 1); 682 DocumentRouteElement stepElementMoveAfter = stepMoveAfter.getAdapter(DocumentRouteElement.class); 683 if (stepElementMoveAfter.isRunning()) { 684 facesMessages.add( 685 StatusMessage.Severity.WARN, 686 resourcesAccessor.getMessages().get( 687 "feedback.casemanagement.document.route.cant.move.step.after.already.running.step")); 688 return null; 689 } 690 documentManager.orderBefore(parentDoc.getRef(), orderedChilds.get(selectedDocumentIndex + 1).getName(), 691 routeElementDocToMove.getName()); 692 } 693 if (docWithAttachedRouteId == null) { 694 return webActions.setCurrentTabAndNavigate(getRelatedRoute().getDocument(), "TAB_DOCUMENT_ROUTE_ELEMENTS"); 695 } 696 697 setRelatedRouteWhenNavigateBackToCase(); 698 return webActions.setCurrentTabAndNavigate(documentManager.getDocument(new IdRef(docWithAttachedRouteId)), 699 "TAB_CASE_MANAGEMENT_VIEW_RELATED_ROUTE"); 700 } 701 702 public String saveRouteElement() { 703 boolean alreadyLockedByCurrentUser = false; 704 DocumentRoute routeModel = getRelatedRoute(); 705 if (getDocumentRoutingService().isLockedByCurrentUser(routeModel, documentManager)) { 706 alreadyLockedByCurrentUser = true; 707 } else { 708 lockRoute(routeModel); 709 } 710 711 DocumentModel newDocument = navigationContext.getChangeableDocument(); 712 // Document has already been created if it has an id. 713 // This will avoid creation of many documents if user hit create button 714 // too many times. 715 if (newDocument.getId() != null) { 716 log.debug("Document " + newDocument.getName() + " already created"); 717 return navigationContext.navigateToDocument(newDocument, "after-create"); 718 } 719 String parentDocumentPath = (String) newDocument.getContextData().get(CoreEventConstants.PARENT_PATH); 720 String sourceDocumentName = (String) newDocument.getContextData().get(SOURCE_DOC_NAME); 721 DocumentRef routeDocRef = (DocumentRef) newDocument.getContextData().get(ROUTE_DOCUMENT_REF); 722 try { 723 getDocumentRoutingService().addRouteElementToRoute(new PathRef(parentDocumentPath), sourceDocumentName, 724 newDocument.getAdapter(DocumentRouteElement.class), documentManager); 725 } catch (DocumentRouteNotLockedException e) { 726 facesMessages.add(StatusMessage.Severity.WARN, 727 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 728 return null; 729 } 730 DocumentModel routeDocument = documentManager.getDocument(routeDocRef); 731 facesMessages.add(StatusMessage.Severity.INFO, resourcesAccessor.getMessages().get("document_saved"), 732 resourcesAccessor.getMessages().get(newDocument.getType())); 733 734 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, routeDocument); 735 // Release the lock only when currentUser had locked it before 736 // entering this method. 737 if (!alreadyLockedByCurrentUser) { 738 getDocumentRoutingService().unlockDocumentRoute(routeModel, documentManager); 739 } 740 return navigationContext.navigateToDocument(routeDocument); 741 } 742 743 @Deprecated 744 // @deprecated since 5.9.2 - Use only routes of type 'graph' 745 public List<DocumentModel> getOrderedChildren(String docRouteElementId, String type) { 746 // xxx move me in serice with query 747 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElementId, 748 documentManager); 749 List<DocumentModel> filteredChildren = new ArrayList<DocumentModel>(); 750 for (DocumentModel documentModel : orderedChildren) { 751 if (type.equals(documentModel.getType())) { 752 filteredChildren.add(documentModel); 753 } 754 } 755 return filteredChildren; 756 } 757 758 @Deprecated 759 // @deprecated since 5.9.2 - Use only routes of type 'graph' 760 public DocumentModel getChildWithPosition(DocumentModel docRouteElement, String pos) { 761 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElement.getId(), 762 documentManager); 763 return orderedChildren.get(Integer.parseInt(pos)); 764 } 765 766 @Deprecated 767 // @deprecated since 5.9.2 - Use only routes of type 'graph' 768 public String getPositionForChild(DocumentModel docRouteElement, DocumentModel docChild) { 769 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElement.getId(), 770 documentManager); 771 return String.valueOf(orderedChildren.indexOf(docChild)); 772 } 773 774 public String getHiddenSourceDocId() { 775 return hiddenSourceDocId; 776 } 777 778 public void setHiddenSourceDocId(String hiddenSourceDocId) { 779 this.hiddenSourceDocId = hiddenSourceDocId; 780 } 781 782 public String getHiddenDocOrder() { 783 return hiddenDocOrder; 784 } 785 786 public void setHiddenDocOrder(String hiddenDocOrder) { 787 this.hiddenDocOrder = hiddenDocOrder; 788 } 789 790 public String getRelatedRouteModelDocumentId() { 791 return relatedRouteModelDocumentId; 792 } 793 794 public void setRelatedRouteModelDocumentId(String relatedRouteModelDocumentId) { 795 this.relatedRouteModelDocumentId = relatedRouteModelDocumentId; 796 } 797 798 public String getDocWithAttachedRouteId() { 799 return docWithAttachedRouteId; 800 } 801 802 public void setDocWithAttachedRouteId(String docWithAttachedRouteId) { 803 this.docWithAttachedRouteId = docWithAttachedRouteId; 804 } 805 806 private void setRelatedRouteWhenNavigateBackToCase() { 807 // recompute factory 808 webActions.resetTabList(); 809 navigationContext.setCurrentDocument(documentManager.getDocument(new IdRef(docWithAttachedRouteId))); 810 relatedRoutes = relatedRouteAction.findRelatedRoute(); 811 } 812 813 @Observer(value = { TaskEventNames.WORKFLOW_ENDED, TaskEventNames.WORKFLOW_CANCELED }, create = false) 814 public void resetCache() { 815 webActions.resetTabList(); 816 } 817 818 /** 819 * @since 5.6 820 */ 821 public DocumentModel getRouteModel(String routeId) { 822 return documentManager.getDocument(new IdRef(routeId)); 823 } 824 825 /** 826 * @since 5.6 827 */ 828 public DocumentModel getRouteInstanceFor(Task task) { 829 final String routeDocId = task.getVariable(DocumentRoutingConstants.TASK_ROUTE_INSTANCE_DOCUMENT_ID_KEY); 830 if (routeDocId == null) { 831 return null; 832 } 833 final DocumentModel[] res = new DocumentModel[1]; 834 new UnrestrictedSessionRunner(documentManager) { 835 @Override 836 public void run() { 837 DocumentModel doc = session.getDocument(new IdRef(routeDocId)); 838 doc.detach(true); 839 res[0] = doc; 840 } 841 }.runUnrestricted(); 842 return res[0]; 843 } 844 845 /** 846 * @since 5.6 847 */ 848 public List<DocumentModel> getFilteredRouteModels() { 849 DocumentRoutingService documentRoutingService = Framework.getLocalService(DocumentRoutingService.class); 850 List<DocumentModel> routeModels = documentRoutingService.searchRouteModels(documentManager, ""); 851 for (Iterator<DocumentModel> it = routeModels.iterator(); it.hasNext();) { 852 DocumentModel route = it.next(); 853 Object graphRouteObj = route.getAdapter(GraphRoute.class); 854 if (graphRouteObj instanceof GraphRoute) { 855 String filter = ((GraphRoute) graphRouteObj).getAvailabilityFilter(); 856 if (!StringUtils.isBlank(filter)) { 857 if (!webActions.checkFilter(filter)) { 858 it.remove(); 859 } 860 } 861 } else { 862 // old workflow document => ignore 863 it.remove(); 864 } 865 } 866 return routeModels; 867 } 868 869 /** 870 * @since 5.6 871 */ 872 public List<Task> getCurrentRouteAllTasks() { 873 TaskService taskService = Framework.getLocalService(TaskService.class); 874 DocumentRoute currentRoute = getRelatedRoute(); 875 if (currentRoute != null) { 876 return taskService.getAllTaskInstances(currentRoute.getDocument().getId(), documentManager); 877 } 878 return null; 879 } 880 881 /** 882 * @since 5.6 883 */ 884 public List<Task> getCurrentRouteCurrentUserTasks() { 885 TaskService taskService = Framework.getLocalService(TaskService.class); 886 DocumentRoute currentRoute = getRelatedRoute(); 887 if (currentRoute != null) { 888 return taskService.getAllTaskInstances(currentRoute.getDocument().getId(), 889 (NuxeoPrincipal) documentManager.getPrincipal(), documentManager); 890 } 891 return null; 892 } 893 894 /** 895 * @since 5.6 896 */ 897 public String getCurrentWorkflowInitiator() { 898 DocumentRoute currentRoute = getRelatedRoute(); 899 if (currentRoute != null) { 900 return (String) currentRoute.getDocument().getPropertyValue(DocumentRoutingConstants.INITIATOR); 901 } 902 return ""; 903 } 904 905 /** 906 * since 5.7 907 */ 908 public boolean isCurrentRouteGraph() { 909 return isRouteGraph(getRelatedRoute()); 910 } 911 912 /** 913 * Checks if a given route is a Graph. 914 * 915 * @since 5.7.2 916 */ 917 public boolean isRouteGraph(DocumentRoute route) { 918 return route != null 919 && ExecutionTypeValues.graph.toString().equals( 920 route.getDocument().getPropertyValue(DocumentRoutingConstants.EXECUTION_TYPE_PROPERTY_NAME)); 921 } 922 923}