001/*
002 * (C) Copyright 2009 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 *     Thierry Delprat
018 */
019package org.nuxeo.ecm.platform.publisher.impl.finder;
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.CoreSession;
030import org.nuxeo.ecm.core.api.DocumentModel;
031import org.nuxeo.ecm.core.api.DocumentModelList;
032import org.nuxeo.ecm.core.api.DocumentNotFoundException;
033import org.nuxeo.ecm.core.api.DocumentRef;
034import org.nuxeo.ecm.core.api.Filter;
035import org.nuxeo.ecm.core.api.IdRef;
036import org.nuxeo.ecm.core.api.PathRef;
037import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
038import org.nuxeo.ecm.core.api.impl.CompoundFilter;
039import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
040import org.nuxeo.ecm.core.api.impl.FacetFilter;
041import org.nuxeo.ecm.core.api.security.SecurityConstants;
042import org.nuxeo.ecm.core.schema.FacetNames;
043import org.nuxeo.ecm.core.schema.SchemaManager;
044import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinder;
045import org.nuxeo.runtime.api.Framework;
046
047public abstract class AbstractRootSectionsFinder extends UnrestrictedSessionRunner implements RootSectionFinder {
048
049    public static final String SCHEMA_PUBLISHING = "publishing";
050
051    public static final String SECTIONS_PROPERTY_NAME = "publish:sections";
052
053    protected static Set<String> sectionRootTypes;
054
055    protected static Set<String> sectionTypes;
056
057    protected CoreSession userSession;
058
059    protected List<String> unrestrictedSectionRootFromWorkspaceConfig;
060
061    protected List<String> unrestrictedDefaultSectionRoot;
062
063    protected DocumentModelList accessibleSectionRoots;
064
065    protected DocumentModel currentDocument;
066
067    protected static final Log log = LogFactory.getLog(AbstractRootSectionsFinder.class);
068
069    protected abstract void computeUserSectionRoots(DocumentModel currentDoc);
070
071    protected abstract String buildQuery(String path);
072
073    protected abstract void computeUnrestrictedRoots(CoreSession session);
074
075    public AbstractRootSectionsFinder(CoreSession userSession) {
076        super(userSession);
077        this.userSession = userSession;
078    }
079
080    @Override
081    public void reset() {
082        this.currentDocument = null;
083    }
084
085    @Override
086    public DocumentModelList getAccessibleSectionRoots(DocumentModel currentDoc) {
087        if ((currentDocument == null) || (!currentDocument.getRef().equals(currentDoc.getRef()))) {
088            computeUserSectionRoots(currentDoc);
089        }
090        return accessibleSectionRoots;
091    }
092
093    @Override
094    public DocumentModelList getSectionRootsForWorkspace(DocumentModel currentDoc, boolean addDefaultSectionRoots)
095            {
096        if ((currentDocument == null) || (!currentDocument.getRef().equals(currentDoc.getRef()))) {
097            computeUserSectionRoots(currentDoc);
098        }
099
100        if (unrestrictedDefaultSectionRoot.isEmpty() && addDefaultSectionRoots) {
101            DocumentModelList defaultSectionRoots = getDefaultSectionRoots(session);
102            unrestrictedDefaultSectionRoot = new ArrayList<>();
103            for (DocumentModel root : defaultSectionRoots) {
104                unrestrictedDefaultSectionRoot.add(root.getPathAsString());
105            }
106        }
107
108        return getFiltredSectionRoots(unrestrictedSectionRootFromWorkspaceConfig, true);
109    }
110
111    @Override
112    public DocumentModelList getSectionRootsForWorkspace(DocumentModel currentDoc) {
113        return getSectionRootsForWorkspace(currentDoc, false);
114    }
115
116    @Override
117    public DocumentModelList getDefaultSectionRoots(boolean onlyHeads, boolean addDefaultSectionRoots)
118            {
119        if (unrestrictedDefaultSectionRoot == null) {
120            computeUserSectionRoots(null);
121        }
122
123        if (unrestrictedDefaultSectionRoot.isEmpty() && addDefaultSectionRoots) {
124            DocumentModelList defaultSectionRoots = getDefaultSectionRoots(session);
125            unrestrictedDefaultSectionRoot = new ArrayList<>();
126            for (DocumentModel root : defaultSectionRoots) {
127                unrestrictedDefaultSectionRoot.add(root.getPathAsString());
128            }
129        }
130
131        return getFiltredSectionRoots(unrestrictedDefaultSectionRoot, onlyHeads);
132    }
133
134    @Override
135    public DocumentModelList getDefaultSectionRoots(boolean onlyHeads) {
136        return getDefaultSectionRoots(onlyHeads, false);
137    }
138
139    protected DocumentModelList getFiltredSectionRoots(List<String> rootPaths, boolean onlyHeads)
140            {
141        List<DocumentRef> filtredDocRef = new ArrayList<>();
142        List<DocumentRef> trashedDocRef = new ArrayList<>();
143
144        for (String rootPath : rootPaths) {
145            DocumentRef rootRef = new PathRef(rootPath);
146            if (userSession.hasPermission(rootRef, SecurityConstants.READ)) {
147                filtredDocRef.add(rootRef);
148            } else {
149                DocumentModelList accessibleSections = userSession.query(buildQuery(rootPath));
150                for (DocumentModel section : accessibleSections) {
151                    if (onlyHeads
152                            && ((filtredDocRef.contains(section.getParentRef())) || (trashedDocRef.contains(section.getParentRef())))) {
153                        trashedDocRef.add(section.getRef());
154                    } else {
155                        filtredDocRef.add(section.getRef());
156                    }
157                }
158            }
159        }
160        DocumentModelList documents = userSession.getDocuments(filtredDocRef.toArray(new DocumentRef[filtredDocRef.size()]));
161        return filterDocuments(documents);
162    }
163
164    protected DocumentModelList filterDocuments(DocumentModelList docs) {
165        DocumentModelList filteredDocuments = new DocumentModelListImpl();
166        FacetFilter facetFilter = new FacetFilter(Arrays.asList(FacetNames.FOLDERISH),
167                Arrays.asList(FacetNames.HIDDEN_IN_NAVIGATION));
168        Filter trashedFilter = docModel -> !docModel.isTrashed();
169        Filter filter = new CompoundFilter(facetFilter, trashedFilter);
170        for (DocumentModel doc : docs) {
171            if (filter.accept(doc)) {
172                filteredDocuments.add(doc);
173            }
174        }
175        return filteredDocuments;
176    }
177
178    protected DocumentModelList getDefaultSectionRoots(CoreSession session) {
179        // XXX replace by a query !!!
180        DocumentModelList sectionRoots = new DocumentModelListImpl();
181        DocumentModelList domains = session.getChildren(session.getRootDocument().getRef(), "Domain");
182        for (DocumentModel domain : domains) {
183            for (String sectionRootNameType : getSectionRootTypes()) {
184                DocumentModelList children = session.getChildren(domain.getRef(), sectionRootNameType);
185                sectionRoots.addAll(children);
186            }
187        }
188        return sectionRoots;
189    }
190
191    protected DocumentModelList getSectionRootsFromWorkspaceConfig(DocumentModel workspace, CoreSession session)
192            {
193
194        DocumentModelList selectedSections = new DocumentModelListImpl();
195
196        if (workspace.hasSchema(SCHEMA_PUBLISHING)) {
197            String[] sectionIdsArray = (String[]) workspace.getPropertyValue(SECTIONS_PROPERTY_NAME);
198
199            List<String> sectionIdsList = new ArrayList<>();
200
201            if (sectionIdsArray != null && sectionIdsArray.length > 0) {
202                sectionIdsList = Arrays.asList(sectionIdsArray);
203            }
204
205            if (sectionIdsList != null) {
206                for (String currentSectionId : sectionIdsList) {
207                    try {
208                        DocumentModel sectionToAdd = session.getDocument(new IdRef(currentSectionId));
209                        selectedSections.add(sectionToAdd);
210                    } catch (DocumentNotFoundException e) {
211                        log.warn("Section with ID=" + currentSectionId + " not found for document with ID="
212                                + workspace.getId());
213                    }
214                }
215            }
216        }
217        return selectedSections;
218    }
219
220    @Override
221    public void run() {
222        computeUnrestrictedRoots(session);
223    }
224
225    protected Set<String> getSectionRootTypes() {
226        if (sectionRootTypes == null) {
227            sectionRootTypes = getTypeNamesForFacet(FacetNames.MASTER_PUBLISH_SPACE);
228            if (sectionRootTypes == null) {
229                sectionRootTypes = new HashSet<>();
230            }
231        }
232        return sectionRootTypes;
233    }
234
235    protected Set<String> getTypeNamesForFacet(String facetName) {
236        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
237        Set<String> publishRoots = schemaManager.getDocumentTypeNamesForFacet(facetName);
238        if (publishRoots == null || publishRoots.isEmpty()) {
239            return null;
240        }
241        return publishRoots;
242    }
243
244    protected Set<String> getSectionTypes() {
245        if (sectionTypes == null) {
246            sectionTypes = getTypeNamesForFacet(FacetNames.MASTER_PUBLISH_SPACE);
247            if (sectionTypes == null) {
248                sectionTypes = new HashSet<>();
249            }
250        }
251        return sectionTypes;
252    }
253
254}