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