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