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.logger.AbstractLogEnabled; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.apache.cocoon.ProcessingException; 037import org.apache.commons.io.FileUtils; 038 039import org.ametys.core.ui.Callable; 040import org.ametys.plugins.skincommons.SkinEditionHelper; 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 Set<String> applyModelToAll(String modelId) throws IOException 243 { 244 Set<String> skins = _skinsManager.getSkins(); 245 Set<String> modifiedSkins = new HashSet<>(); 246 247 for (String skinId : skins) 248 { 249 Skin skin = _skinsManager.getSkin(skinId); 250 if (modelId.equals(_modelsManager.getModelOfSkin(skin))) 251 { 252 applyModel(skin, modelId); 253 254 modifiedSkins.add(skinId); 255 } 256 } 257 258 return modifiedSkins; 259 } 260 261 /** 262 * Apply model to the skin 263 * @param skinId The skin id 264 * @param modelId The id of model 265 * @throws IOException if an error occurs when manipulating files 266 */ 267 @Callable 268 public void applyModel(String skinId, String modelId) throws IOException 269 { 270 Skin skin = _skinsManager.getSkin(skinId); 271 272 applyModel(skin, modelId); 273 } 274 275 /** 276 * Apply model to the skin 277 * @param skin The skin 278 * @param modelId The id of model 279 * @throws IOException if an error occurs when manipulating files 280 */ 281 protected void applyModel(Skin skin, String modelId) throws IOException 282 { 283 File skinDir = skin.getFile(); 284 285 // Prepare skin in temporary file 286 File tmpDir = new File (skinDir.getParentFile(), skin.getId() + "." + _DATE_FORMAT.format(new Date())); 287 288 // Copy the model 289 File modelDir = _modelsManager.getModel(modelId).getFile(); 290 FileUtils.copyDirectory(modelDir, tmpDir, new ModelFileFilter(modelDir), false); 291 292 // Copy upload images if exists 293 File uploadDir = new File(skinDir, "model/_uploads"); 294 if (uploadDir.exists()) 295 { 296 File tmpUploadDir = new File(tmpDir, "model/_uploads"); 297 tmpUploadDir.mkdirs(); 298 FileUtils.copyDirectory(uploadDir, tmpUploadDir); 299 } 300 301 // Copy model.xml file 302 File xmlFile = new File(skinDir, "model.xml"); 303 FileUtils.copyFileToDirectory(xmlFile, tmpDir); 304 File tmpXmlFile = new File(tmpDir, "model.xml"); 305 306 // Apply parameters 307 _skinFactoryManager.applyModelParameters(modelId, tmpDir); 308 _skinFactoryManager.updateHash(tmpXmlFile, _modelsManager.getModelHash(modelId)); 309 310 _skinHelper.deleteQuicklyDirectory(skinDir); 311 FileUtils.moveDirectory(tmpDir, skinDir); 312 } 313 314 /** 315 * Delete a model 316 * @param modelId The model id 317 * @throws IOException if an error occurs when manipulating files 318 */ 319 @Callable 320 public void delete(String modelId) throws IOException 321 { 322 SkinModel model = _modelsManager.getModel(modelId); 323 324 // Unlink skins 325 Set<String> skins = _skinsManager.getSkins(); 326 for (String skinId : skins) 327 { 328 Skin skin = _skinsManager.getSkin(skinId); 329 if (modelId.equals(_modelsManager.getModelOfSkin(skin))) 330 { 331 unlinkModel(skin); 332 } 333 } 334 335 File file = model.getFile(); 336 if (file.exists()) 337 { 338 FileUtils.deleteDirectory(file); 339 } 340 } 341 342 /** 343 * Unlink the skin from its model 344 * @param skinId The id of the skin 345 * @param modelId The id of the model 346 * @return An error code, or null on success 347 */ 348 @Callable 349 public String unlinkModel(String skinId, String modelId) 350 { 351 Skin skin = _skinsManager.getSkin(skinId); 352 353 if (!modelId.equals(_modelsManager.getModelOfSkin(skin))) 354 { 355 return "incorrect-model"; 356 } 357 358 unlinkModel(skin); 359 360 return null; 361 } 362 363 private void unlinkModel(Skin skin) 364 { 365 File skinDir = skin.getFile(); 366 367 File modelFile = new File (skinDir, "model.xml"); 368 File bakFile = new File (skinDir, "model.xml.bak"); 369 370 if (bakFile.exists()) 371 { 372 // Delete old bak file if exists 373 bakFile.delete(); 374 } 375 376 if (modelFile.exists()) 377 { 378 modelFile.renameTo(bakFile); 379 } 380 } 381}