001/* 002 * Copyright 2015 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 */ 016 017package org.ametys.skinfactory.model; 018 019import java.io.File; 020import java.io.IOException; 021import java.text.DateFormat; 022import java.text.SimpleDateFormat; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.cocoon.ProcessingException; 036import org.apache.commons.io.FileUtils; 037 038import org.ametys.core.ui.Callable; 039import org.ametys.plugins.skincommons.SkinEditionHelper; 040import org.ametys.runtime.plugin.component.AbstractLogEnabled; 041import org.ametys.runtime.util.AmetysHomeHelper; 042import org.ametys.skinfactory.SkinFactoryComponent; 043import org.ametys.skinfactory.filefilter.ModelFileFilter; 044import org.ametys.web.cocoon.I18nTransformer; 045import org.ametys.web.cocoon.I18nUtils; 046import org.ametys.web.skin.Skin; 047import org.ametys.web.skin.SkinDAO; 048import org.ametys.web.skin.SkinModel; 049import org.ametys.web.skin.SkinModelsManager; 050import org.ametys.web.skin.SkinsManager; 051 052/** 053 * Component for interact with a skin model 054 */ 055public class SkinModelDAO extends AbstractLogEnabled implements Serviceable, Component 056{ 057 private static final DateFormat _DATE_FORMAT = new SimpleDateFormat("yyyyMMdd-HHmm"); 058 059 private SkinsManager _skinsManager; 060 private SkinModelsManager _modelsManager; 061 private SkinFactoryComponent _skinFactoryManager; 062 private SkinEditionHelper _skinHelper; 063 private SkinDAO _skinDAO; 064 private I18nUtils _i18nUtils; 065 066 @Override 067 public void service(ServiceManager manager) throws ServiceException 068 { 069 _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE); 070 _modelsManager = (SkinModelsManager) manager.lookup(SkinModelsManager.ROLE); 071 _skinFactoryManager = (SkinFactoryComponent) manager.lookup(SkinFactoryComponent.ROLE); 072 _skinHelper = (SkinEditionHelper) manager.lookup(SkinEditionHelper.ROLE); 073 _skinDAO = (SkinDAO) manager.lookup(SkinDAO.ROLE); 074 _i18nUtils = (I18nUtils) manager.lookup(org.ametys.core.util.I18nUtils.ROLE); 075 } 076 077 /** 078 * Retrieve informations on a skin 079 * @param modelId The skin id 080 * @return the informations of a skin 081 */ 082 @Callable 083 public Map<String, Object> getModel(String modelId) 084 { 085 Map<String, Object> result = new HashMap<>(); 086 087 SkinModel model = _modelsManager.getModel(modelId); 088 089 if (model != null) 090 { 091 result.put("name", model.getId()); 092 result.put("title", model.getLabel()); 093 } 094 095 return result; 096 } 097 098 /** 099 * Retrieve the list of models and skins available 100 * @return a map of skins and models 101 * @throws ProcessingException if something goes wrong when retrieving the skins 102 */ 103 @Callable 104 public Map<String, Object> getSkinsAndModels() throws ProcessingException 105 { 106 Map<String, Object> result = _skinDAO.getSkins(); 107 108 result.put("models", _models2JsonObject()); 109 110 return result; 111 } 112 113 private List<Object> _models2JsonObject() throws ProcessingException 114 { 115 List<Object> modelsList = new ArrayList<>(); 116 Set<String> models = _modelsManager.getModels(); 117 for (String modelName : models) 118 { 119 Map<String, Object> jsonModel = new HashMap<>(); 120 SkinModel model = _modelsManager.getModel(modelName); 121 122 jsonModel.put("id", modelName); 123 jsonModel.put("label", model.getLabel()); 124 jsonModel.put("description", model.getDescription()); 125 jsonModel.put("iconLarge", model.getLargeImage()); 126 jsonModel.put("iconSmall", model.getSmallImage()); 127 128 modelsList.add(jsonModel); 129 } 130 131 return modelsList; 132 } 133 134 /** 135 * Determines if a model exists 136 * @param modelId The model id 137 * @return true if model exists. 138 * @throws ProcessingException if something goes wrong when retrieving the list of models 139 */ 140 @Callable 141 public boolean modelExists (String modelId) throws ProcessingException 142 { 143 return _modelsManager.getModels().contains(modelId); 144 } 145 146 /** 147 * Import a model from a zip file 148 * @param modelName The name of the new model 149 * @param tmpDirPath the tmp dir path where the zip has been uploaded 150 * @return The model name 151 * @throws IOException if something goes wrong when manipulating files 152 */ 153 @Callable 154 public String importModel(String modelName, String tmpDirPath) throws IOException 155 { 156 File ametysTmpDir = AmetysHomeHelper.getAmetysHomeTmp(); 157 File tmpDir = new File(ametysTmpDir, tmpDirPath.replace('/', File.separatorChar)); 158 159 if (tmpDir.isDirectory()) 160 { 161 File rootLocation = new File(_modelsManager.getModelsLocation()); 162 163 // If exists: remove. 164 File modelDir = new File(rootLocation, modelName); 165 if (modelDir.exists()) 166 { 167 FileUtils.deleteDirectory(modelDir); 168 } 169 170 // Move to models 171 FileUtils.moveDirectory(tmpDir, modelDir); 172 173 _i18nUtils.reloadCatalogues(); 174 I18nTransformer.needsReload(); 175 176 } 177 178 return modelName; 179 } 180 181 /** 182 * Generate a new skin from a model 183 * @param skinId The new skin id 184 * @param modelId The model 185 * @return An error message, or null on success 186 * @throws IOException if an error occurs when manipulating files 187 * @throws ProcessingException if an exception occurred during the generation processs 188 */ 189 @Callable 190 public String generateSkin(String skinId, String modelId) throws IOException, ProcessingException 191 { 192 // Check if exists 193 if (_skinsManager.getSkins().contains(skinId)) 194 { 195 return "already-exists"; 196 } 197 198 File modelDir = _modelsManager.getModel(modelId).getFile(); 199 File skinDir = new File(_skinsManager.getSkinsLocation(), skinId); 200 201 FileUtils.copyDirectory(modelDir, skinDir, false); 202 203 try 204 { 205 Skin skin = _skinsManager.getSkin(skinId); 206 207 // Create model.xml file 208 _modelsManager.generateModelFile (skinDir, modelId); 209 210 SkinModel model = _modelsManager.getModel(modelId); 211 String defaultColorTheme = model.getDefaultColorTheme(); 212 if (defaultColorTheme != null) 213 { 214 _skinFactoryManager.saveColorTheme(skin.getFile(), defaultColorTheme); 215 } 216 217 // Apply all parameters 218 _skinFactoryManager.applyModelParameters(modelId, skin.getFile()); 219 220 I18nTransformer.needsReload(); 221 _i18nUtils.reloadCatalogues(); 222 } 223 catch (Exception e) 224 { 225 // Delete skin directory if the generation failed 226 FileUtils.deleteDirectory(skinDir); 227 228 throw new ProcessingException("The generation of skin failed", e); 229 230 } 231 232 return null; 233 } 234 235 /** 236 * Apply the model to all its skins 237 * @param modelId The model id 238 * @return The set of modified skins id 239 * @throws IOException if an error occurs when manipulating files 240 */ 241 @Callable 242 public Map<String, Object> applyModelToAll(String modelId) throws IOException 243 { 244 Map<String, Object> result = new HashMap<>(); 245 246 result.put("modifiedSkins", new ArrayList<Map<String, Object>>()); 247 result.put("unmodifiedSkins", new ArrayList<Map<String, Object>>()); 248 249 Set<String> skins = _skinsManager.getSkins(); 250 251 for (String skinId : skins) 252 { 253 Skin skin = _skinsManager.getSkin(skinId); 254 if (modelId.equals(_modelsManager.getModelOfSkin(skin))) 255 { 256 if (applyModel(skin, modelId)) 257 { 258 @SuppressWarnings("unchecked") 259 List<Map<String, Object>> modifiedSkins = (List<Map<String, Object>>) result.get("modifiedSkins"); 260 modifiedSkins.add(_getSkinProperty(skin)); 261 } 262 else 263 { 264 @SuppressWarnings("unchecked") 265 List<Map<String, Object>> unmodifiedSkins = (List<Map<String, Object>>) result.get("unmodifiedSkins"); 266 unmodifiedSkins.add(_getSkinProperty(skin)); 267 } 268 } 269 } 270 271 return result; 272 } 273 274 private Map<String, Object> _getSkinProperty(Skin skin) 275 { 276 Map<String, Object> info = new HashMap<>(); 277 info.put("name", skin.getId()); 278 info.put("label", skin.getLabel()); 279 return info; 280 } 281 282 /** 283 * Apply model to the skin 284 * @param skinId The skin id 285 * @param modelId The id of model 286 * @return true if the model was applyed successfully 287 * @throws IOException if an error occurs when manipulating files 288 */ 289 @Callable 290 public boolean applyModel(String skinId, String modelId) throws IOException 291 { 292 Skin skin = _skinsManager.getSkin(skinId); 293 294 return applyModel(skin, modelId); 295 } 296 297 /** 298 * Apply model to the skin 299 * @param skin The skin 300 * @param modelId The id of model 301 * @return true if the model was applyed successfully 302 * @throws IOException if an error occurs when manipulating files 303 */ 304 protected boolean applyModel(Skin skin, String modelId) throws IOException 305 { 306 File skinDir = skin.getFile(); 307 308 // Prepare skin in temporary file 309 File tmpDir = new File (skinDir.getParentFile(), skin.getId() + "." + _DATE_FORMAT.format(new Date())); 310 311 // Copy the model 312 File modelDir = _modelsManager.getModel(modelId).getFile(); 313 FileUtils.copyDirectory(modelDir, tmpDir, new ModelFileFilter(modelDir), false); 314 315 // Copy upload images if exists 316 File uploadDir = new File(skinDir, "model/_uploads"); 317 if (uploadDir.exists()) 318 { 319 File tmpUploadDir = new File(tmpDir, "model/_uploads"); 320 tmpUploadDir.mkdirs(); 321 FileUtils.copyDirectory(uploadDir, tmpUploadDir); 322 } 323 324 // Copy model.xml file 325 File xmlFile = new File(skinDir, "model.xml"); 326 FileUtils.copyFileToDirectory(xmlFile, tmpDir); 327 File tmpXmlFile = new File(tmpDir, "model.xml"); 328 329 // Apply parameters 330 _skinFactoryManager.applyModelParameters(modelId, tmpDir); 331 _skinFactoryManager.updateHash(tmpXmlFile, _modelsManager.getModelHash(modelId)); 332 333 if (!_skinHelper.deleteQuicklyDirectory(skinDir)) 334 { 335 getLogger().error("Cannot delete skin directory {}", skinDir.getAbsolutePath()); 336 return false; 337 } 338 339 FileUtils.moveDirectory(tmpDir, skinDir); 340 return true; 341 } 342 343 /** 344 * Delete a model 345 * @param modelId The model id 346 * @throws IOException if an error occurs when manipulating files 347 */ 348 @Callable 349 public void delete(String modelId) throws IOException 350 { 351 SkinModel model = _modelsManager.getModel(modelId); 352 353 // Unlink skins 354 Set<String> skins = _skinsManager.getSkins(); 355 for (String skinId : skins) 356 { 357 Skin skin = _skinsManager.getSkin(skinId); 358 if (modelId.equals(_modelsManager.getModelOfSkin(skin))) 359 { 360 unlinkModel(skin); 361 } 362 } 363 364 File file = model.getFile(); 365 if (file.exists()) 366 { 367 FileUtils.deleteDirectory(file); 368 } 369 } 370 371 /** 372 * Unlink the skin from its model 373 * @param skinId The id of the skin 374 * @param modelId The id of the model 375 * @return An error code, or null on success 376 */ 377 @Callable 378 public String unlinkModel(String skinId, String modelId) 379 { 380 Skin skin = _skinsManager.getSkin(skinId); 381 382 if (!modelId.equals(_modelsManager.getModelOfSkin(skin))) 383 { 384 return "incorrect-model"; 385 } 386 387 unlinkModel(skin); 388 389 return null; 390 } 391 392 private void unlinkModel(Skin skin) 393 { 394 File skinDir = skin.getFile(); 395 396 File modelFile = new File (skinDir, "model.xml"); 397 File bakFile = new File (skinDir, "model.xml.bak"); 398 399 if (bakFile.exists()) 400 { 401 // Delete old bak file if exists 402 bakFile.delete(); 403 } 404 405 if (modelFile.exists()) 406 { 407 modelFile.renameTo(bakFile); 408 } 409 } 410}