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