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