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.webapp.contentbrowser; 018 019import static org.jboss.seam.ScopeType.CONVERSATION; 020import static org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfigurationConstants.CONTENT_VIEW_CONFIGURATION_CATEGORY; 021import static org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfigurationConstants.CONTENT_VIEW_CONFIGURATION_FACET; 022 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.LinkedHashMap; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.jboss.seam.annotations.In; 034import org.jboss.seam.annotations.Name; 035import org.jboss.seam.annotations.Observer; 036import org.jboss.seam.annotations.Scope; 037import org.jboss.seam.annotations.intercept.BypassInterceptors; 038import org.nuxeo.ecm.core.api.DocumentModel; 039import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService; 040import org.nuxeo.ecm.platform.actions.Action; 041import org.nuxeo.ecm.platform.actions.ActionContext; 042import org.nuxeo.ecm.platform.contentview.jsf.ContentView; 043import org.nuxeo.ecm.platform.contentview.jsf.ContentViewHeader; 044import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService; 045import org.nuxeo.ecm.platform.types.adapter.TypeInfo; 046import org.nuxeo.ecm.platform.types.localconfiguration.ContentViewConfiguration; 047import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 048import org.nuxeo.ecm.platform.ui.web.api.WebActions; 049import org.nuxeo.ecm.webapp.action.ActionContextProvider; 050import org.nuxeo.ecm.webapp.helpers.EventNames; 051import org.nuxeo.runtime.api.Framework; 052 053/** 054 * Handles available content views defined on a document type per category, as well as helper methods to retrieve 055 * selection actions for a given content view. 056 * 057 * @author Anahide Tchertchian 058 * @since 5.4 059 */ 060@Name("documentContentViewActions") 061@Scope(CONVERSATION) 062public class DocumentContentViewActions implements Serializable { 063 064 private static final long serialVersionUID = 1L; 065 066 private static final Log log = LogFactory.getLog(DocumentContentViewActions.class); 067 068 @In(create = true, required = false) 069 protected transient NavigationContext navigationContext; 070 071 @In(create = true, required = false) 072 protected transient WebActions webActions; 073 074 @In(create = true, required = false) 075 protected transient ActionContextProvider actionContextProvider; 076 077 @In(create = true) 078 protected ContentViewService contentViewService; 079 080 /** 081 * Map caching content views defined on a given document type 082 */ 083 protected Map<String, Map<String, List<ContentViewHeader>>> typeToContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>(); 084 085 protected Map<String, List<ContentViewHeader>> currentAvailableContentViews; 086 087 /** 088 * Map caching content views shown in export defined on a given document type 089 */ 090 protected Map<String, Map<String, List<ContentViewHeader>>> typeToExportContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>(); 091 092 protected Map<String, List<ContentViewHeader>> currentExportContentViews; 093 094 // API for content view support 095 096 protected Map<String, List<ContentViewHeader>> getContentViewHeaders(TypeInfo typeInfo, boolean export) 097 { 098 Map<String, List<ContentViewHeader>> res = new LinkedHashMap<String, List<ContentViewHeader>>(); 099 Map<String, String[]> cvNamesByCat; 100 if (export) { 101 cvNamesByCat = typeInfo.getContentViewsForExport(); 102 } else { 103 cvNamesByCat = typeInfo.getContentViews(); 104 } 105 if (cvNamesByCat != null) { 106 for (Map.Entry<String, String[]> cvNameByCat : cvNamesByCat.entrySet()) { 107 List<ContentViewHeader> headers = new ArrayList<ContentViewHeader>(); 108 String[] cvNames = cvNameByCat.getValue(); 109 if (cvNames != null) { 110 for (String cvName : cvNames) { 111 ContentViewHeader header = contentViewService.getContentViewHeader(cvName); 112 if (header != null) { 113 headers.add(header); 114 } 115 } 116 } 117 res.put(cvNameByCat.getKey(), headers); 118 } 119 } 120 return res; 121 } 122 123 protected void retrieveContentViewHeaders(DocumentModel doc) { 124 String docType = doc.getType(); 125 if (!typeToContentView.containsKey(docType)) { 126 TypeInfo typeInfo = doc.getAdapter(TypeInfo.class); 127 Map<String, List<ContentViewHeader>> byCategories = getContentViewHeaders(typeInfo, false); 128 typeToContentView.put(docType, byCategories); 129 } 130 } 131 132 protected void retrieveExportContentViewHeaders(DocumentModel doc) { 133 String docType = doc.getType(); 134 if (!typeToExportContentView.containsKey(docType)) { 135 TypeInfo typeInfo = doc.getAdapter(TypeInfo.class); 136 Map<String, List<ContentViewHeader>> byCategories = getContentViewHeaders(typeInfo, true); 137 typeToExportContentView.put(docType, byCategories); 138 } 139 } 140 141 /** 142 * Returns true if content views are defined on given document for given category. 143 * <p> 144 * Also fetches content view headers defined on a document type 145 */ 146 public boolean hasContentViewSupport(DocumentModel doc, String category) { 147 if (doc == null) { 148 return false; 149 } 150 retrieveContentViewHeaders(doc); 151 String docType = doc.getType(); 152 if (!typeToContentView.get(docType).containsKey(category)) { 153 return false; 154 } 155 return !typeToContentView.get(docType).get(category).isEmpty(); 156 } 157 158 public Map<String, List<ContentViewHeader>> getAvailableContentViewsForDocument(DocumentModel doc) 159 { 160 if (doc == null) { 161 return Collections.emptyMap(); 162 } 163 retrieveContentViewHeaders(doc); 164 String docType = doc.getType(); 165 return typeToContentView.get(docType); 166 } 167 168 public List<ContentViewHeader> getAvailableContentViewsForDocument(DocumentModel doc, String category) 169 { 170 if (doc == null) { 171 return Collections.emptyList(); 172 } 173 if (CONTENT_VIEW_CONFIGURATION_CATEGORY.equals(category)) { 174 List<ContentViewHeader> localHeaders = getLocalConfiguredContentViews(doc); 175 if (localHeaders != null) { 176 return localHeaders; 177 } 178 } 179 retrieveContentViewHeaders(doc); 180 String docType = doc.getType(); 181 if (!typeToContentView.get(docType).containsKey(category)) { 182 return Collections.emptyList(); 183 } 184 return typeToContentView.get(doc.getType()).get(category); 185 } 186 187 public Map<String, List<ContentViewHeader>> getAvailableContentViewsForCurrentDocument() { 188 if (currentAvailableContentViews == null) { 189 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 190 currentAvailableContentViews = getAvailableContentViewsForDocument(currentDocument); 191 } 192 return currentAvailableContentViews; 193 } 194 195 public List<ContentViewHeader> getAvailableContentViewsForCurrentDocument(String category) { 196 if (CONTENT_VIEW_CONFIGURATION_CATEGORY.equals(category)) { 197 DocumentModel currentDoc = navigationContext.getCurrentDocument(); 198 List<ContentViewHeader> localHeaders = getLocalConfiguredContentViews(currentDoc); 199 if (localHeaders != null) { 200 return localHeaders; 201 } 202 } 203 getAvailableContentViewsForCurrentDocument(); 204 return currentAvailableContentViews.get(category); 205 } 206 207 public List<ContentViewHeader> getLocalConfiguredContentViews(DocumentModel doc) { 208 LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class); 209 ContentViewConfiguration configuration = localConfigurationService.getConfiguration( 210 ContentViewConfiguration.class, CONTENT_VIEW_CONFIGURATION_FACET, doc); 211 if (configuration == null) { 212 return null; 213 } 214 List<String> cvNames = configuration.getContentViewsForType(doc.getType()); 215 if (cvNames == null) { 216 return null; 217 } 218 List<ContentViewHeader> headers = new ArrayList<ContentViewHeader>(); 219 for (String cvName : cvNames) { 220 ContentViewHeader header = contentViewService.getContentViewHeader(cvName); 221 if (header != null) { 222 headers.add(header); 223 } 224 } 225 if (!headers.isEmpty()) { 226 return headers; 227 } 228 return null; 229 } 230 231 public Map<String, List<ContentViewHeader>> getExportContentViewsForDocument(DocumentModel doc) 232 { 233 if (doc == null) { 234 return Collections.emptyMap(); 235 } 236 retrieveExportContentViewHeaders(doc); 237 String docType = doc.getType(); 238 return typeToExportContentView.get(docType); 239 } 240 241 public Map<String, List<ContentViewHeader>> getExportContentViewsForCurrentDocument() { 242 if (currentExportContentViews == null) { 243 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 244 currentExportContentViews = getExportContentViewsForDocument(currentDocument); 245 } 246 return currentExportContentViews; 247 } 248 249 public List<ContentViewHeader> getExportContentViewsForCurrentDocument(String category) { 250 getExportContentViewsForCurrentDocument(); 251 return currentExportContentViews.get(category); 252 } 253 254 @Observer(value = { EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED }, create = false) 255 @BypassInterceptors 256 public void documentChanged() { 257 currentAvailableContentViews = null; 258 currentExportContentViews = null; 259 } 260 261 /** 262 * Resets typeToContentView cache on {@link EventNames#FLUSH_EVENT}, triggered by hot reload when dev mode is set. 263 * 264 * @since 5.7.1 265 */ 266 @Observer(value = { EventNames.FLUSH_EVENT }) 267 @BypassInterceptors 268 public void onHotReloadFlush() { 269 typeToContentView = new HashMap<String, Map<String, List<ContentViewHeader>>>(); 270 } 271 272 /** 273 * Helper to retrieve selection actions for a given content view, passing additional variables to the 274 * {@link ActionContext} used for resolution. 275 * 276 * @since 2.17 277 * @param category 278 * @param contentView 279 * @param selectedEntries 280 */ 281 public List<Action> getSelectionActions(String category, ContentView contentView, List<Object> selectedEntries) { 282 ActionContext ctx = actionContextProvider.createActionContext(); 283 ctx.putLocalVariable("contentView", contentView); 284 ctx.putLocalVariable("selectedDocuments", selectedEntries); 285 return webActions.getActionsList(category, ctx, false); 286 } 287}