/*
* (C) Copyright 2010 Nuxeo SA (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Contributors:
* Anahide Tchertchian
*/
package org.nuxeo.ecm.platform.contentview.seam;
import static org.jboss.seam.ScopeType.CONVERSATION;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.SortInfo;
import org.nuxeo.ecm.platform.actions.Action;
import org.nuxeo.ecm.platform.actions.ActionContext;
import org.nuxeo.ecm.platform.actions.jsf.JSFActionContext;
import org.nuxeo.ecm.platform.actions.seam.SeamActionContext;
import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
import org.nuxeo.ecm.platform.contentview.jsf.ContentViewCache;
import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState;
import org.nuxeo.ecm.platform.query.api.PageProvider;
import org.nuxeo.ecm.platform.ui.web.api.WebActions;
import org.nuxeo.ecm.platform.ui.web.util.SeamContextHelper;
/**
* Handles cache and refresh for named content views.
*
* @author Anahide Tchertchian
* @since 5.4
* @since 5.4.2: moved to content-view-jsf module
*/
@Name("contentViewActions")
@Scope(CONVERSATION)
public class ContentViewActions implements Serializable {
private static final long serialVersionUID = 1L;
@In(create = true)
protected ContentViewRestActions contentViewRestActions;
@In(create = true)
protected ContentViewService contentViewService;
protected final ContentViewCache cache = new ContentViewCache();
protected Long globalPageSize;
@In(create = true)
private transient NuxeoPrincipal currentNuxeoPrincipal;
@In(create = true, required = false)
private transient CoreSession documentManager;
@In(create = true, required = false)
protected transient WebActions webActions;
protected ContentView currentContentView;
/**
* Returns the current global content view.
* <p>
* Current content view is usually the last one displayed on the page, but
* this may change depending on calls to
* {@link #setCurrentContentView(ContentView)}.
*/
public ContentView getCurrentContentView() {
return currentContentView;
}
/**
* Sets the current global content view.
*
* @see #getCurrentGlobalPageSize()
*/
public void setCurrentContentView(ContentView cv) {
currentContentView = cv;
}
/**
* Returns the global page size, or returns the page size on current
* content view if set.
*/
public Long getCurrentGlobalPageSize() {
if (currentContentView != null
&& currentContentView.getUseGlobalPageSize()) {
return currentContentView.getCurrentPageSize();
}
return globalPageSize;
}
/**
* Sets the global page size, useful to set the value having the
* appropriate selection set, see {@link #getCurrentContentView()}
*/
public void setCurrentGlobalPageSize(Long pageSize) {
globalPageSize = pageSize;
}
/**
* Returns the global page size
*/
public Long getGlobalPageSize() {
return globalPageSize;
}
/**
* Returns the list of available options for page size selections, for
* given content view.
* <p>
* This method relies on a hard-coded set of available values, and adapts
* it to the current content view page size and max page size information
* to present more or less items from this list.
*
* @since 5.8
*/
@SuppressWarnings("boxing")
public List<SelectItem> getPageSizeOptions(ContentView cv) {
List<SelectItem> items = new ArrayList<SelectItem>();
if (cv == null) {
return items;
}
List<Long> values = new ArrayList<Long>(Arrays.asList(5L, 10L, 20L,
30L, 40L, 50L));
if (cv.getUseGlobalPageSize()) {
// add the global page size if not present
Long globalSize = getGlobalPageSize();
if (globalSize != null && globalSize > 0
&& !values.contains(globalSize)) {
values.add(globalSize);
}
}
long maxPageSize = 0;
PageProvider<?> pp = cv.getCurrentPageProvider();
if (pp != null) {
// include the actual page size of page provider if not present
long ppsize = pp.getPageSize();
if (ppsize > 0 && !values.contains(ppsize)) {
values.add(Long.valueOf(ppsize));
}
maxPageSize = pp.getMaxPageSize();
}
Collections.sort(values);
for (Long value : values) {
// remove every element that would be larger than max page size
if (maxPageSize > 0 && maxPageSize < value) {
break;
}
items.add(new SelectItem(value));
}
// make sure the list is not empty
if (items.isEmpty()) {
if (maxPageSize > 0) {
items.add(new SelectItem(maxPageSize));
} else {
items.add(new SelectItem(1));
}
}
return items;
}
/**
* Sets the global page size
*/
public void setGlobalPageSize(Long pageSize) {
globalPageSize = pageSize;
}
public ContentView getContentView(String name) throws ClientException {
return getContentView(name, null);
}
/**
* Returns content view with given name, or null if no content view with
* this name is found.
* <p>
* If parameter searchDocumentModel is not null, it will be set on the
* content view. If it is null and the content is using a provider that
* needs it, a new document model is created and attached to it. This
* document model is resolved from the binding put in the content view XML
* definition, or from the document type in this definition if no binding
* is set.
* <p>
* If not null, this content view is set as the current content view so
* that subsequent calls to other methods can take information from it,
* like {@link #getCurrentGlobalPageSize()}
* <p>
* 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 changes (if defined).
*
* @throws ClientException
*/
public ContentView getContentView(String name,
DocumentModel searchDocumentModel) throws ClientException {
ContentView cView = cache.get(name);
if (cView == null) {
cView = contentViewService.getContentView(name);
if (cView != null) {
cache.add(cView);
}
}
if (cView != null) {
if (searchDocumentModel != null) {
cView.setSearchDocumentModel(searchDocumentModel);
}
setCurrentContentView(cView);
}
return cView;
}
public ContentView getContentViewWithProvider(String name)
throws ClientException {
return getContentViewWithProvider(name, null, null, null, null);
}
public ContentView getContentViewWithProvider(String name,
DocumentModel searchDocumentModel) throws ClientException {
return getContentViewWithProvider(name, searchDocumentModel, null,
null, null);
}
public ContentView getContentViewWithProvider(String name,
DocumentModel searchDocumentModel, List<SortInfo> sortInfos,
Long pageSize, Long currentPage) throws ClientException {
return getContentViewWithProvider(name, searchDocumentModel, sortInfos,
pageSize, currentPage, (Object[]) null);
}
public ContentView getContentViewWithProvider(String name,
DocumentModel searchDocumentModel, List<SortInfo> sortInfos,
Long defaultPageSize, Long pageSize, Long currentPage)
throws ClientException {
return getContentViewWithProvider(name, searchDocumentModel, sortInfos,
defaultPageSize, pageSize, currentPage, (Object[]) null);
}
/**
* @since 5.6
*/
public ContentView getContentViewWithProvider(String name,
DocumentModel searchDocumentModel, List<SortInfo> sortInfos,
Long pageSize, Long currentPage, Object... params)
throws ClientException {
return getContentViewWithProvider(name, searchDocumentModel, sortInfos,
Long.valueOf(-1), pageSize, currentPage, params);
}
/**
* Helper method to retrieve a content view, taking care of initialization
* of page provider according to parameters and current global page size.
* <p>
* This method is not public to avoid EL method resolution issues.
*/
protected ContentView getContentViewWithProvider(String name,
DocumentModel searchDocumentModel, List<SortInfo> sortInfos,
Long defaultPageSize, Long pageSize, Long currentPage,
Object... params) throws ClientException {
ContentView cView = getContentView(name, searchDocumentModel);
if (cView != null) {
if (cView.getUseGlobalPageSize()) {
cView.setCurrentPageSize(globalPageSize);
}
if (cView.getCurrentPageSize() == null && defaultPageSize != null
&& defaultPageSize.longValue() >= 0) {
cView.setCurrentPageSize(defaultPageSize);
}
// initialize provider
cView.getPageProvider(searchDocumentModel, sortInfos, pageSize,
currentPage, params);
}
return cView;
}
/**
* Restore a content view from the given parameters.
* <p>
* 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 changes (if defined).
*
* @since 5.7
*/
public ContentView restoreContentView(String contentViewName,
Long currentPage, Long pageSize, List<SortInfo> sortInfos,
String jsonContentViewState) throws UnsupportedEncodingException,
ClientException {
ContentView cv = contentViewRestActions.restoreContentView(
contentViewName, currentPage, pageSize, sortInfos,
jsonContentViewState);
cache.add(cv);
return cv;
}
/**
* Restore a Content View from the given ContentView state.
* <p>
* 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 changes (if defined).
*
* @since 6.0
*/
public ContentView restoreContentView(ContentViewState state)
throws UnsupportedEncodingException,
ClientException {
ContentView cv = contentViewService.restoreContentView(state);
cache.add(cv);
return cv;
}
/**
* Refreshes all content views that have declared the given seam event name
* as a refresh event in their XML configuration.
*/
@BypassInterceptors
public void refreshOnSeamEvent(String seamEventName) {
cache.refreshOnEvent(seamEventName);
}
/**
* Resets all content views page providers that have declared the given
* seam event name as a reset event in their XML configuration.
*/
@BypassInterceptors
public void resetPageProviderOnSeamEvent(String seamEventName) {
cache.resetPageProviderOnEvent(seamEventName);
}
@BypassInterceptors
public void refresh(String contentViewName) {
cache.refresh(contentViewName, false);
}
@BypassInterceptors
public void refreshAndRewind(String contentViewName) {
cache.refresh(contentViewName, true);
}
/**
* @since 6.0
*/
@BypassInterceptors
public void resetAggregates(String contentViewName) {
cache.resetPageProviderAggregates(contentViewName);
}
@BypassInterceptors
public void resetPageProvider(String contentViewName) {
cache.resetPageProvider(contentViewName);
}
@BypassInterceptors
public void reset(String contentViewName) {
cache.reset(contentViewName);
}
@BypassInterceptors
public void resetAllContent() {
cache.resetAllContent();
}
@BypassInterceptors
public void resetAll() {
cache.resetAll();
}
/**
* @since 5.7
*/
@BypassInterceptors
public void refreshAll() {
cache.refreshAll();
}
/**
* @since 5.7
*/
@BypassInterceptors
public void refreshAndRewindAll() {
cache.refreshAndRewindAll();
}
/**
* Returns actions filtered depending on given custom context.
* <p>
* Boolean values are declared as objects to avoid conversion to "false"
* when variable is not defined, and keep "null" value.
*
* @since 6.0
*/
public List<Action> getActionsList(String category,
DocumentModel currentDocument, ContentView contentView,
Object showPageSizeSelector, Object showRefreshCommand,
Object showCSVExport, Object showPDFExport,
Object showSyndicationLinks, Object showSlideshow,
Object showEditColumns, Object showEditRows, Object showSpreadsheet) {
return webActions.getActionsList(
category,
createContentViewActionContext(currentDocument, contentView,
showPageSizeSelector, showRefreshCommand,
showCSVExport, showPDFExport, showSyndicationLinks,
showSlideshow, showEditColumns, showEditRows,
showSpreadsheet));
}
protected ActionContext createContentViewActionContext(
DocumentModel currentDocument, ContentView contentView,
Object showPageSizeSelector, Object showRefreshCommand,
Object showCSVExport, Object showPDFExport,
Object showSyndicationLinks, Object showSlideshow,
Object showEditColumns, Object showEditRows, Object showSpreadsheet) {
ActionContext ctx;
FacesContext faces = FacesContext.getCurrentInstance();
if (faces == null) {
ctx = new SeamActionContext();
} else {
ctx = new JSFActionContext(faces);
}
ctx.setCurrentPrincipal(currentNuxeoPrincipal);
ctx.setDocumentManager(documentManager);
ctx.setCurrentDocument(currentDocument);
ctx.putLocalVariable("SeamContext", new SeamContextHelper());
ctx.putLocalVariable("contentView", contentView);
// additional local variables for action filters
ctx.putLocalVariable("showPageSizeSelector", showPageSizeSelector);
ctx.putLocalVariable("showRefreshCommand", showRefreshCommand);
ctx.putLocalVariable("showCSVExport", showCSVExport);
ctx.putLocalVariable("showPDFExport", showPDFExport);
ctx.putLocalVariable("showSyndicationLinks", showSyndicationLinks);
ctx.putLocalVariable("showSlideshow", showSlideshow);
ctx.putLocalVariable("showEditColumns", showEditColumns);
ctx.putLocalVariable("showEditRows", showEditRows);
ctx.putLocalVariable("showSpreadsheet", showSpreadsheet);
return ctx;
}
}