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.HashSet;
56  import java.util.Iterator;
57  import java.util.List;
58  import java.util.Map;
59  import java.util.Set;
60  import java.util.concurrent.ConcurrentHashMap;
61  
62  import javax.servlet.ServletContext;
63  
64  import org.apache.commons.logging.Log;
65  import org.apache.commons.logging.LogFactory;
66  
67  /**
68   * <a href="ThemeLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
69   *
70   * @author Brian Wing Shun Chan
71   * @author Jorge Ferrer
72   *
73   */
74  public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
75  
76      public ColorScheme getColorScheme(
77          long companyId, String themeId, String colorSchemeId,
78          boolean wapTheme) {
79  
80          colorSchemeId = GetterUtil.getString(colorSchemeId);
81  
82          Theme theme = getTheme(companyId, themeId, wapTheme);
83  
84          Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
85  
86          ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
87  
88          if (colorScheme == null) {
89              List<ColorScheme> colorSchemes = theme.getColorSchemes();
90  
91              if (colorSchemes.size() > 0) {
92                  for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
93                      colorScheme = colorSchemes.get(i);
94  
95                      if (colorScheme.isDefaultCs()) {
96                          break;
97                      }
98                  }
99              }
100         }
101 
102         if (colorScheme == null) {
103             if (wapTheme) {
104                 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
105             }
106             else {
107                 colorSchemeId =
108                     ColorSchemeImpl.getDefaultRegularColorSchemeId();
109             }
110         }
111 
112         if (colorScheme == null) {
113             colorScheme = ColorSchemeImpl.getNullColorScheme();
114         }
115 
116         return colorScheme;
117     }
118 
119     public Theme getTheme(long companyId, String themeId, boolean wapTheme) {
120         themeId = GetterUtil.getString(themeId);
121 
122         Theme theme = _getThemes(companyId).get(themeId);
123 
124         if (theme == null) {
125             if (_log.isWarnEnabled()) {
126                 _log.warn(
127                     "No theme found for specified theme id " + themeId +
128                         ". Returning the default theme.");
129             }
130 
131             if (wapTheme) {
132                 themeId = ThemeImpl.getDefaultWapThemeId();
133             }
134             else {
135                 themeId = ThemeImpl.getDefaultRegularThemeId();
136             }
137 
138             theme = _themes.get(themeId);
139         }
140 
141         if (theme == null) {
142             if (_themes.isEmpty()) {
143                 if (_log.isDebugEnabled()) {
144                     _log.debug("No themes are installed");
145                 }
146 
147                 return null;
148             }
149 
150             _log.error(
151                 "No theme found for default theme id " + themeId +
152                     ". Returning a random theme.");
153 
154             Iterator<Map.Entry<String, Theme>> itr =
155                 _themes.entrySet().iterator();
156 
157             while (itr.hasNext()) {
158                 Map.Entry<String, Theme> entry = itr.next();
159 
160                 theme = entry.getValue();
161             }
162         }
163 
164         return theme;
165     }
166 
167     public List<Theme> getThemes(long companyId) {
168         List<Theme> themes = ListUtil.fromCollection(
169             _getThemes(companyId).values());
170 
171         return ListUtil.sort(themes);
172     }
173 
174     public List<Theme> getThemes(
175             long companyId, long groupId, long userId, boolean wapTheme)
176         throws PortalException, SystemException {
177 
178         List<Theme> themes = getThemes(companyId);
179 
180         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
181 
182         Iterator<Theme> itr = themes.iterator();
183 
184         while (itr.hasNext()) {
185             Theme theme = itr.next();
186 
187             if ((!theme.isGroupAvailable(groupId)) ||
188                 (theme.isWapTheme() != wapTheme)) {
189 
190                 itr.remove();
191             }
192         }
193 
194         return themes;
195     }
196 
197     public List<String> init(
198         ServletContext servletContext, String themesPath,
199         boolean loadFromServletContext, String[] xmls,
200         PluginPackage pluginPackage) {
201 
202         return init(
203             null, servletContext, themesPath, loadFromServletContext, xmls,
204             pluginPackage);
205     }
206 
207     public List<String> init(
208         String servletContextName, ServletContext servletContext,
209         String themesPath, boolean loadFromServletContext, String[] xmls,
210         PluginPackage pluginPackage) {
211 
212         List<String> themeIds = new ArrayList<String>();
213 
214         try {
215             for (int i = 0; i < xmls.length; i++) {
216                 Set<String> themes = _readThemes(
217                     servletContextName, servletContext, themesPath,
218                     loadFromServletContext, xmls[i], pluginPackage);
219 
220                 Iterator<String> itr = themes.iterator();
221 
222                 while (itr.hasNext()) {
223                     String themeId = itr.next();
224 
225                     if (!themeIds.contains(themeId)) {
226                         themeIds.add(themeId);
227                     }
228                 }
229             }
230         }
231         catch (Exception e) {
232             e.printStackTrace();
233         }
234 
235         _themesPool.clear();
236 
237         return themeIds;
238     }
239 
240     public void uninstallThemes(List<String> themeIds) {
241         for (int i = 0; i < themeIds.size(); i++) {
242             String themeId = themeIds.get(i);
243 
244             _themes.remove(themeId);
245 
246             LayoutTemplateLocalServiceUtil.uninstallLayoutTemplates(themeId);
247         }
248 
249         _themesPool.clear();
250     }
251 
252     private List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
253         List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
254 
255         if (el != null) {
256             List<Element> companyIds = el.elements("company-id");
257 
258             for (int i = 0; i < companyIds.size(); i++) {
259                 Element companyIdEl = companyIds.get(i);
260 
261                 String name = companyIdEl.attributeValue("name");
262                 String pattern = companyIdEl.attributeValue("pattern");
263 
264                 ThemeCompanyId themeCompanyId = null;
265 
266                 if (Validator.isNotNull(name)) {
267                     themeCompanyId = new ThemeCompanyId(name, false);
268                 }
269                 else if (Validator.isNotNull(pattern)) {
270                     themeCompanyId = new ThemeCompanyId(pattern, true);
271                 }
272 
273                 if (themeCompanyId != null) {
274                     includes.add(themeCompanyId);
275                 }
276             }
277         }
278 
279         return includes;
280     }
281 
282     private List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
283         return _getCompanyLimitExcludes(el);
284     }
285 
286     private List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
287         List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
288 
289         if (el != null) {
290             List<Element> groupIds = el.elements("group-id");
291 
292             for (int i = 0; i < groupIds.size(); i++) {
293                 Element groupIdEl = groupIds.get(i);
294 
295                 String name = groupIdEl.attributeValue("name");
296                 String pattern = groupIdEl.attributeValue("pattern");
297 
298                 ThemeGroupId themeGroupId = null;
299 
300                 if (Validator.isNotNull(name)) {
301                     themeGroupId = new ThemeGroupId(name, false);
302                 }
303                 else if (Validator.isNotNull(pattern)) {
304                     themeGroupId = new ThemeGroupId(pattern, true);
305                 }
306 
307                 if (themeGroupId != null) {
308                     includes.add(themeGroupId);
309                 }
310             }
311         }
312 
313         return includes;
314     }
315 
316     private List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
317         return _getGroupLimitExcludes(el);
318     }
319 
320     private Map<String, Theme> _getThemes(long companyId) {
321         Map<String, Theme> themes = _themesPool.get(companyId);
322 
323         if (themes == null) {
324             themes = new ConcurrentHashMap<String, Theme>();
325 
326             Iterator<Map.Entry<String, Theme>> itr =
327                 _themes.entrySet().iterator();
328 
329             while (itr.hasNext()) {
330                 Map.Entry<String, Theme> entry = itr.next();
331 
332                 String themeId = entry.getKey();
333                 Theme theme = entry.getValue();
334 
335                 if (theme.isCompanyAvailable(companyId)) {
336                     themes.put(themeId, theme);
337                 }
338             }
339 
340             _themesPool.put(companyId, themes);
341         }
342 
343         return themes;
344     }
345 
346     private Version _getVersion(String version) {
347         if (version.equals("${current-version}")) {
348             version = ReleaseInfo.getVersion();
349         }
350 
351         return Version.getInstance(version);
352     }
353 
354     private void _readColorSchemes(
355         Element theme, Map<String, ColorScheme> colorSchemes,
356         ContextReplace themeContextReplace) {
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 Set<String> _readThemes(
413             String servletContextName, ServletContext servletContext,
414             String themesPath, boolean loadFromServletContext, String xml,
415             PluginPackage pluginPackage)
416         throws Exception {
417 
418         Set<String> themeIds = new HashSet<String>();
419 
420         if (xml == null) {
421             return themeIds;
422         }
423 
424         Document doc = SAXReaderUtil.read(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 + PortletConstants.WAR_SEPARATOR +
518                         servletContextName;
519             }
520 
521             themeId = PortalUtil.getJsSafePortletId(themeId);
522 
523             themeContextReplace.addValue("theme-id", themeId);
524 
525             themeIds.add(themeId);
526 
527             Theme themeModel = _themes.get(themeId);
528 
529             if (themeModel == null) {
530                 themeModel = new ThemeImpl(themeId);
531 
532                 _themes.put(themeId, themeModel);
533             }
534 
535             themeModel.setTimestamp(System.currentTimeMillis());
536 
537             PluginSetting pluginSetting =
538                 PluginSettingLocalServiceUtil.getDefaultPluginSetting();
539 
540             themeModel.setPluginPackage(pluginPackage);
541             themeModel.setDefaultPluginSetting(pluginSetting);
542 
543             themeModel.setThemeCompanyLimit(companyLimit);
544             themeModel.setThemeGroupLimit(groupLimit);
545 
546             if (servletContextName != null) {
547                 themeModel.setServletContextName(servletContextName);
548             }
549 
550             themeModel.setLoadFromServletContext(loadFromServletContext);
551 
552             String name = GetterUtil.getString(
553                 theme.attributeValue("name"), themeModel.getName());
554 
555             String rootPath = GetterUtil.getString(
556                 theme.elementText("root-path"), themeModel.getRootPath());
557 
558             rootPath = themeContextReplace.replace(rootPath);
559 
560             themeContextReplace.addValue("root-path", rootPath);
561 
562             String templatesPath = GetterUtil.getString(
563                 theme.elementText("templates-path"),
564                 themeModel.getTemplatesPath());
565 
566             templatesPath = themeContextReplace.replace(templatesPath);
567             templatesPath = StringUtil.safePath(templatesPath);
568 
569             themeContextReplace.addValue("templates-path", templatesPath);
570 
571             String cssPath = GetterUtil.getString(
572                 theme.elementText("css-path"), themeModel.getCssPath());
573 
574             cssPath = themeContextReplace.replace(cssPath);
575             cssPath = StringUtil.safePath(cssPath);
576 
577             themeContextReplace.addValue("css-path", cssPath);
578 
579             String imagesPath = GetterUtil.getString(
580                 theme.elementText("images-path"),
581                 themeModel.getImagesPath());
582 
583             imagesPath = themeContextReplace.replace(imagesPath);
584             imagesPath = StringUtil.safePath(imagesPath);
585 
586             themeContextReplace.addValue("images-path", imagesPath);
587 
588             String javaScriptPath = GetterUtil.getString(
589                 theme.elementText("javascript-path"),
590                 themeModel.getJavaScriptPath());
591 
592             javaScriptPath = themeContextReplace.replace(javaScriptPath);
593             javaScriptPath = StringUtil.safePath(javaScriptPath);
594 
595             themeContextReplace.addValue("javascript-path", javaScriptPath);
596 
597             String virtualPath = GetterUtil.getString(
598                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
599 
600             String templateExtension = GetterUtil.getString(
601                 theme.elementText("template-extension"),
602                 themeModel.getTemplateExtension());
603 
604             themeModel.setName(name);
605             themeModel.setRootPath(rootPath);
606             themeModel.setTemplatesPath(templatesPath);
607             themeModel.setCssPath(cssPath);
608             themeModel.setImagesPath(imagesPath);
609             themeModel.setJavaScriptPath(javaScriptPath);
610             themeModel.setVirtualPath(virtualPath);
611             themeModel.setTemplateExtension(templateExtension);
612 
613             Element settingsEl = theme.element("settings");
614 
615             if (settingsEl != null) {
616                 Iterator<Element> itr2 = settingsEl.elements(
617                     "setting").iterator();
618 
619                 while (itr2.hasNext()) {
620                     Element settingEl = itr2.next();
621 
622                     String key = settingEl.attributeValue("key");
623                     String value = settingEl.attributeValue("value");
624 
625                     themeModel.setSetting(key, value);
626                 }
627             }
628 
629             themeModel.setWapTheme(GetterUtil.getBoolean(
630                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
631 
632             Element rolesEl = theme.element("roles");
633 
634             if (rolesEl != null) {
635                 Iterator<Element> itr2 = rolesEl.elements(
636                     "role-name").iterator();
637 
638                 while (itr2.hasNext()) {
639                     Element roleNameEl = itr2.next();
640 
641                     pluginSetting.addRole(roleNameEl.getText());
642                 }
643             }
644 
645             _readColorSchemes(
646                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
647             _readColorSchemes(
648                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
649 
650             Element layoutTemplatesEl = theme.element("layout-templates");
651 
652             if (layoutTemplatesEl != null) {
653                 Element standardEl = layoutTemplatesEl.element("standard");
654 
655                 if (standardEl != null) {
656                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
657                         servletContextName, servletContext, null,
658                         standardEl, true, themeId, pluginPackage);
659                 }
660 
661                 Element customEl = layoutTemplatesEl.element("custom");
662 
663                 if (customEl != null) {
664                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
665                         servletContextName, servletContext, null,
666                         customEl, false, themeId, pluginPackage);
667                 }
668             }
669         }
670 
671         return themeIds;
672     }
673 
674     private static Log _log = LogFactory.getLog(ThemeLocalServiceImpl.class);
675 
676     private static Map<String, Theme> _themes =
677         new ConcurrentHashMap<String, Theme>();
678     private static Map<Long, Map<String, Theme>> _themesPool =
679         new ConcurrentHashMap<Long, Map<String, Theme>>();
680 
681 }