001/* 002 * Copyright 2010 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.web.skin; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.Date; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 029import org.apache.excalibur.source.Source; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.ametys.plugins.repository.model.parsing.RepeaterDefinitionParser; 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.runtime.model.Enumerator; 036import org.ametys.runtime.model.View; 037import org.ametys.runtime.parameter.Validator; 038import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 039import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 040import org.ametys.web.parameters.view.GlobalViewParametersManager.ViewParametersType; 041import org.ametys.web.parameters.view.ViewParameterDefinitionParser; 042import org.ametys.web.parameters.view.ViewParametersManager; 043import org.ametys.web.parameters.view.ViewParametersModel; 044 045/** 046 * Represent a skin template. 047 */ 048public class SkinTemplate 049{ 050 private static Logger _logger = LoggerFactory.getLogger(SkinTemplate.class); 051 052 /** The skin id */ 053 protected String _skinId; 054 /** Template id (e.g. the directory name) */ 055 protected String _id; 056 /** Template label */ 057 protected I18nizableText _label; 058 /** Template description */ 059 protected I18nizableText _description; 060 /** Template thumbnail 16px */ 061 protected String _smallImage; 062 /** Template thumbnail 32px */ 063 protected String _mediumImage; 064 /** Template thumbnail 48px */ 065 protected String _largeImage; 066 /** Template zones. the key is the zone id */ 067 protected Map<String, SkinTemplateZone> _zones; 068 /** The template view parameters */ 069 protected Optional<ViewParametersModel> _viewParameters; 070 071 /** The last time the file was loaded */ 072 protected long _lastConfUpdate; 073 /** The skins manager */ 074 protected SkinsManager _skinsManager; 075 076 private List<ThreadSafeComponentManager> _components; 077 078 /** 079 * Creates a template 080 * @param skinId The skin id 081 * @param templateId The template id 082 * @param skinsManager The skins manager 083 */ 084 public SkinTemplate(String skinId, String templateId, SkinsManager skinsManager) 085 { 086 _skinId = skinId; 087 _id = templateId; 088 _skinsManager = skinsManager; 089 090 _viewParameters = Optional.empty(); 091 092 _components = new ArrayList<>(); 093 } 094 095 /** 096 * The configuration default values (if configuration file does not exist or is unreadable) 097 */ 098 protected void _defaultValues() 099 { 100 _lastConfUpdate = new Date().getTime(); 101 102 this._label = new I18nizableText(this._id); 103 this._description = new I18nizableText(""); 104 this._smallImage = "/plugins/web/resources/img/skin/template_16.png"; 105 this._mediumImage = "/plugins/web/resources/img/skin/template_32.png"; 106 this._largeImage = "/plugins/web/resources/img/skin/template_48.png"; 107 this._zones = new LinkedHashMap<>(); 108 } 109 110 /** 111 * Refresh the configuration values 112 */ 113 public void refreshValues() 114 { 115 Source configurationFile = null; 116 try 117 { 118 configurationFile = _skinsManager.getSourceResolver().resolveURI("skin:" + _skinId + "://" + Skin.TEMPLATES_PATH + "/" + _id + "/template.xml"); 119 if (configurationFile.exists()) 120 { 121 long fileTime = configurationFile.getLastModified(); 122 if (_lastConfUpdate < fileTime) 123 { 124 _defaultValues(); 125 126 _lastConfUpdate = fileTime; 127 try (InputStream is = configurationFile.getInputStream()) 128 { 129 Configuration configuration = new DefaultConfigurationBuilder().build(is); 130 131 this._label = _configureI18n(configuration.getChild("label", false), this._label); 132 this._description = _configureI18n(configuration.getChild("description", false), this._description); 133 this._smallImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("small").getValue(null), this._smallImage); 134 this._mediumImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("medium").getValue(null), this._mediumImage); 135 this._largeImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("marge").getValue(null), this._largeImage); 136 137 _viewParameters = _configureTemplateViewParameters(this._skinId, this._id, configuration); 138 139 this._zones = new LinkedHashMap<>(); 140 for (Configuration zoneConfiguration : configuration.getChild("zones").getChildren("zone")) 141 { 142 String zoneId = zoneConfiguration.getAttribute("id"); 143 String zoneType = zoneConfiguration.getAttribute("type", SkinTemplateZone.TYPE_PRIMARY); 144 I18nizableText label = _configureI18n(zoneConfiguration.getChild("label", false), new I18nizableText(zoneId)); 145 I18nizableText description = _configureI18n(zoneConfiguration.getChild("description", false), new I18nizableText(zoneId)); 146 String smallImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("small").getValue(null), "/plugins/web/resources/img/skin/zone_16.png"); 147 String mediumImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("medium").getValue(null), "/plugins/web/resources/img/skin/zone_32.png"); 148 String largeImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("marge").getValue(null), "/plugins/web/resources/img/skin/zone_48.png"); 149 150 String inheritanceString = zoneConfiguration.getAttribute("inherit", null); 151 152 Optional<ViewParametersModel> zoneViewParameters = _configureZoneViewParameters(this._skinId, this._id, zoneId, zoneConfiguration); 153 Optional<ViewParametersModel> zoneItemViewParameters = _configureZoneItemViewParameters(this._skinId, this._id, zoneId, zoneConfiguration); 154 155 SkinTemplateZone zone = new SkinTemplateZone( 156 this._skinId, 157 this._id, 158 zoneId, 159 zoneType, 160 label, 161 description, 162 smallImage, 163 mediumImage, 164 largeImage, 165 inheritanceString, 166 zoneViewParameters, 167 zoneItemViewParameters 168 ); 169 170 _zones.put(zoneId, zone); 171 } 172 } 173 } 174 } 175 else 176 { 177 _defaultValues(); 178 } 179 } 180 catch (Exception e) 181 { 182 _defaultValues(); 183 if (_logger.isWarnEnabled()) 184 { 185 _logger.warn("Cannot read the configuration file templates/" + this._id + "/template.xml for the skin '" + this._id + "'. Continue as if file was not existing", e); 186 } 187 } 188 finally 189 { 190 _skinsManager.getSourceResolver().release(configurationFile); 191 } 192 } 193 194 /** 195 * Parse template view parameters 196 * @param skinId the skin id 197 * @param templateId the template id 198 * @param templateConfiguration the template configuration 199 * @return the zone item view parameters 200 * @throws ConfigurationException if a configuration error occurred 201 */ 202 protected Optional<ViewParametersModel> _configureTemplateViewParameters(String skinId, String templateId, Configuration templateConfiguration) throws ConfigurationException 203 { 204 Configuration parametersConf = templateConfiguration.getChild(ViewParametersManager.VIEW_PARAMETERS_TEMPLATE_CONF_NAME); 205 String viewParametersUniqueId = skinId + "_" + templateId + "_template_view_parameters"; 206 207 Optional<ViewParametersModel> viewParameters = _configureViewParameters(parametersConf, viewParametersUniqueId); 208 209 return _skinsManager.getViewParametersManager().addGlobalViewParameters(skinId, ViewParametersType.TEMPLATES, viewParameters); 210 } 211 212 /** 213 * Parse zone view parameters 214 * @param skinId the skin id 215 * @param templateId the template id 216 * @param zoneId the zone id 217 * @param zoneConfiguration the zone configuration 218 * @return the zone view parameters 219 * @throws ConfigurationException if a configuration error occurred 220 */ 221 protected Optional<ViewParametersModel> _configureZoneViewParameters(String skinId, String templateId, String zoneId, Configuration zoneConfiguration) throws ConfigurationException 222 { 223 Configuration parametersConf = zoneConfiguration.getChild(ViewParametersManager.VIEW_PARAMETERS_ZONE_CONF_NAME); 224 String viewParametersUniqueId = skinId + "_" + templateId + "_" + zoneId + "_zone_view_parameters"; 225 226 Optional<ViewParametersModel> viewParameters = _configureViewParameters(parametersConf, viewParametersUniqueId); 227 228 return _skinsManager.getViewParametersManager().addGlobalViewParameters(skinId, ViewParametersType.ZONES , viewParameters); 229 } 230 231 /** 232 * Parse zone item view parameters 233 * @param skinId the skin id 234 * @param templateId the template id 235 * @param zoneId the zone id 236 * @param zoneConfiguration the zone configuration 237 * @return the zone item view parameters 238 * @throws ConfigurationException if a configuration error occurred 239 */ 240 protected Optional<ViewParametersModel> _configureZoneItemViewParameters(String skinId, String templateId, String zoneId, Configuration zoneConfiguration) throws ConfigurationException 241 { 242 Configuration parametersConf = zoneConfiguration.getChild(ViewParametersManager.VIEW_PARAMETERS_ZONE_ITEM_PARENT_CONF_NAME).getChild(ViewParametersManager.VIEW_PARAMETERS_ZONE_ITEM_CONF_NAME); 243 String viewParametersUniqueId = skinId + "_" + templateId + "_" + zoneId + "_zone_item_view_parameters"; 244 245 Optional<ViewParametersModel> viewParameters = _configureViewParameters(parametersConf, viewParametersUniqueId); 246 247 return _skinsManager.getViewParametersManager().addGlobalViewParameters(skinId, ViewParametersType.ZONEITEMS , viewParameters); 248 } 249 250 /** 251 * Parse view parameters from configuration 252 * @param paramConfiguration the configuration 253 * @param viewParametersId the view parameters id 254 * @return the view parameters 255 * @throws ConfigurationException if a configuration error occurred 256 */ 257 protected Optional<ViewParametersModel> _configureViewParameters(Configuration paramConfiguration, String viewParametersId) throws ConfigurationException 258 { 259 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 260 validatorManager.setLogger(_logger); 261 validatorManager.contextualize(_skinsManager.getContext()); 262 validatorManager.service(_skinsManager.getServiceManager()); 263 _components.add(validatorManager); 264 265 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 266 enumeratorManager.setLogger(_logger); 267 enumeratorManager.contextualize(_skinsManager.getContext()); 268 enumeratorManager.service(_skinsManager.getServiceManager()); 269 _components.add(enumeratorManager); 270 271 ViewParametersModel viewParameters = new ViewParametersModel(viewParametersId, new View(), new LinkedHashMap<>()); 272 273 ViewParameterDefinitionParser elementDefinitionParser = new ViewParameterDefinitionParser(_skinsManager.getViewParameterTypeExtensionPoint(), enumeratorManager, validatorManager); 274 RepeaterDefinitionParser repeaterDefinitionParser = new RepeaterDefinitionParser(_skinsManager.getViewParameterTypeExtensionPoint()); 275 276 String catalog = "skin." + this._skinId; 277 String defaultPlugin = "web"; // Default plugin name for enumerator and validator component 278 ViewAndParameters viewAndParameters = _skinsManager.getViewAndParametersParser().parseParameters(paramConfiguration, defaultPlugin, catalog, viewParameters, elementDefinitionParser, repeaterDefinitionParser); 279 viewParameters.setView(viewAndParameters.getView()); 280 viewParameters.setModelItems(viewAndParameters.getParameters()); 281 282 try 283 { 284 elementDefinitionParser.lookupComponents(); 285 } 286 catch (Exception e) 287 { 288 throw new ConfigurationException("Unable to lookup parameter local components", paramConfiguration, e); 289 } 290 291 return Optional.ofNullable(viewParameters); 292 } 293 294 /** 295 * Dispose skin template component 296 */ 297 public void dispose() 298 { 299 for (ThreadSafeComponentManager component : _components) 300 { 301 component.dispose(); 302 } 303 } 304 305 private String _configureThumbnail(String value, String defaultImage) 306 { 307 if (value == null) 308 { 309 return defaultImage; 310 } 311 else 312 { 313 return "/skins/" + this._skinId + "/templates/" + this._id + "/resources/" + value; 314 } 315 } 316 317 private I18nizableText _configureI18n(Configuration child, I18nizableText defaultValue) throws ConfigurationException 318 { 319 if (child != null) 320 { 321 String value = child.getValue(); 322 if (child.getAttributeAsBoolean("i18n", false)) 323 { 324 return new I18nizableText("skin." + this._skinId, value); 325 } 326 else 327 { 328 return new I18nizableText(value); 329 } 330 } 331 else 332 { 333 return defaultValue; 334 } 335 } 336 337 /** 338 * The template id 339 * @return the id 340 */ 341 public String getId() 342 { 343 return _id; 344 } 345 346 /** 347 * The template label 348 * @return The label 349 */ 350 public I18nizableText getLabel() 351 { 352 return _label; 353 } 354 /** 355 * The template description 356 * @return The description. Can not be null but can be empty 357 */ 358 public I18nizableText getDescription() 359 { 360 return _description; 361 } 362 363 /** 364 * The small image file uri 365 * @return The small image file uri 366 */ 367 public String getSmallImage() 368 { 369 return _smallImage; 370 } 371 372 /** 373 * The medium image file uri 374 * @return The medium image file uri 375 */ 376 public String getMediumImage() 377 { 378 return _mediumImage; 379 } 380 381 /** 382 * The large image file uri 383 * @return The large image file uri 384 */ 385 public String getLargeImage() 386 { 387 return _largeImage; 388 } 389 390 /** 391 * The zones defined in by the template def 392 * @return The zones 393 */ 394 public Map<String, SkinTemplateZone> getZones() 395 { 396 return _zones; 397 } 398 399 /** 400 * The zone identifier by its id 401 * @param zoneId The id of the zone definition to get 402 * @return The zone or null if no zone has this name 403 */ 404 public SkinTemplateZone getZone(String zoneId) 405 { 406 return _zones.get(zoneId); 407 } 408 409 /** 410 * Get the primary default zone. 411 * That is the first primary zone, or the 'default' zone if it exists and is primary 412 * @return The default zone or null if there is no primary zone 413 */ 414 public String getDefaultZoneId() 415 { 416 String defaultZoneId = null; 417 418 Map<String, SkinTemplateZone> zones = this.getZones(); 419 for (SkinTemplateZone zone : zones.values()) 420 { 421 if (SkinTemplateZone.TYPE_PRIMARY.equals(zone.getType()) 422 && ("default".equals(zone.getId()) || defaultZoneId == null)) 423 { 424 defaultZoneId = zone.getId(); 425 } 426 } 427 428 return defaultZoneId; 429 } 430 431 /** 432 * Get the view parameters model 433 * @return the view parameters 434 */ 435 public Optional<ViewParametersModel> getViewParameters() 436 { 437 return _viewParameters; 438 } 439} 440