001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.workflow; 029 030import org.opencms.ade.publish.CmsPublishService; 031import org.opencms.ade.publish.I_CmsVirtualProject; 032import org.opencms.ade.publish.shared.CmsPublishOptions; 033import org.opencms.ade.publish.shared.CmsPublishResource; 034import org.opencms.ade.publish.shared.CmsWorkflow; 035import org.opencms.ade.publish.shared.CmsWorkflowAction; 036import org.opencms.ade.publish.shared.CmsWorkflowResponse; 037import org.opencms.db.CmsResourceState; 038import org.opencms.file.CmsGroup; 039import org.opencms.file.CmsObject; 040import org.opencms.file.CmsProject; 041import org.opencms.file.CmsResource; 042import org.opencms.file.CmsUser; 043import org.opencms.i18n.CmsLocaleManager; 044import org.opencms.lock.CmsLock; 045import org.opencms.main.CmsException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.OpenCms; 048import org.opencms.publish.CmsPublishEventAdapter; 049import org.opencms.publish.CmsPublishJobEnqueued; 050import org.opencms.publish.CmsPublishJobRunning; 051import org.opencms.security.CmsPermissionSet; 052import org.opencms.util.CmsStringUtil; 053import org.opencms.util.CmsUUID; 054 055import java.text.DateFormat; 056import java.util.ArrayList; 057import java.util.Date; 058import java.util.HashMap; 059import java.util.LinkedHashMap; 060import java.util.List; 061import java.util.Locale; 062import java.util.Map; 063 064import org.apache.commons.logging.Log; 065 066/** 067 * The default workflow manager implementation, which supports 2 basic actions, Release and Publish. 068 */ 069public class CmsExtendedWorkflowManager extends CmsDefaultWorkflowManager { 070 071 /** The release workflow action. */ 072 public static final String ACTION_RELEASE = "release"; 073 074 /** The parameter which points to the XML content used for notifications. */ 075 public static final String PARAM_NOTIFICATION_CONTENT = "notificationContent"; 076 077 /** The key for the configurable workflow project manager group. */ 078 public static final String PARAM_WORKFLOW_PROJECT_MANAGER_GROUP = "workflowProjectManagerGroup"; 079 080 /** The key for the configurable workflow project user group. */ 081 public static final String PARAM_WORKFLOW_PROJECT_USER_GROUP = "workflowProjectUserGroup"; 082 083 /** The key for the 'release' workflow. */ 084 public static final String WORKFLOW_RELEASE = "WORKFLOW_RELEASE"; 085 086 /** The logger instance for this class. */ 087 private static final Log LOG = CmsLog.getLog(CmsExtendedWorkflowManager.class); 088 089 /** 090 * @see org.opencms.workflow.CmsDefaultWorkflowManager#createFormatter(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflow, org.opencms.ade.publish.shared.CmsPublishOptions) 091 */ 092 @Override 093 public I_CmsPublishResourceFormatter createFormatter( 094 CmsObject cms, 095 CmsWorkflow workflow, 096 CmsPublishOptions options) { 097 098 String workflowKey = workflow.getId(); 099 boolean release = WORKFLOW_RELEASE.equals(workflowKey); 100 CmsExtendedPublishResourceFormatter formatter = new CmsExtendedPublishResourceFormatter(cms); 101 formatter.setRelease(release); 102 return formatter; 103 } 104 105 /** 106 * @see org.opencms.workflow.CmsDefaultWorkflowManager#executeAction(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflowAction, org.opencms.ade.publish.shared.CmsPublishOptions, java.util.List) 107 */ 108 @Override 109 public CmsWorkflowResponse executeAction( 110 CmsObject userCms, 111 CmsWorkflowAction action, 112 CmsPublishOptions options, 113 List<CmsResource> resources) 114 throws CmsException { 115 116 if (LOG.isInfoEnabled()) { 117 LOG.info( 118 "workflow action: " 119 + userCms.getRequestContext().getCurrentUser().getName() 120 + " " 121 + action.getAction()); 122 List<String> resourceNames = new ArrayList<String>(); 123 for (CmsResource resource : resources) { 124 resourceNames.add(resource.getRootPath()); 125 } 126 LOG.info("Resources: " + CmsStringUtil.listAsString(resourceNames, ",")); 127 } 128 try { 129 130 String actionKey = action.getAction(); 131 if (ACTION_RELEASE.equals(actionKey)) { 132 return actionRelease(userCms, resources); 133 } else { 134 return super.executeAction(userCms, action, options, resources); 135 } 136 } catch (CmsException e) { 137 LOG.error("workflow action failed"); 138 LOG.error(e.getLocalizedMessage(), e); 139 throw e; 140 } 141 } 142 143 /** 144 * @see org.opencms.workflow.CmsDefaultWorkflowManager#getRealOrVirtualProject(org.opencms.util.CmsUUID) 145 */ 146 @Override 147 public I_CmsVirtualProject getRealOrVirtualProject(CmsUUID projectId) { 148 149 I_CmsVirtualProject result = m_virtualProjects.get(projectId); 150 if (result == null) { 151 result = new CmsExtendedRealProjectWrapper(projectId); 152 } 153 return result; 154 } 155 156 /** 157 * Gets the name of the group which should be used as the 'manager' group for newly created workflow projects.<p> 158 * 159 * @return a group name 160 */ 161 public String getWorkflowProjectManagerGroup() { 162 163 return getParameter(PARAM_WORKFLOW_PROJECT_MANAGER_GROUP, OpenCms.getDefaultUsers().getGroupAdministrators()); 164 } 165 166 /** 167 * Gets the name of the group which should be used as the 'user' group for newly created workflow projects.<p> 168 * 169 * @return a group name 170 */ 171 public String getWorkflowProjectUserGroup() { 172 173 return getParameter(PARAM_WORKFLOW_PROJECT_USER_GROUP, OpenCms.getDefaultUsers().getGroupAdministrators()); 174 } 175 176 /** 177 * @see org.opencms.workflow.CmsDefaultWorkflowManager#getWorkflowResources(org.opencms.file.CmsObject, org.opencms.ade.publish.shared.CmsWorkflow, org.opencms.ade.publish.shared.CmsPublishOptions, boolean, boolean) 178 */ 179 @Override 180 public CmsWorkflowResources getWorkflowResources( 181 CmsObject cms, 182 CmsWorkflow workflow, 183 CmsPublishOptions options, 184 boolean canOverrideWorkflow, 185 boolean ignoreLimit) { 186 187 String workflowKey = workflow.getId(); 188 String overrideId = null; 189 Integer tooManyCount = null; 190 if (WORKFLOW_RELEASE.equals(workflowKey)) { 191 boolean tooMany = false; 192 CmsWorkflowResources workflowResourcesBean = super.getWorkflowResources( 193 cms, 194 workflow, 195 options, 196 canOverrideWorkflow, 197 ignoreLimit); 198 List<CmsResource> result = workflowResourcesBean.getWorkflowResources(); 199 tooMany = workflowResourcesBean.isTooMany(); 200 if (tooMany) { 201 tooManyCount = workflowResourcesBean.getTooManyCount(); 202 } 203 204 if (canOverrideWorkflow && !workflowResourcesBean.isTooMany()) { 205 boolean override = false; 206 for (CmsResource permCheckResource : result) { 207 try { 208 boolean canPublish = cms.hasPermissions( 209 permCheckResource, 210 CmsPermissionSet.ACCESS_DIRECT_PUBLISH); 211 if (canPublish) { 212 override = true; 213 } 214 } catch (Exception e) { 215 LOG.error( 216 "Can't check permissions for " 217 + permCheckResource.getRootPath() 218 + ":" 219 + e.getLocalizedMessage(), 220 e); 221 } 222 if (override) { 223 CmsWorkflowResources overrideWorkflowResources = getWorkflowResources( 224 cms, 225 getWorkflows(cms).get(CmsDefaultWorkflowManager.WORKFLOW_PUBLISH), 226 options, 227 false, 228 false); 229 tooMany = overrideWorkflowResources.isTooMany(); 230 tooManyCount = overrideWorkflowResources.getTooManyCount(); 231 result = overrideWorkflowResources.getWorkflowResources(); 232 overrideId = WORKFLOW_PUBLISH; 233 } 234 } 235 } 236 CmsWorkflowResources realResult = new CmsWorkflowResources( 237 result, 238 getWorkflows(cms).get(overrideId), 239 tooManyCount); 240 return realResult; 241 } else { 242 CmsWorkflowResources realResult = super.getWorkflowResources( 243 cms, 244 workflow, 245 options, 246 canOverrideWorkflow, 247 ignoreLimit); 248 return realResult; 249 } 250 } 251 252 /** 253 * @see org.opencms.workflow.I_CmsWorkflowManager#getWorkflows(org.opencms.file.CmsObject) 254 */ 255 @Override 256 public Map<String, CmsWorkflow> getWorkflows(CmsObject cms) { 257 258 Map<String, CmsWorkflow> parentWorkflows = super.getWorkflows(cms); 259 Map<String, CmsWorkflow> result = new LinkedHashMap<String, CmsWorkflow>(); 260 String releaseLabel = getLabel(cms, Messages.GUI_WORKFLOW_ACTION_RELEASE_0); 261 CmsWorkflowAction release = new CmsWorkflowAction(ACTION_RELEASE, releaseLabel, true); 262 List<CmsWorkflowAction> actions = new ArrayList<CmsWorkflowAction>(); 263 actions.add(release); 264 CmsWorkflow releaseWorkflow = new CmsWorkflow(WORKFLOW_RELEASE, releaseLabel, actions); 265 try { 266 boolean isProjectManager = isProjectManager(cms); 267 // make release action always available, but make it the default if the user 268 // isn't a project manager. 269 if (isProjectManager) { 270 result.putAll(parentWorkflows); 271 result.put(WORKFLOW_RELEASE, releaseWorkflow); 272 } else { 273 result.put(WORKFLOW_RELEASE, releaseWorkflow); 274 result.putAll(parentWorkflows); 275 } 276 } catch (CmsException e) { 277 result = parentWorkflows; 278 } 279 return result; 280 } 281 282 /** 283 * @see org.opencms.workflow.A_CmsWorkflowManager#initialize(org.opencms.file.CmsObject) 284 */ 285 @Override 286 public void initialize(CmsObject adminCms) { 287 288 super.initialize(adminCms); 289 OpenCms.getPublishManager().addPublishListener(new CmsPublishEventAdapter() { 290 291 @Override 292 public void onFinish(CmsPublishJobRunning publishJob) { 293 294 CmsExtendedWorkflowManager.this.onFinishPublishJob(publishJob); 295 } 296 297 /** 298 * @see org.opencms.publish.CmsPublishEventAdapter#onStart(org.opencms.publish.CmsPublishJobEnqueued) 299 */ 300 @Override 301 public void onStart(CmsPublishJobEnqueued publishJob) { 302 303 CmsExtendedWorkflowManager.this.onStartPublishJob(publishJob); 304 } 305 }); 306 } 307 308 /** 309 * Implementation of the 'release' workflow action.<p> 310 * 311 * @param userCms the current user's CMS context 312 * @param resources the resources which should be released 313 * 314 * @return the workflow response for this action 315 * 316 * @throws CmsException if something goes wrong 317 */ 318 protected CmsWorkflowResponse actionRelease(CmsObject userCms, List<CmsResource> resources) throws CmsException { 319 320 checkNewParentsInList(userCms, resources); 321 String projectName = generateProjectName(userCms); 322 String projectDescription = generateProjectDescription(userCms); 323 CmsObject offlineAdminCms = OpenCms.initCmsObject(m_adminCms); 324 offlineAdminCms.getRequestContext().setCurrentProject(userCms.getRequestContext().getCurrentProject()); 325 String managerGroup = getWorkflowProjectManagerGroup(); 326 String userGroup = getWorkflowProjectUserGroup(); 327 CmsProject workflowProject = m_adminCms.createProject( 328 projectName, 329 projectDescription, 330 userGroup, 331 managerGroup, 332 CmsProject.PROJECT_TYPE_WORKFLOW); 333 CmsObject newProjectCms = OpenCms.initCmsObject(offlineAdminCms); 334 newProjectCms.getRequestContext().setCurrentProject(workflowProject); 335 newProjectCms.getRequestContext().setSiteRoot(""); 336 newProjectCms.copyResourceToProject("/"); 337 CmsUser admin = offlineAdminCms.getRequestContext().getCurrentUser(); 338 clearLocks(userCms.getRequestContext().getCurrentProject(), resources); 339 for (CmsResource resource : resources) { 340 CmsLock lock = offlineAdminCms.getLock(resource); 341 if (lock.isUnlocked()) { 342 offlineAdminCms.lockResource(resource); 343 } else if (!lock.isOwnedBy(admin)) { 344 offlineAdminCms.changeLock(resource); 345 } 346 offlineAdminCms.writeProjectLastModified(resource, workflowProject); 347 offlineAdminCms.unlockResource(resource); 348 } 349 for (CmsUser user : getNotificationMailRecipients()) { 350 sendNotification(userCms, user, workflowProject, resources); 351 } 352 return new CmsWorkflowResponse( 353 true, 354 "", 355 new ArrayList<CmsPublishResource>(), 356 new ArrayList<CmsWorkflowAction>(), 357 workflowProject.getUuid()); 358 } 359 360 /** 361 * Checks that the parent folders of new resources which are released are either not new or are also released.<p> 362 * 363 * @param userCms the user CMS context 364 * @param resources the resources to check 365 * 366 * @throws CmsException if the check fails 367 */ 368 protected void checkNewParentsInList(CmsObject userCms, List<CmsResource> resources) throws CmsException { 369 370 Map<String, CmsResource> resourcesByPath = new HashMap<String, CmsResource>(); 371 CmsObject rootCms = OpenCms.initCmsObject(m_adminCms); 372 rootCms.getRequestContext().setCurrentProject(userCms.getRequestContext().getCurrentProject()); 373 rootCms.getRequestContext().setSiteRoot(""); 374 for (CmsResource resource : resources) { 375 resourcesByPath.put(resource.getRootPath(), resource); 376 } 377 for (CmsResource resource : resources) { 378 if (resource.getState().isNew()) { 379 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 380 CmsResource parent = resourcesByPath.get(parentPath); 381 if (parent == null) { 382 parent = rootCms.readResource(parentPath); 383 if (parent.getState().isNew()) { 384 throw new CmsNewParentNotInWorkflowException( 385 Messages.get().container( 386 Messages.ERR_NEW_PARENT_NOT_IN_WORKFLOW_1, 387 resource.getRootPath())); 388 } 389 } 390 } 391 } 392 } 393 394 /** 395 * Cleans up empty workflow projects.<p> 396 * 397 * @param projects the workflow projects to clean up 398 * 399 * @throws CmsException if something goes wrong 400 */ 401 protected void cleanupEmptyWorkflowProjects(List<CmsProject> projects) throws CmsException { 402 403 if (projects == null) { 404 projects = OpenCms.getOrgUnitManager().getAllManageableProjects(m_adminCms, "", true); 405 } 406 for (CmsProject project : projects) { 407 if (project.isWorkflowProject()) { 408 if (isProjectEmpty(project)) { 409 m_adminCms.deleteProject(project.getUuid()); 410 } 411 } 412 } 413 } 414 415 /** 416 * Removes a project if there are no longer any resources which have been last modified in that project.<p> 417 * 418 * @param project the project 419 * @throws CmsException if something goes wrong 420 */ 421 protected void cleanupProjectIfEmpty(CmsProject project) throws CmsException { 422 423 if ((project.getType().getMode() == CmsProject.PROJECT_TYPE_WORKFLOW.getMode()) && isProjectEmpty(project)) { 424 LOG.info("Removing project " + project.getName() + " because it is an empty workflow project."); 425 m_adminCms.deleteProject(project.getUuid()); 426 } 427 } 428 429 /** 430 * Ensures that the resources to be released are unlocked.<p> 431 * 432 * @param project the project in which to operate 433 * @param resources the resources for which the locks should be removed 434 * 435 * @throws CmsException if something goes wrong 436 */ 437 protected void clearLocks(CmsProject project, List<CmsResource> resources) throws CmsException { 438 439 CmsObject rootCms = OpenCms.initCmsObject(m_adminCms); 440 rootCms.getRequestContext().setCurrentProject(project); 441 rootCms.getRequestContext().setSiteRoot(""); 442 for (CmsResource resource : resources) { 443 CmsLock lock = rootCms.getLock(resource); 444 if (lock.isUnlocked()) { 445 continue; 446 } 447 String currentPath = resource.getRootPath(); 448 while (lock.isInherited()) { 449 currentPath = CmsResource.getParentFolder(currentPath); 450 lock = rootCms.getLock(currentPath); 451 } 452 rootCms.changeLock(currentPath); 453 rootCms.unlockResource(currentPath); 454 } 455 } 456 457 /** 458 * Helper method to check whether a project exists.<p> 459 * 460 * @param projectName the project name 461 * 462 * @return true if the project exists 463 */ 464 protected boolean existsProject(String projectName) { 465 466 try { 467 m_adminCms.readProject(projectName); 468 return true; 469 } catch (CmsException e) { 470 return false; 471 } 472 } 473 474 /** 475 * Generates the description for a new workflow project based on the user for whom it is created.<p> 476 * 477 * @param userCms the user's current CMS context 478 * 479 * @return the workflow project description 480 */ 481 protected String generateProjectDescription(CmsObject userCms) { 482 483 CmsUser user = userCms.getRequestContext().getCurrentUser(); 484 OpenCms.getLocaleManager(); 485 Locale locale = CmsLocaleManager.getDefaultLocale(); 486 long time = System.currentTimeMillis(); 487 Date date = new Date(time); 488 DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale); 489 String dateString = format.format(date); 490 String result = Messages.get().getBundle(locale).key( 491 Messages.GUI_WORKFLOW_PROJECT_DESCRIPTION_2, 492 user.getName(), 493 dateString); 494 return result; 495 } 496 497 /** 498 * Generates the name for a new workflow project based on the user for whom it is created.<p> 499 * 500 * @param userCms the user's current CMS context 501 * 502 * @return the workflow project name 503 */ 504 protected String generateProjectName(CmsObject userCms) { 505 506 String projectName = generateProjectName(userCms, true); 507 if (existsProject(projectName)) { 508 projectName = generateProjectName(userCms, false); 509 } 510 return projectName; 511 } 512 513 /** 514 * Generates the name for a new workflow project based on the user for whom it is created.<p> 515 * 516 * @param userCms the user's current CMS context 517 * @param shortTime if true, the short time format will be used, else the medium time format 518 * 519 * @return the workflow project name 520 */ 521 protected String generateProjectName(CmsObject userCms, boolean shortTime) { 522 523 CmsUser user = userCms.getRequestContext().getCurrentUser(); 524 long time = System.currentTimeMillis(); 525 Date date = new Date(time); 526 OpenCms.getLocaleManager(); 527 Locale locale = CmsLocaleManager.getDefaultLocale(); 528 DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale); 529 DateFormat timeFormat = DateFormat.getTimeInstance(shortTime ? DateFormat.SHORT : DateFormat.MEDIUM, locale); 530 String dateStr = dateFormat.format(date) + " " + timeFormat.format(date); 531 String username = user.getName(); 532 String result = Messages.get().getBundle(locale).key(Messages.GUI_WORKFLOW_PROJECT_NAME_2, username, dateStr); 533 result = result.replaceAll("/", "|"); 534 535 return result; 536 } 537 538 /** 539 * Gets the list of recipients for the notifications.<p> 540 * 541 * @return the list of users which should be notified when resources are released 542 */ 543 protected List<CmsUser> getNotificationMailRecipients() { 544 545 String group = getWorkflowProjectManagerGroup(); 546 CmsObject cms = m_adminCms; 547 try { 548 List<CmsUser> users = cms.getUsersOfGroup(group); 549 return users; 550 } catch (CmsException e) { 551 LOG.error(e.getLocalizedMessage(), e); 552 return new ArrayList<CmsUser>(); 553 } 554 } 555 556 /** 557 * Gets the resource notification content path.<p> 558 * 559 * @param cms the cms context 560 * 561 * @return the resource notification content path 562 */ 563 protected String getNotificationResource(CmsObject cms) { 564 565 String result = getParameter( 566 PARAM_NOTIFICATION_CONTENT, 567 OpenCms.getSystemInfo().getConfigFilePath(cms, "notification/workflow-notification")); 568 return result; 569 } 570 571 /** 572 * Helper method for generating the workflow response which should be sent when publishing the resources would break relations.<p> 573 * 574 * @param userCms the user's CMS context 575 * @param publishResources the resources whose links would be broken 576 * 577 * @return the workflow response 578 */ 579 protected CmsWorkflowResponse getPublishBrokenRelationsResponse( 580 CmsObject userCms, 581 List<CmsPublishResource> publishResources) { 582 583 List<CmsWorkflowAction> actions = new ArrayList<CmsWorkflowAction>(); 584 String forcePublishLabel = Messages.get().getBundle(getLocale(userCms)).key( 585 Messages.GUI_WORKFLOW_ACTION_FORCE_PUBLISH_0); 586 587 CmsWorkflowAction forcePublish = new CmsWorkflowAction(ACTION_FORCE_PUBLISH, forcePublishLabel, true, true); 588 actions.add(forcePublish); 589 return new CmsWorkflowResponse( 590 false, 591 Messages.get().getBundle(getLocale(userCms)).key(Messages.GUI_BROKEN_LINKS_0), 592 publishResources, 593 actions, 594 null); 595 } 596 597 /** 598 * Gets the workflow response which should be sent when the resources have successfully been published.<p> 599 * 600 * @return the successful workflow response 601 */ 602 protected CmsWorkflowResponse getSuccessResponse() { 603 604 return new CmsWorkflowResponse( 605 true, 606 "", 607 new ArrayList<CmsPublishResource>(), 608 new ArrayList<CmsWorkflowAction>(), 609 null); 610 } 611 612 /** 613 * Checks whether there are resources which have last been modified in a given project.<p> 614 * 615 * @param project the project which should be checked 616 * @return true if there are no resources which have been last modified inside the project 617 * 618 * @throws CmsException if something goes wrong 619 */ 620 protected boolean isProjectEmpty(CmsProject project) throws CmsException { 621 622 List<CmsResource> resources = m_adminCms.readProjectView(project.getUuid(), CmsResourceState.STATE_KEEP); 623 return resources.isEmpty(); 624 } 625 626 /** 627 * Checks whether the user for a given CMS context can manage workflow projects.<p> 628 * 629 * @param userCms the user CMS Context 630 * @return true if this user can manage workflow projects 631 * 632 * @throws CmsException if something goes wrong 633 */ 634 protected boolean isProjectManager(CmsObject userCms) throws CmsException { 635 636 CmsGroup managerGroup = m_adminCms.readGroup(getWorkflowProjectManagerGroup()); 637 List<CmsGroup> groups = m_adminCms.getGroupsOfUser( 638 userCms.getRequestContext().getCurrentUser().getName(), 639 false); 640 return groups.contains(managerGroup); 641 } 642 643 /** 644 * Handles finished publish jobs by removing projects of resources in the publish job if they are empty workflow projects.<p> 645 * 646 * @param publishJob the finished published job 647 */ 648 protected void onFinishPublishJob(CmsPublishJobRunning publishJob) { 649 650 try { 651 cleanupEmptyWorkflowProjects(null); 652 } catch (CmsException e) { 653 LOG.error(e.getLocalizedMessage(), e); 654 } 655 } 656 657 /** 658 * This is called when a publish job is started.<p> 659 * 660 * @param publishJob the publish job being started 661 */ 662 protected void onStartPublishJob(CmsPublishJobEnqueued publishJob) { 663 664 // do nothing 665 } 666 667 /** 668 * Sends the notification for released resources.<p> 669 * 670 * @param userCms the user's CMS context 671 * @param recipient the OpenCms user to whom the notification should be sent 672 * @param workflowProject the workflow project which 673 * @param resources the resources which have been affected by a workflow action 674 */ 675 protected void sendNotification( 676 CmsObject userCms, 677 CmsUser recipient, 678 CmsProject workflowProject, 679 List<CmsResource> resources) { 680 681 try { 682 String linkHref = OpenCms.getLinkManager().getServerLink( 683 userCms, 684 "/system/workplace/commons/publish.jsp?" 685 + CmsPublishService.PARAM_PUBLISH_PROJECT_ID 686 + "=" 687 + workflowProject.getUuid() 688 + "&" 689 + CmsPublishService.PARAM_CONFIRM 690 + "=true"); 691 CmsWorkflowNotification notification = new CmsWorkflowNotification( 692 m_adminCms, 693 userCms, 694 recipient, 695 getNotificationResource(m_adminCms), 696 workflowProject, 697 resources, 698 linkHref); 699 notification.send(); 700 } catch (Throwable e) { 701 LOG.error(e.getLocalizedMessage(), e); 702 } 703 } 704}