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