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