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