001/* 002 * Copyright 2013 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.model; 017 018import java.io.InputStream; 019import java.nio.file.Files; 020import java.nio.file.Path; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import org.apache.avalon.framework.activity.Initializable; 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.configuration.Configuration; 030import org.apache.avalon.framework.configuration.ConfigurationException; 031import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 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.avalon.framework.thread.ThreadSafe; 037import org.apache.commons.lang3.StringUtils; 038 039import org.ametys.core.cache.AbstractCacheManager; 040import org.ametys.core.cache.Cache; 041import org.ametys.runtime.i18n.I18nizableText; 042import org.ametys.skinfactory.SkinFactoryComponent; 043import org.ametys.skinfactory.parameters.AbstractSkinParameter; 044import org.ametys.skinfactory.parameters.I18nizableTextParameter; 045import org.ametys.skinfactory.parameters.ImageParameter; 046import org.ametys.web.skin.SkinModel; 047import org.ametys.web.skin.SkinModelsManager; 048 049/** 050 * Manages the design conceptions of a model 051 */ 052public class ModelDesignsManager extends AbstractLogEnabled implements ThreadSafe, Serviceable, Initializable, Component 053{ 054 /** The avalon role name */ 055 public static final String ROLE = ModelDesignsManager.class.getName(); 056 057 private static final String __MODEL_DESIGN_CACHE = ModelDesignsManager.class.getName() + "$modelDesign"; 058 059 private SkinModelsManager _modelsManager; 060 private SkinFactoryComponent _skinFactoryManager; 061 062 private AbstractCacheManager _cacheManager; 063 064 @Override 065 public void service(ServiceManager smanager) throws ServiceException 066 { 067 _modelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE); 068 _skinFactoryManager = (SkinFactoryComponent) smanager.lookup(SkinFactoryComponent.ROLE); 069 _cacheManager = (AbstractCacheManager) smanager.lookup(AbstractCacheManager.ROLE); 070 } 071 072 public void initialize() throws Exception 073 { 074 _cacheManager.createMemoryCache(__MODEL_DESIGN_CACHE, 075 new I18nizableText("plugin.skinfactory", "PLUGINS_SKINFACTORY_CACHE_MODEL_DESIGN_LABEL"), 076 new I18nizableText("plugin.skinfactory", "PLUGINS_SKINFACTORY_CACHE_MODEL_DESIGN_DESCRIPTION"), 077 true, 078 null); 079 } 080 081 /** 082 * Get all design instances for given model 083 * @param modelName The model name 084 * @return all design instances for given model 085 */ 086 public Set<Design> getDesigns (String modelName) 087 { 088 return _getDesignCache().get(modelName, this::_getDesigns); 089 } 090 091 /** 092 * Get design instance of given id and model name 093 * @param modelName The model name 094 * @param id The id 095 * @return design instance 096 */ 097 public Design getDesign (String modelName, String id) 098 { 099 Set<Design> designs = _getDesignCache().get(modelName, this::_getDesigns); 100 return designs.stream() 101 .filter(design -> StringUtils.equals(design.getId(), id)) 102 .findFirst().orElse(null); 103 } 104 105 /** 106 * Apply a design 107 * @param modelName The model name 108 * @param id Id of design 109 * @param skinDir The skin directory (could be temp, work or skins) 110 */ 111 public void applyDesign (String modelName, String id, Path skinDir) 112 { 113 SkinModel model = _modelsManager.getModel(modelName); 114 115 Path file = model.getPath().resolve("model/designs/" + id + ".xml"); 116 if (Files.exists(file)) 117 { 118 // Apply color theme 119 String themeId = _getColorTheme(file); 120 if (themeId != null) 121 { 122 _skinFactoryManager.saveColorTheme(skinDir, themeId); 123 } 124 125 // Apply values 126 Map<String, Object> values = _getParameterValues (modelName, file); 127 _skinFactoryManager.applyModelParameters(modelName, skinDir, values); 128 } 129 } 130 131 132 private Set<Design> _getDesigns (String modelName) 133 { 134 SkinModel model = _modelsManager.getModel(modelName); 135 136 Set<Design> designs; 137 138 Path designDir = model.getPath().resolve("model/designs"); 139 if (Files.exists(designDir)) 140 { 141 try 142 { 143 designs = Files.walk(designDir, 1) 144 .filter(f -> f.getFileName().toString().toLowerCase().endsWith(".xml")) 145 .map(f -> _configureDesign(modelName, f)) 146 .filter(design -> design != null) 147 .collect(Collectors.toSet()); 148 } 149 catch (Exception e) 150 { 151 throw new RuntimeException("Cannot read the configuration file model/designs for the model " + modelName); 152 } 153 } 154 else 155 { 156 designs = new HashSet<>(); 157 } 158 return designs; 159 } 160 161 162 private Design _configureDesign (String modelName, Path configurationFile) 163 { 164 try (InputStream is = Files.newInputStream(configurationFile)) 165 { 166 String fileName = configurationFile.getFileName().toString(); 167 String id = fileName.substring(0, fileName.lastIndexOf(".")); 168 169 Configuration configuration = new DefaultConfigurationBuilder().build(is); 170 I18nizableText label = _configureI18nizableText(configuration.getChild("label", false), new I18nizableText(id), modelName); 171 I18nizableText description = _configureI18nizableText(configuration.getChild("description", false), new I18nizableText(id), modelName); 172 173 String iconName = id + ".png"; 174 String icon = "/plugins/skinfactory/resources/img/actions/designs_32.png"; 175 Path iconFile = configurationFile.getParent().resolve(iconName); 176 if (Files.exists(iconFile)) 177 { 178 icon = "/plugins/skinfactory/" + modelName + "/_thumbnail/32/32/model/designs/" + iconName; 179 } 180 181 return new Design(id, label, description, icon); 182 } 183 catch (Exception e) 184 { 185 if (getLogger().isWarnEnabled()) 186 { 187 getLogger().warn("Cannot read the configuration file model/designs/" + configurationFile.getFileName().toString() + " for the model '" + modelName + "'. Continue as if file was not existing", e); 188 } 189 return null; 190 } 191 192 } 193 194 private String _getColorTheme(Path file) 195 { 196 try (InputStream is = Files.newInputStream(file)) 197 { 198 199 Configuration configuration = new DefaultConfigurationBuilder(true).build(is); 200 return configuration.getChild("color-theme").getValue(null); 201 } 202 catch (Exception e) 203 { 204 getLogger().error("Unable to get color theme", e); 205 return null; 206 } 207 } 208 209 private Map<String, Object> _getParameterValues (String modelName, Path file) 210 { 211 Map<String, Object> values = new HashMap<>(); 212 213 try (InputStream is = Files.newInputStream(file)) 214 { 215 216 Configuration configuration = new DefaultConfigurationBuilder(true).build(is); 217 Configuration[] parametersConf = configuration.getChild("parameters").getChildren("parameter"); 218 219 Map<String, AbstractSkinParameter> modelParameters = _skinFactoryManager.getModelParameters(modelName); 220 221 for (Configuration paramConf : parametersConf) 222 { 223 String id = paramConf.getAttribute("id"); 224 AbstractSkinParameter modelParam = modelParameters.get(id); 225 if (modelParam != null) 226 { 227 if (modelParam instanceof I18nizableTextParameter) 228 { 229 Configuration[] children = paramConf.getChildren(); 230 Map<String, String> langValues = new HashMap<>(); 231 for (Configuration langConfig : children) 232 { 233 langValues.put(langConfig.getName(), langConfig.getValue("")); 234 } 235 values.put(id, langValues); 236 } 237 else if (modelParam instanceof ImageParameter) 238 { 239 values.put(id, new ImageParameter.FileValue(paramConf.getValue(""), false)); 240 } 241 else 242 { 243 values.put(id, paramConf.getValue("")); 244 } 245 } 246 } 247 248 return values; 249 } 250 catch (Exception e) 251 { 252 getLogger().error("Unable to get values of all parameters", e); 253 return new HashMap<>(); 254 } 255 } 256 257 258 private I18nizableText _configureI18nizableText(Configuration configuration, I18nizableText defaultValue, String modelName) throws ConfigurationException 259 { 260 if (configuration != null) 261 { 262 boolean i18nSupported = configuration.getAttributeAsBoolean("i18n", false); 263 if (i18nSupported) 264 { 265 String catalogue = configuration.getAttribute("catalogue", null); 266 if (catalogue == null) 267 { 268 catalogue = "model." + modelName; 269 } 270 271 return new I18nizableText(catalogue, configuration.getValue()); 272 } 273 else 274 { 275 return new I18nizableText(configuration.getValue("")); 276 } 277 } 278 else 279 { 280 return defaultValue; 281 } 282 283 } 284 285 private Cache<String, Set<Design>> _getDesignCache() 286 { 287 return _cacheManager.get(__MODEL_DESIGN_CACHE); 288 } 289 290 /** 291 * Bean representing a model design 292 * 293 */ 294 public static class Design 295 { 296 private String _id; 297 private I18nizableText _label; 298 private I18nizableText _description; 299 private String _icon; 300 301 /** 302 * Constructor 303 * @param id the theme id 304 * @param label the theme's label 305 * @param description the theme's description 306 * @param icon the icon 307 */ 308 public Design (String id, I18nizableText label, I18nizableText description, String icon) 309 { 310 _id = id; 311 _label = label; 312 _description = description; 313 _icon = icon; 314 } 315 316 /** 317 * Get the id 318 * @return the id 319 */ 320 public String getId () 321 { 322 return _id; 323 } 324 325 /** 326 * Get the label 327 * @return the label 328 */ 329 public I18nizableText getLabel () 330 { 331 return _label; 332 } 333 334 /** 335 * Get the description 336 * @return the description 337 */ 338 public I18nizableText getDescription () 339 { 340 return _description; 341 } 342 343 /** 344 * Get the icon 345 * @return the icon 346 */ 347 public String getIcon () 348 { 349 return _icon; 350 } 351 } 352 353}