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.platform.contentview.seam;
020
021import static org.jboss.seam.ScopeType.CONVERSATION;
022
023import java.io.Serializable;
024import java.io.UnsupportedEncodingException;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.List;
028
029import javax.faces.context.FacesContext;
030import javax.faces.model.SelectItem;
031
032import org.jboss.seam.annotations.In;
033import org.jboss.seam.annotations.Name;
034import org.jboss.seam.annotations.Scope;
035import org.jboss.seam.annotations.intercept.BypassInterceptors;
036import org.nuxeo.ecm.core.api.CoreSession;
037import org.nuxeo.ecm.core.api.DocumentModel;
038import org.nuxeo.ecm.core.api.NuxeoPrincipal;
039import org.nuxeo.ecm.core.api.SortInfo;
040import org.nuxeo.ecm.platform.actions.Action;
041import org.nuxeo.ecm.platform.actions.ActionContext;
042import org.nuxeo.ecm.platform.actions.jsf.JSFActionContext;
043import org.nuxeo.ecm.platform.actions.seam.SeamActionContext;
044import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
045import org.nuxeo.ecm.platform.contentview.jsf.ContentViewCache;
046import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
047import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState;
048import org.nuxeo.ecm.platform.query.api.PageProvider;
049import org.nuxeo.ecm.platform.ui.web.api.WebActions;
050import org.nuxeo.ecm.platform.ui.web.util.SeamContextHelper;
051
052/**
053 * Handles cache and refresh for named content views.
054 *
055 * @author Anahide Tchertchian
056 * @since 5.4
057 * @since 5.4.2: moved to content-view-jsf module
058 */
059@Name("contentViewActions")
060@Scope(CONVERSATION)
061public class ContentViewActions implements Serializable {
062
063    private static final long serialVersionUID = 1L;
064
065    @In(create = true)
066    protected ContentViewRestActions contentViewRestActions;
067
068    @In(create = true)
069    protected ContentViewService contentViewService;
070
071    protected final ContentViewCache cache = new ContentViewCache();
072
073    protected Long globalPageSize;
074
075    @In(create = true)
076    private transient NuxeoPrincipal currentNuxeoPrincipal;
077
078    @In(create = true, required = false)
079    private transient CoreSession documentManager;
080
081    @In(create = true, required = false)
082    protected transient WebActions webActions;
083
084    protected ContentView currentContentView;
085
086    /**
087     * Returns the current global content view.
088     * <p>
089     * Current content view is usually the last one displayed on the page, but this may change depending on calls to
090     * {@link #setCurrentContentView(ContentView)}.
091     */
092    public ContentView getCurrentContentView() {
093        return currentContentView;
094    }
095
096    /**
097     * Sets the current global content view.
098     *
099     * @see #getCurrentGlobalPageSize()
100     */
101    public void setCurrentContentView(ContentView cv) {
102        currentContentView = cv;
103    }
104
105    /**
106     * Returns the global page size, or returns the page size on current content view if set.
107     */
108    public Long getCurrentGlobalPageSize() {
109        if (currentContentView != null && currentContentView.getUseGlobalPageSize()) {
110            return currentContentView.getCurrentPageSize();
111        }
112        return globalPageSize;
113    }
114
115    /**
116     * Sets the global page size, useful to set the value having the appropriate selection set, see
117     * {@link #getCurrentContentView()}
118     */
119    public void setCurrentGlobalPageSize(Long pageSize) {
120        globalPageSize = pageSize;
121    }
122
123    /**
124     * Returns the global page size
125     */
126    public Long getGlobalPageSize() {
127        return globalPageSize;
128    }
129
130    /**
131     * Returns the list of available options for page size selections, for given content view.
132     * <p>
133     * This method relies on a hard-coded set of available values, and adapts it to the current content view page size
134     * and max page size information to present more or less items from this list.
135     *
136     * @since 5.8
137     */
138    @SuppressWarnings("boxing")
139    public List<SelectItem> getPageSizeOptions(ContentView cv) {
140        List<SelectItem> items = new ArrayList<SelectItem>();
141        if (cv == null) {
142            return items;
143        }
144        List<Long> values = new ArrayList<Long>();
145        if (cv.getUseGlobalPageSize()) {
146            // add the global page size if not present
147            Long globalSize = getGlobalPageSize();
148            if (globalSize != null && globalSize > 0 && !values.contains(globalSize)) {
149                values.add(globalSize);
150            }
151        }
152        long maxPageSize = 0;
153        PageProvider<?> pp = cv.getCurrentPageProvider();
154        if (pp != null) {
155            // include original page size options set on the page provider definition, as well as current page size if
156            // not present
157            List<Long> options = pp.getPageSizeOptions();
158            if (options != null) {
159                values.addAll(options);
160            }
161            maxPageSize = pp.getMaxPageSize();
162        }
163        Collections.sort(values);
164        for (Long value : values) {
165            // remove every element that would be larger than max page size
166            if (maxPageSize > 0 && maxPageSize < value) {
167                break;
168            }
169            items.add(new SelectItem(value));
170        }
171        // make sure the list is not empty
172        if (items.isEmpty()) {
173            if (maxPageSize > 0) {
174                items.add(new SelectItem(maxPageSize));
175            } else {
176                items.add(new SelectItem(1));
177            }
178        }
179        return items;
180    }
181
182    /**
183     * Sets the global page size
184     */
185    public void setGlobalPageSize(Long pageSize) {
186        globalPageSize = pageSize;
187    }
188
189    public ContentView getContentView(String name) {
190        return getContentView(name, null);
191    }
192
193    /**
194     * Returns content view with given name, or null if no content view with this name is found.
195     * <p>
196     * If parameter searchDocumentModel is not null, it will be set on the content view. If it is null and the content
197     * is using a provider that needs it, a new document model is created and attached to it. This document model is
198     * resolved from the binding put in the content view XML definition, or from the document type in this definition if
199     * no binding is set.
200     * <p>
201     * If not null, this content view is set as the current content view so that subsequent calls to other methods can
202     * take information from it, like {@link #getCurrentGlobalPageSize()}
203     * <p>
204     * The content view is put in a cache map so that it's not rebuilt at each call. It is rebuilt when its cache key
205     * changes (if defined).
206     *
207     */
208    public ContentView getContentView(String name, DocumentModel searchDocumentModel) {
209        ContentView cView = cache.get(name);
210        if (cView == null) {
211            cView = contentViewService.getContentView(name);
212            if (cView != null) {
213                cache.add(cView);
214            }
215        }
216        if (cView != null) {
217            if (searchDocumentModel != null) {
218                cView.setSearchDocumentModel(searchDocumentModel);
219            }
220            setCurrentContentView(cView);
221        }
222        return cView;
223    }
224
225    public ContentView getContentViewWithProvider(String name) {
226        return getContentViewWithProvider(name, null, null, null, null);
227    }
228
229    public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel)
230            {
231        return getContentViewWithProvider(name, searchDocumentModel, null, null, null);
232    }
233
234    public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel,
235            List<SortInfo> sortInfos, Long pageSize, Long currentPage) {
236        return getContentViewWithProvider(name, searchDocumentModel, sortInfos, pageSize, currentPage, (Object[]) null);
237    }
238
239    public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel,
240            List<SortInfo> sortInfos, Long defaultPageSize, Long pageSize, Long currentPage) {
241        return getContentViewWithProvider(name, searchDocumentModel, sortInfos, defaultPageSize, pageSize, currentPage,
242                (Object[]) null);
243    }
244
245    /**
246     * @since 5.6
247     */
248    public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel,
249            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Object... params) {
250        return getContentViewWithProvider(name, searchDocumentModel, sortInfos, Long.valueOf(-1), pageSize,
251                currentPage, params);
252    }
253
254    /**
255     * Helper method to retrieve a content view, taking care of initialization of page provider according to parameters
256     * and current global page size.
257     * <p>
258     * This method is not public to avoid EL method resolution issues.
259     */
260    protected ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel,
261            List<SortInfo> sortInfos, Long defaultPageSize, Long pageSize, Long currentPage, Object... params)
262            {
263        ContentView cView = getContentView(name, searchDocumentModel);
264        if (cView != null) {
265            if (cView.getUseGlobalPageSize()) {
266                cView.setCurrentPageSize(globalPageSize);
267            }
268            if (cView.getCurrentPageSize() == null && defaultPageSize != null && defaultPageSize.longValue() >= 0) {
269                cView.setCurrentPageSize(defaultPageSize);
270            }
271            // initialize provider
272            cView.getPageProvider(searchDocumentModel, sortInfos, pageSize, currentPage, params);
273        }
274        return cView;
275    }
276
277    /**
278     * Restore a content view from the given parameters.
279     * <p>
280     * The content view is put in a cache map so that it's not rebuilt at each call. It is rebuilt when its cache key
281     * changes (if defined).
282     *
283     * @since 5.7
284     */
285    public ContentView restoreContentView(String contentViewName, Long currentPage, Long pageSize,
286            List<SortInfo> sortInfos, String jsonContentViewState) throws UnsupportedEncodingException {
287        ContentView cv = contentViewRestActions.restoreContentView(contentViewName, currentPage, pageSize, sortInfos,
288                jsonContentViewState);
289        cache.add(cv);
290        return cv;
291    }
292
293    /**
294     * Restore a Content View from the given ContentView state.
295     * <p>
296     * The content view is put in a cache map so that it's not rebuilt at each call. It is rebuilt when its cache key
297     * changes (if defined).
298     *
299     * @since 6.0
300     */
301    public ContentView restoreContentView(ContentViewState state) throws UnsupportedEncodingException {
302        ContentView cv = contentViewService.restoreContentView(state);
303        cache.add(cv);
304        return cv;
305    }
306
307    /**
308     * Refreshes all content views that have declared the given seam event name as a refresh event in their XML
309     * configuration.
310     */
311    @BypassInterceptors
312    public void refreshOnSeamEvent(String seamEventName) {
313        cache.refreshOnEvent(seamEventName);
314    }
315
316    /**
317     * Resets all content views page providers that have declared the given seam event name as a reset event in their
318     * XML configuration.
319     */
320    @BypassInterceptors
321    public void resetPageProviderOnSeamEvent(String seamEventName) {
322        cache.resetPageProviderOnEvent(seamEventName);
323    }
324
325    @BypassInterceptors
326    public void refresh(String contentViewName) {
327        cache.refresh(contentViewName, false);
328    }
329
330    @BypassInterceptors
331    public void refreshAndRewind(String contentViewName) {
332        cache.refresh(contentViewName, true);
333    }
334
335    /**
336     * @since 6.0
337     */
338    @BypassInterceptors
339    public void resetAggregates(String contentViewName) {
340        cache.resetPageProviderAggregates(contentViewName);
341    }
342
343    @BypassInterceptors
344    public void resetPageProvider(String contentViewName) {
345        cache.resetPageProvider(contentViewName);
346    }
347
348    @BypassInterceptors
349    public void reset(String contentViewName) {
350        cache.reset(contentViewName);
351    }
352
353    @BypassInterceptors
354    public void resetAllContent() {
355        cache.resetAllContent();
356    }
357
358    @BypassInterceptors
359    public void resetAll() {
360        cache.resetAll();
361    }
362
363    /**
364     * @since 5.7
365     */
366    @BypassInterceptors
367    public void refreshAll() {
368        cache.refreshAll();
369    }
370
371    /**
372     * @since 5.7
373     */
374    @BypassInterceptors
375    public void refreshAndRewindAll() {
376        cache.refreshAndRewindAll();
377    }
378
379    /**
380     * Returns actions filtered depending on given custom context.
381     * <p>
382     * Boolean values are declared as objects to avoid conversion to "false" when variable is not defined, and keep
383     * "null" value.
384     *
385     * @since 6.0
386     */
387    public List<Action> getActionsList(String category, DocumentModel currentDocument, ContentView contentView,
388            Object showPageSizeSelector, Object showRefreshCommand, Object showCSVExport, Object showPDFExport,
389            Object showSyndicationLinks, Object showSlideshow, Object showEditColumns, Object showEditRows,
390            Object showSpreadsheet) {
391        return webActions.getActionsList(
392                category,
393                createContentViewActionContext(currentDocument, contentView, showPageSizeSelector, showRefreshCommand,
394                        showCSVExport, showPDFExport, showSyndicationLinks, showSlideshow, showEditColumns,
395                        showEditRows, showSpreadsheet));
396    }
397
398    protected ActionContext createContentViewActionContext(DocumentModel currentDocument, ContentView contentView,
399            Object showPageSizeSelector, Object showRefreshCommand, Object showCSVExport, Object showPDFExport,
400            Object showSyndicationLinks, Object showSlideshow, Object showEditColumns, Object showEditRows,
401            Object showSpreadsheet) {
402        ActionContext ctx;
403        FacesContext faces = FacesContext.getCurrentInstance();
404        if (faces == null) {
405            ctx = new SeamActionContext();
406        } else {
407            ctx = new JSFActionContext(faces);
408        }
409        ctx.setCurrentPrincipal(currentNuxeoPrincipal);
410        ctx.setDocumentManager(documentManager);
411        ctx.setCurrentDocument(currentDocument);
412        ctx.putLocalVariable("SeamContext", new SeamContextHelper());
413        ctx.putLocalVariable("contentView", contentView);
414        // additional local variables for action filters
415        ctx.putLocalVariable("showPageSizeSelector", showPageSizeSelector);
416        ctx.putLocalVariable("showRefreshCommand", showRefreshCommand);
417        ctx.putLocalVariable("showCSVExport", showCSVExport);
418        ctx.putLocalVariable("showPDFExport", showPDFExport);
419        ctx.putLocalVariable("showSyndicationLinks", showSyndicationLinks);
420        ctx.putLocalVariable("showSlideshow", showSlideshow);
421        ctx.putLocalVariable("showEditColumns", showEditColumns);
422        ctx.putLocalVariable("showEditRows", showEditRows);
423        ctx.putLocalVariable("showSpreadsheet", showSpreadsheet);
424        return ctx;
425    }
426}