1   /**
2    * Copyright (c) 2000-2007 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.StringUtil;
30  import com.liferay.portal.kernel.util.Validator;
31  import com.liferay.portal.model.ColorScheme;
32  import com.liferay.portal.model.PluginSetting;
33  import com.liferay.portal.model.Theme;
34  import com.liferay.portal.model.impl.ColorSchemeImpl;
35  import com.liferay.portal.model.impl.PortletImpl;
36  import com.liferay.portal.model.impl.ThemeImpl;
37  import com.liferay.portal.plugin.PluginUtil;
38  import com.liferay.portal.service.PluginSettingLocalServiceUtil;
39  import com.liferay.portal.theme.ThemeCompanyId;
40  import com.liferay.portal.theme.ThemeCompanyLimit;
41  import com.liferay.portal.theme.ThemeGroupId;
42  import com.liferay.portal.theme.ThemeGroupLimit;
43  import com.liferay.portal.util.PortalUtil;
44  import com.liferay.portal.util.ReleaseInfo;
45  import com.liferay.util.CollectionFactory;
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  
60  import javax.servlet.ServletContext;
61  
62  import org.apache.commons.logging.Log;
63  import org.apache.commons.logging.LogFactory;
64  
65  import org.dom4j.Document;
66  import org.dom4j.DocumentException;
67  import org.dom4j.Element;
68  
69  /**
70   * <a href="ThemeLocalUtil.java.html"><b><i>View Source</i></b></a>
71   *
72   * @author Brian Wing Shun Chan
73   * @author Jorge Ferrer
74   *
75   */
76  public class ThemeLocalUtil {
77  
78      public static ColorScheme getColorScheme(
79              long companyId, String themeId, String colorSchemeId,
80              boolean wapTheme)
81          throws PortalException, SystemException {
82  
83          colorSchemeId = GetterUtil.getString(colorSchemeId);
84  
85          Theme theme = getTheme(companyId, themeId, wapTheme);
86  
87          Map colorSchemesMap = theme.getColorSchemesMap();
88  
89          ColorScheme colorScheme = (ColorScheme)colorSchemesMap.get(
90              colorSchemeId);
91  
92          if (colorScheme == null) {
93              List colorSchemes = theme.getColorSchemes();
94  
95              if (colorSchemes.size() > 0) {
96                  for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
97                      colorScheme = (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 = (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 = (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 itr = _themes.entrySet().iterator();
154 
155             while (itr.hasNext()) {
156                 Map.Entry entry = (Map.Entry)itr.next();
157 
158                 theme = (Theme)entry.getValue();
159             }
160         }
161 
162         return theme;
163     }
164 
165     public static List getThemes(long companyId) {
166         List themes = ListUtil.fromCollection(_getThemes(companyId).values());
167 
168         Collections.sort(themes);
169 
170         return themes;
171     }
172 
173     public static List getThemes(
174             long companyId, long groupId, long userId, boolean wapTheme)
175         throws PortalException, SystemException {
176 
177         List themes = getThemes(companyId);
178 
179         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
180 
181         Iterator itr = themes.iterator();
182 
183         while (itr.hasNext()) {
184             Theme theme = (Theme)itr.next();
185 
186             if ((!theme.isGroupAvailable(groupId)) ||
187                 (theme.isWapTheme() != wapTheme)) {
188 
189                 itr.remove();
190             }
191         }
192 
193         return themes;
194     }
195 
196     public static List init(
197         ServletContext ctx, String themesPath, boolean loadFromServletContext,
198         String[] xmls, PluginPackage pluginPackage) {
199 
200         return init(
201             null, ctx, themesPath, loadFromServletContext, xmls, pluginPackage);
202     }
203 
204     public static List init(
205         String servletContextName, ServletContext ctx, String themesPath,
206         boolean loadFromServletContext, String[] xmls,
207         PluginPackage pluginPackage) {
208 
209         List themeIds = new ArrayList();
210 
211         try {
212             for (int i = 0; i < xmls.length; i++) {
213                 Set themes = _readThemes(
214                     servletContextName, ctx, themesPath, loadFromServletContext,
215                     xmls[i], pluginPackage);
216 
217                 Iterator itr = themes.iterator();
218 
219                 while (itr.hasNext()) {
220                     String themeId = (String)itr.next();
221 
222                     if (!themeIds.contains(themeId)) {
223                         themeIds.add(themeId);
224                     }
225                 }
226             }
227         }
228         catch (Exception e) {
229             e.printStackTrace();
230         }
231 
232         _themesPool.clear();
233 
234         return themeIds;
235     }
236 
237     public static void uninstallThemes(List themeIds) {
238         for (int i = 0; i < themeIds.size(); i++) {
239             String themeId = (String)themeIds.get(i);
240 
241             _themes.remove(themeId);
242 
243             LayoutTemplateLocalUtil.uninstallLayoutTemplates(themeId);
244         }
245 
246         _themesPool.clear();
247     }
248 
249     private static List _getCompanyLimitExcludes(Element el) {
250         List includes = new ArrayList();
251 
252         if (el != null) {
253             List companyIds = el.elements("company-id");
254 
255             for (int i = 0; i < companyIds.size(); i++) {
256                 Element companyIdEl = (Element)companyIds.get(i);
257 
258                 String name = companyIdEl.attributeValue("name");
259                 String pattern = companyIdEl.attributeValue("pattern");
260 
261                 ThemeCompanyId themeCompanyId = null;
262 
263                 if (Validator.isNotNull(name)) {
264                     themeCompanyId = new ThemeCompanyId(name, false);
265                 }
266                 else if (Validator.isNotNull(pattern)) {
267                     themeCompanyId = new ThemeCompanyId(pattern, true);
268                 }
269 
270                 if (themeCompanyId != null) {
271                     includes.add(themeCompanyId);
272                 }
273             }
274         }
275 
276         return includes;
277     }
278 
279     private static List _getCompanyLimitIncludes(Element el) {
280         return _getCompanyLimitExcludes(el);
281     }
282 
283     private static List _getGroupLimitExcludes(Element el) {
284         List includes = new ArrayList();
285 
286         if (el != null) {
287             List groupIds = el.elements("group-id");
288 
289             for (int i = 0; i < groupIds.size(); i++) {
290                 Element groupIdEl = (Element)groupIds.get(i);
291 
292                 String name = groupIdEl.attributeValue("name");
293                 String pattern = groupIdEl.attributeValue("pattern");
294 
295                 ThemeGroupId themeGroupId = null;
296 
297                 if (Validator.isNotNull(name)) {
298                     themeGroupId = new ThemeGroupId(name, false);
299                 }
300                 else if (Validator.isNotNull(pattern)) {
301                     themeGroupId = new ThemeGroupId(pattern, true);
302                 }
303 
304                 if (themeGroupId != null) {
305                     includes.add(themeGroupId);
306                 }
307             }
308         }
309 
310         return includes;
311     }
312 
313     private static List _getGroupLimitIncludes(Element el) {
314         return _getGroupLimitExcludes(el);
315     }
316 
317     private static Map _getThemes(long companyId) {
318         Long companyIdObj = new Long(companyId);
319 
320         Map themes = (Map)_themesPool.get(companyIdObj);
321 
322         if (themes == null) {
323             themes = CollectionFactory.getSyncHashMap();
324 
325             Iterator itr = _themes.entrySet().iterator();
326 
327             while (itr.hasNext()) {
328                 Map.Entry entry = (Map.Entry)itr.next();
329 
330                 String themeId = (String)entry.getKey();
331                 Theme theme = (Theme)entry.getValue();
332 
333                 if (theme.isCompanyAvailable(companyId)) {
334                     themes.put(themeId, theme);
335                 }
336             }
337 
338             _themesPool.put(companyIdObj, themes);
339         }
340 
341         return themes;
342     }
343 
344     private static Version _getVersion(String version) {
345         if (version.equals("${current-version}")) {
346             version = ReleaseInfo.getVersion();
347         }
348 
349         return Version.getInstance(version);
350     }
351 
352     private static void _readColorSchemes(
353             Element theme, Map colorSchemes, ContextReplace themeContextReplace)
354         throws IOException {
355 
356         Iterator itr = theme.elements("color-scheme").iterator();
357 
358         while (itr.hasNext()) {
359             Element colorScheme = (Element)itr.next();
360 
361             ContextReplace colorSchemeContextReplace =
362                 (ContextReplace)themeContextReplace.clone();
363 
364             String id = colorScheme.attributeValue("id");
365 
366             colorSchemeContextReplace.addValue("color-scheme-id", id);
367 
368             ColorScheme colorSchemeModel =
369                 (ColorScheme)colorSchemes.get(id);
370 
371             if (colorSchemeModel == null) {
372                 colorSchemeModel = new ColorSchemeImpl(id);
373             }
374 
375             String name = GetterUtil.getString(
376                 colorScheme.attributeValue("name"), colorSchemeModel.getName());
377 
378             name = colorSchemeContextReplace.replace(name);
379 
380             boolean defaultCs = GetterUtil.getBoolean(
381                 colorScheme.elementText("default-cs"),
382                 colorSchemeModel.isDefaultCs());
383 
384             String cssClass = GetterUtil.getString(
385                 colorScheme.elementText("css-class"),
386                 colorSchemeModel.getCssClass());
387 
388             cssClass = colorSchemeContextReplace.replace(cssClass);
389 
390             colorSchemeContextReplace.addValue("css-class", cssClass);
391 
392             String colorSchemeImagesPath = GetterUtil.getString(
393                 colorScheme.elementText("color-scheme-images-path"),
394                 colorSchemeModel.getColorSchemeImagesPath());
395 
396             colorSchemeImagesPath = colorSchemeContextReplace.replace(
397                 colorSchemeImagesPath);
398 
399             colorSchemeContextReplace.addValue(
400                 "color-scheme-images-path", colorSchemeImagesPath);
401 
402             colorSchemeModel.setName(name);
403             colorSchemeModel.setDefaultCs(defaultCs);
404             colorSchemeModel.setCssClass(cssClass);
405             colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
406 
407             colorSchemes.put(id, colorSchemeModel);
408         }
409     }
410 
411     private static Set _readThemes(
412             String servletContextName, ServletContext ctx, String themesPath,
413             boolean loadFromServletContext, String xml,
414             PluginPackage pluginPackage)
415         throws DocumentException, IOException {
416 
417         Set themeIds = new HashSet();
418 
419         if (xml == null) {
420             return themeIds;
421         }
422 
423         Document doc = PortalUtil.readDocumentFromXML(xml, true);
424 
425         Element root = doc.getRootElement();
426 
427         Version portalVersion = _getVersion(ReleaseInfo.getVersion());
428 
429         boolean compatible = false;
430 
431         Element compatibilityEl = root.element("compatibility");
432 
433         if (compatibilityEl != null) {
434             Iterator itr = compatibilityEl.elements("version").iterator();
435 
436             while (itr.hasNext()) {
437                 Element versionEl = (Element)itr.next();
438 
439                 Version version = _getVersion(versionEl.getTextTrim());
440 
441                 if (version.includes(portalVersion)) {
442                     compatible = true;
443 
444                     break;
445                 }
446             }
447         }
448 
449         if (!compatible) {
450             _log.error(
451                 "Themes in this WAR are not compatible with " +
452                     ReleaseInfo.getServerInfo());
453 
454             return themeIds;
455         }
456 
457         ThemeCompanyLimit companyLimit = null;
458 
459         Element companyLimitEl = root.element("company-limit");
460 
461         if (companyLimitEl != null) {
462             companyLimit = new ThemeCompanyLimit();
463 
464             Element companyIncludesEl =
465                 companyLimitEl.element("company-includes");
466 
467             if (companyIncludesEl != null) {
468                 companyLimit.setIncludes(
469                     _getCompanyLimitIncludes(companyIncludesEl));
470             }
471 
472             Element companyExcludesEl =
473                 companyLimitEl.element("company-excludes");
474 
475             if (companyExcludesEl != null) {
476                 companyLimit.setExcludes(
477                     _getCompanyLimitExcludes(companyExcludesEl));
478             }
479         }
480 
481         ThemeGroupLimit groupLimit = null;
482 
483         Element groupLimitEl = root.element("group-limit");
484 
485         if (groupLimitEl != null) {
486             groupLimit = new ThemeGroupLimit();
487 
488             Element groupIncludesEl = groupLimitEl.element("group-includes");
489 
490             if (groupIncludesEl != null) {
491                 groupLimit.setIncludes(_getGroupLimitIncludes(groupIncludesEl));
492             }
493 
494             Element groupExcludesEl =
495                 groupLimitEl.element("group-excludes");
496 
497             if (groupExcludesEl != null) {
498                 groupLimit.setExcludes(_getGroupLimitExcludes(groupExcludesEl));
499             }
500         }
501 
502         Iterator itr1 = root.elements("theme").iterator();
503 
504         while (itr1.hasNext()) {
505             Element theme = (Element)itr1.next();
506 
507             ContextReplace themeContextReplace = new ContextReplace();
508 
509             themeContextReplace.addValue("themes-path", themesPath);
510 
511             String themeId = theme.attributeValue("id");
512 
513             if (servletContextName != null) {
514                 themeId =
515                     themeId + PortletImpl.WAR_SEPARATOR + servletContextName;
516             }
517 
518             themeId = PortalUtil.getJsSafePortletId(themeId);
519 
520             themeContextReplace.addValue("theme-id", themeId);
521 
522             themeIds.add(themeId);
523 
524             Theme themeModel = (Theme)_themes.get(themeId);
525 
526             if (themeModel == null) {
527                 themeModel = new ThemeImpl(themeId);
528 
529                 _themes.put(themeId, themeModel);
530             }
531 
532             PluginSetting pluginSetting =
533                 PluginSettingLocalServiceUtil.getDefaultPluginSetting();
534 
535             themeModel.setPluginPackage(pluginPackage);
536             themeModel.setDefaultPluginSetting(pluginSetting);
537 
538             themeModel.setThemeCompanyLimit(companyLimit);
539             themeModel.setThemeGroupLimit(groupLimit);
540 
541             if (servletContextName != null) {
542                 themeModel.setServletContextName(servletContextName);
543             }
544 
545             themeModel.setLoadFromServletContext(loadFromServletContext);
546 
547             themeModel.setTimestamp(System.currentTimeMillis());
548 
549             String name = GetterUtil.getString(
550                 theme.attributeValue("name"), themeModel.getName());
551 
552             String rootPath = GetterUtil.getString(
553                 theme.elementText("root-path"), themeModel.getRootPath());
554 
555             rootPath = themeContextReplace.replace(rootPath);
556 
557             themeContextReplace.addValue("root-path", rootPath);
558 
559             String templatesPath = GetterUtil.getString(
560                 theme.elementText("templates-path"),
561                 themeModel.getTemplatesPath());
562 
563             templatesPath = themeContextReplace.replace(templatesPath);
564             templatesPath = StringUtil.safePath(templatesPath);
565 
566             themeContextReplace.addValue("templates-path", templatesPath);
567 
568             String cssPath = GetterUtil.getString(
569                 theme.elementText("css-path"), themeModel.getCssPath());
570 
571             cssPath = themeContextReplace.replace(cssPath);
572             cssPath = StringUtil.safePath(cssPath);
573 
574             themeContextReplace.addValue("css-path", cssPath);
575 
576             String imagesPath = GetterUtil.getString(
577                 theme.elementText("images-path"),
578                 themeModel.getImagesPath());
579 
580             imagesPath = themeContextReplace.replace(imagesPath);
581             imagesPath = StringUtil.safePath(imagesPath);
582 
583             themeContextReplace.addValue("images-path", imagesPath);
584 
585             String javaScriptPath = GetterUtil.getString(
586                 theme.elementText("javascript-path"),
587                 themeModel.getJavaScriptPath());
588 
589             javaScriptPath = themeContextReplace.replace(javaScriptPath);
590             javaScriptPath = StringUtil.safePath(javaScriptPath);
591 
592             themeContextReplace.addValue("javascript-path", javaScriptPath);
593 
594             String virtualPath = GetterUtil.getString(
595                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
596 
597             String templateExtension = GetterUtil.getString(
598                 theme.elementText("template-extension"),
599                 themeModel.getTemplateExtension());
600 
601             themeModel.setName(name);
602             themeModel.setRootPath(rootPath);
603             themeModel.setTemplatesPath(templatesPath);
604             themeModel.setCssPath(cssPath);
605             themeModel.setImagesPath(imagesPath);
606             themeModel.setJavaScriptPath(javaScriptPath);
607             themeModel.setVirtualPath(virtualPath);
608             themeModel.setTemplateExtension(templateExtension);
609 
610             Element settingsEl = theme.element("settings");
611 
612             if (settingsEl != null) {
613                 Iterator itr2 = settingsEl.elements("setting").iterator();
614 
615                 while (itr2.hasNext()) {
616                     Element settingEl = (Element)itr2.next();
617 
618                     String key = settingEl.attributeValue("key");
619                     String value = settingEl.attributeValue("value");
620 
621                     themeModel.setSetting(key, value);
622                 }
623             }
624 
625             themeModel.setWapTheme(GetterUtil.getBoolean(
626                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
627 
628             Element rolesEl = theme.element("roles");
629 
630             if (rolesEl != null) {
631                 Iterator itr2 = rolesEl.elements("role-name").iterator();
632 
633                 while (itr2.hasNext()) {
634                     Element roleNameEl = (Element)itr2.next();
635 
636                     pluginSetting.addRole(roleNameEl.getText());
637                 }
638             }
639 
640             _readColorSchemes(
641                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
642             _readColorSchemes(
643                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
644 
645             Element layoutTemplatesEl = theme.element("layout-templates");
646 
647             if (layoutTemplatesEl != null) {
648                 Element standardEl = layoutTemplatesEl.element("standard");
649 
650                 if (standardEl != null) {
651                     LayoutTemplateLocalUtil.readLayoutTemplate(
652                         servletContextName, ctx, null, standardEl, true,
653                         themeId, pluginPackage);
654                 }
655 
656                 Element customEl = layoutTemplatesEl.element("custom");
657 
658                 if (customEl != null) {
659                     LayoutTemplateLocalUtil.readLayoutTemplate(
660                         servletContextName, ctx, null, customEl, false, themeId,
661                         pluginPackage);
662                 }
663             }
664         }
665 
666         return themeIds;
667     }
668 
669     private static Log _log = LogFactory.getLog(ThemeLocalUtil.class);
670 
671     private static Map _themes = CollectionFactory.getSyncHashMap();
672     private static Map _themesPool = CollectionFactory.getSyncHashMap();
673 
674 }