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