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.Iterator; 029import java.util.List; 030import java.util.Map; 031 032import javax.faces.convert.Converter; 033 034import org.apache.commons.lang3.StringUtils; 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.jboss.seam.ScopeType; 038import org.jboss.seam.annotations.Factory; 039import org.jboss.seam.annotations.In; 040import org.jboss.seam.annotations.Install; 041import org.jboss.seam.annotations.Name; 042import org.jboss.seam.annotations.Observer; 043import org.jboss.seam.annotations.Scope; 044import org.jboss.seam.annotations.web.RequestParameter; 045import org.jboss.seam.contexts.Contexts; 046import org.jboss.seam.core.Events; 047import org.jboss.seam.faces.FacesMessages; 048import org.jboss.seam.international.StatusMessage; 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.getService(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 changeableDocument.putContextData(CoreEventConstants.PARENT_PATH, parentPath); 613 changeableDocument.putContextData(SOURCE_DOC_NAME, sourceDocName); 614 changeableDocument.putContextData(ROUTE_DOCUMENT_REF, routeRef); 615 navigationContext.setChangeableDocument(changeableDocument); 616 return "create_route_element"; 617 } 618 619 /** 620 * Moves the step in the parent container in the specified direction. If the step is in a parallel container, it 621 * can't be moved. A step can't be moved before a step already done or running. Assumed that the route is already 622 * locked to have this action available, so no check is done. 623 * 624 * @deprecated since 5.9.2 - Use only routes of type 'graph' 625 */ 626 @Deprecated 627 public String moveRouteElement(String direction) { 628 if (StringUtils.isEmpty(stepId)) { 629 return null; 630 } 631 DocumentModel routeElementDocToMove = documentManager.getDocument(new IdRef(stepId)); 632 DocumentModel parentDoc = documentManager.getDocument(routeElementDocToMove.getParentRef()); 633 ExecutionTypeValues executionType = ExecutionTypeValues.valueOf((String) parentDoc.getPropertyValue(DocumentRoutingConstants.EXECUTION_TYPE_PROPERTY_NAME)); 634 if (!DocumentRoutingConstants.ExecutionTypeValues.serial.equals(executionType)) { 635 facesMessages.add( 636 StatusMessage.Severity.WARN, 637 resourcesAccessor.getMessages().get( 638 "feedback.casemanagement.document.route.cant.move.steps.in.parallel.container")); 639 return null; 640 } 641 DocumentModelList orderedChilds = getDocumentRoutingService().getOrderedRouteElement(parentDoc.getId(), 642 documentManager); 643 int selectedDocumentIndex = orderedChilds.indexOf(routeElementDocToMove); 644 if (DocumentRoutingWebConstants.MOVE_STEP_UP.equals(direction)) { 645 if (selectedDocumentIndex == 0) { 646 facesMessages.add( 647 StatusMessage.Severity.WARN, 648 resourcesAccessor.getMessages().get( 649 "feedback.casemanagement.document.route.already.first.step.in.container")); 650 return null; 651 } 652 routeElementDocToMove.getAdapter(DocumentRouteElement.class); 653 DocumentModel stepMoveBefore = orderedChilds.get(selectedDocumentIndex - 1); 654 DocumentRouteElement stepElementMoveBefore = stepMoveBefore.getAdapter(DocumentRouteElement.class); 655 if (stepElementMoveBefore.isRunning()) { 656 facesMessages.add( 657 StatusMessage.Severity.WARN, 658 resourcesAccessor.getMessages().get( 659 "feedback.casemanagement.document.route.cant.move.step.before.already.running.step")); 660 return null; 661 } 662 if (!stepElementMoveBefore.isModifiable()) { 663 facesMessages.add( 664 StatusMessage.Severity.WARN, 665 resourcesAccessor.getMessages().get( 666 "feedback.casemanagement.document.route.cant.move.step.after.no.modifiable.step")); 667 return null; 668 } 669 documentManager.orderBefore(parentDoc.getRef(), routeElementDocToMove.getName(), stepMoveBefore.getName()); 670 } 671 if (DocumentRoutingWebConstants.MOVE_STEP_DOWN.equals(direction)) { 672 if (selectedDocumentIndex == orderedChilds.size() - 1) { 673 facesMessages.add( 674 StatusMessage.Severity.WARN, 675 resourcesAccessor.getMessages().get( 676 "feedback.casemanagement.document.already.last.step.in.container")); 677 return null; 678 } 679 routeElementDocToMove.getAdapter(DocumentRouteElement.class); 680 DocumentModel stepMoveAfter = orderedChilds.get(selectedDocumentIndex + 1); 681 DocumentRouteElement stepElementMoveAfter = stepMoveAfter.getAdapter(DocumentRouteElement.class); 682 if (stepElementMoveAfter.isRunning()) { 683 facesMessages.add( 684 StatusMessage.Severity.WARN, 685 resourcesAccessor.getMessages().get( 686 "feedback.casemanagement.document.route.cant.move.step.after.already.running.step")); 687 return null; 688 } 689 documentManager.orderBefore(parentDoc.getRef(), orderedChilds.get(selectedDocumentIndex + 1).getName(), 690 routeElementDocToMove.getName()); 691 } 692 if (docWithAttachedRouteId == null) { 693 return webActions.setCurrentTabAndNavigate(getRelatedRoute().getDocument(), "TAB_DOCUMENT_ROUTE_ELEMENTS"); 694 } 695 696 setRelatedRouteWhenNavigateBackToCase(); 697 return webActions.setCurrentTabAndNavigate(documentManager.getDocument(new IdRef(docWithAttachedRouteId)), 698 "TAB_CASE_MANAGEMENT_VIEW_RELATED_ROUTE"); 699 } 700 701 public String saveRouteElement() { 702 boolean alreadyLockedByCurrentUser = false; 703 DocumentRoute routeModel = getRelatedRoute(); 704 if (getDocumentRoutingService().isLockedByCurrentUser(routeModel, documentManager)) { 705 alreadyLockedByCurrentUser = true; 706 } else { 707 lockRoute(routeModel); 708 } 709 710 DocumentModel newDocument = navigationContext.getChangeableDocument(); 711 // Document has already been created if it has an id. 712 // This will avoid creation of many documents if user hit create button 713 // too many times. 714 if (newDocument.getId() != null) { 715 log.debug("Document " + newDocument.getName() + " already created"); 716 return navigationContext.navigateToDocument(newDocument, "after-create"); 717 } 718 String parentDocumentPath = (String) newDocument.getContextData(CoreEventConstants.PARENT_PATH); 719 String sourceDocumentName = (String) newDocument.getContextData(SOURCE_DOC_NAME); 720 DocumentRef routeDocRef = (DocumentRef) newDocument.getContextData(ROUTE_DOCUMENT_REF); 721 try { 722 getDocumentRoutingService().addRouteElementToRoute(new PathRef(parentDocumentPath), sourceDocumentName, 723 newDocument.getAdapter(DocumentRouteElement.class), documentManager); 724 } catch (DocumentRouteNotLockedException e) { 725 facesMessages.add(StatusMessage.Severity.WARN, 726 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 727 return null; 728 } 729 DocumentModel routeDocument = documentManager.getDocument(routeDocRef); 730 facesMessages.add(StatusMessage.Severity.INFO, resourcesAccessor.getMessages().get("document_saved"), 731 resourcesAccessor.getMessages().get(newDocument.getType())); 732 733 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, routeDocument); 734 // Release the lock only when currentUser had locked it before 735 // entering this method. 736 if (!alreadyLockedByCurrentUser) { 737 getDocumentRoutingService().unlockDocumentRoute(routeModel, documentManager); 738 } 739 return navigationContext.navigateToDocument(routeDocument); 740 } 741 742 @Deprecated 743 // @deprecated since 5.9.2 - Use only routes of type 'graph' 744 public List<DocumentModel> getOrderedChildren(String docRouteElementId, String type) { 745 // xxx move me in serice with query 746 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElementId, 747 documentManager); 748 List<DocumentModel> filteredChildren = new ArrayList<DocumentModel>(); 749 for (DocumentModel documentModel : orderedChildren) { 750 if (type.equals(documentModel.getType())) { 751 filteredChildren.add(documentModel); 752 } 753 } 754 return filteredChildren; 755 } 756 757 @Deprecated 758 // @deprecated since 5.9.2 - Use only routes of type 'graph' 759 public DocumentModel getChildWithPosition(DocumentModel docRouteElement, String pos) { 760 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElement.getId(), 761 documentManager); 762 return orderedChildren.get(Integer.parseInt(pos)); 763 } 764 765 @Deprecated 766 // @deprecated since 5.9.2 - Use only routes of type 'graph' 767 public String getPositionForChild(DocumentModel docRouteElement, DocumentModel docChild) { 768 DocumentModelList orderedChildren = getDocumentRoutingService().getOrderedRouteElement(docRouteElement.getId(), 769 documentManager); 770 return String.valueOf(orderedChildren.indexOf(docChild)); 771 } 772 773 public String getHiddenSourceDocId() { 774 return hiddenSourceDocId; 775 } 776 777 public void setHiddenSourceDocId(String hiddenSourceDocId) { 778 this.hiddenSourceDocId = hiddenSourceDocId; 779 } 780 781 public String getHiddenDocOrder() { 782 return hiddenDocOrder; 783 } 784 785 public void setHiddenDocOrder(String hiddenDocOrder) { 786 this.hiddenDocOrder = hiddenDocOrder; 787 } 788 789 public String getRelatedRouteModelDocumentId() { 790 return relatedRouteModelDocumentId; 791 } 792 793 public void setRelatedRouteModelDocumentId(String relatedRouteModelDocumentId) { 794 this.relatedRouteModelDocumentId = relatedRouteModelDocumentId; 795 } 796 797 public String getDocWithAttachedRouteId() { 798 return docWithAttachedRouteId; 799 } 800 801 public void setDocWithAttachedRouteId(String docWithAttachedRouteId) { 802 this.docWithAttachedRouteId = docWithAttachedRouteId; 803 } 804 805 private void setRelatedRouteWhenNavigateBackToCase() { 806 // recompute factory 807 webActions.resetTabList(); 808 navigationContext.setCurrentDocument(documentManager.getDocument(new IdRef(docWithAttachedRouteId))); 809 relatedRoutes = relatedRouteAction.findRelatedRoute(); 810 } 811 812 @Observer(value = { TaskEventNames.WORKFLOW_ENDED, TaskEventNames.WORKFLOW_CANCELED, 813 TaskEventNames.WORKFLOW_TASK_COMPLETED }, create = false) 814 public void resetCache() { 815 relatedRoutes = null; 816 if (!hasRelatedRoute()) { 817 webActions.resetTabList(); 818 } 819 } 820 821 /** 822 * @since 5.6 823 */ 824 public DocumentModel getRouteModel(String routeId) { 825 return documentManager.getDocument(new IdRef(routeId)); 826 } 827 828 /** 829 * @since 5.6 830 */ 831 public DocumentModel getRouteInstanceFor(Task task) { 832 final String routeDocId = task.getVariable(DocumentRoutingConstants.TASK_ROUTE_INSTANCE_DOCUMENT_ID_KEY); 833 if (routeDocId == null) { 834 return null; 835 } 836 final DocumentModel[] res = new DocumentModel[1]; 837 new UnrestrictedSessionRunner(documentManager) { 838 @Override 839 public void run() { 840 DocumentModel doc = session.getDocument(new IdRef(routeDocId)); 841 doc.detach(true); 842 res[0] = doc; 843 } 844 }.runUnrestricted(); 845 return res[0]; 846 } 847 848 /** 849 * @since 5.6 850 */ 851 public List<DocumentModel> getFilteredRouteModels() { 852 DocumentRoutingService documentRoutingService = Framework.getService(DocumentRoutingService.class); 853 List<DocumentModel> routeModels = documentRoutingService.searchRouteModels(documentManager, ""); 854 for (Iterator<DocumentModel> it = routeModels.iterator(); it.hasNext();) { 855 DocumentModel route = it.next(); 856 Object graphRouteObj = route.getAdapter(GraphRoute.class); 857 if (graphRouteObj instanceof GraphRoute) { 858 String filter = ((GraphRoute) graphRouteObj).getAvailabilityFilter(); 859 if (!StringUtils.isBlank(filter)) { 860 if (!webActions.checkFilter(filter)) { 861 it.remove(); 862 } 863 } 864 } else { 865 // old workflow document => ignore 866 it.remove(); 867 } 868 } 869 return routeModels; 870 } 871 872 /** 873 * @since 5.6 874 */ 875 public List<Task> getCurrentRouteAllTasks() { 876 TaskService taskService = Framework.getService(TaskService.class); 877 DocumentRoute currentRoute = getRelatedRoute(); 878 if (currentRoute != null) { 879 return taskService.getAllTaskInstances(currentRoute.getDocument().getId(), documentManager); 880 } 881 return null; 882 } 883 884 /** 885 * @since 5.6 886 */ 887 public List<Task> getCurrentRouteCurrentUserTasks() { 888 TaskService taskService = Framework.getService(TaskService.class); 889 DocumentRoute currentRoute = getRelatedRoute(); 890 if (currentRoute != null) { 891 return taskService.getAllTaskInstances(currentRoute.getDocument().getId(), 892 (NuxeoPrincipal) documentManager.getPrincipal(), documentManager); 893 } 894 return null; 895 } 896 897 /** 898 * @since 5.6 899 */ 900 public String getCurrentWorkflowInitiator() { 901 DocumentRoute currentRoute = getRelatedRoute(); 902 if (currentRoute != null) { 903 return (String) currentRoute.getDocument().getPropertyValue(DocumentRoutingConstants.INITIATOR); 904 } 905 return ""; 906 } 907 908 /** 909 * since 5.7 910 */ 911 public boolean isCurrentRouteGraph() { 912 return isRouteGraph(getRelatedRoute()); 913 } 914 915 /** 916 * Checks if a given route is a Graph. 917 * 918 * @since 5.7.2 919 */ 920 public boolean isRouteGraph(DocumentRoute route) { 921 return route != null 922 && ExecutionTypeValues.graph.toString().equals( 923 route.getDocument().getPropertyValue(DocumentRoutingConstants.EXECUTION_TYPE_PROPERTY_NAME)); 924 } 925 926}