/* * (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.jsf; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.faces.context.FacesContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.SortInfo; import org.nuxeo.ecm.platform.query.api.PageProvider; import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; import org.nuxeo.ecm.platform.query.api.PageProviderService; import org.nuxeo.ecm.platform.query.core.CoreQueryPageProviderDescriptor; import org.nuxeo.ecm.platform.query.core.GenericPageProviderDescriptor; import org.nuxeo.ecm.platform.query.core.ReferencePageProviderDescriptor; import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.model.ComponentInstance; import org.nuxeo.runtime.model.DefaultComponent; /** * @author Anahide Tchertchian * @since 5.4 */ public class ContentViewServiceImpl extends DefaultComponent implements ContentViewService { public static final String CONTENT_VIEW_EP = "contentViews"; private static final long serialVersionUID = 1L; private static final Log log = LogFactory.getLog(ContentViewServiceImpl.class); protected ContentViewRegistry contentViewReg = new ContentViewRegistry(); @Override public ContentView getContentView(String name) throws ClientException { ContentViewDescriptor desc = contentViewReg.getContentView(name); if (desc == null) { return null; } Boolean useGlobalPageSize = desc.getUseGlobalPageSize(); if (useGlobalPageSize == null) { useGlobalPageSize = Boolean.FALSE; } Boolean translateTitle = desc.getTranslateTitle(); if (translateTitle == null) { translateTitle = Boolean.FALSE; } Boolean translateEmptySentence = desc.getTranslateEmptySentence(); if (translateEmptySentence == null) { translateEmptySentence = Boolean.FALSE; } Boolean showTitle = desc.getShowTitle(); if (showTitle == null) { showTitle = Boolean.FALSE; } Boolean showPageSizeSelector = desc.getShowPageSizeSelector(); if (showPageSizeSelector == null) { showPageSizeSelector = Boolean.FALSE; } Boolean showRefreshPage = desc.getShowRefreshCommand(); if (showRefreshPage == null) { showRefreshPage = Boolean.TRUE; } Boolean showFilterForm = desc.getShowFilterForm(); if (showFilterForm == null) { showFilterForm = Boolean.FALSE; } String[] queryParams = null; String searchDocumentType = null; String sortInfosBinding = null; String pageSizeBinding = null; CoreQueryPageProviderDescriptor coreDesc = desc.getCoreQueryPageProvider(); GenericPageProviderDescriptor genDesc = desc.getGenericPageProvider(); ReferencePageProviderDescriptor refDesc = desc.getReferencePageProvider(); String[] refQueryParams = null; if (refDesc != null && refDesc.isEnabled()) { PageProviderService ppService = Framework.getLocalService(PageProviderService.class); if (ppService == null) { throw new ClientException( "Page provider service cannot be resolved"); } PageProviderDefinition def = ppService.getPageProviderDefinition(refDesc.getName()); if (def == null) { log.error("Could not resolve page provider with name " + refDesc.getName()); } else if (def instanceof CoreQueryPageProviderDescriptor) { coreDesc = (CoreQueryPageProviderDescriptor) def; refQueryParams = refDesc.getQueryParameters(); } else if (def instanceof GenericPageProviderDescriptor) { genDesc = (GenericPageProviderDescriptor) def; refQueryParams = refDesc.getQueryParameters(); } } if (coreDesc != null && coreDesc.isEnabled()) { queryParams = coreDesc.getQueryParameters(); sortInfosBinding = coreDesc.getSortInfosBinding(); pageSizeBinding = coreDesc.getPageSizeBinding(); searchDocumentType = coreDesc.getSearchDocumentType(); } else if (genDesc != null && genDesc.isEnabled()) { queryParams = genDesc.getQueryParameters(); sortInfosBinding = genDesc.getSortInfosBinding(); pageSizeBinding = genDesc.getPageSizeBinding(); searchDocumentType = genDesc.getSearchDocumentType(); } List<String> allQueryParams = new ArrayList<String>(); if (queryParams != null) { allQueryParams.addAll(Arrays.asList(queryParams)); } if (refQueryParams != null) { allQueryParams.addAll(Arrays.asList(refQueryParams)); } String searchDocBinding = desc.getSearchDocumentBinding(); ContentViewImpl contentView = new ContentViewImpl(name, desc.getTitle(), translateTitle.booleanValue(), desc.getIconPath(), desc.getSelectionListName(), desc.getPagination(), desc.getActionCategories(), desc.getSearchLayout(), desc.getResultLayouts(), desc.getFlags(), desc.getCacheKey(), desc.getCacheSize(), desc.getRefreshEventNames(), desc.getResetEventNames(), useGlobalPageSize.booleanValue(), allQueryParams.toArray(new String[] {}), searchDocBinding, searchDocumentType, desc.getResultColumnsBinding(), desc.getResultLayoutBinding(), sortInfosBinding, pageSizeBinding, showTitle.booleanValue(), showPageSizeSelector.booleanValue(), showRefreshPage.booleanValue(), showFilterForm.booleanValue(), desc.getEmptySentence(), translateEmptySentence.booleanValue()); return contentView; } protected ContentViewHeader getContentViewHeader(ContentViewDescriptor desc) { return new ContentViewHeader(desc.getName(), desc.getTitle(), Boolean.TRUE.equals(desc.getTranslateTitle()), desc.getIconPath()); } @Override public ContentViewHeader getContentViewHeader(String name) { ContentViewDescriptor desc = contentViewReg.getContentView(name); if (desc == null) { return null; } return getContentViewHeader(desc); } public Set<String> getContentViewNames() { return Collections.unmodifiableSet(contentViewReg.getContentViewNames()); } @Override public Set<ContentViewHeader> getContentViewHeaders() { Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); for (ContentViewDescriptor desc : contentViewReg.getContentViews()) { res.add(getContentViewHeader(desc)); } return Collections.unmodifiableSet(res); } public Set<String> getContentViewNames(String flag) { Set<String> res = new HashSet<String>(); Set<String> items = contentViewReg.getContentViewsByFlag(flag); if (items != null) { res.addAll(items); } return res; } @Override public Set<ContentViewHeader> getContentViewHeaders(String flag) { Set<String> cvs = getContentViewNames(flag); Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); for (String cv : cvs) { ContentViewHeader header = getContentViewHeader(cv); if (header != null) { res.add(header); } } return Collections.unmodifiableSet(res); } public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage, DocumentModel searchDocument, Object... parameters) throws ClientException { ContentViewDescriptor contentViewDesc = contentViewReg.getContentView(name); if (contentViewDesc == null) { return null; } PageProviderService ppService = Framework.getLocalService(PageProviderService.class); if (ppService == null) { throw new ClientException("Page provider service is null"); } String ppName = contentViewDesc.getPageProviderName(); PageProvider<?> provider = ppService.getPageProvider(ppName, searchDocument, sortInfos, pageSize, currentPage, resolvePageProviderProperties(contentViewDesc.getPageProviderProperties()), parameters); return provider; } public Map<String, Serializable> resolvePageProviderProperties( Map<String, String> stringProps) throws ClientException { // resolve properties Map<String, Serializable> resolvedProps = new HashMap<String, Serializable>(); for (Map.Entry<String, String> prop : stringProps.entrySet()) { resolvedProps.put(prop.getKey(), resolveProperty(prop.getValue())); } return resolvedProps; } protected Serializable resolveProperty(String elExpression) { FacesContext context = FacesContext.getCurrentInstance(); Object value = ComponentTagUtils.resolveElExpression(context, elExpression); if (value != null && !(value instanceof Serializable)) { log.error(String.format("Error processing expression '%s', " + "result is not serializable: %s", elExpression, value)); return null; } return (Serializable) value; } @Override @SuppressWarnings("unchecked") public <T> T getAdapter(Class<T> adapter) { if (adapter.isAssignableFrom(ContentViewService.class)) { return (T) this; } return null; } @Override public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) throws Exception { if (CONTENT_VIEW_EP.equals(extensionPoint)) { ContentViewDescriptor desc = (ContentViewDescriptor) contribution; contentViewReg.addContribution(desc); registerPageProvider(desc); } } protected void registerPageProvider(ContentViewDescriptor desc) { ReferencePageProviderDescriptor refDesc = desc .getReferencePageProvider(); if (refDesc != null && refDesc.isEnabled()) { // we use an already registered pp return; } PageProviderService ppService = Framework .getLocalService(PageProviderService.class); if (ppService == null) { throw new ClientException("PageProviderService is not available"); } String name = desc.getName(); PageProviderDefinition coreDef = getPageProviderDefWithName(name, desc.getCoreQueryPageProvider()); PageProviderDefinition genDef = getPageProviderDefWithName(name, desc.getGenericPageProvider()); if (coreDef != null && genDef != null) { log.error(String .format("Only one page provider should be registered on " + "content view '%s': take the reference " + "descriptor by default, then core query descriptor, " + "and then generic descriptor", name)); } PageProviderDefinition ppDef = (coreDef != null) ? coreDef : genDef; if (ppDef != null) { // log.debug("Register PP: " + ppDef.getName() + " " + ppDef); ppService.registerPageProviderDefinition(ppDef); } } protected PageProviderDefinition getPageProviderDefWithName(String name, PageProviderDefinition ppDef) { if (ppDef != null && ppDef.isEnabled()) { if (ppDef.getName() == null) { ppDef.setName(name); } return ppDef; } return null; } @Override public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) throws Exception { if (CONTENT_VIEW_EP.equals(extensionPoint)) { ContentViewDescriptor desc = (ContentViewDescriptor) contribution; unregisterPageProvider(desc); contentViewReg.removeContribution(desc); } } protected void unregisterPageProvider(ContentViewDescriptor desc) { PageProviderService ppService = Framework.getLocalService(PageProviderService.class); if (ppService == null) { log.info("PageProviderServer is not available, failed to unregister pp of the cv"); return; } if (desc.getCoreQueryPageProvider() != null) { ppService.unregisterPageProviderDefinition((PageProviderDefinition) desc.getCoreQueryPageProvider()); } if (desc.getGenericPageProvider() != null) { ppService.unregisterPageProviderDefinition((PageProviderDefinition) desc.getGenericPageProvider()); } } @Override public ContentView restoreContentView(ContentViewState contentViewState) throws ClientException { if (contentViewState == null) { return null; } String name = contentViewState.getContentViewName(); ContentView cv = getContentView(name); if (cv != null) { // save some info directly on content view, they will be needed // when re-building the provider Long pageSize = contentViewState.getPageSize(); cv.setCurrentPageSize(pageSize); DocumentModel searchDocument = contentViewState.getSearchDocumentModel(); cv.setSearchDocumentModel(searchDocument); if (searchDocument != null) { // check that restored doc type is still in sync with doc type // set on content view String searchType = cv.getSearchDocumentModelType(); if (!searchDocument.getType().equals(searchType)) { log.warn(String.format( "Restored document type '%s' is different from " + "the one declared on content view " + "with name '%s': should be '%s'", searchDocument.getType(), name, searchType)); } } Long currentPage = contentViewState.getCurrentPage(); Object[] params = contentViewState.getQueryParameters(); // init page provider cv.getPageProvider(searchDocument, contentViewState.getSortInfos(), pageSize, currentPage, params); // restore rendering info, unless bindings are present on content // view configuration if (!cv.hasResultLayoutBinding()) { cv.setCurrentResultLayout(contentViewState.getResultLayout()); } if (!cv.hasResultLayoutColumnsBinding()) { cv.setCurrentResultLayoutColumns(contentViewState.getResultColumns()); } } else { throw new ClientException(String.format( "Unknown content view with name '%s'", name)); } return cv; } @Override public ContentViewState saveContentView(ContentView contentView) { if (contentView == null) { return null; } ContentViewState state = new ContentViewStateImpl(); state.setContentViewName(contentView.getName()); state.setPageSize(contentView.getCurrentPageSize()); // provider info PageProvider<?> pp = contentView.getCurrentPageProvider(); if (pp != null) { state.setSearchDocumentModel(pp.getSearchDocumentModel()); state.setCurrentPage(new Long(pp.getCurrentPageIndex())); state.setQueryParameters(pp.getParameters()); state.setSortInfos(pp.getSortInfos()); } else { // take at least info available on content view state.setSearchDocumentModel(contentView.getSearchDocumentModel()); state.setQueryParameters(contentView.getQueryParameters()); } // rendering info state.setResultLayout(contentView.getCurrentResultLayout()); state.setResultColumns(contentView.getCurrentResultLayoutColumns()); return state; } }