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