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.ecm.core.api.CoreSession; 051import org.nuxeo.ecm.core.api.DocumentModel; 052import org.nuxeo.ecm.core.api.DocumentModelList; 053import org.nuxeo.ecm.core.api.DocumentRef; 054import org.nuxeo.ecm.core.api.IdRef; 055import org.nuxeo.ecm.core.api.NuxeoPrincipal; 056import org.nuxeo.ecm.core.api.PathRef; 057import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 058import org.nuxeo.ecm.core.api.event.CoreEventConstants; 059import org.nuxeo.ecm.platform.routing.api.DocumentRoute; 060import org.nuxeo.ecm.platform.routing.api.DocumentRouteElement; 061import org.nuxeo.ecm.platform.routing.api.DocumentRouteTableElement; 062import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants; 063import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants.ExecutionTypeValues; 064import org.nuxeo.ecm.platform.routing.api.DocumentRoutingService; 065import org.nuxeo.ecm.platform.routing.api.LockableDocumentRoute; 066import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteAlredayLockedException; 067import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteNotLockedException; 068import org.nuxeo.ecm.platform.routing.core.api.DocumentRoutingEngineService; 069import org.nuxeo.ecm.platform.routing.core.impl.GraphRoute; 070import org.nuxeo.ecm.platform.task.Task; 071import org.nuxeo.ecm.platform.task.TaskEventNames; 072import org.nuxeo.ecm.platform.task.TaskService; 073import org.nuxeo.ecm.platform.types.TypeManager; 074import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 075import org.nuxeo.ecm.platform.ui.web.api.WebActions; 076import org.nuxeo.ecm.webapp.action.TypesTool; 077import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 078import org.nuxeo.ecm.webapp.edit.lock.LockActions; 079import org.nuxeo.ecm.webapp.helpers.EventManager; 080import org.nuxeo.ecm.webapp.helpers.EventNames; 081import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor; 082import org.nuxeo.runtime.api.Framework; 083 084/** 085 * Actions for current document route 086 * 087 * @author Mariana Cedica 088 */ 089@Scope(CONVERSATION) 090@Name("routingActions") 091@Install(precedence = Install.FRAMEWORK) 092public class DocumentRoutingActionsBean implements Serializable { 093 private static final long serialVersionUID = 1L; 094 095 private static final Log log = LogFactory.getLog(DocumentRoutingActionsBean.class); 096 097 public static final String SOURCE_DOC_NAME = "source_doc_name"; 098 099 public static final String ROUTE_DOCUMENT_REF = "route_doc_ref"; 100 101 @In(required = true, create = true) 102 protected NavigationContext navigationContext; 103 104 @In(create = true, required = false) 105 protected CoreSession documentManager; 106 107 @In(create = true, required = false) 108 protected FacesMessages facesMessages; 109 110 @In(create = true) 111 protected WebActions webActions; 112 113 @In(create = true) 114 protected LockActions lockActions; 115 116 @In(create = true, required = false) 117 protected TypesTool typesTool; 118 119 @In(create = true) 120 protected ResourcesAccessor resourcesAccessor; 121 122 @In(create = true) 123 protected TypeManager typeManager; 124 125 @In(create = true) 126 protected EventManager eventManager; 127 128 @In(required = true, create = true) 129 protected NuxeoPrincipal currentUser; 130 131 @In(create = true) 132 protected List<DocumentModel> relatedRoutes; 133 134 @In(create = true) 135 protected RelatedRouteActionBean relatedRouteAction; 136 137 @In(create = true) 138 protected DocumentsListsManager documentsListsManager; 139 140 @RequestParameter("stepId") 141 protected String stepId; 142 143 protected String relatedRouteModelDocumentId; 144 145 protected String docWithAttachedRouteId; 146 147 protected String hiddenSourceDocId; 148 149 protected String hiddenDocOrder; 150 151 enum StepOrder { 152 before, in, after 153 } 154 155 public DocumentRoutingService getDocumentRoutingService() { 156 return Framework.getService(DocumentRoutingService.class); 157 } 158 159 @Observer(value = { EventNames.DOCUMENT_CHANGED, EventNames.DOCUMENT_SELECTION_CHANGED }) 160 public void resetRelatedRouteDocumentId() { 161 relatedRouteModelDocumentId = null; 162 } 163 164 public boolean isRoutable() { 165 return getDocumentRoutingService().isRoutable(navigationContext.getCurrentDocument()); 166 } 167 168 public String startRoute() { 169 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 170 DocumentRoute currentRoute = currentDocument.getAdapter(DocumentRoute.class); 171 if (currentRoute == null) { 172 log.warn("Current document is not a workflow model"); 173 facesMessages.add(StatusMessage.Severity.ERROR, 174 resourcesAccessor.getMessages().get("label.document.routing.no.workflow")); 175 return null; 176 } 177 getDocumentRoutingService().createNewInstance(currentDocument.getName(), currentRoute.getAttachedDocuments(), 178 documentManager, true); 179 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument); 180 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_NEW_STARTED); 181 webActions.resetTabList(); 182 return null; 183 } 184 185 /** 186 * Gets the first related route. 187 * <p> 188 * When called on an actual route or route element, the route is returned. 189 * <p> 190 * When called on a regular document, the routing service is queried to get the routes which have the current 191 * document attached. 192 * <p> 193 * When dealing with a regular document, this is DEPRECATED as several graph routes may be related to the current 194 * document (for instance in the case of sub-workflows). Use {@link #getRelatedRoutes} instead. 195 */ 196 public DocumentRoute getRelatedRoute() { 197 // try to see if actually the current document is a route 198 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 199 200 if (currentDocument.hasFacet(DocumentRoutingConstants.DOCUMENT_ROUTE_DOCUMENT_FACET)) { 201 docWithAttachedRouteId = null; 202 return currentDocument.getAdapter(DocumentRoute.class); 203 } 204 // try to see if the current document is a routeElement 205 DocumentRouteElement relatedRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 206 if (relatedRouteElement != null) { 207 docWithAttachedRouteId = null; 208 return relatedRouteElement.getDocumentRoute(documentManager); 209 } 210 // else we must be in a document attached to a route 211 List<DocumentRoute> routes = getRelatedRoutes(); 212 if (routes.isEmpty()) { 213 return null; 214 } 215 docWithAttachedRouteId = currentDocument.getId(); 216 return routes.get(0); 217 } 218 219 /** 220 * Gets the list of routes related to the current document, by querying the routing service. 221 * 222 * @return the list of routes (may be empty) 223 * @since 5.7.2 224 */ 225 public List<DocumentRoute> getRelatedRoutes() { 226 queryForRelatedRoutes(); 227 List<DocumentRoute> routes = new ArrayList<DocumentRoute>(relatedRoutes.size()); 228 for (DocumentModel doc : relatedRoutes) { 229 routes.add(doc.getAdapter(DocumentRoute.class)); 230 } 231 return routes; 232 } 233 234 protected void queryForRelatedRoutes() { 235 if (relatedRoutes == null) { 236 relatedRoutes = relatedRouteAction.findRelatedRoute(); 237 } 238 } 239 240 /** 241 * Cancels the first workflow found on the current document 242 */ 243 public String cancelRoute() { 244 List<DocumentRoute> routes = getRelatedRoutes(); 245 if (routes.size() == 0) { 246 log.error("No workflow to cancel"); 247 return null; 248 } 249 DocumentRoute route = routes.get(0); 250 Framework.getLocalService(DocumentRoutingEngineService.class).cancel(route, documentManager); 251 // force computing of tabs 252 webActions.resetTabList(); 253 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_CANCELED); 254 Contexts.removeFromAllContexts("relatedRoutes"); 255 documentManager.save(); 256 return navigationContext.navigateToDocument(navigationContext.getCurrentDocument()); 257 } 258 259 public void saveRouteAsNewInstance() { 260 getDocumentRoutingService().saveRouteAsNewModel(getRelatedRoute(), documentManager); 261 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 262 facesMessages.add(StatusMessage.Severity.INFO, 263 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.route_duplicated")); 264 } 265 266 public void saveSelectedRouteAsNewInstance() { 267 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION); 268 if (!docs.isEmpty()) { 269 DocumentRoute route; 270 for (DocumentModel doc : docs) { 271 route = doc.getAdapter(DocumentRoute.class); 272 if (route != null) { 273 getDocumentRoutingService().saveRouteAsNewModel(route, documentManager); 274 } 275 } 276 } 277 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 278 facesMessages.add(StatusMessage.Severity.INFO, 279 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.selected_route_duplicated")); 280 } 281 282 public boolean getCanDuplicateRouteInstance() { 283 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION); 284 if (docs.isEmpty()) { 285 return false; 286 } 287 for (DocumentModel doc : docs) { 288 if (!doc.hasFacet(DocumentRoutingConstants.DOCUMENT_ROUTE_DOCUMENT_FACET)) { 289 return false; 290 } 291 } 292 return true; 293 } 294 295 public String validateRouteModel() { 296 DocumentRoute currentRouteModel = getRelatedRoute(); 297 try { 298 getDocumentRoutingService().validateRouteModel(currentRouteModel, documentManager); 299 } catch (DocumentRouteNotLockedException e) { 300 facesMessages.add(StatusMessage.Severity.WARN, 301 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 302 return null; 303 } 304 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentRouteModel.getDocument()); 305 getDocumentRoutingService().unlockDocumentRouteUnrestrictedSession(currentRouteModel, documentManager); 306 return null; 307 } 308 309 /** 310 * @deprecated since 5.9.2 - Use only routes of type 'graph' 311 */ 312 @Deprecated 313 protected List<DocumentRouteTableElement> computeRouteElements() { 314 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 315 DocumentRoute currentRoute = currentDocument.getAdapter(DocumentRoute.class); 316 return getElements(currentRoute); 317 } 318 319 /** 320 * @deprecated since 5.9.2 - Use only routes of type 'graph' 321 */ 322 @Deprecated 323 protected List<DocumentRouteTableElement> computeRelatedRouteElements() { 324 if (relatedRoutes.isEmpty()) { 325 return new ArrayList<DocumentRouteTableElement>(); 326 } 327 DocumentModel relatedRouteDocumentModel = documentManager.getDocument(new IdRef(relatedRoutes.get(0).getId())); 328 DocumentRoute currentRoute = relatedRouteDocumentModel.getAdapter(DocumentRoute.class); 329 return getElements(currentRoute); 330 } 331 332 /** 333 * @deprecated since 5.9.2 - Use only routes of type 'graph' 334 */ 335 @Deprecated 336 protected List<DocumentRouteTableElement> getElements(DocumentRoute currentRoute) { 337 return getDocumentRoutingService().getRouteElements(currentRoute, documentManager); 338 } 339 340 /** 341 * Check if the related route to this case is started (ready or running) or no 342 */ 343 public boolean hasRelatedRoute() { 344 queryForRelatedRoutes(); 345 return !relatedRoutes.isEmpty(); 346 } 347 348 public String startRouteRelatedToCurrentDocument() { 349 DocumentRoute route = getRelatedRoute(); 350 // check relatedRoutedoc id 351 if (!StringUtils.isEmpty(relatedRouteModelDocumentId)) { 352 DocumentModel model = documentManager.getDocument(new IdRef(relatedRouteModelDocumentId)); 353 route = model.getAdapter(DocumentRoute.class); 354 } 355 if (route == null) { 356 facesMessages.add(StatusMessage.Severity.WARN, 357 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.no.valid.route")); 358 return null; 359 } 360 361 List<String> documentIds = new ArrayList<String>(); 362 documentIds.add(navigationContext.getCurrentDocument().getId()); 363 route.setAttachedDocuments(documentIds); 364 getDocumentRoutingService().createNewInstance(route.getDocument().getName(), documentIds, documentManager, true); 365 Events.instance().raiseEvent(TaskEventNames.WORKFLOW_NEW_STARTED); 366 webActions.resetTabList(); 367 return null; 368 } 369 370 /** 371 * returns true if the routeStarted on the current Document is editable (is Ready ) 372 */ 373 public boolean routeRelatedToCurrentDocumentIsRunning() { 374 DocumentRoute route = getRelatedRoute(); 375 if (route == null) { 376 return false; 377 } 378 return route.isRunning(); 379 } 380 381 public String getTypeDescription(DocumentRouteTableElement localizable) { 382 return depthFormatter(localizable.getDepth(), localizable.getElement().getDocument().getType()); 383 } 384 385 private String depthFormatter(int depth, String type) { 386 StringBuilder depthFormatter = new StringBuilder(); 387 for (int i = 0; i < depth - 1; i++) { 388 depthFormatter.append("__"); 389 } 390 depthFormatter.append(type); 391 return depthFormatter.toString(); 392 } 393 394 public Converter getDocumentModelConverter() { 395 return new DocumentModelConvertor(documentManager); 396 } 397 398 public boolean isStep(DocumentModel doc) { 399 return (doc.hasFacet(DocumentRoutingConstants.ROUTE_STEP_FACET)); 400 } 401 402 public boolean currentRouteModelIsDraft() { 403 DocumentModel relatedRouteModel = navigationContext.getCurrentDocument(); 404 DocumentRoute routeModel = relatedRouteModel.getAdapter(DocumentRoute.class); 405 if (routeModel == null) { 406 return false; 407 } 408 return routeModel.isDraft(); 409 } 410 411 @Deprecated 412 // @deprecated since 5.9.2 - Use only routes of type 'graph' 413 public String removeStep() { 414 boolean alreadyLockedByCurrentUser = false; 415 DocumentRoute routeModel = getRelatedRoute(); 416 if (getDocumentRoutingService().isLockedByCurrentUser(routeModel, documentManager)) { 417 alreadyLockedByCurrentUser = true; 418 } else { 419 if (lockRoute(routeModel) == null) { 420 return null; 421 } 422 } 423 if (StringUtils.isEmpty(stepId)) { 424 return null; 425 } 426 DocumentRef docRef = new IdRef(stepId); 427 DocumentModel stepToDelete = documentManager.getDocument(docRef); 428 try { 429 getDocumentRoutingService().removeRouteElement(stepToDelete.getAdapter(DocumentRouteElement.class), 430 documentManager); 431 } catch (DocumentRouteNotLockedException e) { 432 facesMessages.add(StatusMessage.Severity.WARN, 433 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.not.locked")); 434 return null; 435 } 436 Contexts.removeFromAllContexts("relatedRoutes"); 437 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, routeModel.getDocument()); 438 // Release the lock only when currentUser had locked it before 439 // entering this method. 440 if (!alreadyLockedByCurrentUser) { 441 getDocumentRoutingService().unlockDocumentRoute(routeModel, documentManager); 442 } 443 return null; 444 } 445 446 /** 447 * Returns true if the givenDoc is a step that can be edited 448 * 449 * @deprecated since 5.9.2 - Use only routes of type 'graph' 450 */ 451 @Deprecated 452 public boolean isEditableStep(DocumentModel stepDoc) { 453 DocumentRouteElement stepElement = stepDoc.getAdapter(DocumentRouteElement.class); 454 // if fork, is not simple editable step 455 if (stepDoc.hasFacet("Folderish")) { 456 return false; 457 } 458 return stepElement.isModifiable(); 459 } 460 461 /** 462 * Returns true if the givenDoc is an routeElement that can be edited 463 * 464 * @deprecated since 5.9.2 - Use only routes of type 'graph' 465 */ 466 @Deprecated 467 public boolean isEditableRouteElement(DocumentModel stepDoc) { 468 DocumentRouteElement stepElement = stepDoc.getAdapter(DocumentRouteElement.class); 469 return stepElement.isModifiable(); 470 } 471 472 @Factory(value = "currentRouteLockedByCurrentUser", scope = ScopeType.EVENT) 473 public boolean isCurrentRouteLockedByCurrentUser() { 474 return getDocumentRoutingService().isLockedByCurrentUser(getRelatedRoute(), documentManager); 475 } 476 477 public boolean isCurrentRouteLocked() { 478 LockableDocumentRoute lockableRoute = getRelatedRoute().getDocument().getAdapter(LockableDocumentRoute.class); 479 return lockableRoute.isLocked(documentManager); 480 } 481 482 public boolean canUnlockRoute() { 483 return Boolean.TRUE.equals(lockActions.getCanUnlockDoc(getRelatedRoute().getDocument())); 484 } 485 486 public boolean canLockRoute() { 487 return Boolean.TRUE.equals(lockActions.getCanLockDoc(getRelatedRoute().getDocument())); 488 } 489 490 public Map<String, Serializable> getCurrentRouteLockDetails() { 491 return lockActions.getLockDetails(getRelatedRoute().getDocument()); 492 } 493 494 public String lockCurrentRoute() { 495 DocumentRoute docRouteElement = getRelatedRoute(); 496 return lockRoute(docRouteElement); 497 } 498 499 protected String lockRoute(DocumentRoute docRouteElement) { 500 try { 501 getDocumentRoutingService().lockDocumentRoute(docRouteElement.getDocumentRoute(documentManager), 502 documentManager); 503 } catch (DocumentRouteAlredayLockedException e) { 504 facesMessages.add(StatusMessage.Severity.WARN, 505 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.already.locked")); 506 return null; 507 } 508 return null; 509 } 510 511 public String unlockCurrentRoute() { 512 DocumentRoute route = getRelatedRoute(); 513 getDocumentRoutingService().unlockDocumentRoute(route, documentManager); 514 return null; 515 } 516 517 public boolean isEmptyFork(DocumentModel forkDoc) { 518 return forkDoc.hasFacet("Folderish") && !documentManager.hasChildren(forkDoc.getRef()); 519 } 520 521 public String editStep() { 522 if (StringUtils.isEmpty(stepId)) { 523 return null; 524 } 525 DocumentRef stepRef = new IdRef(stepId); 526 DocumentModel currentDoc = navigationContext.getCurrentDocument(); 527 if (currentDoc.getAdapter(DocumentRoute.class) == null) { 528 setDocWithAttachedRouteId(currentDoc.getId()); 529 } 530 return navigationContext.navigateToDocument(documentManager.getDocument(stepRef), "edit"); 531 } 532 533 public String updateRouteElement() { 534 boolean alreadyLockedByCurrentUser = false; 535 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 536 DocumentRouteElement docRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 537 DocumentRoute route = docRouteElement.getDocumentRoute(documentManager); 538 if (getDocumentRoutingService().isLockedByCurrentUser(route, documentManager)) { 539 alreadyLockedByCurrentUser = true; 540 } else { 541 if (lockRoute(route) == null) { 542 return null; 543 } 544 } 545 try { 546 getDocumentRoutingService().updateRouteElement(docRouteElement, documentManager); 547 } catch (DocumentRouteNotLockedException e) { 548 facesMessages.add(StatusMessage.Severity.WARN, 549 resourcesAccessor.getMessages().get("feedback.casemanagement.document.route.already.locked")); 550 return null; 551 } 552 navigationContext.invalidateCurrentDocument(); 553 facesMessages.add(StatusMessage.Severity.INFO, resourcesAccessor.getMessages().get("document_modified"), 554 resourcesAccessor.getMessages().get(currentDocument.getType())); 555 EventManager.raiseEventsOnDocumentChange(currentDocument); 556 // Release the lock only when currentUser had locked it before 557 // entering this method. 558 if (!alreadyLockedByCurrentUser) { 559 getDocumentRoutingService().unlockDocumentRoute(route, documentManager); 560 } 561 if (docWithAttachedRouteId == null) { 562 return webActions.setCurrentTabAndNavigate(docRouteElement.getDocumentRoute(documentManager).getDocument(), 563 "TAB_DOCUMENT_ROUTE_ELEMENTS"); 564 } 565 566 setRelatedRouteWhenNavigateBackToCase(); 567 return webActions.setCurrentTabAndNavigate(documentManager.getDocument(new IdRef(docWithAttachedRouteId)), 568 "TAB_CASE_MANAGEMENT_VIEW_RELATED_ROUTE"); 569 } 570 571 public String goBackToRoute() { 572 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 573 DocumentRouteElement docRouteElement = currentDocument.getAdapter(DocumentRouteElement.class); 574 return webActions.setCurrentTabAndNavigate(docRouteElement.getDocumentRoute(documentManager).getDocument(), 575 "TAB_DOCUMENT_ROUTE_ELEMENTS"); 576 } 577 578 /** 579 * @deprecated since 5.9.2 - Use only routes of type 'graph' 580 */ 581 @Deprecated 582 public String createRouteElement(String typeName) { 583 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 584 DocumentRef routeRef = currentDocument.getRef(); 585 DocumentRef sourceDocRef = new IdRef(hiddenSourceDocId); 586 DocumentModel sourceDoc = documentManager.getDocument(sourceDocRef); 587 String sourceDocName = null; 588 String parentPath = null; 589 if (StepOrder.in.toString().equals(hiddenDocOrder)) { 590 parentPath = sourceDoc.getPathAsString(); 591 } else { 592 DocumentModel parentDoc = documentManager.getParentDocument(sourceDocRef); 593 parentPath = parentDoc.getPathAsString(); 594 if (StepOrder.before.toString().equals(hiddenDocOrder)) { 595 sourceDocName = sourceDoc.getName(); 596 } else { 597 DocumentModelList orderedChilds = getDocumentRoutingService().getOrderedRouteElement(parentDoc.getId(), 598 documentManager); 599 int selectedDocumentIndex = orderedChilds.indexOf(sourceDoc); 600 int nextIndex = selectedDocumentIndex + 1; 601 if (nextIndex >= orderedChilds.size()) { 602 sourceDocName = null; 603 } else { 604 sourceDocName = orderedChilds.get(nextIndex).getName(); 605 } 606 } 607 } 608 org.nuxeo.ecm.platform.types.Type docType = typeManager.getType(typeName); 609 // we cannot use typesTool as intermediary since the DataModel callback 610 // will alter whatever type we set 611 typesTool.setSelectedType(docType); 612 DocumentModel changeableDocument = documentManager.createDocumentModel(typeName); 613 changeableDocument.putContextData(CoreEventConstants.PARENT_PATH, parentPath); 614 changeableDocument.putContextData(SOURCE_DOC_NAME, sourceDocName); 615 changeableDocument.putContextData(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(CoreEventConstants.PARENT_PATH); 720 String sourceDocumentName = (String) newDocument.getContextData(SOURCE_DOC_NAME); 721 DocumentRef routeDocRef = (DocumentRef) newDocument.getContextData(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}