001/* 002 * Copyright 2011 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.skinfactory.parameters; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.FileOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.excalibur.source.SourceUtil; 032import org.xml.sax.ContentHandler; 033import org.xml.sax.SAXException; 034 035import org.ametys.runtime.i18n.I18nizableText; 036import org.ametys.web.skin.SkinModel; 037 038/** 039 * Implementation of {@link AbstractSkinParameter} for an image 040 */ 041public class ImageParameter extends AbstractSkinParameter 042{ 043 private String _iconGlyph; 044 private String _iconSmall; 045 private String _iconLarge; 046 private boolean _localUploadEnabled; 047 private String _imagePath; 048 049 /** 050 * Constructor 051 * @param relPath the relative path of the target image 052 * @param label the label 053 * @param description the description 054 */ 055 public ImageParameter(String relPath, I18nizableText label, I18nizableText description) 056 { 057 super(relPath.replaceAll("\\\\", "/"), label, description); 058 _imagePath = relPath.replaceAll("\\\\", "/"); 059 _localUploadEnabled = false; 060 } 061 062 /** 063 * Constructor 064 * @param relPath the relative path of the target image 065 * @param label the label 066 * @param description the description 067 * @param iconGlyph The CSS classe for icon, to use instead of small and large icon 068 * @param iconSmall The small icon 069 * @param iconLarge The large icon 070 */ 071 public ImageParameter(String relPath, I18nizableText label, I18nizableText description, String iconGlyph, String iconSmall, String iconLarge) 072 { 073 super(relPath.replaceAll("\\\\", "/"), label, description); 074 _imagePath = relPath.replaceAll("\\\\", "/"); 075 _localUploadEnabled = false; 076 _iconGlyph = iconGlyph; 077 _iconLarge = iconLarge; 078 _iconSmall = iconSmall; 079 } 080 081 @Override 082 public SkinParameterType getType() 083 { 084 return SkinParameterType.IMAGE; 085 } 086 087 /** 088 * Determines if local upload is enabled 089 * @return true if local upload is enabled 090 */ 091 public boolean isLocalUploadEnabled () 092 { 093 return _localUploadEnabled; 094 } 095 096 /** 097 * Get relative path of images 098 * @return the relative path of images 099 */ 100 public String getLibraryPath () 101 { 102 return this._imagePath; 103 } 104 105 /** 106 * Set the CSS icon 107 * @param iconGlyph the CSS icon 108 */ 109 public void setIconGlyph(String iconGlyph) 110 { 111 _iconGlyph = iconGlyph; 112 } 113 114 /** 115 * Get the CSS icon 116 * @return The CSS icon 117 */ 118 public String getIconGlyph () 119 { 120 return _iconGlyph; 121 } 122 123 /** 124 * Set the small icon relative path 125 * @param iconSmall the relative path of the small icon 126 */ 127 public void setIconSmall(String iconSmall) 128 { 129 _iconSmall = iconSmall; 130 } 131 132 /** 133 * Get the small icon 134 * @return The small icon 135 */ 136 public String getIconSmall () 137 { 138 return _iconSmall; 139 } 140 141 /** 142 * Set the large icon relative path 143 * @param iconLarge the relative path of the large icon 144 */ 145 public void setIconLarge(String iconLarge) 146 { 147 _iconLarge = iconLarge; 148 } 149 150 /** 151 * Get the large icon 152 * @return The large icon 153 */ 154 public String getIconLarge () 155 { 156 return _iconLarge; 157 } 158 159 @Override 160 public void apply(File tempDir, File modelDir, Object value, String lang) 161 { 162 File targetFile = new File (tempDir, "resources/img/" + this._imagePath); 163 File srcFile = null; 164 165 boolean uploaded = value instanceof FileValue ? ((FileValue) value).isUploaded() : false; 166 File libraryFile = _getLibraryFile(tempDir, modelDir, uploaded); 167 168 String filePath = value instanceof FileValue ? ((FileValue) value).getPath() : (String) value; 169 srcFile = new File (libraryFile, filePath); 170 171 _copyFile (srcFile, targetFile); 172 173 // Update css file last modified date to avoid cache issue in case of background image 174 for (File file : _listCSSFiles (new File(tempDir, "/resources"))) 175 { 176 file.setLastModified(new Date().getTime()); 177 } 178 } 179 180 private List<File> _listCSSFiles (File file) 181 { 182 List<File> files = new ArrayList<>(); 183 184 File[] listFiles = file.listFiles(); 185 if (listFiles != null) 186 { 187 for (File child : listFiles) 188 { 189 if (child.isFile() && child.getName().endsWith(".css")) 190 { 191 files.add(child); 192 } 193 else if (child.isDirectory()) 194 { 195 files.addAll(_listCSSFiles(child)); 196 } 197 } 198 } 199 200 return files; 201 } 202 private File _getLibraryFile (File tempDir, File modelDir, boolean uploaded) 203 { 204 if (uploaded) 205 { 206 return new File(tempDir, "model/_uploads/" + this._imagePath); 207 } 208 else 209 { 210 return new File(modelDir, "model/images/" + this._imagePath); 211 } 212 } 213 214 private void _copyFile (File srcFile, File targetFile) 215 { 216 if (!srcFile.exists()) 217 { 218 return; 219 } 220 221 try (InputStream is = new FileInputStream(srcFile)) 222 { 223 if (!targetFile.exists()) 224 { 225 targetFile.getParentFile().mkdirs(); 226 targetFile.createNewFile(); 227 } 228 229 SourceUtil.copy(new FileInputStream(srcFile), new FileOutputStream(targetFile)); 230 231 targetFile.setLastModified(new Date().getTime()); 232 } 233 catch (IOException e) 234 { 235 throw new SkinParameterException ("Unable to apply image parameter '" + getId() + "'", e); 236 } 237 } 238 239 @Override 240 public void toSAX(ContentHandler contentHandler, String modelName) throws SAXException 241 { 242 AttributesImpl attrs = new AttributesImpl(); 243 attrs.addCDATAAttribute("id", getId()); 244 attrs.addCDATAAttribute("type", SkinParameterType.IMAGE.name().toLowerCase()); 245 246 XMLUtils.startElement(contentHandler, "parameter", attrs); 247 248 getLabel().toSAX(contentHandler, "label"); 249 getDescription().toSAX(contentHandler, "description"); 250 XMLUtils.createElement(contentHandler, "path", this._imagePath); 251 252 if (getIconGlyph() != null) 253 { 254 XMLUtils.createElement(contentHandler, "iconGlyph", getIconGlyph()); 255 } 256 257 if (getIconSmall() != null) 258 { 259 XMLUtils.createElement(contentHandler, "iconSmall", "/plugins/skinfactory/" + modelName + "/_thumbnail/16/16/model/images/" + this._imagePath + "/" + getIconSmall()); 260 } 261 else 262 { 263 XMLUtils.createElement(contentHandler, "iconSmall", "/plugins/skinfactory/resources/img/button/image_16.png"); 264 } 265 266 if (getIconLarge() != null) 267 { 268 XMLUtils.createElement(contentHandler, "iconLarge", "/plugins/skinfactory/" + modelName + "/_thumbnail/32/32/model/images/" + this._imagePath + "/" + getIconLarge()); 269 } 270 else 271 { 272 XMLUtils.createElement(contentHandler, "iconLarge", "/plugins/skinfactory/resources/img/button/image_32.png"); 273 } 274 275 XMLUtils.endElement(contentHandler, "parameter"); 276 } 277 278 @Override 279 public Map<String, Object> toJson(String modelName) 280 { 281 Map<String, Object> jsonObject = new HashMap<>(); 282 283 jsonObject.put("id", getId()); 284 jsonObject.put("type", SkinParameterType.IMAGE.name().toLowerCase()); 285 286 jsonObject.put("label", getLabel()); 287 jsonObject.put("description", getDescription()); 288 jsonObject.put("path", this._imagePath); 289 290 if (getIconGlyph() != null) 291 { 292 jsonObject.put("iconGlyph", getIconGlyph()); 293 } 294 295 if (getIconSmall() != null) 296 { 297 jsonObject.put("iconSmall", "/plugins/skinfactory/" + modelName + "/_thumbnail/16/16/model/images/" + this._imagePath + "/" + getIconSmall()); 298 } 299 else 300 { 301 jsonObject.put("iconSmall", "/plugins/skinfactory/resources/img/button/image_16.png"); 302 } 303 304 if (getIconLarge() != null) 305 { 306 jsonObject.put("iconLarge", "/plugins/skinfactory/" + modelName + "/_thumbnail/32/32/model/images/" + this._imagePath + "/" + getIconLarge()); 307 } 308 else 309 { 310 jsonObject.put("iconLarge", "/plugins/skinfactory/resources/img/button/image_32.png"); 311 } 312 313 return jsonObject; 314 } 315 316 317 @Override 318 public FileValue getDefaultValue(SkinModel model) 319 { 320 File libraryFile = new File(model.getFile(), "model/images/" + this._imagePath); 321 322 for (File file : libraryFile.listFiles()) 323 { 324 if (file.isDirectory()) 325 { 326 for (File child : file.listFiles()) 327 { 328 if (_isImage(child)) 329 { 330 String defaultPath = child.getAbsolutePath().substring(libraryFile.getAbsolutePath().length() + 1); 331 defaultPath.replaceAll(File.separator.equals("\\") ? File.separator + File.separator : File.separator, "."); 332 333 return new FileValue(defaultPath, false); 334 } 335 } 336 } 337 else if (_isImage(file)) 338 { 339 String defaultPath = file.getAbsolutePath().substring(libraryFile.getAbsolutePath().length() + 1); 340 defaultPath.replaceAll(File.separator.equals("\\") ? File.separator + File.separator : File.separator, "."); 341 342 return new FileValue(defaultPath, false); 343 } 344 } 345 346 return null; 347 } 348 349 private boolean _isImage(File file) 350 { 351 if (file.isDirectory()) 352 { 353 return false; 354 } 355 356 String name = file.getName().toLowerCase(); 357 int index = name.lastIndexOf("."); 358 String ext = name.substring(index + 1); 359 360 if (name.equals("thumbnail_16.png") || name.equals("thumbnail_32.png") || name.equals("thumbnail_48.png")) 361 { 362 return false; 363 } 364 365 return "png".equals(ext) || "gif".equals(ext) || "jpg".equals(ext) || "jpeg".equals(ext); 366 } 367 368 @Override 369 public FileValue getDefaultValue(SkinModel model, String lang) 370 { 371 return getDefaultValue(model); 372 } 373 374 /** 375 * Class representing a file value 376 * 377 */ 378 public static class FileValue 379 { 380 private String _path; 381 private boolean _uploaded; 382 383 /** 384 * Constructor 385 * @param path The relative file path 386 * @param uploaded <code>true</code> if the file was uploaded 387 */ 388 public FileValue(String path, boolean uploaded) 389 { 390 _uploaded = uploaded; 391 _path = path; 392 } 393 394 /** 395 * Determines if the file was uploaded 396 * @return <code>true</code> if the file was uploaded 397 */ 398 public boolean isUploaded () 399 { 400 return _uploaded; 401 } 402 403 /** 404 * Get the relative file path 405 * @return the relative file path 406 */ 407 public String getPath () 408 { 409 return _path; 410 } 411 } 412 413}