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