001/* 002 * Copyright 2012 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; 017 018import java.io.File; 019import java.io.FileFilter; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.configuration.Configuration; 033import org.apache.avalon.framework.configuration.ConfigurationException; 034import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 035import org.apache.avalon.framework.logger.AbstractLogEnabled; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.avalon.framework.thread.ThreadSafe; 040import org.apache.commons.io.IOUtils; 041import org.apache.commons.lang.StringUtils; 042import org.apache.excalibur.source.Source; 043import org.apache.excalibur.source.SourceResolver; 044import org.apache.excalibur.source.SourceUtil; 045import org.apache.excalibur.source.impl.FileSource; 046 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.skinfactory.parameters.CSSColorParameter; 049import org.ametys.skinfactory.parameters.CSSParameter; 050import org.ametys.skinfactory.parameters.I18nizableTextParameter; 051import org.ametys.skinfactory.parameters.ImageParameter; 052import org.ametys.skinfactory.parameters.ImageParameter.FileValue; 053import org.ametys.skinfactory.parameters.AbstractSkinParameter; 054import org.ametys.skinfactory.parameters.TextParameter; 055import org.ametys.skinfactory.parameters.Variant; 056import org.ametys.skinfactory.parameters.VariantParameter; 057import org.ametys.web.skin.SkinModel; 058import org.ametys.web.skin.SkinModelsManager; 059 060/** 061 * Manager for skin parameters 062 * 063 */ 064public class SkinFactoryComponent extends AbstractLogEnabled implements Component, ThreadSafe, Serviceable 065{ 066 /** Avalon role */ 067 public static final String ROLE = SkinFactoryComponent.class.getName(); 068 069 /** Pattern for CSS parameters. E.g: color: #EEEEEE /* AMETYS("myuniqueid", "Label", DESCRIPTION_I18N_KEY) *\/; **/ 070 public static final Pattern CSS_PARAM_PATTERN = Pattern.compile("\\s*([^,:\\s]*)\\s*:\\s*([^:;!]*)\\s*(?:!important)?\\s*\\/\\*\\s*AMETYS\\s*\\(\\s*\"([^\"]+)\"\\s*(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?\\)\\s*\\*\\/\\s*;?\\s*", Pattern.MULTILINE); 071 072 /** Pattern for i18n text parameters. E.g: <message key="SKIN_TITLE">Ametys, Web Java Open Source CMS<!-- Ametys("text.i18n.site.title", "Titre du site") --></message> */ 073 public static final Pattern I18N_PARAM_PATTERN = Pattern.compile("^\\s*<message key=\"([^,:\\s]*)\">([^<]*)<!--\\s*AMETYS\\s*\\(\\s*\"([^\"]+)\"\\s*(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?\\)\\s*--></message>\\s*$", Pattern.MULTILINE); 074 075 /** Pattern for text parameters. E.g: <xsl:variable name="foo">test<!-- Ametys("variable.foo", "Foo", "Foo") --></xsl:variable> */ 076 public static final Pattern TXT_PARAM_PATTERN = Pattern.compile("^[^>]*>([^<]*)<!--\\s*AMETYS\\s*\\(\\s*\"([^\"]+)\"\\s*(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?\\)\\s*-->.*$", Pattern.MULTILINE); 077 078 private static final Pattern __I18N_CATALOG_LANGUAGE = Pattern.compile("^\\s*<catalogue xml:lang=\"([a-z]{2})\">\\s*$", Pattern.MULTILINE); 079 080 private final Map<String, Map<String, AbstractSkinParameter>> _modelsParameters = new HashMap<>(); 081 private final Map<String, String> _modelsHash = new HashMap<>(); 082 083 private SkinModelsManager _modelsManager; 084 private SourceResolver _resolver; 085 086 @Override 087 public void service(ServiceManager smanager) throws ServiceException 088 { 089 _modelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE); 090 _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE); 091 } 092 093 /** 094 * Remove all model parameters from cache 095 */ 096 public void clearModelsParametersCache() 097 { 098 _modelsParameters.clear(); 099 } 100 101 /** 102 * Get the skin parameter of the given model 103 * @param modelName The model name 104 * @param paramId The id of the parameter 105 * @return The skin parameter or <code>null</code> if it doesn't exist 106 */ 107 public AbstractSkinParameter getModelParamater (String modelName, String paramId) 108 { 109 Map<String, AbstractSkinParameter> modelParameters = getModelParameters(modelName); 110 return modelParameters.get(paramId); 111 } 112 113 /** 114 * Determines if the model was changed 115 * @param modelName The model name 116 * @return <code>true</code> if the model was changed 117 */ 118 public boolean isModelUpToDate (String modelName) 119 { 120 String hash = _modelsManager.getModelHash(modelName); 121 if (!_modelsHash.containsKey(modelName) || !_modelsHash.get(modelName).equals(hash)) 122 { 123 // The model is not up-to-date, clear cache 124 _modelsHash.put(modelName, hash); 125 return false; 126 } 127 return true; 128 } 129 130 /** 131 * Get the skin parameters of a model 132 * @param modelName the model name 133 * @return skin parameters in a List 134 */ 135 public Map<String, AbstractSkinParameter> getModelParameters (String modelName) 136 { 137 if (!isModelUpToDate(modelName)) 138 { 139 // The model is not up-to-date, clear cache 140 _modelsParameters.remove(modelName); 141 } 142 143 if (_modelsParameters.containsKey(modelName)) 144 { 145 return _modelsParameters.get(modelName); 146 } 147 148 _modelsParameters.put(modelName, new LinkedHashMap<String, AbstractSkinParameter>()); 149 150 Map<String, AbstractSkinParameter> skinParams = _modelsParameters.get(modelName); 151 152 File modelDir = _modelsManager.getModel(modelName).getFile(); 153 154 // Variants parameters 155 skinParams.putAll(_getVariantParameters(modelDir, modelName)); 156 157 // Images Parameters 158 skinParams.putAll(_getImageParameters(modelDir, modelName)); 159 160 // CSS parameters 161 skinParams.putAll(_getCSSParameters(modelDir, modelName)); 162 163 // I18n parameters 164 skinParams.putAll(_getI18nTextParameters(modelDir, modelName)); 165 166 // Text parameters 167 skinParams.putAll(_getTextParameters(modelDir, modelName)); 168 169 return skinParams; 170 } 171 172 private Map<String, VariantParameter> _getVariantParameters (File modelDir, String modelName) 173 { 174 Map<String, VariantParameter> params = new HashMap<>(); 175 176 File variantsDir = new File(modelDir, "model/variants"); 177 if (variantsDir.exists()) 178 { 179 _findVariantsParameters (variantsDir, params, modelName); 180 } 181 return params; 182 } 183 184 private void _findVariantsParameters (File variantsDir, Map<String, VariantParameter> params, String modelName) 185 { 186 File[] listFiles = variantsDir.listFiles(new SkinDirectoryFilter()); 187 for (File child : listFiles) 188 { 189 String filename = child.getName(); 190 List<Variant> values = _getVariantValues (child, modelName); 191 192 // avoid false directory such as CVS 193 if (values.size() != 0) 194 { 195 VariantParameter variantParameter = new VariantParameter(filename, new I18nizableText(filename), new I18nizableText(""), values); 196 _configureVariant (child, variantParameter, modelName); 197 params.put(variantParameter.getId(), variantParameter); 198 } 199 else 200 { 201 getLogger().debug("Discarding variant " + child.getAbsolutePath() + " because it has no sub directories as values"); 202 } 203 } 204 } 205 206 private List<Variant> _getVariantValues (File variant, String modelName) 207 { 208 List<Variant> values = new ArrayList<>(); 209 210 for (File child : variant.listFiles(new SkinDirectoryFilter())) 211 { 212 String id = child.getName(); 213 214 // Thumbnail 215 String thumbnailPath = null; 216 File thumbnailFile = new File(child, id + ".png"); 217 if (thumbnailFile.exists()) 218 { 219 thumbnailPath = thumbnailFile.getAbsolutePath().substring(variant.getParentFile().getAbsolutePath().length() + 1).replaceAll("\\\\", "/"); 220 } 221 222 I18nizableText label = new I18nizableText(id); 223 I18nizableText description = new I18nizableText(""); 224 File file = new File(child, id + ".xml"); 225 if (file.exists()) 226 { 227 try (InputStream is = new FileInputStream(file)) 228 { 229 Configuration configuration = new DefaultConfigurationBuilder().build(is); 230 label = _configureI18nizableText(configuration.getChild("label"), modelName); 231 232 description = _configureI18nizableText(configuration.getChild("description"), modelName); 233 } 234 catch (Exception e) 235 { 236 getLogger().error("Unable to configure variant '" + id + "'", e); 237 } 238 } 239 240 values.add(new Variant(id, label, description, thumbnailPath)); 241 } 242 return values; 243 } 244 245 private void _configureVariant (File variantFile, VariantParameter param, String modelName) 246 { 247 File file = new File(variantFile + "/" + variantFile.getName() + ".xml"); 248 249 if (file.exists()) 250 { 251 try (InputStream is = new FileInputStream(file)) 252 { 253 Configuration configuration = new DefaultConfigurationBuilder().build(is); 254 255 I18nizableText label = _configureI18nizableText(configuration.getChild("label"), modelName); 256 param.setLabel(label); 257 258 I18nizableText description = _configureI18nizableText(configuration.getChild("description"), modelName); 259 param.setDescription(description); 260 261 param.setIconGlyph(configuration.getChild("icon-glyph").getValue(null)); 262 } 263 catch (Exception e) 264 { 265 getLogger().error("Unable to configure variant '" + param.getId() + "'", e); 266 } 267 } 268 269 File iconSmall = new File(variantFile + "/thumbnail_16.png"); 270 if (iconSmall.exists()) 271 { 272 param.setIconSmall(iconSmall.getName()); 273 } 274 275 File iconLarge = new File(variantFile + "/thumbnail_32.png"); 276 if (iconLarge.exists()) 277 { 278 param.setIconLarge(iconLarge.getName()); 279 } 280 } 281 282 private Map<String, ImageParameter> _getImageParameters (File modelDir, String modelName) 283 { 284 Map<String, ImageParameter> params = new HashMap<>(); 285 286 File imagesDir = new File(modelDir, "model/images"); 287 if (imagesDir.exists()) 288 { 289 _findImagesParameters (modelDir, imagesDir, imagesDir, params, modelName); 290 } 291 return params; 292 } 293 294 private void _findImagesParameters (File modelDir, File imagesDir, File file, Map<String, ImageParameter> params, String modelName) 295 { 296 File[] listFiles = file.listFiles(); 297 for (File child : listFiles) 298 { 299 if (child.isDirectory()) 300 { 301 String filename = child.getName(); 302 String lcFilename = filename.toLowerCase(); 303 if (lcFilename.endsWith(".png") || lcFilename.endsWith(".jpg") || lcFilename.endsWith(".jpeg") || lcFilename.endsWith(".gif")) 304 { 305 String imagePath = child.getAbsolutePath().substring(imagesDir.getAbsolutePath().length() + 1); 306 ImageParameter imageParameter = new ImageParameter(imagePath, new I18nizableText(filename), new I18nizableText("")); 307 _configureImage (child, imageParameter, modelName); 308 params.put(imageParameter.getId(), imageParameter); 309 } 310 else 311 { 312 _findImagesParameters (modelDir, imagesDir, child, params, modelName); 313 } 314 } 315 } 316 } 317 318 private void _configureImage (File imageFile, ImageParameter param, String modelName) 319 { 320 String filename = imageFile.getName(); 321 int i = filename.lastIndexOf("."); 322 if (i > 0) 323 { 324 filename = filename.substring(0, i); 325 } 326 File file = new File(imageFile + "/" + filename + ".xml"); 327 328 if (file.exists()) 329 { 330 try (InputStream is = new FileInputStream(file)) 331 { 332 Configuration configuration = new DefaultConfigurationBuilder().build(is); 333 334 I18nizableText label = _configureI18nizableText(configuration.getChild("label"), modelName); 335 param.setLabel(label); 336 337 I18nizableText description = _configureI18nizableText(configuration.getChild("description"), modelName); 338 param.setDescription(description); 339 340 param.setIconGlyph(configuration.getChild("icon-glyph").getValue(null)); 341 } 342 catch (Exception e) 343 { 344 getLogger().error("Unable to configure image parameter '" + param.getId() + "'", e); 345 } 346 } 347 348 File iconSmall = new File(imageFile + "/thumbnail_16.png"); 349 if (iconSmall.exists()) 350 { 351 param.setIconSmall(iconSmall.getName()); 352 } 353 File iconLarge = new File(imageFile + "/thumbnail_32.png"); 354 if (iconLarge.exists()) 355 { 356 param.setIconLarge(iconLarge.getName()); 357 } 358 } 359 360 361 private Map<String, CSSParameter> _getCSSParameters(File modelDir, String modelName) 362 { 363 Map<String, CSSParameter> cssParams = new HashMap<>(); 364 365 // Parse css files in the root resources directory. 366 for (File file : _listCSSFiles (new File(modelDir, "/resources"))) 367 { 368 _parseCSSFile(cssParams, modelName, file); 369 } 370 371 // Parse css files for each templates. 372 File templatesDir = new File(modelDir, "/templates"); 373 if (templatesDir.isDirectory()) 374 { 375 File[] templates = templatesDir.listFiles(); 376 if (templates != null) 377 { 378 for (File template : templates) 379 { 380 for (File file : _listCSSFiles (new File(template, "resources"))) 381 { 382 _parseCSSFile(cssParams, modelName, file); 383 } 384 } 385 } 386 } 387 388 // Parse xsl files for inline css 389 for (File file : _listXSLFiles(modelDir)) 390 { 391 if (file.isFile()) 392 { 393 _parseCSSFile(cssParams, modelName, file); 394 } 395 } 396 397 return cssParams; 398 } 399 400 private List<File> _listCSSFiles (File file) 401 { 402 List<File> files = new ArrayList<>(); 403 404 File[] listFiles = file.listFiles(); 405 if (listFiles != null) 406 { 407 for (File child : listFiles) 408 { 409 if (child.isFile() && child.getName().endsWith(".css")) 410 { 411 files.add(child); 412 } 413 else if (child.isDirectory()) 414 { 415 files.addAll(_listCSSFiles(child)); 416 } 417 } 418 } 419 420 return files; 421 } 422 423 private void _parseCSSFile (Map<String, CSSParameter> cssParams, String modelName, File cssFile) 424 { 425 try (InputStream is = new FileInputStream(cssFile)) 426 { 427 String string = IOUtils.toString(is, "UTF-8"); 428 429 Matcher m = CSS_PARAM_PATTERN.matcher(string); 430 while (m.find()) 431 { 432 String id = m.group(3); 433 if (cssParams.containsKey(id)) 434 { 435 CSSParameter cssParameter = cssParams.get(id); 436 cssParameter.addCSSFile(cssFile); 437 438 I18nizableText label = m.group(4) != null ? _parseI18nizableText(m.group(4), modelName) : null; 439 if (label != null) 440 { 441 cssParameter.setLabel(label); 442 } 443 444 I18nizableText description = m.group(5) != null ? _parseI18nizableText(m.group(5), modelName) : null; 445 if (description != null) 446 { 447 cssParameter.setDescription(description); 448 } 449 } 450 else 451 { 452 String cssProperty = m.group(1); 453 String defaultValue = m.group(2).trim(); 454 455 I18nizableText label = m.group(4) != null ? _parseI18nizableText(m.group(4), modelName) : null; 456 I18nizableText description = m.group(5) != null ? _parseI18nizableText(m.group(5), modelName) : null; 457 458 if (cssProperty.equals("color") || cssProperty.equals("background-color") || cssProperty.equals("border-color")) 459 { 460 CSSColorParameter cssParameter = new CSSColorParameter (id, label, description, cssFile, cssProperty, defaultValue, _modelsManager.getModel(modelName), this); 461 cssParams.put(id, cssParameter); 462 } 463 else 464 { 465 CSSParameter cssParameter = new CSSParameter(id, label, description, cssFile, cssProperty, defaultValue); 466 cssParams.put(id, cssParameter); 467 } 468 } 469 } 470 } 471 catch (IOException e) 472 { 473 getLogger().error("Unable to parse file '" + cssFile.getName() + "'", e); 474 } 475 } 476 477 private Map<String, TextParameter> _getTextParameters(File modelDir, String modelName) 478 { 479 Map<String, TextParameter> textParams = new HashMap<>(); 480 481 for (File file : _listXSLFiles(modelDir)) 482 { 483 if (file.isFile()) 484 { 485 textParams.putAll(_parseXSLFile(modelName, file)); 486 } 487 } 488 489 return textParams; 490 } 491 492 private List<File> _listXSLFiles (File file) 493 { 494 List<File> files = new ArrayList<>(); 495 496 File[] listFiles = file.listFiles(); 497 if (listFiles != null) 498 { 499 for (File child : listFiles) 500 { 501 if (child.isFile() && child.getName().endsWith(".xsl")) 502 { 503 files.add(child); 504 } 505 else if (child.isDirectory()) 506 { 507 files.addAll(_listXSLFiles(child)); 508 } 509 } 510 } 511 512 return files; 513 } 514 515 private Map<String, TextParameter> _parseXSLFile (String skinName, File xslFile) 516 { 517 Map<String, TextParameter> txtParams = new LinkedHashMap<>(); 518 519 try (InputStream is = new FileInputStream(xslFile)) 520 { 521 String string = IOUtils.toString(is, "UTF-8"); 522 523 Matcher m = TXT_PARAM_PATTERN.matcher(string); 524 while (m.find()) 525 { 526 String id = m.group(2); 527 String defaultValue = m.group(1); 528 529 I18nizableText label = m.group(3) != null ? _parseI18nizableText(m.group(3), skinName) : new I18nizableText(id); 530 I18nizableText description = m.group(4) != null ? _parseI18nizableText(m.group(4), skinName) : new I18nizableText(""); 531 532 TextParameter txtParam = new TextParameter(id, label, description, xslFile, defaultValue); 533 txtParams.put(id, txtParam); 534 } 535 } 536 catch (IOException e) 537 { 538 getLogger().error("Unable to parse file '" + xslFile.getName() + "'", e); 539 } 540 541 return txtParams; 542 } 543 544 private Map<String, I18nizableTextParameter> _getI18nTextParameters(File modelDir, String modelName) 545 { 546 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 547 548 File i18nDir = new File(modelDir, "/i18n"); 549 for (File file : i18nDir.listFiles()) 550 { 551 if (file.getName().equals("messages.xml")) 552 { 553 i18nParams.putAll(_parseI18nFile(modelName, file)); 554 } 555 } 556 557 return i18nParams; 558 } 559 560 561 private Map<String, I18nizableTextParameter> _parseI18nFile (String skinName, File i18nFile) 562 { 563 Map<String, I18nizableTextParameter> i18nParams = new LinkedHashMap<>(); 564 565 try (InputStream is = new FileInputStream(i18nFile)) 566 { 567 String string = IOUtils.toString(is); 568 569 String defaultLang = _getCatalogLanguage (string, i18nFile.getName()); 570 571 Matcher m = I18N_PARAM_PATTERN.matcher(string); 572 while (m.find()) 573 { 574 String i18nKey = m.group(1); 575 String defaultValue = m.group(2).trim(); 576 String id = m.group(3); 577 I18nizableText label = m.group(4) != null ? _parseI18nizableText(m.group(4), skinName) : new I18nizableText(id); 578 I18nizableText description = m.group(5) != null ? _parseI18nizableText(m.group(5), skinName) : new I18nizableText(""); 579 580 // Default values 581 Map<String, String> defaultValues = new HashMap<>(); 582 defaultValues.put(defaultLang, defaultValue); 583 defaultValues.putAll(_getI18nOtherDefaultValues (i18nFile, i18nKey)); 584 585 I18nizableTextParameter i18nParam = new I18nizableTextParameter(id, label, description, i18nKey, defaultValues); 586 i18nParams.put(id, i18nParam); 587 } 588 } 589 catch (IOException e) 590 { 591 getLogger().error("Unable to parse file '" + i18nFile.getName() + "'", e); 592 } 593 594 return i18nParams; 595 } 596 597 private String _getCatalogLanguage (String string, String fileName) 598 { 599 Matcher m = __I18N_CATALOG_LANGUAGE.matcher(string); 600 if (m.find()) 601 { 602 return m.group(1); 603 } 604 else if (fileName.startsWith("messages_")) 605 { 606 return fileName.substring("messages_".length(), 2); 607 } 608 return "en"; 609 } 610 611 private Map<String, String> _getI18nOtherDefaultValues (File defaultCatalog, String i18nKey) 612 { 613 Pattern pattern = Pattern.compile("^\\s*<message key=\"" + i18nKey + "\">([^<]*)</message>\\s*$", Pattern.MULTILINE); 614 615 Map<String, String> defaultValues = new HashMap<>(); 616 617 for (File file : defaultCatalog.getParentFile().listFiles()) 618 { 619 String filename = file.getName(); 620 if (filename.startsWith("messages") && !filename.equals(defaultCatalog.getName())) 621 { 622 try (InputStream is = new FileInputStream(file)) 623 { 624 String string = org.apache.commons.io.IOUtils.toString(is, "UTF-8"); 625 626 String lang = _getCatalogLanguage (string, filename); 627 628 Matcher m = pattern.matcher(string); 629 if (m.find()) 630 { 631 String value = m.group(1); 632 defaultValues.put(lang, value); 633 } 634 } 635 catch (IOException e) 636 { 637 getLogger().error("Unable to parse file '" + file.getName() + "'", e); 638 } 639 } 640 } 641 642 return defaultValues; 643 } 644 645 private I18nizableText _parseI18nizableText (String label, String modelName) 646 { 647 if (label.startsWith("\"") && label.endsWith("\"")) 648 { 649 return new I18nizableText(label.substring(1, label.length() - 1)); 650 } 651 else 652 { 653 return new I18nizableText("model." + modelName, label); 654 } 655 } 656 657 /** 658 * Get the parameter's value 659 * @param skinDir The skin directory (could be in temp, work or skin) 660 * @param modelName The model name 661 * @param id The parameter id 662 * @return The parameter's value 663 */ 664 public Object getParameterValue (File skinDir, String modelName, String id) 665 { 666 List<String> ids = new ArrayList<>(); 667 ids.add(id); 668 669 return getParameterValues (skinDir, modelName, ids).get(id); 670 } 671 672 /** 673 * Get the parameters' values 674 * @param skinDir The skin directory (could be in temp, work or skin) 675 * @param modelName The model name 676 * @param ids The parameters' id 677 * @return The parameters' values 678 */ 679 public Map<String, Object> getParameterValues (File skinDir, String modelName, List<String> ids) 680 { 681 Map<String, Object> values = new HashMap<>(); 682 683 File modelFile = new File(skinDir, "model.xml"); 684 685 Source src = null; 686 try 687 { 688 src = new FileSource("file", modelFile); 689 690 Configuration configuration = new DefaultConfigurationBuilder(true).build(src.getInputStream()); 691 Configuration[] parametersConf = configuration.getChild("parameters").getChildren("parameter"); 692 693 for (Configuration paramConf : parametersConf) 694 { 695 String id = paramConf.getAttribute("id"); 696 if (ids.contains(id)) 697 { 698 AbstractSkinParameter modelParam = getModelParamater(modelName, id); 699 if (modelParam instanceof I18nizableTextParameter) 700 { 701 Configuration[] children = paramConf.getChildren(); 702 Map<String, String> langValues = new HashMap<>(); 703 for (Configuration langConfig : children) 704 { 705 langValues.put(langConfig.getName(), langConfig.getValue("")); 706 } 707 } 708 else 709 { 710 values.put(id, paramConf.getValue("")); 711 } 712 } 713 } 714 715 return values; 716 } 717 catch (Exception e) 718 { 719 getLogger().error("Unable to get values for parameters '" + StringUtils.join(ids, ", ") + "'", e); 720 return new HashMap<>(); 721 } 722 finally 723 { 724 if (src != null) 725 { 726 _resolver.release(src); 727 } 728 } 729 } 730 731 /** 732 * Get the current theme 733 * @param skinDir The skin directory (could be in temp, work or skin) 734 * @return The current theme id 735 */ 736 public String getColorTheme (File skinDir) 737 { 738 File modelFile = new File(skinDir, "model.xml"); 739 740 Source src = null; 741 try 742 { 743 src = new FileSource("file", modelFile); 744 745 Configuration configuration = new DefaultConfigurationBuilder(true).build(src.getInputStream()); 746 return configuration.getChild("color-theme").getValue(null); 747 } 748 catch (Exception e) 749 { 750 getLogger().error("Unable to get theme value", e); 751 return null; 752 } 753 finally 754 { 755 if (src != null) 756 { 757 _resolver.release(src); 758 } 759 } 760 } 761 762 /** 763 * Get all parameters' values 764 * @param skinDir The skin directory (could be in temp, work or skin) 765 * @param modelName The model name 766 * @return The all parameters' values 767 */ 768 public Map<String, Object> getParameterValues (File skinDir, String modelName) 769 { 770 Map<String, Object> values = new HashMap<>(); 771 772 File modelFile = new File(skinDir, "model.xml"); 773 774 Source src = null; 775 try 776 { 777 778 src = new FileSource("file", modelFile); 779 780 Configuration configuration = new DefaultConfigurationBuilder(true).build(src.getInputStream()); 781 Configuration[] parametersConf = configuration.getChild("parameters").getChildren("parameter"); 782 783 Map<String, AbstractSkinParameter> modelParameters = getModelParameters(modelName); 784 785 for (Configuration paramConf : parametersConf) 786 { 787 String id = paramConf.getAttribute("id"); 788 AbstractSkinParameter modelParam = modelParameters.get(id); 789 if (modelParam != null) 790 { 791 if (modelParam instanceof I18nizableTextParameter) 792 { 793 Configuration[] children = paramConf.getChildren(); 794 Map<String, String> langValues = new HashMap<>(); 795 for (Configuration langConfig : children) 796 { 797 langValues.put(langConfig.getName(), langConfig.getValue("")); 798 } 799 values.put(id, langValues); 800 } 801 else if (modelParam instanceof ImageParameter) 802 { 803 boolean uploaded = Boolean.valueOf(paramConf.getAttribute("uploaded", "false")); 804 values.put(id, new ImageParameter.FileValue(paramConf.getValue(""), uploaded)); 805 } 806 else 807 { 808 values.put(id, paramConf.getValue("")); 809 } 810 } 811 } 812 813 return values; 814 } 815 catch (Exception e) 816 { 817 getLogger().error("Unable to get values of all parameters", e); 818 return new HashMap<>(); 819 } 820 finally 821 { 822 if (src != null) 823 { 824 _resolver.release(src); 825 } 826 } 827 } 828 829 /** 830 * Modify the color theme 831 * @param skinDir The skin directory (could be in temp, work or skin) 832 * @param themeId The id of the theme. Can be null to clear theme. 833 * @return <code>true</code> is modification success 834 */ 835 public boolean saveColorTheme (File skinDir, String themeId) 836 { 837 File currentFile = new File(skinDir, "model.xml"); 838 839 Source currentSrc = null; 840 Source src = null; 841 try 842 { 843 currentSrc = _resolver.resolveURI("file://" + currentFile.getAbsolutePath()); 844 845 Map<String, Object> parentContext = new HashMap<>(); 846 parentContext.put("modelUri", currentSrc.getURI()); 847 if (themeId != null) 848 { 849 parentContext.put("themeId", themeId); 850 } 851 852 src = _resolver.resolveURI("cocoon://_plugins/skinfactory/change-color-theme", null, parentContext); 853 854 SourceUtil.copy(src, currentSrc); 855 856 return true; 857 } 858 catch (IOException e) 859 { 860 getLogger().error("Unable to update color theme for skin '" + skinDir.getName() + "'", e); 861 return false; 862 } 863 finally 864 { 865 _resolver.release(src); 866 _resolver.release(currentSrc); 867 } 868 } 869 870 /** 871 * Modify a skin parameter's value 872 * @param skinDir The skin directory (could be in temp, work or skin) 873 * @param id the id of the parameter 874 * @param value the new value 875 * @return <code>true</code> is modification success 876 */ 877 public boolean saveParameter(File skinDir, String id, Object value) 878 { 879 Map<String, Object> parameters = new HashMap<>(); 880 parameters.put(id, value); 881 882 return saveParameters (skinDir, parameters); 883 } 884 885 /** 886 * Save skin parameters 887 * @param skinDir The skin directory (could be in temp, work or skin) 888 * @param parameters The skins parameters to save 889 * @return <code>true</code> is modification success 890 */ 891 public boolean saveParameters(File skinDir, Map<String, Object> parameters) 892 { 893 File currentFile = new File(skinDir, "model.xml"); 894 895 Source currentSrc = null; 896 Source src = null; 897 try 898 { 899 currentSrc = _resolver.resolveURI("file://" + currentFile.getAbsolutePath()); 900 901 Map<String, Object> parentContext = new HashMap<>(); 902 parentContext.put("modelUri", currentSrc.getURI()); 903 parentContext.put("skinParameters", parameters); 904 905 src = _resolver.resolveURI("cocoon://_plugins/skinfactory/change-parameters", null, parentContext); 906 907 SourceUtil.copy(src, currentSrc); 908 909 return true; 910 } 911 catch (IOException e) 912 { 913 getLogger().error("Unable to save parameters from skin '" + skinDir.getName() + "'", e); 914 return false; 915 } 916 finally 917 { 918 _resolver.release(src); 919 _resolver.release(currentSrc); 920 } 921 } 922 923 /** 924 * Apply model parameters in given skin 925 * @param modelName The model name 926 * @param skinDir The skin directory (could be temp, work or skins) 927 */ 928 public void applyModelParameters (String modelName, File skinDir) 929 { 930 Map<String, Object> currentValues = getParameterValues(skinDir, modelName); 931 applyModelParameters (modelName, skinDir, currentValues); 932 } 933 934 /** 935 * Apply model parameters in given skin 936 * @param modelName The model name 937 * @param skinDir The skin directory 938 * @param values The values to set 939 */ 940 public void applyModelParameters (String modelName, File skinDir, Map<String, Object> values) 941 { 942 Map<String, Object> parameterValues = new HashMap<>(); 943 SkinModel model = _modelsManager.getModel(modelName); 944 945 Map<String, String> modelDefaultValues = model.getDefaultValues(); 946 947 Map<String, AbstractSkinParameter> modelParameters = getModelParameters(modelName); 948 for (AbstractSkinParameter skinParameter : modelParameters.values()) 949 { 950 String paramId = skinParameter.getId(); 951 952 if (skinParameter instanceof I18nizableTextParameter) 953 { 954 Map<String, String> defaultValues = ((I18nizableTextParameter) skinParameter).getDefaultValues(); 955 @SuppressWarnings("unchecked") 956 Map<String, String> currentValues = (Map<String, String>) values.get(paramId); 957 if (currentValues == null) 958 { 959 currentValues = new HashMap<>(); 960 } 961 for (String lang : defaultValues.keySet()) 962 { 963 if (currentValues.get(lang) != null) 964 { 965 applyParameter(skinParameter, skinDir, modelName, currentValues.get(lang), lang); 966 } 967 else 968 { 969 // Default value 970 applyParameter(skinParameter, skinDir, modelName, defaultValues.get(lang), lang); 971 currentValues.put(lang, defaultValues.get(lang)); 972 } 973 } 974 parameterValues.put(skinParameter.getId(), currentValues); 975 } 976 else if (skinParameter instanceof ImageParameter) 977 { 978 FileValue imgValue = (FileValue) _getValue(model, skinParameter, values, modelDefaultValues); 979 applyParameter(skinParameter, skinDir, modelName, imgValue, null); 980 parameterValues.put(skinParameter.getId(), imgValue); 981 } 982 else 983 { 984 String value = (String) _getValue (model, skinParameter, values, modelDefaultValues); 985 applyParameter(skinParameter, skinDir, modelName, value, null); 986 parameterValues.put(skinParameter.getId(), value); 987 } 988 } 989 990 saveParameters(skinDir, parameterValues); 991 } 992 993 private Object _getValue (SkinModel model, AbstractSkinParameter param, Map<String, Object> values, Map<String, String> defaultValues) 994 { 995 String id = param.getId(); 996 997 // First search in current values 998 if (values.containsKey(id)) 999 { 1000 return values.get(id); 1001 } 1002 1003 // Then search in default values from model 1004 if (defaultValues.containsKey(id)) 1005 { 1006 String valueAsStr = defaultValues.get(id); 1007 1008 if (param instanceof ImageParameter) 1009 { 1010 return new FileValue (valueAsStr, false); 1011 } 1012 else 1013 { 1014 return valueAsStr; 1015 } 1016 } 1017 1018 // If nor found, get the parameter default value 1019 return param.getDefaultValue(model); 1020 } 1021 /** 1022 * Update hash 1023 * @param xmlFile the xml {@link File} 1024 * @param hash the new hash of the file 1025 * @throws IOException if an error occurs while manipulating the file 1026 */ 1027 public void updateHash (File xmlFile, String hash) throws IOException 1028 { 1029 Source currentSrc = null; 1030 Source src = null; 1031 try 1032 { 1033 currentSrc = _resolver.resolveURI("file://" + xmlFile.getAbsolutePath()); 1034 1035 Map<String, Object> parentContext = new HashMap<>(); 1036 parentContext.put("modelUri", currentSrc.getURI()); 1037 parentContext.put("hash", hash); 1038 1039 src = _resolver.resolveURI("cocoon://_plugins/skinfactory/change-hash", null, parentContext); 1040 1041 SourceUtil.copy(src, currentSrc); 1042 } 1043 finally 1044 { 1045 _resolver.release(src); 1046 _resolver.release(currentSrc); 1047 } 1048 1049 } 1050 1051 /** 1052 * Apply parameter 1053 * @param parameter the skin parameter 1054 * @param skinDir the skin directory (could be temp, work or skins) 1055 * @param modelName the model name 1056 * @param value the parameter value 1057 * @param lang The language 1058 */ 1059 public void applyParameter (AbstractSkinParameter parameter, File skinDir, String modelName, Object value, String lang) 1060 { 1061 File modelDir = _modelsManager.getModel(modelName).getFile(); 1062 parameter.apply(skinDir, modelDir, value, lang); 1063 } 1064 1065 /** 1066 * Apply new color theme 1067 * @param skinDir the skin directory (could be temp, work or skins) 1068 * @param modelName the model name 1069 */ 1070 public void applyColorTheme (String modelName, File skinDir) 1071 { 1072 File modelDir = _modelsManager.getModel(modelName).getFile(); 1073 1074 Map<String, Object> currentValues = getParameterValues(skinDir, modelName); 1075 1076 Map<String, AbstractSkinParameter> modelParameters = getModelParameters(modelName); 1077 for (AbstractSkinParameter skinParameter : modelParameters.values()) 1078 { 1079 if (skinParameter instanceof CSSColorParameter) 1080 { 1081 String value = (String) currentValues.get(skinParameter.getId()); 1082 if (StringUtils.isEmpty(value)) 1083 { 1084 value = (String) skinParameter.getDefaultValue(_modelsManager.getModel(modelName)); 1085 } 1086 1087 skinParameter.apply(skinDir, modelDir, value, null); 1088 } 1089 } 1090 } 1091 1092 private I18nizableText _configureI18nizableText(Configuration configuration, String modelName) throws ConfigurationException 1093 { 1094 boolean i18nSupported = configuration.getAttributeAsBoolean("i18n", false); 1095 1096 if (i18nSupported) 1097 { 1098 String catalogue = configuration.getAttribute("catalogue", null); 1099 if (catalogue == null) 1100 { 1101 catalogue = "model." + modelName; 1102 } 1103 1104 return new I18nizableText(catalogue, configuration.getValue()); 1105 } 1106 else 1107 { 1108 return new I18nizableText(configuration.getValue("")); 1109 } 1110 } 1111 1112 class SkinDirectoryFilter implements FileFilter 1113 { 1114 @Override 1115 public boolean accept(File file) 1116 { 1117 String filename = file.getName(); 1118 return file.isDirectory() && !filename.equals("CVS") && !filename.equals(".svn"); 1119 } 1120 } 1121}