001/*
002 * (C) Copyright 2010 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 *     Anahide Tchertchian
018 */
019package org.nuxeo.ecm.webapp.contentbrowser;
020
021import static org.jboss.seam.ScopeType.CONVERSATION;
022import static org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfigurationConstants.CONTENT_VIEW_CONFIGURATION_CATEGORY;
023import static org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfigurationConstants.CONTENT_VIEW_CONFIGURATION_FACET;
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.jboss.seam.annotations.In;
036import org.jboss.seam.annotations.Name;
037import org.jboss.seam.annotations.Observer;
038import org.jboss.seam.annotations.Scope;
039import org.jboss.seam.annotations.intercept.BypassInterceptors;
040import org.nuxeo.ecm.core.api.DocumentModel;
041import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService;
042import org.nuxeo.ecm.platform.actions.Action;
043import org.nuxeo.ecm.platform.actions.ActionContext;
044import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
045import org.nuxeo.ecm.platform.contentview.jsf.ContentViewHeader;
046import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
047import org.nuxeo.ecm.platform.types.adapter.TypeInfo;
048import org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfiguration;
049import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
050import org.nuxeo.ecm.platform.ui.web.api.WebActions;
051import org.nuxeo.ecm.webapp.action.ActionContextProvider;
052import org.nuxeo.ecm.webapp.helpers.EventNames;
053import org.nuxeo.runtime.api.Framework;
054
055/**
056 * Handles available content views defined on a document type per category, as well as helper methods to retrieve
057 * selection actions for a given content view.
058 *
059 * @author Anahide Tchertchian
060 * @since 5.4
061 */
062@Name("documentContentViewActions")
063@Scope(CONVERSATION)
064public class DocumentContentViewActions implements Serializable {
065
066    private static final long serialVersionUID = 1L;
067
068    private static final Log log = LogFactory.getLog(DocumentContentViewActions.class);
069
070    @In(create = true, required = false)
071    protected transient NavigationContext navigationContext;
072
073    @In(create = true, required = false)
074    protected transient WebActions webActions;
075
076    @In(create = true, required = false)
077    protected transient ActionContextProvider actionContextProvider;
078
079    @In(create = true)
080    protected ContentViewService contentViewService;
081
082    /**
083     * Map caching content views defined on a given document type
084     */
085    protected Map<String, Map<String, List<ContentViewHeader>>> typeToContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>();
086
087    protected Map<String, List<ContentViewHeader>> currentAvailableContentViews;
088
089    /**
090     * Map caching content views shown in export defined on a given document type
091     */
092    protected Map<String, Map<String, List<ContentViewHeader>>> typeToExportContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>();
093
094    protected Map<String, List<ContentViewHeader>> currentExportContentViews;
095
096    // API for content view support
097
098    protected Map<String, List<ContentViewHeader>> getContentViewHeaders(TypeInfo typeInfo, boolean export)
099            {
100        Map<String, List<ContentViewHeader>> res = new LinkedHashMap<String, List<ContentViewHeader>>();
101        Map<String, String[]> cvNamesByCat;
102        if (export) {
103            cvNamesByCat = typeInfo.getContentViewsForExport();
104        } else {
105            cvNamesByCat = typeInfo.getContentViews();
106        }
107        if (cvNamesByCat != null) {
108            for (Map.Entry<String, String[]> cvNameByCat : cvNamesByCat.entrySet()) {
109                List<ContentViewHeader> headers = new ArrayList<ContentViewHeader>();
110                String[] cvNames = cvNameByCat.getValue();
111                if (cvNames != null) {
112                    for (String cvName : cvNames) {
113                        ContentViewHeader header = contentViewService.getContentViewHeader(cvName);
114                        if (header != null) {
115                            headers.add(header);
116                        }
117                    }
118                }
119                res.put(cvNameByCat.getKey(), headers);
120            }
121        }
122        return res;
123    }
124
125    protected void retrieveContentViewHeaders(DocumentModel doc) {
126        String docType = doc.getType();
127        if (!typeToContentView.containsKey(docType)) {
128            TypeInfo typeInfo = doc.getAdapter(TypeInfo.class);
129            Map<String, List<ContentViewHeader>> byCategories = getContentViewHeaders(typeInfo, false);
130            typeToContentView.put(docType, byCategories);
131        }
132    }
133
134    protected void retrieveExportContentViewHeaders(DocumentModel doc) {
135        String docType = doc.getType();
136        if (!typeToExportContentView.containsKey(docType)) {
137            TypeInfo typeInfo = doc.getAdapter(TypeInfo.class);
138            Map<String, List<ContentViewHeader>> byCategories = getContentViewHeaders(typeInfo, true);
139            typeToExportContentView.put(docType, byCategories);
140        }
141    }
142
143    /**
144     * Returns true if content views are defined on given document for given category.
145     * <p>
146     * Also fetches content view headers defined on a document type
147     */
148    public boolean hasContentViewSupport(DocumentModel doc, String category) {
149        if (doc == null) {
150            return false;
151        }
152        retrieveContentViewHeaders(doc);
153        String docType = doc.getType();
154        if (!typeToContentView.get(docType).containsKey(category)) {
155            return false;
156        }
157        return !typeToContentView.get(docType).get(category).isEmpty();
158    }
159
160    public Map<String, List<ContentViewHeader>> getAvailableContentViewsForDocument(DocumentModel doc)
161            {
162        if (doc == null) {
163            return Collections.emptyMap();
164        }
165        retrieveContentViewHeaders(doc);
166        String docType = doc.getType();
167        return typeToContentView.get(docType);
168    }
169
170    public List<ContentViewHeader> getAvailableContentViewsForDocument(DocumentModel doc, String category)
171            {
172        if (doc == null) {
173            return Collections.emptyList();
174        }
175        if (CONTENT_VIEW_CONFIGURATION_CATEGORY.equals(category)) {
176            List<ContentViewHeader> localHeaders = getLocalConfiguredContentViews(doc);
177            if (localHeaders != null) {
178                return localHeaders;
179            }
180        }
181        retrieveContentViewHeaders(doc);
182        String docType = doc.getType();
183        if (!typeToContentView.get(docType).containsKey(category)) {
184            return Collections.emptyList();
185        }
186        return typeToContentView.get(doc.getType()).get(category);
187    }
188
189    public Map<String, List<ContentViewHeader>> getAvailableContentViewsForCurrentDocument() {
190        if (currentAvailableContentViews == null) {
191            DocumentModel currentDocument = navigationContext.getCurrentDocument();
192            currentAvailableContentViews = getAvailableContentViewsForDocument(currentDocument);
193        }
194        return currentAvailableContentViews;
195    }
196
197    public List<ContentViewHeader> getAvailableContentViewsForCurrentDocument(String category) {
198        if (CONTENT_VIEW_CONFIGURATION_CATEGORY.equals(category)) {
199            DocumentModel currentDoc = navigationContext.getCurrentDocument();
200            List<ContentViewHeader> localHeaders = getLocalConfiguredContentViews(currentDoc);
201            if (localHeaders != null) {
202                return localHeaders;
203            }
204        }
205        getAvailableContentViewsForCurrentDocument();
206        return currentAvailableContentViews.get(category);
207    }
208
209    public List<ContentViewHeader> getLocalConfiguredContentViews(DocumentModel doc) {
210        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
211        ContentViewConfiguration configuration = localConfigurationService.getConfiguration(
212                ContentViewConfiguration.class, CONTENT_VIEW_CONFIGURATION_FACET, doc);
213        if (configuration == null) {
214            return null;
215        }
216        List<String> cvNames = configuration.getContentViewsForType(doc.getType());
217        if (cvNames == null) {
218            return null;
219        }
220        List<ContentViewHeader> headers = new ArrayList<ContentViewHeader>();
221        for (String cvName : cvNames) {
222            ContentViewHeader header = contentViewService.getContentViewHeader(cvName);
223            if (header != null) {
224                headers.add(header);
225            }
226        }
227        if (!headers.isEmpty()) {
228            return headers;
229        }
230        return null;
231    }
232
233    public Map<String, List<ContentViewHeader>> getExportContentViewsForDocument(DocumentModel doc)
234            {
235        if (doc == null) {
236            return Collections.emptyMap();
237        }
238        retrieveExportContentViewHeaders(doc);
239        String docType = doc.getType();
240        return typeToExportContentView.get(docType);
241    }
242
243    public Map<String, List<ContentViewHeader>> getExportContentViewsForCurrentDocument() {
244        if (currentExportContentViews == null) {
245            DocumentModel currentDocument = navigationContext.getCurrentDocument();
246            currentExportContentViews = getExportContentViewsForDocument(currentDocument);
247        }
248        return currentExportContentViews;
249    }
250
251    public List<ContentViewHeader> getExportContentViewsForCurrentDocument(String category) {
252        getExportContentViewsForCurrentDocument();
253        return currentExportContentViews.get(category);
254    }
255
256    @Observer(value = { EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED }, create = false)
257    @BypassInterceptors
258    public void documentChanged() {
259        currentAvailableContentViews = null;
260        currentExportContentViews = null;
261    }
262
263    /**
264     * Resets typeToContentView cache on {@link EventNames#FLUSH_EVENT}, triggered by hot reload when dev mode is set.
265     *
266     * @since 5.7.1
267     */
268    @Observer(value = { EventNames.FLUSH_EVENT })
269    @BypassInterceptors
270    public void onHotReloadFlush() {
271        typeToContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>();
272    }
273
274    /**
275     * Helper to retrieve selection actions for a given content view, passing additional variables to the
276     * {@link ActionContext} used for resolution.
277     *
278     * @since 2.17
279     * @param category
280     * @param contentView
281     * @param selectedEntries
282     */
283    public List<Action> getSelectionActions(String category, ContentView contentView, List<Object> selectedEntries) {
284        ActionContext ctx = actionContextProvider.createActionContext();
285        ctx.putLocalVariable("contentView", contentView);
286        ctx.putLocalVariable("selectedDocuments", selectedEntries);
287        return webActions.getActionsList(category, ctx, false);
288    }
289}