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