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