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