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.model.ColorScheme;
34  import com.liferay.portal.model.PluginSetting;
35  import com.liferay.portal.model.PortletConstants;
36  import com.liferay.portal.model.Theme;
37  import com.liferay.portal.model.impl.ColorSchemeImpl;
38  import com.liferay.portal.model.impl.ThemeImpl;
39  import com.liferay.portal.plugin.PluginUtil;
40  import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
41  import com.liferay.portal.service.PluginSettingLocalServiceUtil;
42  import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
43  import com.liferay.portal.theme.ThemeCompanyId;
44  import com.liferay.portal.theme.ThemeCompanyLimit;
45  import com.liferay.portal.theme.ThemeGroupId;
46  import com.liferay.portal.theme.ThemeGroupLimit;
47  import com.liferay.portal.util.DocumentUtil;
48  import com.liferay.portal.util.PortalUtil;
49  import com.liferay.portal.xml.ElementImpl;
50  import com.liferay.util.ContextReplace;
51  import com.liferay.util.Version;
52  
53  import java.util.ArrayList;
54  import java.util.Collections;
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  import org.dom4j.Document;
68  import org.dom4j.DocumentException;
69  import org.dom4j.Element;
70  
71  /**
72   * <a href="ThemeLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
73   *
74   * @author Brian Wing Shun Chan
75   * @author Jorge Ferrer
76   *
77   */
78  public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
79  
80      public ColorScheme getColorScheme(
81          long companyId, String themeId, String colorSchemeId,
82          boolean wapTheme) {
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 Theme getTheme(long companyId, String themeId, boolean wapTheme) {
124         themeId = GetterUtil.getString(themeId);
125 
126         Theme theme = _getThemes(companyId).get(themeId);
127 
128         if (theme == null) {
129             if (_log.isWarnEnabled()) {
130                 _log.warn(
131                     "No theme found for specified theme id " + themeId +
132                         ". Returning the default theme.");
133             }
134 
135             if (wapTheme) {
136                 themeId = ThemeImpl.getDefaultWapThemeId();
137             }
138             else {
139                 themeId = ThemeImpl.getDefaultRegularThemeId();
140             }
141 
142             theme = _themes.get(themeId);
143         }
144 
145         if (theme == null) {
146             _log.error(
147                 "No theme found for default theme id " + themeId +
148                     ". Returning a random theme.");
149 
150             Iterator<Map.Entry<String, Theme>> itr =
151                 _themes.entrySet().iterator();
152 
153             while (itr.hasNext()) {
154                 Map.Entry<String, Theme> entry = itr.next();
155 
156                 theme = entry.getValue();
157             }
158         }
159 
160         return theme;
161     }
162 
163     public List<Theme> getThemes(long companyId) {
164         List<Theme> themes = ListUtil.fromCollection(
165             _getThemes(companyId).values());
166 
167         Collections.sort(themes);
168 
169         return themes;
170     }
171 
172     public List<Theme> getThemes(
173             long companyId, long groupId, long userId, boolean wapTheme)
174         throws PortalException, SystemException {
175 
176         List<Theme> themes = getThemes(companyId);
177 
178         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
179 
180         Iterator<Theme> itr = themes.iterator();
181 
182         while (itr.hasNext()) {
183             Theme theme = itr.next();
184 
185             if ((!theme.isGroupAvailable(groupId)) ||
186                 (theme.isWapTheme() != wapTheme)) {
187 
188                 itr.remove();
189             }
190         }
191 
192         return themes;
193     }
194 
195     public List<String> init(
196         ServletContext servletContext, String themesPath,
197         boolean loadFromServletContext, String[] xmls,
198         PluginPackage pluginPackage) {
199 
200         return init(
201             null, servletContext, themesPath, loadFromServletContext, xmls,
202             pluginPackage);
203     }
204 
205     public List<String> init(
206         String servletContextName, ServletContext servletContext,
207         String themesPath, boolean loadFromServletContext, String[] xmls,
208         PluginPackage pluginPackage) {
209 
210         List<String> themeIds = new ArrayList<String>();
211 
212         try {
213             for (int i = 0; i < xmls.length; i++) {
214                 Set<String> themes = _readThemes(
215                     servletContextName, servletContext, themesPath,
216                     loadFromServletContext, xmls[i], pluginPackage);
217 
218                 Iterator<String> itr = themes.iterator();
219 
220                 while (itr.hasNext()) {
221                     String themeId = itr.next();
222 
223                     if (!themeIds.contains(themeId)) {
224                         themeIds.add(themeId);
225                     }
226                 }
227             }
228         }
229         catch (Exception e) {
230             e.printStackTrace();
231         }
232 
233         _themesPool.clear();
234 
235         return themeIds;
236     }
237 
238     public void uninstallThemes(List<String> themeIds) {
239         for (int i = 0; i < themeIds.size(); i++) {
240             String themeId = themeIds.get(i);
241 
242             _themes.remove(themeId);
243 
244             LayoutTemplateLocalServiceUtil.uninstallLayoutTemplates(themeId);
245         }
246 
247         _themesPool.clear();
248     }
249 
250     private List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
251         List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
252 
253         if (el != null) {
254             List<Element> companyIds = el.elements("company-id");
255 
256             for (int i = 0; i < companyIds.size(); i++) {
257                 Element companyIdEl = companyIds.get(i);
258 
259                 String name = companyIdEl.attributeValue("name");
260                 String pattern = companyIdEl.attributeValue("pattern");
261 
262                 ThemeCompanyId themeCompanyId = null;
263 
264                 if (Validator.isNotNull(name)) {
265                     themeCompanyId = new ThemeCompanyId(name, false);
266                 }
267                 else if (Validator.isNotNull(pattern)) {
268                     themeCompanyId = new ThemeCompanyId(pattern, true);
269                 }
270 
271                 if (themeCompanyId != null) {
272                     includes.add(themeCompanyId);
273                 }
274             }
275         }
276 
277         return includes;
278     }
279 
280     private List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
281         return _getCompanyLimitExcludes(el);
282     }
283 
284     private List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
285         List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
286 
287         if (el != null) {
288             List<Element> groupIds = el.elements("group-id");
289 
290             for (int i = 0; i < groupIds.size(); i++) {
291                 Element groupIdEl = groupIds.get(i);
292 
293                 String name = groupIdEl.attributeValue("name");
294                 String pattern = groupIdEl.attributeValue("pattern");
295 
296                 ThemeGroupId themeGroupId = null;
297 
298                 if (Validator.isNotNull(name)) {
299                     themeGroupId = new ThemeGroupId(name, false);
300                 }
301                 else if (Validator.isNotNull(pattern)) {
302                     themeGroupId = new ThemeGroupId(pattern, true);
303                 }
304 
305                 if (themeGroupId != null) {
306                     includes.add(themeGroupId);
307                 }
308             }
309         }
310 
311         return includes;
312     }
313 
314     private List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
315         return _getGroupLimitExcludes(el);
316     }
317 
318     private Map<String, Theme> _getThemes(long companyId) {
319         Map<String, Theme> themes = _themesPool.get(companyId);
320 
321         if (themes == null) {
322             themes = new ConcurrentHashMap<String, Theme>();
323 
324             Iterator<Map.Entry<String, Theme>> itr =
325                 _themes.entrySet().iterator();
326 
327             while (itr.hasNext()) {
328                 Map.Entry<String, Theme> entry = itr.next();
329 
330                 String themeId = entry.getKey();
331                 Theme theme = entry.getValue();
332 
333                 if (theme.isCompanyAvailable(companyId)) {
334                     themes.put(themeId, theme);
335                 }
336             }
337 
338             _themesPool.put(companyId, themes);
339         }
340 
341         return themes;
342     }
343 
344     private 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 void _readColorSchemes(
353         Element theme, Map<String, ColorScheme> colorSchemes,
354         ContextReplace themeContextReplace) {
355 
356         Iterator<Element> itr = theme.elements("color-scheme").iterator();
357 
358         while (itr.hasNext()) {
359             Element colorScheme = 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 = colorSchemes.get(id);
369 
370             if (colorSchemeModel == null) {
371                 colorSchemeModel = new ColorSchemeImpl(id);
372             }
373 
374             String name = GetterUtil.getString(
375                 colorScheme.attributeValue("name"), colorSchemeModel.getName());
376 
377             name = colorSchemeContextReplace.replace(name);
378 
379             boolean defaultCs = GetterUtil.getBoolean(
380                 colorScheme.elementText("default-cs"),
381                 colorSchemeModel.isDefaultCs());
382 
383             String cssClass = GetterUtil.getString(
384                 colorScheme.elementText("css-class"),
385                 colorSchemeModel.getCssClass());
386 
387             cssClass = colorSchemeContextReplace.replace(cssClass);
388 
389             colorSchemeContextReplace.addValue("css-class", cssClass);
390 
391             String colorSchemeImagesPath = GetterUtil.getString(
392                 colorScheme.elementText("color-scheme-images-path"),
393                 colorSchemeModel.getColorSchemeImagesPath());
394 
395             colorSchemeImagesPath = colorSchemeContextReplace.replace(
396                 colorSchemeImagesPath);
397 
398             colorSchemeContextReplace.addValue(
399                 "color-scheme-images-path", colorSchemeImagesPath);
400 
401             colorSchemeModel.setName(name);
402             colorSchemeModel.setDefaultCs(defaultCs);
403             colorSchemeModel.setCssClass(cssClass);
404             colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
405 
406             colorSchemes.put(id, colorSchemeModel);
407         }
408     }
409 
410     private Set<String> _readThemes(
411             String servletContextName, ServletContext servletContext,
412             String themesPath, boolean loadFromServletContext, String xml,
413             PluginPackage pluginPackage)
414         throws DocumentException {
415 
416         Set<String> themeIds = new HashSet<String>();
417 
418         if (xml == null) {
419             return themeIds;
420         }
421 
422         Document doc = DocumentUtil.readDocumentFromXML(xml, true);
423 
424         Element root = doc.getRootElement();
425 
426         Version portalVersion = _getVersion(ReleaseInfo.getVersion());
427 
428         boolean compatible = false;
429 
430         Element compatibilityEl = root.element("compatibility");
431 
432         if (compatibilityEl != null) {
433             Iterator<Element> itr = compatibilityEl.elements(
434                 "version").iterator();
435 
436             while (itr.hasNext()) {
437                 Element versionEl = 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<Element> itr1 = root.elements("theme").iterator();
503 
504         while (itr1.hasNext()) {
505             Element theme = 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 + PortletConstants.WAR_SEPARATOR +
516                         servletContextName;
517             }
518 
519             themeId = PortalUtil.getJsSafePortletId(themeId);
520 
521             themeContextReplace.addValue("theme-id", themeId);
522 
523             themeIds.add(themeId);
524 
525             Theme themeModel = _themes.get(themeId);
526 
527             if (themeModel == null) {
528                 themeModel = new ThemeImpl(themeId);
529 
530                 _themes.put(themeId, themeModel);
531             }
532 
533             themeModel.setTimestamp(System.currentTimeMillis());
534 
535             PluginSetting pluginSetting =
536                 PluginSettingLocalServiceUtil.getDefaultPluginSetting();
537 
538             themeModel.setPluginPackage(pluginPackage);
539             themeModel.setDefaultPluginSetting(pluginSetting);
540 
541             themeModel.setThemeCompanyLimit(companyLimit);
542             themeModel.setThemeGroupLimit(groupLimit);
543 
544             if (servletContextName != null) {
545                 themeModel.setServletContextName(servletContextName);
546             }
547 
548             themeModel.setLoadFromServletContext(loadFromServletContext);
549 
550             String name = GetterUtil.getString(
551                 theme.attributeValue("name"), themeModel.getName());
552 
553             String rootPath = GetterUtil.getString(
554                 theme.elementText("root-path"), themeModel.getRootPath());
555 
556             rootPath = themeContextReplace.replace(rootPath);
557 
558             themeContextReplace.addValue("root-path", rootPath);
559 
560             String templatesPath = GetterUtil.getString(
561                 theme.elementText("templates-path"),
562                 themeModel.getTemplatesPath());
563 
564             templatesPath = themeContextReplace.replace(templatesPath);
565             templatesPath = StringUtil.safePath(templatesPath);
566 
567             themeContextReplace.addValue("templates-path", templatesPath);
568 
569             String cssPath = GetterUtil.getString(
570                 theme.elementText("css-path"), themeModel.getCssPath());
571 
572             cssPath = themeContextReplace.replace(cssPath);
573             cssPath = StringUtil.safePath(cssPath);
574 
575             themeContextReplace.addValue("css-path", cssPath);
576 
577             String imagesPath = GetterUtil.getString(
578                 theme.elementText("images-path"),
579                 themeModel.getImagesPath());
580 
581             imagesPath = themeContextReplace.replace(imagesPath);
582             imagesPath = StringUtil.safePath(imagesPath);
583 
584             themeContextReplace.addValue("images-path", imagesPath);
585 
586             String javaScriptPath = GetterUtil.getString(
587                 theme.elementText("javascript-path"),
588                 themeModel.getJavaScriptPath());
589 
590             javaScriptPath = themeContextReplace.replace(javaScriptPath);
591             javaScriptPath = StringUtil.safePath(javaScriptPath);
592 
593             themeContextReplace.addValue("javascript-path", javaScriptPath);
594 
595             String virtualPath = GetterUtil.getString(
596                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
597 
598             String templateExtension = GetterUtil.getString(
599                 theme.elementText("template-extension"),
600                 themeModel.getTemplateExtension());
601 
602             themeModel.setName(name);
603             themeModel.setRootPath(rootPath);
604             themeModel.setTemplatesPath(templatesPath);
605             themeModel.setCssPath(cssPath);
606             themeModel.setImagesPath(imagesPath);
607             themeModel.setJavaScriptPath(javaScriptPath);
608             themeModel.setVirtualPath(virtualPath);
609             themeModel.setTemplateExtension(templateExtension);
610 
611             Element settingsEl = theme.element("settings");
612 
613             if (settingsEl != null) {
614                 Iterator<Element> itr2 = settingsEl.elements(
615                     "setting").iterator();
616 
617                 while (itr2.hasNext()) {
618                     Element settingEl = itr2.next();
619 
620                     String key = settingEl.attributeValue("key");
621                     String value = settingEl.attributeValue("value");
622 
623                     themeModel.setSetting(key, value);
624                 }
625             }
626 
627             themeModel.setWapTheme(GetterUtil.getBoolean(
628                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
629 
630             Element rolesEl = theme.element("roles");
631 
632             if (rolesEl != null) {
633                 Iterator<Element> itr2 = rolesEl.elements(
634                     "role-name").iterator();
635 
636                 while (itr2.hasNext()) {
637                     Element roleNameEl = itr2.next();
638 
639                     pluginSetting.addRole(roleNameEl.getText());
640                 }
641             }
642 
643             _readColorSchemes(
644                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
645             _readColorSchemes(
646                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
647 
648             Element layoutTemplatesEl = theme.element("layout-templates");
649 
650             if (layoutTemplatesEl != null) {
651                 Element standardEl = layoutTemplatesEl.element("standard");
652 
653                 if (standardEl != null) {
654                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
655                         servletContextName, servletContext, null,
656                         new ElementImpl(standardEl), true, themeId,
657                         pluginPackage);
658                 }
659 
660                 Element customEl = layoutTemplatesEl.element("custom");
661 
662                 if (customEl != null) {
663                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
664                         servletContextName, servletContext, null,
665                         new ElementImpl(customEl), false, themeId,
666                         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 }