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.jsf; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.LinkedHashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031 032import javax.faces.context.FacesContext; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.nuxeo.ecm.core.api.DocumentModel; 037import org.nuxeo.ecm.core.api.NuxeoException; 038import org.nuxeo.ecm.core.api.SortInfo; 039import org.nuxeo.ecm.platform.query.api.PageProvider; 040import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; 041import org.nuxeo.ecm.platform.query.api.PageProviderService; 042import org.nuxeo.ecm.platform.query.core.CoreQueryPageProviderDescriptor; 043import org.nuxeo.ecm.platform.query.core.GenericPageProviderDescriptor; 044import org.nuxeo.ecm.platform.query.core.ReferencePageProviderDescriptor; 045import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 046import org.nuxeo.runtime.api.Framework; 047import org.nuxeo.runtime.model.ComponentInstance; 048import org.nuxeo.runtime.model.DefaultComponent; 049 050/** 051 * @author Anahide Tchertchian 052 * @since 5.4 053 */ 054public class ContentViewServiceImpl extends DefaultComponent implements ContentViewService { 055 056 public static final String CONTENT_VIEW_EP = "contentViews"; 057 058 private static final long serialVersionUID = 1L; 059 060 private static final Log log = LogFactory.getLog(ContentViewServiceImpl.class); 061 062 protected ContentViewRegistry contentViewReg = new ContentViewRegistry(); 063 064 @Override 065 public ContentView getContentView(String name) { 066 ContentViewDescriptor desc = contentViewReg.getContentView(name); 067 if (desc == null) { 068 return null; 069 } 070 Boolean useGlobalPageSize = desc.getUseGlobalPageSize(); 071 if (useGlobalPageSize == null) { 072 useGlobalPageSize = Boolean.FALSE; 073 } 074 Boolean translateTitle = desc.getTranslateTitle(); 075 if (translateTitle == null) { 076 translateTitle = Boolean.FALSE; 077 } 078 Boolean translateEmptySentence = desc.getTranslateEmptySentence(); 079 if (translateEmptySentence == null) { 080 translateEmptySentence = Boolean.FALSE; 081 } 082 Boolean showTitle = desc.getShowTitle(); 083 if (showTitle == null) { 084 showTitle = Boolean.FALSE; 085 } 086 Boolean showPageSizeSelector = desc.getShowPageSizeSelector(); 087 if (showPageSizeSelector == null) { 088 showPageSizeSelector = Boolean.FALSE; 089 } 090 Boolean showRefreshPage = desc.getShowRefreshCommand(); 091 if (showRefreshPage == null) { 092 showRefreshPage = Boolean.TRUE; 093 } 094 Boolean showFilterForm = desc.getShowFilterForm(); 095 if (showFilterForm == null) { 096 showFilterForm = Boolean.FALSE; 097 } 098 099 String[] queryParams = null; 100 String searchDocumentType = null; 101 String sortInfosBinding = null; 102 String pageSizeBinding = null; 103 CoreQueryPageProviderDescriptor coreDesc = desc.getCoreQueryPageProvider(); 104 GenericPageProviderDescriptor genDesc = desc.getGenericPageProvider(); 105 ReferencePageProviderDescriptor refDesc = desc.getReferencePageProvider(); 106 String[] refQueryParams = null; 107 if (refDesc != null && refDesc.isEnabled()) { 108 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 109 PageProviderDefinition def = ppService.getPageProviderDefinition(refDesc.getName()); 110 if (def == null) { 111 log.error("Could not resolve page provider with name " + refDesc.getName()); 112 } else if (def instanceof CoreQueryPageProviderDescriptor) { 113 coreDesc = (CoreQueryPageProviderDescriptor) def; 114 refQueryParams = refDesc.getQueryParameters(); 115 } else if (def instanceof GenericPageProviderDescriptor) { 116 genDesc = (GenericPageProviderDescriptor) def; 117 refQueryParams = refDesc.getQueryParameters(); 118 } 119 } 120 if (coreDesc != null && coreDesc.isEnabled()) { 121 queryParams = coreDesc.getQueryParameters(); 122 sortInfosBinding = coreDesc.getSortInfosBinding(); 123 pageSizeBinding = coreDesc.getPageSizeBinding(); 124 searchDocumentType = coreDesc.getSearchDocumentType(); 125 } else if (genDesc != null && genDesc.isEnabled()) { 126 queryParams = genDesc.getQueryParameters(); 127 sortInfosBinding = genDesc.getSortInfosBinding(); 128 pageSizeBinding = genDesc.getPageSizeBinding(); 129 searchDocumentType = genDesc.getSearchDocumentType(); 130 } 131 List<String> allQueryParams = new ArrayList<String>(); 132 if (queryParams != null) { 133 allQueryParams.addAll(Arrays.asList(queryParams)); 134 } 135 if (refQueryParams != null) { 136 allQueryParams.addAll(Arrays.asList(refQueryParams)); 137 } 138 String searchDocBinding = desc.getSearchDocumentBinding(); 139 ContentViewImpl contentView = new ContentViewImpl(name, desc.getTitle(), translateTitle.booleanValue(), 140 desc.getIconPath(), desc.getSelectionListName(), desc.getPagination(), desc.getActionCategories(), 141 desc.getSearchLayout(), desc.getResultLayouts(), desc.getFlags(), desc.getCacheKey(), 142 desc.getCacheSize(), desc.getRefreshEventNames(), desc.getResetEventNames(), 143 useGlobalPageSize.booleanValue(), allQueryParams.toArray(new String[] {}), searchDocBinding, 144 searchDocumentType, desc.getResultColumnsBinding(), desc.getResultLayoutBinding(), sortInfosBinding, 145 pageSizeBinding, showTitle.booleanValue(), showPageSizeSelector.booleanValue(), 146 showRefreshPage.booleanValue(), showFilterForm.booleanValue(), desc.getEmptySentence(), 147 translateEmptySentence.booleanValue()); 148 contentView.setWaitForExecutionSentence(desc.getWaitForExecutionSentence()); 149 if (desc.getWaitForExecution() != null) { 150 contentView.setWaitForExecution(desc.getWaitForExecution().booleanValue()); 151 } 152 return contentView; 153 } 154 155 protected ContentViewHeader getContentViewHeader(ContentViewDescriptor desc) { 156 return new ContentViewHeader(desc.getName(), desc.getTitle(), Boolean.TRUE.equals(desc.getTranslateTitle()), 157 desc.getIconPath()); 158 } 159 160 @Override 161 public ContentViewHeader getContentViewHeader(String name) { 162 ContentViewDescriptor desc = contentViewReg.getContentView(name); 163 if (desc == null) { 164 return null; 165 } 166 return getContentViewHeader(desc); 167 } 168 169 @Override 170 public Set<String> getContentViewNames() { 171 return Collections.unmodifiableSet(contentViewReg.getContentViewNames()); 172 } 173 174 @Override 175 public Set<ContentViewHeader> getContentViewHeaders() { 176 Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); 177 for (ContentViewDescriptor desc : contentViewReg.getContentViews()) { 178 res.add(getContentViewHeader(desc)); 179 } 180 return Collections.unmodifiableSet(res); 181 } 182 183 @Override 184 public Set<String> getContentViewNames(String flag) { 185 Set<String> res = new LinkedHashSet<String>(); 186 Set<String> items = contentViewReg.getContentViewsByFlag(flag); 187 if (items != null) { 188 res.addAll(items); 189 } 190 return res; 191 } 192 193 @Override 194 public Set<ContentViewHeader> getContentViewHeaders(String flag) { 195 Set<String> cvs = getContentViewNames(flag); 196 Set<ContentViewHeader> res = new HashSet<ContentViewHeader>(); 197 for (String cv : cvs) { 198 ContentViewHeader header = getContentViewHeader(cv); 199 if (header != null) { 200 res.add(header); 201 } 202 } 203 return Collections.unmodifiableSet(res); 204 } 205 206 @Override 207 public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage, 208 DocumentModel searchDocument, Object... parameters) { 209 ContentViewDescriptor contentViewDesc = contentViewReg.getContentView(name); 210 if (contentViewDesc == null) { 211 return null; 212 } 213 PageProviderService ppService = Framework.getLocalService(PageProviderService.class); 214 String ppName = contentViewDesc.getPageProviderName(); 215 PageProvider<?> provider = ppService.getPageProvider(ppName, searchDocument, sortInfos, pageSize, currentPage, 216 resolvePageProviderProperties(contentViewDesc.getPageProviderProperties()), parameters); 217 return provider; 218 } 219 220 public Map<String, Serializable> resolvePageProviderProperties(Map<String, String> stringProps) { 221 // resolve properties 222 Map<String, Serializable> resolvedProps = new HashMap<String, Serializable>(); 223 for (Map.Entry<String, String> prop : stringProps.entrySet()) { 224 resolvedProps.put(prop.getKey(), resolveProperty(prop.getValue())); 225 } 226 return resolvedProps; 227 } 228 229 protected Serializable resolveProperty(String elExpression) { 230 FacesContext context = FacesContext.getCurrentInstance(); 231 Object value = ComponentTagUtils.resolveElExpression(context, elExpression); 232 if (value != null && !(value instanceof Serializable)) { 233 log.error("Error processing expression '" + elExpression + "', result is not serializable: " + 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("Unknown content view with name '" + 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( 348 "Restored document type '%s' is different from " 349 + "the one declared on content view with name '%s': should be '%s'", 350 searchDocument.getType(), contentViewState.getContentViewName(), searchType)); 351 } 352 } 353 Long currentPage = contentViewState.getCurrentPage(); 354 Object[] params = contentViewState.getQueryParameters(); 355 356 // init page provider 357 contentView.setExecuted(contentViewState.isExecuted()); 358 contentView.getPageProvider(searchDocument, contentViewState.getSortInfos(), pageSize, currentPage, params); 359 // restore rendering info, unless bindings are present on content 360 // view configuration 361 if (!contentView.hasResultLayoutBinding()) { 362 contentView.setCurrentResultLayout(contentViewState.getResultLayout()); 363 } 364 if (!contentView.hasResultLayoutColumnsBinding()) { 365 contentView.setCurrentResultLayoutColumns(contentViewState.getResultColumns()); 366 } 367 } 368 369 @Override 370 public ContentViewState saveContentView(ContentView contentView) { 371 if (contentView == null) { 372 return null; 373 } 374 ContentViewState state = new ContentViewStateImpl(); 375 state.setContentViewName(contentView.getName()); 376 state.setPageSize(contentView.getCurrentPageSize()); 377 // provider info 378 PageProvider<?> pp = contentView.getCurrentPageProvider(); 379 if (pp != null) { 380 state.setPageProviderName(pp.getName()); 381 state.setSearchDocumentModel(pp.getSearchDocumentModel()); 382 state.setCurrentPage(new Long(pp.getCurrentPageIndex())); 383 state.setQueryParameters(pp.getParameters()); 384 state.setSortInfos(pp.getSortInfos()); 385 } else { 386 // take at least info available on content view 387 state.setSearchDocumentModel(contentView.getSearchDocumentModel()); 388 state.setQueryParameters(contentView.getQueryParameters()); 389 } 390 // rendering info 391 state.setResultLayout(contentView.getCurrentResultLayout()); 392 state.setResultColumns(contentView.getCurrentResultLayoutColumns()); 393 state.setExecuted(contentView.isExecuted()); 394 return state; 395 } 396 397}