1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.service.impl;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.plugin.PluginPackage;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.ReleaseInfo;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.kernel.util.Validator;
32  import com.liferay.portal.model.ColorScheme;
33  import com.liferay.portal.model.PluginSetting;
34  import com.liferay.portal.model.Theme;
35  import com.liferay.portal.model.impl.ColorSchemeImpl;
36  import com.liferay.portal.model.impl.PortletImpl;
37  import com.liferay.portal.model.impl.ThemeImpl;
38  import com.liferay.portal.plugin.PluginUtil;
39  import com.liferay.portal.service.PluginSettingLocalServiceUtil;
40  import com.liferay.portal.theme.ThemeCompanyId;
41  import com.liferay.portal.theme.ThemeCompanyLimit;
42  import com.liferay.portal.theme.ThemeGroupId;
43  import com.liferay.portal.theme.ThemeGroupLimit;
44  import com.liferay.portal.util.DocumentUtil;
45  import com.liferay.portal.util.PortalUtil;
46  import com.liferay.util.ContextReplace;
47  import com.liferay.util.ListUtil;
48  import com.liferay.util.Version;
49  
50  import java.io.IOException;
51  
52  import java.util.ArrayList;
53  import java.util.Collections;
54  import java.util.HashSet;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Map;
58  import java.util.Set;
59  import java.util.concurrent.ConcurrentHashMap;
60  
61  import javax.servlet.ServletContext;
62  
63  import org.apache.commons.logging.Log;
64  import org.apache.commons.logging.LogFactory;
65  
66  import org.dom4j.Document;
67  import org.dom4j.DocumentException;
68  import org.dom4j.Element;
69  
70  /**
71   * <a href="ThemeLocalUtil.java.html"><b><i>View Source</i></b></a>
72   *
73   * @author Brian Wing Shun Chan
74   * @author Jorge Ferrer
75   *
76   */
77  public class ThemeLocalUtil {
78  
79      public static ColorScheme getColorScheme(
80              long companyId, String themeId, String colorSchemeId,
81              boolean wapTheme)
82          throws PortalException, SystemException {
83  
84          colorSchemeId = GetterUtil.getString(colorSchemeId);
85  
86          Theme theme = getTheme(companyId, themeId, wapTheme);
87  
88          Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
89  
90          ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
91  
92          if (colorScheme == null) {
93              List<ColorScheme> colorSchemes = theme.getColorSchemes();
94  
95              if (colorSchemes.size() > 0) {
96                  for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
97                      colorScheme = colorSchemes.get(i);
98  
99                      if (colorScheme.isDefaultCs()) {
100                         break;
101                     }
102                 }
103             }
104         }
105 
106         if (colorScheme == null) {
107             if (wapTheme) {
108                 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
109             }
110             else {
111                 colorSchemeId =
112                     ColorSchemeImpl.getDefaultRegularColorSchemeId();
113             }
114         }
115 
116         if (colorScheme == null) {
117             colorScheme = ColorSchemeImpl.getNullColorScheme();
118         }
119 
120         return colorScheme;
121     }
122 
123     public static Theme getTheme(
124             long companyId, String themeId, boolean wapTheme)
125         throws PortalException, SystemException {
126 
127         themeId = GetterUtil.getString(themeId);
128 
129         Theme theme = _getThemes(companyId).get(themeId);
130 
131         if (theme == null) {
132             if (_log.isWarnEnabled()) {
133                 _log.warn(
134                     "No theme found for specified theme id " + themeId +
135                         ". Returning the default theme.");
136             }
137 
138             if (wapTheme) {
139                 themeId = ThemeImpl.getDefaultWapThemeId();
140             }
141             else {
142                 themeId = ThemeImpl.getDefaultRegularThemeId();
143             }
144 
145             theme = _themes.get(themeId);
146         }
147 
148         if (theme == null) {
149             _log.error(
150                 "No theme found for default theme id " + themeId +
151                     ". Returning a random theme.");
152 
153             Iterator<Map.Entry<String, Theme>> itr =
154                 _themes.entrySet().iterator();
155 
156             while (itr.hasNext()) {
157                 Map.Entry<String, Theme> entry = itr.next();
158 
159                 theme = entry.getValue();
160             }
161         }
162 
163         return theme;
164     }
165 
166     public static List<Theme> getThemes(long companyId) {
167         List<Theme> themes = ListUtil.fromCollection(
168             _getThemes(companyId).values());
169 
170         Collections.sort(themes);
171 
172         return themes;
173     }
174 
175     public static List<Theme> getThemes(
176             long companyId, long groupId, long userId, boolean wapTheme)
177         throws PortalException, SystemException {
178 
179         List<Theme> themes = getThemes(companyId);
180 
181         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
182 
183         Iterator<Theme> itr = themes.iterator();
184 
185         while (itr.hasNext()) {
186             Theme theme = itr.next();
187 
188             if ((!theme.isGroupAvailable(groupId)) ||
189                 (theme.isWapTheme() != wapTheme)) {
190 
191                 itr.remove();
192             }
193         }
194 
195         return themes;
196     }
197 
198     public static List<String> init(
199         ServletContext ctx, String themesPath, boolean loadFromServletContext,
200         String[] xmls, PluginPackage pluginPackage) {
201 
202         return init(
203             null, ctx, themesPath, loadFromServletContext, xmls, pluginPackage);
204     }
205 
206     public static List<String> init(
207         String servletContextName, ServletContext ctx, String themesPath,
208         boolean loadFromServletContext, String[] xmls,
209         PluginPackage pluginPackage) {
210 
211         List<String> themeIds = new ArrayList<String>();
212 
213         try {
214             for (int i = 0; i < xmls.length; i++) {
215                 Set<String> themes = _readThemes(
216                     servletContextName, ctx, themesPath, loadFromServletContext,
217                     xmls[i], pluginPackage);
218 
219                 Iterator<String> itr = themes.iterator();
220 
221                 while (itr.hasNext()) {
222                     String themeId = itr.next();
223 
224                     if (!themeIds.contains(themeId)) {
225                         themeIds.add(themeId);
226                     }
227                 }
228             }
229         }
230         catch (Exception e) {
231             e.printStackTrace();
232         }
233 
234         _themesPool.clear();
235 
236         return themeIds;
237     }
238 
239     public static void uninstallThemes(List<String> themeIds) {
240         for (int i = 0; i < themeIds.size(); i++) {
241             String themeId = themeIds.get(i);
242 
243             _themes.remove(themeId);
244 
245             LayoutTemplateLocalUtil.uninstallLayoutTemplates(themeId);
246         }
247 
248         _themesPool.clear();
249     }
250 
251     private static List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
252         List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
253 
254         if (el != null) {
255             List<Element> companyIds = el.elements("company-id");
256 
257             for (int i = 0; i < companyIds.size(); i++) {
258                 Element companyIdEl = companyIds.get(i);
259 
260                 String name = companyIdEl.attributeValue("name");
261                 String pattern = companyIdEl.attributeValue("pattern");
262 
263                 ThemeCompanyId themeCompanyId = null;
264 
265                 if (Validator.isNotNull(name)) {
266                     themeCompanyId = new ThemeCompanyId(name, false);
267                 }
268                 else if (Validator.isNotNull(pattern)) {
269                     themeCompanyId = new ThemeCompanyId(pattern, true);
270                 }
271 
272                 if (themeCompanyId != null) {
273                     includes.add(themeCompanyId);
274                 }
275             }
276         }
277 
278         return includes;
279     }
280 
281     private static List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
282         return _getCompanyLimitExcludes(el);
283     }
284 
285     private static List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
286         List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
287 
288         if (el != null) {
289             List<Element> groupIds = el.elements("group-id");
290 
291             for (int i = 0; i < groupIds.size(); i++) {
292                 Element groupIdEl = groupIds.get(i);
293 
294                 String name = groupIdEl.attributeValue("name");
295                 String pattern = groupIdEl.attributeValue("pattern");
296 
297                 ThemeGroupId themeGroupId = null;
298 
299                 if (Validator.isNotNull(name)) {
300                     themeGroupId = new ThemeGroupId(name, false);
301                 }
302                 else if (Validator.isNotNull(pattern)) {
303                     themeGroupId = new ThemeGroupId(pattern, true);
304                 }
305 
306                 if (themeGroupId != null) {
307                     includes.add(themeGroupId);
308                 }
309             }
310         }
311 
312         return includes;
313     }
314 
315     private static List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
316         return _getGroupLimitExcludes(el);
317     }
318 
319     private static Map<String, Theme> _getThemes(long companyId) {
320         Map<String, Theme> themes = _themesPool.get(companyId);
321 
322         if (themes == null) {
323             themes = new ConcurrentHashMap();
324 
325             Iterator<Map.Entry<String, Theme>> itr =
326                 _themes.entrySet().iterator();
327 
328             while (itr.hasNext()) {
329                 Map.Entry<String, Theme> entry = itr.next();
330 
331                 String themeId = entry.getKey();
332                 Theme theme = entry.getValue();
333 
334                 if (theme.isCompanyAvailable(companyId)) {
335                     themes.put(themeId, theme);
336                 }
337             }
338 
339             _themesPool.put(companyId, themes);
340         }
341 
342         return themes;
343     }
344 
345     private static Version _getVersion(String version) {
346         if (version.equals("${current-version}")) {
347             version = ReleaseInfo.getVersion();
348         }
349 
350         return Version.getInstance(version);
351     }
352 
353     private static void _readColorSchemes(
354             Element theme, Map<String, ColorScheme> colorSchemes,
355             ContextReplace themeContextReplace)
356         throws IOException {
357 
358         Iterator<Element> itr = theme.elements("color-scheme").iterator();
359 
360         while (itr.hasNext()) {
361             Element colorScheme = itr.next();
362 
363             ContextReplace colorSchemeContextReplace =
364                 (ContextReplace)themeContextReplace.clone();
365 
366             String id = colorScheme.attributeValue("id");
367 
368             colorSchemeContextReplace.addValue("color-scheme-id", id);
369 
370             ColorScheme colorSchemeModel = colorSchemes.get(id);
371 
372             if (colorSchemeModel == null) {
373                 colorSchemeModel = new ColorSchemeImpl(id);
374             }
375 
376             String name = GetterUtil.getString(
377                 colorScheme.attributeValue("name"), colorSchemeModel.getName());
378 
379             name = colorSchemeContextReplace.replace(name);
380 
381             boolean defaultCs = GetterUtil.getBoolean(
382                 colorScheme.elementText("default-cs"),
383                 colorSchemeModel.isDefaultCs());
384 
385             String cssClass = GetterUtil.getString(
386                 colorScheme.elementText("css-class"),
387                 colorSchemeModel.getCssClass());
388 
389             cssClass = colorSchemeContextReplace.replace(cssClass);
390 
391             colorSchemeContextReplace.addValue("css-class", cssClass);
392 
393             String colorSchemeImagesPath = GetterUtil.getString(
394                 colorScheme.elementText("color-scheme-images-path"),
395                 colorSchemeModel.getColorSchemeImagesPath());
396 
397             colorSchemeImagesPath = colorSchemeContextReplace.replace(
398                 colorSchemeImagesPath);
399 
400             colorSchemeContextReplace.addValue(
401                 "color-scheme-images-path", colorSchemeImagesPath);
402 
403             colorSchemeModel.setName(name);
404             colorSchemeModel.setDefaultCs(defaultCs);
405             colorSchemeModel.setCssClass(cssClass);
406             colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
407 
408             colorSchemes.put(id, colorSchemeModel);
409         }
410     }
411 
412     private static Set<String> _readThemes(
413             String servletContextName, ServletContext ctx, String themesPath,
414             boolean loadFromServletContext, String xml,
415             PluginPackage pluginPackage)
416         throws DocumentException, IOException {
417 
418         Set<String> themeIds = new HashSet<String>();
419 
420         if (xml == null) {
421             return themeIds;
422         }
423 
424         Document doc = DocumentUtil.readDocumentFromXML(xml, true);
425 
426         Element root = doc.getRootElement();
427 
428         Version portalVersion = _getVersion(ReleaseInfo.getVersion());
429 
430         boolean compatible = false;
431 
432         Element compatibilityEl = root.element("compatibility");
433 
434         if (compatibilityEl != null) {
435             Iterator<Element> itr = compatibilityEl.elements(
436                 "version").iterator();
437 
438             while (itr.hasNext()) {
439                 Element versionEl = itr.next();
440 
441                 Version version = _getVersion(versionEl.getTextTrim());
442 
443                 if (version.includes(portalVersion)) {
444                     compatible = true;
445 
446                     break;
447                 }
448             }
449         }
450 
451         if (!compatible) {
452             _log.error(
453                 "Themes in this WAR are not compatible with " +
454                     ReleaseInfo.getServerInfo());
455 
456             return themeIds;
457         }
458 
459         ThemeCompanyLimit companyLimit = null;
460 
461         Element companyLimitEl = root.element("company-limit");
462 
463         if (companyLimitEl != null) {
464             companyLimit = new ThemeCompanyLimit();
465 
466             Element companyIncludesEl =
467                 companyLimitEl.element("company-includes");
468 
469             if (companyIncludesEl != null) {
470                 companyLimit.setIncludes(
471                     _getCompanyLimitIncludes(companyIncludesEl));
472             }
473 
474             Element companyExcludesEl =
475                 companyLimitEl.element("company-excludes");
476 
477             if (companyExcludesEl != null) {
478                 companyLimit.setExcludes(
479                     _getCompanyLimitExcludes(companyExcludesEl));
480             }
481         }
482 
483         ThemeGroupLimit groupLimit = null;
484 
485         Element groupLimitEl = root.element("group-limit");
486 
487         if (groupLimitEl != null) {
488             groupLimit = new ThemeGroupLimit();
489 
490             Element groupIncludesEl = groupLimitEl.element("group-includes");
491 
492             if (groupIncludesEl != null) {
493                 groupLimit.setIncludes(_getGroupLimitIncludes(groupIncludesEl));
494             }
495 
496             Element groupExcludesEl =
497                 groupLimitEl.element("group-excludes");
498 
499             if (groupExcludesEl != null) {
500                 groupLimit.setExcludes(_getGroupLimitExcludes(groupExcludesEl));
501             }
502         }
503 
504         Iterator<Element> itr1 = root.elements("theme").iterator();
505 
506         while (itr1.hasNext()) {
507             Element theme = itr1.next();
508 
509             ContextReplace themeContextReplace = new ContextReplace();
510 
511             themeContextReplace.addValue("themes-path", themesPath);
512 
513             String themeId = theme.attributeValue("id");
514 
515             if (servletContextName != null) {
516                 themeId =
517                     themeId + PortletImpl.WAR_SEPARATOR + servletContextName;
518             }
519 
520             themeId = PortalUtil.getJsSafePortletId(themeId);
521 
522             themeContextReplace.addValue("theme-id", themeId);
523 
524             themeIds.add(themeId);
525 
526             Theme themeModel = _themes.get(themeId);
527 
528             if (themeModel == null) {
529                 themeModel = new ThemeImpl(themeId);
530 
531                 _themes.put(themeId, themeModel);
532             }
533 
534             PluginSetting pluginSetting =
535                 PluginSettingLocalServiceUtil.getDefaultPluginSetting();
536 
537             themeModel.setPluginPackage(pluginPackage);
538             themeModel.setDefaultPluginSetting(pluginSetting);
539 
540             themeModel.setThemeCompanyLimit(companyLimit);
541             themeModel.setThemeGroupLimit(groupLimit);
542 
543             if (servletContextName != null) {
544                 themeModel.setServletContextName(servletContextName);
545             }
546 
547             themeModel.setLoadFromServletContext(loadFromServletContext);
548 
549             themeModel.setTimestamp(System.currentTimeMillis());
550 
551             String name = GetterUtil.getString(
552                 theme.attributeValue("name"), themeModel.getName());
553 
554             String rootPath = GetterUtil.getString(
555                 theme.elementText("root-path"), themeModel.getRootPath());
556 
557             rootPath = themeContextReplace.replace(rootPath);
558 
559             themeContextReplace.addValue("root-path", rootPath);
560 
561             String templatesPath = GetterUtil.getString(
562                 theme.elementText("templates-path"),
563                 themeModel.getTemplatesPath());
564 
565             templatesPath = themeContextReplace.replace(templatesPath);
566             templatesPath = StringUtil.safePath(templatesPath);
567 
568             themeContextReplace.addValue("templates-path", templatesPath);
569 
570             String cssPath = GetterUtil.getString(
571                 theme.elementText("css-path"), themeModel.getCssPath());
572 
573             cssPath = themeContextReplace.replace(cssPath);
574             cssPath = StringUtil.safePath(cssPath);
575 
576             themeContextReplace.addValue("css-path", cssPath);
577 
578             String imagesPath = GetterUtil.getString(
579                 theme.elementText("images-path"),
580                 themeModel.getImagesPath());
581 
582             imagesPath = themeContextReplace.replace(imagesPath);
583             imagesPath = StringUtil.safePath(imagesPath);
584 
585             themeContextReplace.addValue("images-path", imagesPath);
586 
587             String javaScriptPath = GetterUtil.getString(
588                 theme.elementText("javascript-path"),
589                 themeModel.getJavaScriptPath());
590 
591             javaScriptPath = themeContextReplace.replace(javaScriptPath);
592             javaScriptPath = StringUtil.safePath(javaScriptPath);
593 
594             themeContextReplace.addValue("javascript-path", javaScriptPath);
595 
596             String virtualPath = GetterUtil.getString(
597                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
598 
599             String templateExtension = GetterUtil.getString(
600                 theme.elementText("template-extension"),
601                 themeModel.getTemplateExtension());
602 
603             themeModel.setName(name);
604             themeModel.setRootPath(rootPath);
605             themeModel.setTemplatesPath(templatesPath);
606             themeModel.setCssPath(cssPath);
607             themeModel.setImagesPath(imagesPath);
608             themeModel.setJavaScriptPath(javaScriptPath);
609             themeModel.setVirtualPath(virtualPath);
610             themeModel.setTemplateExtension(templateExtension);
611 
612             Element settingsEl = theme.element("settings");
613 
614             if (settingsEl != null) {
615                 Iterator<Element> itr2 = settingsEl.elements(
616                     "setting").iterator();
617 
618                 while (itr2.hasNext()) {
619                     Element settingEl = itr2.next();
620 
621                     String key = settingEl.attributeValue("key");
622                     String value = settingEl.attributeValue("value");
623 
624                     themeModel.setSetting(key, value);
625                 }
626             }
627 
628             themeModel.setWapTheme(GetterUtil.getBoolean(
629                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
630 
631             Element rolesEl = theme.element("roles");
632 
633             if (rolesEl != null) {
634                 Iterator<Element> itr2 = rolesEl.elements(
635                     "role-name").iterator();
636 
637                 while (itr2.hasNext()) {
638                     Element roleNameEl = itr2.next();
639 
640                     pluginSetting.addRole(roleNameEl.getText());
641                 }
642             }
643 
644             _readColorSchemes(
645                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
646             _readColorSchemes(
647                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
648 
649             Element layoutTemplatesEl = theme.element("layout-templates");
650 
651             if (layoutTemplatesEl != null) {
652                 Element standardEl = layoutTemplatesEl.element("standard");
653 
654                 if (standardEl != null) {
655                     LayoutTemplateLocalUtil.readLayoutTemplate(
656                         servletContextName, ctx, null, standardEl, true,
657                         themeId, pluginPackage);
658                 }
659 
660                 Element customEl = layoutTemplatesEl.element("custom");
661 
662                 if (customEl != null) {
663                     LayoutTemplateLocalUtil.readLayoutTemplate(
664                         servletContextName, ctx, null, customEl, false, themeId,
665                         pluginPackage);
666                 }
667             }
668         }
669 
670         return themeIds;
671     }
672 
673     private static Log _log = LogFactory.getLog(ThemeLocalUtil.class);
674 
675     private static Map<String, Theme> _themes =
676         new ConcurrentHashMap<String, Theme>();
677     private static Map<Long, Map<String, Theme>> _themesPool =
678         new ConcurrentHashMap<Long, Map<String, Theme>>();
679 
680 }