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