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}