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