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.content; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.io.Serializable; 021import java.net.URLDecoder; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.avalon.framework.parameters.ParameterException; 029import org.apache.avalon.framework.parameters.Parameters; 030import org.apache.cocoon.ProcessingException; 031import org.apache.cocoon.caching.CacheableProcessingComponent; 032import org.apache.cocoon.environment.ObjectModelHelper; 033import org.apache.cocoon.environment.Request; 034import org.apache.cocoon.environment.Response; 035import org.apache.cocoon.environment.SourceResolver; 036import org.apache.cocoon.reading.AbstractReader; 037import org.apache.commons.io.IOUtils; 038import org.apache.excalibur.source.SourceValidity; 039import org.apache.excalibur.source.impl.validity.TimeStampValidity; 040import org.xml.sax.SAXException; 041 042import org.ametys.cms.repository.Content; 043import org.ametys.core.util.ImageHelper; 044import org.ametys.plugins.repository.metadata.CompositeMetadata; 045import org.ametys.plugins.repository.metadata.File; 046import org.ametys.plugins.repository.metadata.Folder; 047import org.ametys.plugins.repository.metadata.RichText; 048 049/** 050 * Reader for binary or file metadata. 051 */ 052public class ContentFileReader extends AbstractReader implements CacheableProcessingComponent 053{ 054 private Content _content; 055 private File _file; 056 057 private String _path; 058 059 private int _width; 060 private int _height; 061 private int _maxWidth; 062 private int _maxHeight; 063 private int _cropWidth; 064 private int _cropHeight; 065 066 private boolean _readForDownload; 067 private Collection<String> _allowedFormats = Arrays.asList(new String[]{"png", "gif", "jpg", "jpeg"}); 068 069 @Override 070 public void setup(SourceResolver res, Map objModel, String src, Parameters par) throws ProcessingException, SAXException, IOException 071 { 072 super.setup(res, objModel, src, par); 073 074 Request request = ObjectModelHelper.getRequest(objectModel); 075 _content = (Content) request.getAttribute(Content.class.getName()); 076 077 String metadataName = parameters.getParameter("metadata", null); 078 RichText richText = _getMeta(_content.getMetadataHolder(), metadataName); 079 080 Folder folder = richText.getAdditionalDataFolder(); 081 082 try 083 { 084 _path = parameters.getParameter("path"); 085 } 086 catch (ParameterException e) 087 { 088 throw new ProcessingException("The path parameter is mandatory for reading binary metadata.", e); 089 } 090 091 List<String> pathElements = Arrays.asList(_path.split("/")); 092 093 Iterator<String> it = pathElements.iterator(); 094 095 while (it.hasNext()) 096 { 097 String pathElement = it.next(); 098 pathElement = URLDecoder.decode(pathElement, "utf-8"); 099 100 if (it.hasNext()) 101 { 102 // not the last segment : it is a composite 103 folder = folder.getFolder(pathElement); 104 } 105 else 106 { 107 File file = folder.getFile(pathElement); 108 _file = file; 109 } 110 } 111 112 _readForDownload = par.getParameterAsBoolean("download", false); 113 Response response = ObjectModelHelper.getResponse(objectModel); 114 115 if (_readForDownload) 116 { 117 response.setHeader("Content-Disposition", "attachment; filename=\"" + _file.getName() + "\""); 118 } 119 120 // parameters for image resizing 121 _width = par.getParameterAsInteger("width", 0); 122 _height = par.getParameterAsInteger("height", 0); 123 _maxWidth = par.getParameterAsInteger("maxWidth", 0); 124 _maxHeight = par.getParameterAsInteger("maxHeight", 0); 125 _cropWidth = par.getParameterAsInteger("cropWidth", 0); 126 _cropHeight = par.getParameterAsInteger("cropHeight", 0); 127 } 128 129 @Override 130 public Serializable getKey() 131 { 132 return _path + "#" + _height + "#" + _width + "#" + _maxHeight + "#" + _maxWidth + "#" + _cropHeight + "#" + _cropWidth; 133 } 134 135 @Override 136 public SourceValidity getValidity() 137 { 138 return new TimeStampValidity(getLastModified()); 139 } 140 141 @Override 142 public long getLastModified() 143 { 144 if (_content != null) 145 { 146 return _content.getLastModified().getTime(); 147 } 148 149 return super.getLastModified(); 150 } 151 152 @Override 153 public String getMimeType() 154 { 155 if (_file != null) 156 { 157 return _file.getResource().getMimeType(); 158 } 159 160 return super.getMimeType(); 161 } 162 163 @Override 164 public void generate() throws IOException, SAXException, ProcessingException 165 { 166 try (InputStream is = _file.getResource().getInputStream()) 167 { 168 if (_isImage()) 169 { 170 // it's an image (which must be resized or not) 171 int i = _file.getName().lastIndexOf('.'); 172 String format = i != -1 ? _file.getName().substring(i + 1) : "png"; 173 format = _allowedFormats.contains(format) ? format : "png"; 174 ImageHelper.generateThumbnail(is, out, format, _height, _width, _maxHeight, _maxWidth, _cropHeight, _cropWidth); 175 } 176 else 177 { 178 Response response = ObjectModelHelper.getResponse(objectModel); 179 response.setHeader("Content-Length", Long.toString(_file.getResource().getLength())); 180 181 // Copy data in response 182 IOUtils.copy(is, out); 183 } 184 } 185 finally 186 { 187 IOUtils.closeQuietly(out); 188 } 189 } 190 191 @Override 192 public void recycle() 193 { 194 super.recycle(); 195 196 _file = null; 197 _content = null; 198 } 199 200 /** 201 * Determines if the file is an image 202 * @return <code>true</code> if file is a image 203 */ 204 protected boolean _isImage() 205 { 206 if (_width > 0 || _height > 0 || _maxHeight > 0 || _maxWidth > 0 || _cropHeight > 0 || _cropWidth > 0) 207 { 208 // resize is required, assume this is a image 209 return true; 210 } 211 else 212 { 213 return getMimeType() != null && getMimeType().startsWith("image/"); 214 } 215 } 216 217 /** 218 * Get the rich text meta 219 * @param meta The composite meta 220 * @param metadataName The metadata name (with /) 221 * @return The rich text meta 222 */ 223 protected RichText _getMeta(CompositeMetadata meta, String metadataName) 224 { 225 int pos = metadataName.indexOf("/"); 226 if (pos == -1) 227 { 228 return meta.getRichText(metadataName); 229 } 230 return _getMeta(meta.getCompositeMetadata(metadataName.substring(0, pos)), metadataName.substring(pos + 1)); 231 } 232 233}