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.seam; 020 021import static org.jboss.seam.ScopeType.CONVERSATION; 022 023import java.io.IOException; 024import java.io.Serializable; 025import java.io.UnsupportedEncodingException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029 030import javax.faces.context.FacesContext; 031import javax.faces.model.SelectItem; 032 033import org.jboss.seam.annotations.In; 034import org.jboss.seam.annotations.Name; 035import org.jboss.seam.annotations.Scope; 036import org.jboss.seam.annotations.intercept.BypassInterceptors; 037import org.nuxeo.ecm.core.api.CoreSession; 038import org.nuxeo.ecm.core.api.DocumentModel; 039import org.nuxeo.ecm.core.api.NuxeoPrincipal; 040import org.nuxeo.ecm.core.api.SortInfo; 041import org.nuxeo.ecm.platform.actions.Action; 042import org.nuxeo.ecm.platform.actions.ActionContext; 043import org.nuxeo.ecm.platform.actions.jsf.JSFActionContext; 044import org.nuxeo.ecm.platform.actions.seam.SeamActionContext; 045import org.nuxeo.ecm.platform.contentview.jsf.ContentView; 046import org.nuxeo.ecm.platform.contentview.jsf.ContentViewCache; 047import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService; 048import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState; 049import org.nuxeo.ecm.platform.query.api.PageProvider; 050import org.nuxeo.ecm.platform.ui.web.api.WebActions; 051import org.nuxeo.ecm.platform.ui.web.util.SeamContextHelper; 052 053/** 054 * Handles cache and refresh for named content views. 055 * 056 * @author Anahide Tchertchian 057 * @since 5.4 058 * @since 5.4.2: moved to content-view-jsf module 059 */ 060@Name("contentViewActions") 061@Scope(CONVERSATION) 062public class ContentViewActions implements Serializable { 063 064 private static final long serialVersionUID = 1L; 065 066 @In(create = true) 067 protected ContentViewRestActions contentViewRestActions; 068 069 @In(create = true) 070 protected ContentViewService contentViewService; 071 072 protected final ContentViewCache cache = new ContentViewCache(); 073 074 protected Long globalPageSize; 075 076 @In(create = true) 077 private transient NuxeoPrincipal currentNuxeoPrincipal; 078 079 @In(create = true, required = false) 080 private transient CoreSession documentManager; 081 082 @In(create = true, required = false) 083 protected transient WebActions webActions; 084 085 protected ContentView currentContentView; 086 087 /** 088 * Returns the current global content view. 089 * <p> 090 * Current content view is usually the last one displayed on the page, but this may change depending on calls to 091 * {@link #setCurrentContentView(ContentView)}. 092 */ 093 public ContentView getCurrentContentView() { 094 return currentContentView; 095 } 096 097 /** 098 * Sets the current global content view. 099 * 100 * @see #getCurrentGlobalPageSize() 101 */ 102 public void setCurrentContentView(ContentView cv) { 103 currentContentView = cv; 104 } 105 106 /** 107 * Returns the global page size, or returns the page size on current content view if set. 108 */ 109 public Long getCurrentGlobalPageSize() { 110 if (currentContentView != null && currentContentView.getUseGlobalPageSize()) { 111 return currentContentView.getCurrentPageSize(); 112 } 113 return globalPageSize; 114 } 115 116 /** 117 * Sets the global page size, useful to set the value having the appropriate selection set, see 118 * {@link #getCurrentContentView()} 119 */ 120 public void setCurrentGlobalPageSize(Long pageSize) { 121 globalPageSize = pageSize; 122 } 123 124 /** 125 * Returns the global page size 126 */ 127 public Long getGlobalPageSize() { 128 return globalPageSize; 129 } 130 131 /** 132 * Returns the list of available options for page size selections, for given content view. 133 * <p> 134 * This method relies on a hard-coded set of available values, and adapts it to the current content view page size 135 * and max page size information to present more or less items from this list. 136 * 137 * @since 5.8 138 */ 139 @SuppressWarnings("boxing") 140 public List<SelectItem> getPageSizeOptions(ContentView cv) { 141 List<SelectItem> items = new ArrayList<SelectItem>(); 142 if (cv == null) { 143 return items; 144 } 145 List<Long> values = new ArrayList<Long>(); 146 long maxPageSize = 0; 147 PageProvider<?> pp = cv.getCurrentPageProvider(); 148 if (pp != null) { 149 // include original page size options set on the page provider definition, as well as current page size if 150 // not present 151 List<Long> options = pp.getPageSizeOptions(); 152 if (options != null) { 153 values.addAll(options); 154 } 155 maxPageSize = pp.getMaxPageSize(); 156 } 157 if (cv.getUseGlobalPageSize()) { 158 // add the global page size if not present 159 Long globalSize = getGlobalPageSize(); 160 if (globalSize != null && globalSize > 0 && !values.contains(globalSize)) { 161 values.add(globalSize); 162 } 163 } 164 Collections.sort(values); 165 for (Long value : values) { 166 // remove every element that would be larger than max page size 167 if (maxPageSize > 0 && maxPageSize < value) { 168 break; 169 } 170 items.add(new SelectItem(value)); 171 } 172 // make sure the list is not empty 173 if (items.isEmpty()) { 174 if (maxPageSize > 0) { 175 items.add(new SelectItem(maxPageSize)); 176 } else { 177 items.add(new SelectItem(1)); 178 } 179 } 180 return items; 181 } 182 183 /** 184 * Sets the global page size 185 */ 186 public void setGlobalPageSize(Long pageSize) { 187 globalPageSize = pageSize; 188 } 189 190 public ContentView getContentView(String name) { 191 return getContentView(name, null); 192 } 193 194 /** 195 * Returns content view with given name, or null if no content view with this name is found. 196 * <p> 197 * If parameter searchDocumentModel is not null, it will be set on the content view. If it is null and the content 198 * is using a provider that needs it, a new document model is created and attached to it. This document model is 199 * resolved from the binding put in the content view XML definition, or from the document type in this definition if 200 * no binding is set. 201 * <p> 202 * If not null, this content view is set as the current content view so that subsequent calls to other methods can 203 * take information from it, like {@link #getCurrentGlobalPageSize()} 204 * <p> 205 * 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 206 * changes (if defined). 207 */ 208 public ContentView getContentView(String name, DocumentModel searchDocumentModel) { 209 ContentView cView = cache.get(name); 210 if (cView == null) { 211 cView = contentViewService.getContentView(name); 212 if (cView != null) { 213 cache.add(cView); 214 } 215 } 216 if (cView != null) { 217 if (searchDocumentModel != null) { 218 cView.setSearchDocumentModel(searchDocumentModel); 219 } 220 setCurrentContentView(cView); 221 } 222 return cView; 223 } 224 225 public ContentView getContentViewWithProvider(String name) { 226 return getContentViewWithProvider(name, null, null, null, null); 227 } 228 229 public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel) { 230 return getContentViewWithProvider(name, searchDocumentModel, null, null, null); 231 } 232 233 public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel, 234 List<SortInfo> sortInfos, Long pageSize, Long currentPage) { 235 return getContentViewWithProvider(name, searchDocumentModel, sortInfos, pageSize, currentPage, (Object[]) null); 236 } 237 238 public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel, 239 List<SortInfo> sortInfos, Long defaultPageSize, Long pageSize, Long currentPage) { 240 return getContentViewWithProvider(name, searchDocumentModel, sortInfos, defaultPageSize, pageSize, currentPage, 241 (Object[]) null); 242 } 243 244 /** 245 * @since 5.6 246 */ 247 public ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel, 248 List<SortInfo> sortInfos, Long pageSize, Long currentPage, Object... params) { 249 return getContentViewWithProvider(name, searchDocumentModel, sortInfos, Long.valueOf(-1), pageSize, currentPage, 250 params); 251 } 252 253 /** 254 * Helper method to retrieve a content view, taking care of initialization of page provider according to parameters 255 * and current global page size. 256 * <p> 257 * This method is not public to avoid EL method resolution issues. 258 */ 259 protected ContentView getContentViewWithProvider(String name, DocumentModel searchDocumentModel, 260 List<SortInfo> sortInfos, Long defaultPageSize, Long pageSize, Long currentPage, Object... params) { 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 IOException { 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(category, 390 createContentViewActionContext(currentDocument, contentView, showPageSizeSelector, showRefreshCommand, 391 showCSVExport, showPDFExport, showSyndicationLinks, showSlideshow, showEditColumns, 392 showEditRows, showSpreadsheet)); 393 } 394 395 protected ActionContext createContentViewActionContext(DocumentModel currentDocument, ContentView contentView, 396 Object showPageSizeSelector, Object showRefreshCommand, Object showCSVExport, Object showPDFExport, 397 Object showSyndicationLinks, Object showSlideshow, Object showEditColumns, Object showEditRows, 398 Object showSpreadsheet) { 399 ActionContext ctx; 400 FacesContext faces = FacesContext.getCurrentInstance(); 401 if (faces == null) { 402 ctx = new SeamActionContext(); 403 } else { 404 ctx = new JSFActionContext(faces); 405 } 406 ctx.setCurrentPrincipal(currentNuxeoPrincipal); 407 ctx.setDocumentManager(documentManager); 408 ctx.setCurrentDocument(currentDocument); 409 ctx.putLocalVariable("SeamContext", new SeamContextHelper()); 410 ctx.putLocalVariable("contentView", contentView); 411 // additional local variables for action filters 412 ctx.putLocalVariable("showPageSizeSelector", showPageSizeSelector); 413 ctx.putLocalVariable("showRefreshCommand", showRefreshCommand); 414 ctx.putLocalVariable("showCSVExport", showCSVExport); 415 ctx.putLocalVariable("showPDFExport", showPDFExport); 416 ctx.putLocalVariable("showSyndicationLinks", showSyndicationLinks); 417 ctx.putLocalVariable("showSlideshow", showSlideshow); 418 ctx.putLocalVariable("showEditColumns", showEditColumns); 419 ctx.putLocalVariable("showEditRows", showEditRows); 420 ctx.putLocalVariable("showSpreadsheet", showSpreadsheet); 421 return ctx; 422 } 423}