001/* 002 * (C) Copyright 2007-2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Narcis Paslaru 016 * Florent Guillaume 017 * Thierry Martins 018 * Thomas Roger 019 */ 020 021package org.nuxeo.ecm.platform.publisher.web; 022 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033 034import javax.faces.context.FacesContext; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.jboss.seam.ScopeType; 039import org.jboss.seam.annotations.Create; 040import org.jboss.seam.annotations.Destroy; 041import org.jboss.seam.annotations.Factory; 042import org.jboss.seam.annotations.In; 043import org.jboss.seam.annotations.Name; 044import org.jboss.seam.annotations.Observer; 045import org.jboss.seam.annotations.Scope; 046import org.jboss.seam.annotations.intercept.BypassInterceptors; 047import org.jboss.seam.contexts.Contexts; 048import org.jboss.seam.core.Events; 049import org.jboss.seam.faces.FacesMessages; 050import org.jboss.seam.international.StatusMessage; 051import org.nuxeo.ecm.core.api.CoreSession; 052import org.nuxeo.ecm.core.api.DocumentModel; 053import org.nuxeo.ecm.core.api.DocumentRef; 054import org.nuxeo.ecm.core.api.IdRef; 055import org.nuxeo.ecm.core.api.NuxeoException; 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.core.api.event.DocumentEventCategories; 060import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 061import org.nuxeo.ecm.core.api.security.SecurityConstants; 062import org.nuxeo.ecm.core.event.Event; 063import org.nuxeo.ecm.core.event.EventProducer; 064import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 065import org.nuxeo.ecm.core.schema.FacetNames; 066import org.nuxeo.ecm.core.schema.SchemaManager; 067import org.nuxeo.ecm.platform.publisher.api.PublicationNode; 068import org.nuxeo.ecm.platform.publisher.api.PublicationTree; 069import org.nuxeo.ecm.platform.publisher.api.PublicationTreeNotAvailable; 070import org.nuxeo.ecm.platform.publisher.api.PublishedDocument; 071import org.nuxeo.ecm.platform.publisher.api.PublisherService; 072import org.nuxeo.ecm.platform.publisher.api.PublishingEvent; 073import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 074import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 075import org.nuxeo.ecm.webapp.helpers.EventManager; 076import org.nuxeo.ecm.webapp.helpers.EventNames; 077import org.nuxeo.runtime.api.Framework; 078 079/** 080 * This Seam bean manages the publishing tab. 081 * 082 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 083 */ 084@Name("publishActions") 085@Scope(ScopeType.CONVERSATION) 086public class PublishActionsBean extends AbstractPublishActions implements Serializable { 087 088 public static class PublicationTreeInformation { 089 090 private final String name; 091 092 private final String title; 093 094 public PublicationTreeInformation(String treeName, String treeTitle) { 095 this.name = treeName; 096 this.title = treeTitle; 097 } 098 099 public String getName() { 100 return name; 101 } 102 103 public String getTitle() { 104 return title; 105 } 106 } 107 108 private static final long serialVersionUID = 1L; 109 110 private static final Log log = LogFactory.getLog(PublishActionsBean.class); 111 112 /** 113 * @since 7.3 114 */ 115 public static final List<String> TREE_TYPES_TO_FILTER = Arrays.asList("RootSectionsPublicationTree", "RenditionPublicationCoreTree"); 116 117 @In(create = true) 118 protected transient DocumentsListsManager documentsListsManager; 119 120 @In(create = true, required = false) 121 protected transient FacesMessages facesMessages; 122 123 protected transient PublisherService publisherService; 124 125 protected String currentPublicationTreeNameForPublishing; 126 127 protected PublicationTree currentPublicationTree; 128 129 protected String publishingComment; 130 131 protected static Set<String> sectionTypes; 132 133 protected Map<String, String> publicationParameters = new HashMap<>(); 134 135 protected String treeSID; 136 137 @Create 138 public void create() { 139 publisherService = Framework.getService(PublisherService.class); 140 } 141 142 @Destroy 143 public void destroy() { 144 if (currentPublicationTree != null) { 145 currentPublicationTree.release(); 146 currentPublicationTree = null; 147 } 148 if (treeSID != null) { 149 publisherService.releaseAllTrees(treeSID); 150 } 151 } 152 153 protected Map<String, String> filterEmptyTrees(Map<String, String> trees) throws PublicationTreeNotAvailable { 154 155 Map<String, String> filteredTrees = new HashMap<>(); 156 157 List<String> prefilteredTrees = filterEmptyTrees(trees.keySet()); 158 159 for (String ptree : prefilteredTrees) { 160 filteredTrees.put(ptree, trees.get(ptree)); 161 } 162 163 return filteredTrees; 164 } 165 166 protected List<String> filterEmptyTrees(Collection<String> trees) throws PublicationTreeNotAvailable { 167 List<String> filteredTrees = new ArrayList<>(); 168 169 for (String tree : trees) { 170 try { 171 PublicationTree pTree = publisherService.getPublicationTree(tree, documentManager, null, 172 navigationContext.getCurrentDocument()); 173 if (pTree != null) { 174 if (TREE_TYPES_TO_FILTER.contains(pTree.getTreeType())) { 175 if (pTree.getChildrenNodes().size() > 0) { 176 filteredTrees.add(tree); 177 } 178 } else { 179 filteredTrees.add(tree); 180 } 181 } 182 } catch (PublicationTreeNotAvailable e) { 183 log.warn("Publication tree " + tree + " is not available : check config"); 184 log.debug("Publication tree " + tree + " is not available : root cause is ", e); 185 } 186 } 187 return filteredTrees; 188 } 189 190 @Factory(value = "availablePublicationTrees", scope = ScopeType.EVENT) 191 public List<PublicationTreeInformation> getAvailablePublicationTrees() { 192 Map<String, String> trees = publisherService.getAvailablePublicationTrees(); 193 // remove empty trees 194 trees = filterEmptyTrees(trees); 195 List<PublicationTreeInformation> treesInformation = new ArrayList<>(); 196 for (Map.Entry<String, String> entry : trees.entrySet()) { 197 treesInformation.add(new PublicationTreeInformation(entry.getKey(), entry.getValue())); 198 } 199 return treesInformation; 200 } 201 202 public String doPublish(PublicationNode publicationNode) { 203 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 204 return doPublish(tree, publicationNode); 205 } 206 207 public String doPublish(PublicationTree tree, PublicationNode publicationNode) { 208 if (tree == null) { 209 return null; 210 } 211 212 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 213 214 PublishedDocument publishedDocument; 215 try { 216 publishedDocument = tree.publish(currentDocument, publicationNode, publicationParameters); 217 } catch (NuxeoException e) { 218 log.error(e, e); 219 facesMessages.add(StatusMessage.Severity.ERROR, messages.get(e.getMessage())); 220 return null; 221 } 222 223 FacesContext context = FacesContext.getCurrentInstance(); 224 if (publishedDocument.isPending()) { 225 String comment = ComponentUtils.translate(context, "publishing.waiting", publicationNode.getPath(), 226 tree.getConfigName()); 227 // Log event on live version 228 notifyEvent(PublishingEvent.documentWaitingPublication.name(), null, comment, null, currentDocument); 229 Events.instance().raiseEvent(EventNames.DOCUMENT_SUBMITED_FOR_PUBLICATION); 230 facesMessages.add(StatusMessage.Severity.INFO, messages.get("document_submitted_for_publication"), 231 messages.get(currentDocument.getType())); 232 } else { 233 String comment = ComponentUtils.translate(context, "publishing.done", publicationNode.getPath(), 234 tree.getConfigName()); 235 // Log event on live version 236 notifyEvent(PublishingEvent.documentPublished.name(), null, comment, null, currentDocument); 237 Events.instance().raiseEvent(EventNames.DOCUMENT_PUBLISHED); 238 // publish may checkin the document -> change 239 Events.instance().raiseEvent(EventNames.DOCUMENT_CHANGED); 240 facesMessages.add(StatusMessage.Severity.INFO, messages.get("document_published"), 241 messages.get(currentDocument.getType())); 242 } 243 navigationContext.invalidateCurrentDocument(); 244 currentPublicationTree = null; 245 resetCache(); 246 return null; 247 } 248 249 /** 250 * @since 5.9.3 251 */ 252 protected void resetCache() { 253 Contexts.getEventContext().remove("availablePublicationTrees"); 254 Contexts.getEventContext().remove("publishedDocuments"); 255 } 256 257 public void setCurrentPublicationTreeNameForPublishing(String currentPublicationTreeNameForPublishing) 258 { 259 this.currentPublicationTreeNameForPublishing = currentPublicationTreeNameForPublishing; 260 if (currentPublicationTree != null) { 261 currentPublicationTree.release(); 262 currentPublicationTree = null; 263 } 264 currentPublicationTree = getCurrentPublicationTreeForPublishing(); 265 } 266 267 public String getCurrentPublicationTreeNameForPublishing() { 268 if (currentPublicationTreeNameForPublishing == null) { 269 List<String> publicationTrees = new ArrayList<>(publisherService.getAvailablePublicationTree()); 270 publicationTrees = filterEmptyTrees(publicationTrees); 271 if (!publicationTrees.isEmpty()) { 272 currentPublicationTreeNameForPublishing = publicationTrees.get(0); 273 } 274 } 275 return currentPublicationTreeNameForPublishing; 276 } 277 278 /** 279 * Returns a list of publication trees. 280 * <p> 281 * Needed on top of {@link #getCurrentPublicationTreeForPublishing()} because RichFaces tree now requires roots to 282 * be a list. 283 * 284 * @since 6.0 285 */ 286 public List<PublicationTree> getCurrentPublicationTreesForPublishing() { 287 List<PublicationTree> trees = new ArrayList<>(); 288 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 289 if (tree != null) { 290 trees.add(tree); 291 } 292 return trees; 293 } 294 295 public PublicationTree getCurrentPublicationTreeForPublishing() { 296 if (currentPublicationTree == null) { 297 if (getCurrentPublicationTreeNameForPublishing() == null) { 298 return currentPublicationTree; 299 } 300 try { 301 treeSID = documentManager.getSessionId(); 302 currentPublicationTree = publisherService.getPublicationTree(currentPublicationTreeNameForPublishing, 303 documentManager, null, navigationContext.getCurrentDocument()); 304 } catch (PublicationTreeNotAvailable e) { 305 currentPublicationTree = null; 306 } 307 } 308 return currentPublicationTree; 309 } 310 311 public String getCurrentPublicationTreeIconExpanded() { 312 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 313 return tree != null ? tree.getIconExpanded() : ""; 314 } 315 316 public String getCurrentPublicationTreeIconCollapsed() { 317 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 318 return tree != null ? tree.getIconCollapsed() : ""; 319 } 320 321 @Factory(value = "publishedDocuments", scope = ScopeType.EVENT) 322 public List<PublishedDocument> getPublishedDocuments() { 323 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 324 if (tree == null) { 325 return Collections.emptyList(); 326 } 327 328 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 329 return tree.getExistingPublishedDocument(new DocumentLocationImpl(currentDocument)); 330 } 331 332 public List<PublishedDocument> getPublishedDocumentsFor(String treeName) { 333 if (treeName == null || "".equals(treeName)) { 334 return null; 335 } 336 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 337 try { 338 PublicationTree tree = publisherService.getPublicationTree(treeName, documentManager, null); 339 return tree.getExistingPublishedDocument(new DocumentLocationImpl(currentDocument)); 340 } catch (PublicationTreeNotAvailable e) { 341 return null; 342 } 343 } 344 345 public String unPublish(PublishedDocument publishedDocument) { 346 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 347 if (tree != null) { 348 tree.unpublish(publishedDocument); 349 } 350 // raise event without the container document as user may not have read 351 // rights on it 352 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 353 resetCache(); 354 return null; 355 } 356 357 public String rePublish(PublishedDocument publishedDocument) { 358 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 359 if (tree == null) { 360 log.error("Publication tree is null - cannot republish"); 361 facesMessages.add(StatusMessage.Severity.ERROR, messages.get("error.document_republished")); 362 return null; 363 } 364 PublicationNode node = tree.getNodeByPath(publishedDocument.getParentPath()); 365 return doPublish(tree, node); 366 } 367 368 public boolean canPublishTo(PublicationNode publicationNode) { 369 DocumentModel doc = navigationContext.getCurrentDocument(); 370 if (doc == null || documentManager.getLockInfo(doc.getRef()) != null) { 371 return false; 372 } 373 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 374 return tree != null ? tree.canPublishTo(publicationNode) : false; 375 } 376 377 public boolean canUnpublish(PublishedDocument publishedDocument) { 378 PublicationTree tree = getCurrentPublicationTreeForPublishing(); 379 return tree != null ? tree.canUnpublish(publishedDocument) : false; 380 } 381 382 public boolean canRepublish(PublishedDocument publishedDocument) { 383 if (!canUnpublish(publishedDocument)) { 384 return false; 385 } 386 DocumentModel doc = navigationContext.getCurrentDocument(); 387 // version label is different, what means it is a previous version 388 if (!publishedDocument.getSourceVersionLabel().equals(doc.getVersionLabel())) { 389 return true; 390 } 391 // in case it is the same version, we have to check if the current 392 // document has been modified since last publishing 393 if (doc.isDirty()) { 394 return true; 395 } 396 return false; 397 } 398 399 public boolean isPublishedDocument() { 400 return publisherService.isPublishedDocument(navigationContext.getCurrentDocument()); 401 } 402 403 public boolean canManagePublishing() { 404 PublicationTree tree = publisherService.getPublicationTreeFor(navigationContext.getCurrentDocument(), 405 documentManager); 406 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(navigationContext.getCurrentDocument()); 407 return tree.canManagePublishing(publishedDocument); 408 } 409 410 public boolean hasValidationTask() { 411 PublicationTree tree = publisherService.getPublicationTreeFor(navigationContext.getCurrentDocument(), 412 documentManager); 413 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(navigationContext.getCurrentDocument()); 414 return tree.hasValidationTask(publishedDocument); 415 } 416 417 public boolean isPending() { 418 PublicationTree tree = publisherService.getPublicationTreeFor(navigationContext.getCurrentDocument(), 419 documentManager); 420 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(navigationContext.getCurrentDocument()); 421 return publishedDocument.isPending(); 422 } 423 424 public String getPublishingComment() { 425 return publishingComment; 426 } 427 428 public void setPublishingComment(String publishingComment) { 429 this.publishingComment = publishingComment; 430 } 431 432 public class ApproverWithoutRestriction extends UnrestrictedSessionRunner { 433 434 public DocumentModel sourceDocument; 435 436 public DocumentModel liveDocument; 437 438 public String comment; 439 440 public PublishedDocument doc; 441 442 public ApproverWithoutRestriction(PublishedDocument doc, String comment, CoreSession session) { 443 super(session); 444 this.doc = doc; 445 this.comment = comment; 446 } 447 448 @Override 449 public void run() { 450 sourceDocument = session.getDocument(doc.getSourceDocumentRef()); 451 452 // soft dependency on Rendition system 453 if (sourceDocument.hasFacet("Rendition")) { 454 String uid = (String) sourceDocument.getPropertyValue("rend:sourceId"); 455 liveDocument = session.getDocument(new IdRef(uid)); 456 } else { 457 liveDocument = session.getSourceDocument(sourceDocument.getRef()); 458 } 459 sendApprovalEventToSourceDocument(session, sourceDocument, liveDocument, comment); 460 461 } 462 463 protected void sendApprovalEventToSourceDocument(CoreSession session, DocumentModel sourceDocument, 464 DocumentModel liveVersion, String comment) { 465 466 notifyEvent(session, PublishingEvent.documentPublicationApproved.name(), null, comment, null, 467 sourceDocument); 468 469 if (!sourceDocument.getRef().equals(liveVersion.getRef())) { 470 notifyEvent(session, PublishingEvent.documentPublicationApproved.name(), null, comment, null, 471 liveVersion); 472 } 473 } 474 475 } 476 477 public String approveDocument() { 478 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 479 PublicationTree tree = publisherService.getPublicationTreeFor(currentDocument, documentManager); 480 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(currentDocument); 481 tree.validatorPublishDocument(publishedDocument, publishingComment); 482 483 FacesContext context = FacesContext.getCurrentInstance(); 484 String comment = publishingComment != null && publishingComment.length() > 0 ? ComponentUtils.translate( 485 context, "publishing.approved.with.comment", publishedDocument.getParentPath(), tree.getConfigName(), 486 publishingComment) : ComponentUtils.translate(context, "publishing.approved.without.comment", 487 publishedDocument.getParentPath(), tree.getConfigName()); 488 489 ApproverWithoutRestriction approver = new ApproverWithoutRestriction(publishedDocument, comment, 490 documentManager); 491 if (documentManager.hasPermission(publishedDocument.getSourceDocumentRef(), SecurityConstants.WRITE)) { 492 approver.run(); 493 } else { 494 approver.runUnrestricted(); 495 } 496 497 Events.instance().raiseEvent(EventNames.DOCUMENT_PUBLISHED); 498 Events.instance().raiseEvent(EventNames.DOCUMENT_PUBLICATION_APPROVED); 499 return null; 500 } 501 502 public String rejectDocument() { 503 if (publishingComment == null || "".equals(publishingComment)) { 504 facesMessages.addToControl("publishingComment", StatusMessage.Severity.ERROR, 505 messages.get("label.publishing.reject.user.comment.mandatory")); 506 return null; 507 } 508 509 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 510 PublicationTree tree = publisherService.getPublicationTreeFor(currentDocument, documentManager); 511 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(currentDocument); 512 tree.validatorRejectPublication(publishedDocument, publishingComment); 513 514 FacesContext context = FacesContext.getCurrentInstance(); 515 String comment = publishingComment != null && publishingComment.length() > 0 ? ComponentUtils.translate( 516 context, "publishing.rejected.with.comment", publishedDocument.getParentPath(), tree.getConfigName(), 517 publishingComment) : ComponentUtils.translate(context, "publishing.rejected.without.comment", 518 publishedDocument.getParentPath(), tree.getConfigName()); 519 RejectWithoutRestrictionRunner runner = new RejectWithoutRestrictionRunner(documentManager, publishedDocument, 520 comment); 521 522 if (documentManager.hasPermission(publishedDocument.getSourceDocumentRef(), SecurityConstants.READ)) { 523 runner.run(); 524 } else { 525 runner.runUnrestricted(); 526 } 527 Events.instance().raiseEvent(EventNames.DOCUMENT_PUBLICATION_REJECTED); 528 529 return navigationContext.navigateToRef(navigationContext.getCurrentDocument().getParentRef()); 530 } 531 532 public void unpublishDocumentsFromCurrentSelection() { 533 if (!documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION)) { 534 unpublish(documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION)); 535 } else { 536 log.debug("No selectable Documents in context to process unpublish on..."); 537 } 538 log.debug("Unpublish the selected document(s) ..."); 539 } 540 541 protected void unpublish(List<DocumentModel> documentModels) { 542 for (DocumentModel documentModel : documentModels) { 543 PublicationTree tree = publisherService.getPublicationTreeFor(documentModel, documentManager); 544 PublishedDocument publishedDocument = tree.wrapToPublishedDocument(documentModel); 545 tree.unpublish(publishedDocument); 546 } 547 548 Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED); 549 550 Object[] params = { documentModels.size() }; 551 // remove from the current selection list 552 documentsListsManager.resetWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION); 553 facesMessages.add(StatusMessage.Severity.INFO, messages.get("n_unpublished_docs"), params); 554 } 555 556 public boolean isRemotePublishedDocument(PublishedDocument publishedDocument) { 557 if (publishedDocument == null) { 558 return false; 559 } 560 return publishedDocument.getType().equals(PublishedDocument.Type.REMOTE); 561 } 562 563 public boolean isFileSystemPublishedDocument(PublishedDocument publishedDocument) { 564 if (publishedDocument == null) { 565 return false; 566 } 567 return publishedDocument.getType().equals(PublishedDocument.Type.FILE_SYSTEM); 568 } 569 570 public boolean isLocalPublishedDocument(PublishedDocument publishedDocument) { 571 if (publishedDocument == null) { 572 return false; 573 } 574 return publishedDocument.getType().equals(PublishedDocument.Type.LOCAL); 575 } 576 577 public String publishWorkList() { 578 return publishDocumentList(DocumentsListsManager.DEFAULT_WORKING_LIST); 579 } 580 581 public DocumentModel getDocumentModelFor(String path) { 582 DocumentRef docRef = new PathRef(path); 583 if (documentManager.exists(docRef) && hasReadRight(path)) { 584 return documentManager.getDocument(docRef); 585 } 586 return null; 587 } 588 589 public boolean hasReadRight(String documentPath) { 590 return documentManager.hasPermission(new PathRef(documentPath), SecurityConstants.READ); 591 } 592 593 public String getFormattedPath(String path) { 594 DocumentModel docModel = getDocumentModelFor(path); 595 return docModel != null ? getFormattedPath(docModel) : path; 596 } 597 598 public String publishDocumentList(String listName) { 599 List<DocumentModel> docs2Publish = documentsListsManager.getWorkingList(listName); 600 DocumentModel target = navigationContext.getCurrentDocument(); 601 602 if (!getSectionTypes().contains(target.getType())) { 603 return null; 604 } 605 606 PublicationNode targetNode = publisherService.wrapToPublicationNode(target, documentManager); 607 if (targetNode == null) { 608 return null; 609 } 610 611 int nbPublishedDocs = 0; 612 for (DocumentModel doc : docs2Publish) { 613 if (!documentManager.hasPermission(doc.getRef(), SecurityConstants.READ_PROPERTIES)) { 614 continue; 615 } 616 617 if (doc.isProxy()) { 618 // TODO copy also copies security. just recreate a proxy. 619 documentManager.copy(doc.getRef(), target.getRef(), doc.getName()); 620 nbPublishedDocs++; 621 } else { 622 if (doc.hasFacet(FacetNames.PUBLISHABLE)) { 623 publisherService.publish(doc, targetNode); 624 nbPublishedDocs++; 625 } else { 626 log.info("Attempted to publish non-publishable document " + doc.getTitle()); 627 } 628 } 629 } 630 631 Object[] params = { nbPublishedDocs }; 632 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get("n_published_docs"), params); 633 634 if (nbPublishedDocs < docs2Publish.size()) { 635 facesMessages.add(StatusMessage.Severity.WARN, messages.get("selection_contains_non_publishable_docs")); 636 } 637 638 EventManager.raiseEventsOnDocumentChildrenChange(target); 639 return null; 640 } 641 642 public Set<String> getSectionTypes() { 643 if (sectionTypes == null) { 644 sectionTypes = getTypeNamesForFacet(FacetNames.PUBLISH_SPACE); 645 if (sectionTypes == null) { 646 sectionTypes = new HashSet<>(); 647 } 648 } 649 return sectionTypes; 650 } 651 652 protected static Set<String> getTypeNamesForFacet(String facetName) { 653 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 654 Set<String> publishRoots = schemaManager.getDocumentTypeNamesForFacet(facetName); 655 if (publishRoots == null || publishRoots.isEmpty()) { 656 return null; 657 } 658 return publishRoots; 659 } 660 661 public Map<String, String> getPublicationParameters() { 662 return publicationParameters; 663 } 664 665 public void notifyEvent(String eventId, Map<String, Serializable> properties, String comment, String category, 666 DocumentModel dm) { 667 notifyEvent(documentManager, eventId, properties, comment, category, dm); 668 } 669 670 public static void notifyEvent(CoreSession session, String eventId, Map<String, Serializable> properties, 671 String comment, String category, DocumentModel dm) { 672 673 // Default category 674 if (category == null) { 675 category = DocumentEventCategories.EVENT_DOCUMENT_CATEGORY; 676 } 677 678 if (properties == null) { 679 properties = new HashMap<>(); 680 } 681 682 properties.put(CoreEventConstants.REPOSITORY_NAME, session.getRepositoryName()); 683 properties.put(CoreEventConstants.SESSION_ID, session.getSessionId()); 684 properties.put(CoreEventConstants.DOC_LIFE_CYCLE, dm.getCurrentLifeCycleState()); 685 686 DocumentEventContext ctx = new DocumentEventContext(session, session.getPrincipal(), dm); 687 688 ctx.setProperties(properties); 689 ctx.setComment(comment); 690 ctx.setCategory(category); 691 692 EventProducer evtProducer = Framework.getService(EventProducer.class); 693 Event event = ctx.newEvent(eventId); 694 evtProducer.fireEvent(event); 695 } 696 697 public String getDomainName(String treeName) { 698 try { 699 PublicationTree tree = publisherService.getPublicationTree(treeName, documentManager, null); 700 Map<String, String> parameters = publisherService.getParametersFor(tree.getConfigName()); 701 String domainName = parameters.get(PublisherService.DOMAIN_NAME_KEY); 702 return domainName != null ? " (" + domainName + ")" : ""; 703 } catch (PublicationTreeNotAvailable e) { 704 return ""; 705 } 706 } 707 708 @Observer(value = { EventNames.DOCUMENT_SELECTION_CHANGED }, create = false) 709 @BypassInterceptors 710 public void documentChanged() { 711 currentPublicationTreeNameForPublishing = null; 712 currentPublicationTree = null; 713 publishingComment = null; 714 } 715 716 class RejectWithoutRestrictionRunner extends UnrestrictedSessionRunner { 717 718 PublishedDocument publishedDocument; 719 720 DocumentModel sourceDocument; 721 722 DocumentModel liveDocument; 723 724 String comment; 725 726 DocumentModel liveVersion; 727 728 public RejectWithoutRestrictionRunner(CoreSession session, PublishedDocument publishedDocument, String comment) { 729 super(session); 730 this.publishedDocument = publishedDocument; 731 this.comment = comment; 732 } 733 734 @Override 735 public void run() { 736 sourceDocument = session.getDocument(publishedDocument.getSourceDocumentRef()); 737 String sourceId = sourceDocument.getSourceId(); 738 // source may be null if the version is placeless (rendition) 739 liveVersion = sourceId == null ? null : session.getDocument(new IdRef(sourceId)); 740 notifyRejectToSourceDocument(); 741 } 742 743 private void notifyRejectToSourceDocument() { 744 notifyEvent(PublishingEvent.documentPublicationRejected.name(), null, comment, null, sourceDocument); 745 if (liveVersion != null && !sourceDocument.getRef().equals(liveVersion.getRef())) { 746 notifyEvent(PublishingEvent.documentPublicationRejected.name(), null, comment, null, liveVersion); 747 } 748 } 749 } 750}