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.jsf; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.LinkedHashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import javax.faces.context.FacesContext; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.nuxeo.ecm.core.api.DocumentModel; 035import org.nuxeo.ecm.core.api.NuxeoException; 036import org.nuxeo.ecm.core.api.SortInfo; 037import org.nuxeo.ecm.platform.query.api.PageProvider; 038import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; 039import org.nuxeo.ecm.platform.query.api.PageProviderService; 040import org.nuxeo.ecm.platform.query.core.CoreQueryPageProviderDescriptor; 041import org.nuxeo.ecm.platform.query.core.GenericPageProviderDescriptor; 042import org.nuxeo.ecm.platform.query.core.ReferencePageProviderDescriptor; 043import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 044import org.nuxeo.runtime.api.Framework; 045import org.nuxeo.runtime.model.ComponentInstance; 046import org.nuxeo.runtime.model.DefaultComponent; 047 048/** 049 * @author Anahide Tchertchian 050 * @since 5.4 051 */ 052public class ContentViewServiceImpl extends DefaultComponent implements ContentViewService { 053 054 public static final String CONTENT_VIEW_EP = "contentViews"; 055 056 private static final long serialVersionUID = 1L; 057 058 private static final Log log = LogFactory.getLog(ContentViewServiceImpl.class); 059 060 protected ContentViewRegistry contentViewReg = new ContentViewRegistry(); 061 062 @Override 063 public ContentView getContentView(String name) { 064 ContentViewDescriptor desc = contentViewReg.getContentView(name); 065 if (desc == null) { 066 return null; 067 } 068 Boolean useGlobalPageSize = desc.getUseGlobalPageSize(); 069 if (useGlobalPageSize == null) { 070 useGlobalPageSize = Boolean.FALSE; 071 } 072 Boolean translateTitle = desc.getTranslateTitle(); 073 if (translateTitle == null) { 074 translateTitle = Boolean.FALSE; 075 } 076 Boolean translateEmptySentence = desc.getTranslateEmptySentence(); 077 if (translateEmptySentence == null) { 078 translateEmptySentence = Boolean.FALSE; 079 } 080 Boolean showTitle = desc.getShowTitle(); 081 if (showTitle == null) { 082 showTitle = Boolean.FALSE; 083 } 084 Boolean showPageSizeSelector = desc.getShowPageSizeSelector(); 085 if (showPageSizeSelector == null) { 086 showPageSizeSelector = Boolean.FALSE; 087 } 088 Boolean showRefreshPage = desc.getShowRefreshCommand(); 089 if (showRefreshPage == null) { 090 showRefreshPage = Boolean.TRUE; 091 } 092 Boolean showFilterForm = desc.getShowFilterForm(); 093 if (showFilterForm == null) { 094 showFilterForm = Boolean.FALSE; 095 } 096 097 String[] queryParams = null; 098 String searchDocumentType = null; 099 String sortInfosBinding = null; 100 String pageSizeBinding = null; 101 CoreQueryPageProviderDescriptor coreDesc = desc.getCoreQueryPageProvider(); 102 GenericPageProviderDescriptor genDesc = desc.getGenericPageProvider(); 103 ReferencePageProviderDescriptor refDesc = desc.getReferencePageProvider(); 104 String[] refQueryParams = null; 105 if (refDesc != null && refDesc.isEnabled()) { 106 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 107 PageProviderDefinition def = ppService.getPageProviderDefinition(refDesc.getName()); 108 if (def == null) { 109 log.error("Could not resolve page provider with name " + refDesc.getName()); 110 } else if (def instanceof CoreQueryPageProviderDescriptor) { 111 coreDesc = (CoreQueryPageProviderDescriptor) def; 112 refQueryParams = refDesc.getQueryParameters(); 113 } else if (def instanceof GenericPageProviderDescriptor) { 114 genDesc = (GenericPageProviderDescriptor) def; 115 refQueryParams = refDesc.getQueryParameters(); 116 } 117 } 118 if (coreDesc != null && coreDesc.isEnabled()) { 119 queryParams = coreDesc.getQueryParameters(); 120 sortInfosBinding = coreDesc.getSortInfosBinding(); 121 pageSizeBinding = coreDesc.getPageSizeBinding(); 122 searchDocumentType = coreDesc.getSearchDocumentType(); 123 } else if (genDesc != null && genDesc.isEnabled()) { 124 queryParams = genDesc.getQueryParameters(); 125 sortInfosBinding = genDesc.getSortInfosBinding(); 126 pageSizeBinding = genDesc.getPageSizeBinding(); 127 searchDocumentType = genDesc.getSearchDocumentType(); 128 } 129 List<String> allQueryParams = new ArrayList<String>(); 130 if (queryParams != null) { 131 allQueryParams.addAll(Arrays.asList(queryParams)); 132 } 133 if (refQueryParams != null) { 134 allQueryParams.addAll(Arrays.asList(refQueryParams)); 135 } 136 String searchDocBinding = desc.getSearchDocumentBinding(); 137 ContentViewImpl contentView = new ContentViewImpl(name, desc.getTitle(), translateTitle.booleanValue(), 138 desc.getIconPath(), desc.getSelectionListName(), desc.getPagination(), desc.getActionCategories(), 139 desc.getSearchLayout(), desc.getResultLayouts(), desc.getFlags(), desc.getCacheKey(), 140 desc.getCacheSize(), desc.getRefreshEventNames(), desc.getResetEventNames(), 141 useGlobalPageSize.booleanValue(), allQueryParams.toArray(new String[] {}), searchDocBinding, 142 searchDocumentType, desc.getResultColumnsBinding(), desc.getResultLayoutBinding(), sortInfosBinding, 143 pageSizeBinding, showTitle.booleanValue(), showPageSizeSelector.booleanValue(), 144 showRefreshPage.booleanValue(), showFilterForm.booleanValue(), desc.getEmptySentence(), 145 translateEmptySentence.booleanValue()); 146 contentView.setWaitForExecutionSentence(desc.getWaitForExecutionSentence()); 147 if (desc.getWaitForExecution() != null) { 148 contentView.setWaitForExecution(desc.getWaitForExecution().booleanValue()); 149 } 150 return contentView; 151 } 152 153 protected ContentViewHeader getContentViewHeader(ContentViewDescriptor desc) { 154 return new ContentViewHeader(desc.getName(), desc.getTitle(), Boolean.TRUE.equals(desc.getTranslateTitle()), 155 desc.getIconPath()); 156 } 157 158 @Override 159 public ContentViewHeader getContentViewHeader(String name) { 160 ContentViewDescriptor desc = contentViewReg.getContentView(name); 161 if (desc == null) { 162 return null; 163 } 164 return getContentViewHeader(desc); 165 } 166 167 @Override 168 public Set<String> getContentViewNames() { 169 return Collections.unmodifiableSet(contentViewReg.getContentViewNames()); 170 } 171 172 @Override 173 public Set<ContentViewHeader> getContentViewHeaders() { 174 Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); 175 for (ContentViewDescriptor desc : contentViewReg.getContentViews()) { 176 res.add(getContentViewHeader(desc)); 177 } 178 return Collections.unmodifiableSet(res); 179 } 180 181 @Override 182 public Set<String> getContentViewNames(String flag) { 183 Set<String> res = new LinkedHashSet<String>(); 184 Set<String> items = contentViewReg.getContentViewsByFlag(flag); 185 if (items != null) { 186 res.addAll(items); 187 } 188 return res; 189 } 190 191 @Override 192 public Set<ContentViewHeader> getContentViewHeaders(String flag) { 193 Set<String> cvs = getContentViewNames(flag); 194 Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); 195 for (String cv : cvs) { 196 ContentViewHeader header = getContentViewHeader(cv); 197 if (header != null) { 198 res.add(header); 199 } 200 } 201 return Collections.unmodifiableSet(res); 202 } 203 204 @Override 205 public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage, 206 DocumentModel searchDocument, Object... parameters) { 207 ContentViewDescriptor contentViewDesc = contentViewReg.getContentView(name); 208 if (contentViewDesc == null) { 209 return null; 210 } 211 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 212 String ppName = contentViewDesc.getPageProviderName(); 213 PageProvider<?> provider = ppService.getPageProvider(ppName, searchDocument, sortInfos, pageSize, currentPage, 214 resolvePageProviderProperties(contentViewDesc.getPageProviderProperties()), parameters); 215 return provider; 216 } 217 218 public Map<String, Serializable> resolvePageProviderProperties(Map<String, String> stringProps) 219 { 220 // resolve properties 221 Map<String, Serializable> resolvedProps = new HashMap<String, Serializable>(); 222 for (Map.Entry<String, String> prop : stringProps.entrySet()) { 223 resolvedProps.put(prop.getKey(), resolveProperty(prop.getValue())); 224 } 225 return resolvedProps; 226 } 227 228 protected Serializable resolveProperty(String elExpression) { 229 FacesContext context = FacesContext.getCurrentInstance(); 230 Object value = ComponentTagUtils.resolveElExpression(context, elExpression); 231 if (value != null && !(value instanceof Serializable)) { 232 log.error(String.format("Error processing expression '%s', " + "result is not serializable: %s", 233 elExpression, value)); 234 return null; 235 } 236 return (Serializable) value; 237 } 238 239 @Override 240 @SuppressWarnings("unchecked") 241 public <T> T getAdapter(Class<T> adapter) { 242 if (adapter.isAssignableFrom(ContentViewService.class)) { 243 return (T) this; 244 } 245 return null; 246 } 247 248 @Override 249 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 250 if (CONTENT_VIEW_EP.equals(extensionPoint)) { 251 ContentViewDescriptor desc = (ContentViewDescriptor) contribution; 252 contentViewReg.addContribution(desc); 253 registerPageProvider(desc); 254 } 255 } 256 257 protected void registerPageProvider(ContentViewDescriptor desc) { 258 ReferencePageProviderDescriptor refDesc = desc.getReferencePageProvider(); 259 if (refDesc != null && refDesc.isEnabled()) { 260 // we use an already registered pp 261 return; 262 } 263 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 264 String name = desc.getName(); 265 PageProviderDefinition coreDef = getPageProviderDefWithName(name, desc.getCoreQueryPageProvider()); 266 PageProviderDefinition genDef = getPageProviderDefWithName(name, desc.getGenericPageProvider()); 267 if (coreDef != null && genDef != null) { 268 log.error(String.format("Only one page provider should be registered on " 269 + "content view '%s': take the reference descriptor by default, then core query descriptor, " 270 + "and then generic descriptor", name)); 271 } 272 PageProviderDefinition ppDef = (coreDef != null) ? coreDef : genDef; 273 if (ppDef != null) { 274 if (log.isDebugEnabled()) { 275 log.debug(String.format("Register PageProvider from ContentView: %s %s", ppDef.getName(), ppDef)); 276 } 277 ppService.registerPageProviderDefinition(ppDef); 278 } 279 } 280 281 protected PageProviderDefinition getPageProviderDefWithName(String name, PageProviderDefinition ppDef) { 282 if (ppDef != null && ppDef.isEnabled()) { 283 if (ppDef.getName() == null) { 284 ppDef.setName(name); 285 } 286 return ppDef; 287 } 288 return null; 289 } 290 291 @Override 292 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 293 if (CONTENT_VIEW_EP.equals(extensionPoint)) { 294 ContentViewDescriptor desc = (ContentViewDescriptor) contribution; 295 unregisterPageProvider(desc); 296 contentViewReg.removeContribution(desc); 297 } 298 } 299 300 protected void unregisterPageProvider(ContentViewDescriptor desc) { 301 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 302 if (ppService == null) { 303 log.info("PageProviderServer is not available, failed to unregister pp of the cv"); 304 return; 305 } 306 if (desc.getCoreQueryPageProvider() != null) { 307 308 ppService.unregisterPageProviderDefinition(desc.getCoreQueryPageProvider()); 309 } 310 if (desc.getGenericPageProvider() != null) { 311 ppService.unregisterPageProviderDefinition(desc.getGenericPageProvider()); 312 } 313 } 314 315 @Override 316 public ContentView restoreContentView(ContentViewState contentViewState) { 317 if (contentViewState == null) { 318 return null; 319 } 320 String name = contentViewState.getContentViewName(); 321 ContentView cv = getContentView(name); 322 if (cv != null) { 323 restoreContentViewState(cv, contentViewState); 324 } else { 325 throw new NuxeoException(String.format("Unknown content view with name '%s'", name)); 326 } 327 return cv; 328 } 329 330 @Override 331 public void restoreContentViewState(ContentView contentView, ContentViewState contentViewState) { 332 if (contentView == null || contentViewState == null) { 333 return; 334 } 335 336 // save some info directly on content view, they will be needed 337 // when re-building the provider 338 Long pageSize = contentViewState.getPageSize(); 339 contentView.setCurrentPageSize(pageSize); 340 DocumentModel searchDocument = contentViewState.getSearchDocumentModel(); 341 contentView.setSearchDocumentModel(searchDocument); 342 if (searchDocument != null) { 343 // check that restored doc type is still in sync with doc type 344 // set on content view 345 String searchType = contentView.getSearchDocumentModelType(); 346 if (!searchDocument.getType().equals(searchType)) { 347 log.warn(String.format("Restored document type '%s' is different from " 348 + "the one declared on content view with name '%s': should be '%s'", 349 searchDocument.getType(), contentViewState.getContentViewName(), searchType)); 350 } 351 } 352 Long currentPage = contentViewState.getCurrentPage(); 353 Object[] params = contentViewState.getQueryParameters(); 354 // init page provider 355 contentView.setExecuted(true); 356 contentView.getPageProvider(searchDocument, contentViewState.getSortInfos(), pageSize, currentPage, params); 357 // restore rendering info, unless bindings are present on content 358 // view configuration 359 if (!contentView.hasResultLayoutBinding()) { 360 contentView.setCurrentResultLayout(contentViewState.getResultLayout()); 361 } 362 if (!contentView.hasResultLayoutColumnsBinding()) { 363 contentView.setCurrentResultLayoutColumns(contentViewState.getResultColumns()); 364 } 365 } 366 367 @Override 368 public ContentViewState saveContentView(ContentView contentView) { 369 if (contentView == null) { 370 return null; 371 } 372 ContentViewState state = new ContentViewStateImpl(); 373 state.setContentViewName(contentView.getName()); 374 state.setPageSize(contentView.getCurrentPageSize()); 375 // provider info 376 PageProvider<?> pp = contentView.getCurrentPageProvider(); 377 if (pp != null) { 378 state.setPageProviderName(pp.getName()); 379 state.setSearchDocumentModel(pp.getSearchDocumentModel()); 380 state.setCurrentPage(new Long(pp.getCurrentPageIndex())); 381 state.setQueryParameters(pp.getParameters()); 382 state.setSortInfos(pp.getSortInfos()); 383 } else { 384 // take at least info available on content view 385 state.setSearchDocumentModel(contentView.getSearchDocumentModel()); 386 state.setQueryParameters(contentView.getQueryParameters()); 387 } 388 // rendering info 389 state.setResultLayout(contentView.getCurrentResultLayout()); 390 state.setResultColumns(contentView.getCurrentResultLayoutColumns()); 391 return state; 392 } 393 394}