001/* 002 * Copyright 2010 Anyware Services 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 */ 016package org.ametys.cms.source; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.avalon.framework.context.Context; 023import org.apache.avalon.framework.context.ContextException; 024import org.apache.avalon.framework.context.Contextualizable; 025import org.apache.avalon.framework.logger.AbstractLogEnabled; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.cocoon.components.ContextHelper; 030import org.apache.cocoon.environment.Request; 031import org.apache.commons.lang3.StringUtils; 032import org.apache.commons.lang3.tuple.Pair; 033import org.apache.excalibur.source.Source; 034import org.apache.excalibur.source.SourceNotFoundException; 035import org.apache.excalibur.source.SourceResolver; 036 037import org.ametys.cms.content.GetContentAction; 038import org.ametys.cms.contenttype.ContentTypeDescriptor; 039import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 040import org.ametys.cms.contenttype.ContentTypesHelper; 041import org.ametys.cms.contenttype.DynamicContentTypeDescriptorExtentionPoint; 042 043/** 044 * Default implementation of a {@link ContentView} 045 * Will first look in directory context://WEB-INF/stylesheets/content/article/article-[view].[extension] 046 * And if the file does not exist will search in plugin:[currentPluginName]://stylesheets/content/article/article-[view].[extension] 047 */ 048public class DefaultContentView extends AbstractLogEnabled implements ContentView, Serviceable, Contextualizable 049{ 050 /** The source resolver */ 051 protected SourceResolver _resolver; 052 /** Helper for content types */ 053 protected ContentTypesHelper _contentTypesHelper; 054 /** The content types extension point */ 055 protected ContentTypeExtensionPoint _contentTypeEP; 056 /** The dynamic content type descriptior extension point */ 057 protected DynamicContentTypeDescriptorExtentionPoint _dynamicContentTypeDescriptorEP; 058 059 060 /** Context to get full content type ID from request */ 061 protected Context _context; 062 063 public void contextualize(Context context) throws ContextException 064 { 065 _context = context; 066 } 067 068 @Override 069 public void service(ServiceManager manager) throws ServiceException 070 { 071 _resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 072 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 073 _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 074 _dynamicContentTypeDescriptorEP = (DynamicContentTypeDescriptorExtentionPoint) manager.lookup(DynamicContentTypeDescriptorExtentionPoint.ROLE); 075 } 076 077 @Override 078 public Source getSource(String location, String contentType, String view, String format, String pluginName, String extension) throws IOException 079 { 080 Request request = ContextHelper.getRequest(_context); 081 String fullContentTypeId = (String) request.getAttribute(GetContentAction.RESULT_CONTENTTYPE); 082 083 for (String s : _getSourceURIs(contentType, fullContentTypeId, view, format, pluginName, extension)) 084 { 085 try 086 { 087 Source src = _resolver.resolveURI(s); 088 if (!src.exists()) 089 { 090 if (getLogger().isDebugEnabled()) 091 { 092 getLogger().debug("Failed to find a stylesheet at '" + s + "'."); 093 } 094 } 095 else 096 { 097 return src; 098 } 099 } 100 catch (IOException e) 101 { 102 if (getLogger().isDebugEnabled()) 103 { 104 getLogger().debug("Resolving protocol failed for resolving '" + s + "'."); 105 } 106 } 107 } 108 109 // should never occur because of the default stylesheet 110 throw new SourceNotFoundException("Can't find a stylesheet for: " + location); 111 } 112 113 /** 114 * Returns the ordered list of URI to be tested to find a stylesheet to render the Content. 115 * @param contentTypeAlias the content type alias for rendering 116 * @param contentTypeId the content type full ID 117 * @param view the content view 118 * @param format the format of the output (html, pdf, ...) 119 * @param pluginName the current plugin name 120 * @param extension the extension (xsl, xml, ...) 121 * @return a list of URIs 122 */ 123 protected List<String> _getSourceURIs(String contentTypeAlias, String contentTypeId, String view, String format, String pluginName, String extension) 124 { 125 String formatSuffix = "html".equals(format) ? "" : "2" + format; 126 127 ArrayList<String> result = new ArrayList<>(); 128 129 ContentTypeDescriptor contentType = _contentTypeEP.getExtension(contentTypeId); 130 if (contentType != null) 131 { 132 result.addAll(_getSourceURIsForContentType(contentTypeAlias, contentTypeId, view, formatSuffix, extension)); 133 result.addAll(_getSourceURIsForSuperTypes(contentTypeAlias, contentTypeId, view, formatSuffix, extension)); 134 } 135 else 136 { 137 contentType = _dynamicContentTypeDescriptorEP.getExtension(contentTypeId); 138 if (contentType != null) 139 { 140 result.addAll(_getSourceURIsForDynamicContentType(contentTypeAlias, view, formatSuffix, contentType.getPluginName(), extension)); 141 } 142 } 143 144 // then a default stylesheet 145 result.addAll(_getDefaultSourceURIsForContentType(contentType, pluginName, formatSuffix, extension)); 146 147 if ("xsl".equals(extension)) 148 { 149 // and finally a dynamically constructed XSL, based on the model 150 result.add("cocoon://_plugins/cms/default-content/" + format + "." + extension + "?viewName=" + view + (StringUtils.isNotBlank(pluginName) ? "&pluginName=" + pluginName : "")); 151 } 152 153 return result; 154 } 155 156 /** 157 * Get the URIs of default stylesheets 158 * @param contentType The content type 159 * @param pluginName The current plugin name 160 * @param formatSuffix the format of the output (html, pdf, ...) 161 * @param extension the extension (xsl, xml, ...) 162 * @return a list of URIs 163 */ 164 protected List<String> _getDefaultSourceURIsForContentType(ContentTypeDescriptor contentType, String pluginName, String formatSuffix, String extension) 165 { 166 List<String> result = new ArrayList<>(); 167 168 if (contentType != null && !contentType.getPluginName().equals(pluginName)) 169 { 170 result.add("plugin:" + contentType.getPluginName() + "://stylesheets/default-content" + formatSuffix + "." + extension); 171 } 172 173 if (pluginName != null) 174 { 175 result.add("plugin:" + pluginName + "://stylesheets/default-content" + formatSuffix + "." + extension); 176 } 177 178 result.addAll(_getDefaultSourceURIs(pluginName, formatSuffix, extension)); 179 180 return result; 181 } 182 183 /** 184 * Get the URIs of default stylesheets 185 * @param pluginName The current plugin name 186 * @param formatSuffix the format of the output (html, pdf, ...) 187 * @param extension the extension (xsl, xml, ...) 188 * @return a list of URIs 189 */ 190 protected List<String> _getDefaultSourceURIs(String pluginName, String formatSuffix, String extension) 191 { 192 List<String> result = new ArrayList<>(); 193 194 if (!"cms".equals(pluginName)) 195 { 196 result.add("plugin:cms://stylesheets/default-content" + formatSuffix + "." + extension); 197 } 198 return result; 199 } 200 201 /** 202 * Get source for a given content type 203 * @param contentTypeAlias the content type alias for rendering 204 * @param contentTypeId the content type full ID 205 * @param view the content view 206 * @param formatSuffix the format of the output (html, pdf, ...) 207 * @param extension the extension (xsl, xml, ...) 208 * @return the list 209 */ 210 protected List<String> _getSourceURIsForSuperTypes(String contentTypeAlias, String contentTypeId, String view, String formatSuffix, String extension) 211 { 212 ArrayList<String> result = new ArrayList<>(); 213 214 Pair<String[], String[]> supertypeIds = _contentTypesHelper.getSupertypeIds(contentTypeId); 215 216 final String dynamicContentTypeId = _contentTypesHelper.getDynamicContentTypeId(supertypeIds.getLeft(), supertypeIds.getRight()); 217 if (dynamicContentTypeId != null) 218 { 219 final String dynamicContentTypePluginName = _contentTypesHelper.getDynamicContentTypePlugin(supertypeIds.getLeft(), supertypeIds.getRight()); 220 result.addAll(_getSourceURIsForDynamicContentType(dynamicContentTypeId, view, formatSuffix, dynamicContentTypePluginName, extension)); 221 } 222 223 for (String superTypeId : supertypeIds.getLeft()) 224 { 225 result.addAll(_getSourceURIsForContentType(superTypeId, superTypeId, view, formatSuffix, extension)); 226 } 227 228 for (String superTypeId : supertypeIds.getLeft()) 229 { 230 result.addAll(_getSourceURIsForSuperTypes(superTypeId, superTypeId, view, formatSuffix, extension)); 231 } 232 233 return result; 234 } 235 236 /** 237 * get source for a dynamic content type 238 * @param contentTypeAlias the content type alias for rendering 239 * @param view the content view 240 * @param formatSuffix the format of the output (html, pdf, ...) 241 * @param pluginName the plugin name 242 * @param extension the extension (xsl, xml, ...) 243 * @return the list 244 */ 245 protected List<String> _getSourceURIsForDynamicContentType(String contentTypeAlias, String view, String formatSuffix, String pluginName, String extension) 246 { 247 ArrayList<String> result = new ArrayList<>(); 248 249 result.add("context://WEB-INF/param/content-types/_dynamic/" + pluginName + "/stylesheets/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "-" + view + "." + extension); 250 result.add("context://WEB-INF/param/content-types/_dynamic/" + pluginName + "/stylesheets/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "." + extension); 251 252 return result; 253 } 254 255 /** 256 * get source for a non dynamic content type 257 * @param contentTypeAlias the content type alias for rendering 258 * @param contentTypeId the content type full ID 259 * @param view the content view 260 * @param formatSuffix the format of the output (html, pdf, ...) 261 * @param extension the extension (xsl, xml, ...) 262 * @return the list 263 */ 264 protected List<String> _getSourceURIsForContentType(String contentTypeAlias, String contentTypeId, String view, String formatSuffix, String extension) 265 { 266 ArrayList<String> result = new ArrayList<>(); 267 268 String pluginName = _contentTypesHelper.getPluginName(contentTypeId); 269 270 result.addAll(_getSourceURIsForContentTypeWithPlugin(pluginName, contentTypeAlias, contentTypeId, view, formatSuffix, extension)); 271 if (!contentTypeAlias.equals(contentTypeId)) 272 { 273 result.addAll(_getSourceURIsForContentTypeWithPlugin(pluginName, contentTypeId, contentTypeId, view, formatSuffix, extension)); 274 } 275 276 return result; 277 } 278 279 /** 280 * Get source for a non dynamic content type, call _getSourceURIsForContentType() which call this method twice if necessary (with the alias and the real content type id). 281 * @param pluginName the plugin name of the content type 282 * @param contentTypeAlias the content type alias for rendering 283 * @param contentTypeId the content type id used to build the URIs 284 * @param view the content view 285 * @param formatSuffix the format of the output (html, pdf, ...) 286 * @param extension the extension (xsl, xml, ...) 287 * @return the list 288 */ 289 protected List<String> _getSourceURIsForContentTypeWithPlugin(String pluginName, String contentTypeAlias, String contentTypeId, String view, String formatSuffix, String extension) 290 { 291 ArrayList<String> result = new ArrayList<>(); 292 293 // first look in the filesystem (case of automatic content types) 294 result.add("context://WEB-INF/param/content-types/" + pluginName + "/stylesheets/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "-" + view + "." + extension); 295 result.add("context://WEB-INF/param/content-types/" + pluginName + "/stylesheets/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "." + extension); 296 297 // then look in the plugin 298 result.add("plugin:" + pluginName + "://stylesheets/content/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "-" + view + "." + extension); 299 result.add("plugin:" + pluginName + "://stylesheets/content/" + contentTypeAlias + "/" + contentTypeAlias + formatSuffix + "." + extension); 300 301 return result; 302 } 303}