001
014
015 package com.liferay.portal.service.impl;
016
017 import com.liferay.portal.kernel.exception.SystemException;
018 import com.liferay.portal.kernel.image.SpriteProcessor;
019 import com.liferay.portal.kernel.image.SpriteProcessorUtil;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.plugin.PluginPackage;
023 import com.liferay.portal.kernel.plugin.Version;
024 import com.liferay.portal.kernel.servlet.ServletContextUtil;
025 import com.liferay.portal.kernel.util.ColorSchemeFactoryUtil;
026 import com.liferay.portal.kernel.util.ContextPathUtil;
027 import com.liferay.portal.kernel.util.GetterUtil;
028 import com.liferay.portal.kernel.util.ListUtil;
029 import com.liferay.portal.kernel.util.ReleaseInfo;
030 import com.liferay.portal.kernel.util.StringPool;
031 import com.liferay.portal.kernel.util.StringUtil;
032 import com.liferay.portal.kernel.util.ThemeFactoryUtil;
033 import com.liferay.portal.kernel.util.UniqueList;
034 import com.liferay.portal.kernel.util.Validator;
035 import com.liferay.portal.kernel.xml.Document;
036 import com.liferay.portal.kernel.xml.Element;
037 import com.liferay.portal.kernel.xml.SAXReaderUtil;
038 import com.liferay.portal.model.ColorScheme;
039 import com.liferay.portal.model.PluginSetting;
040 import com.liferay.portal.model.PortletConstants;
041 import com.liferay.portal.model.Theme;
042 import com.liferay.portal.plugin.PluginUtil;
043 import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
044 import com.liferay.portal.theme.ThemeCompanyId;
045 import com.liferay.portal.theme.ThemeCompanyLimit;
046 import com.liferay.portal.theme.ThemeGroupId;
047 import com.liferay.portal.theme.ThemeGroupLimit;
048 import com.liferay.portal.util.PortalUtil;
049 import com.liferay.portal.util.PropsValues;
050 import com.liferay.util.ContextReplace;
051
052 import java.net.URL;
053
054 import java.util.ArrayList;
055 import java.util.HashSet;
056 import java.util.Iterator;
057 import java.util.List;
058 import java.util.Map;
059 import java.util.Properties;
060 import java.util.Set;
061 import java.util.concurrent.ConcurrentHashMap;
062
063 import javax.servlet.ServletContext;
064
065
070 public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
071
072 @Override
073 public ColorScheme fetchColorScheme(
074 long companyId, String themeId, String colorSchemeId) {
075
076 colorSchemeId = GetterUtil.getString(colorSchemeId);
077
078 Theme theme = fetchTheme(companyId, themeId);
079
080 if (theme == null) {
081 return null;
082 }
083
084 Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
085
086 return colorSchemesMap.get(colorSchemeId);
087 }
088
089 @Override
090 public Theme fetchTheme(long companyId, String themeId) {
091 themeId = GetterUtil.getString(themeId);
092
093 Map<String, Theme> themes = _getThemes(companyId);
094
095 return themes.get(themeId);
096 }
097
098 @Override
099 public ColorScheme getColorScheme(
100 long companyId, String themeId, String colorSchemeId,
101 boolean wapTheme)
102 throws SystemException {
103
104 colorSchemeId = GetterUtil.getString(colorSchemeId);
105
106 Theme theme = getTheme(companyId, themeId, wapTheme);
107
108 Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
109
110 ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
111
112 if (colorScheme == null) {
113 List<ColorScheme> colorSchemes = theme.getColorSchemes();
114
115 if (colorSchemes.size() > 0) {
116 for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
117 colorScheme = colorSchemes.get(i);
118
119 if (colorScheme.isDefaultCs()) {
120 break;
121 }
122 }
123 }
124 }
125
126 if (colorScheme == null) {
127 if (wapTheme) {
128 colorScheme = ColorSchemeFactoryUtil.getDefaultWapColorScheme();
129 }
130 else {
131 colorScheme =
132 ColorSchemeFactoryUtil.getDefaultRegularColorScheme();
133 }
134 }
135
136 return colorScheme;
137 }
138
139 @Override
140 public Theme getTheme(long companyId, String themeId, boolean wapTheme)
141 throws SystemException {
142
143 themeId = GetterUtil.getString(themeId);
144
145 Map<String, Theme> themes = _getThemes(companyId);
146
147 Theme theme = themes.get(themeId);
148
149 if (theme == null) {
150 if (_log.isWarnEnabled()) {
151 _log.warn(
152 "No theme found for specified theme id " + themeId +
153 ". Returning the default theme.");
154 }
155
156 if (wapTheme) {
157 themeId = ThemeFactoryUtil.getDefaultWapThemeId(companyId);
158 }
159 else {
160 themeId = ThemeFactoryUtil.getDefaultRegularThemeId(companyId);
161 }
162
163 theme = _themes.get(themeId);
164 }
165
166 if (theme == null) {
167 if (_themes.isEmpty()) {
168 if (_log.isDebugEnabled()) {
169 _log.debug("No themes are installed");
170 }
171
172 return null;
173 }
174
175 if (!themeId.contains(PortletConstants.WAR_SEPARATOR)) {
176 _log.error(
177 "No theme found for default theme id " + themeId +
178 ". Returning a random theme.");
179 }
180
181 for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
182 theme = entry.getValue();
183 }
184 }
185
186 return theme;
187 }
188
189 @Override
190 public List<Theme> getThemes(long companyId) {
191 Map<String, Theme> themes = _getThemes(companyId);
192
193 List<Theme> themesList = ListUtil.fromMapValues(themes);
194
195 return ListUtil.sort(themesList);
196 }
197
198 @Override
199 public List<Theme> getThemes(
200 long companyId, long groupId, long userId, boolean wapTheme)
201 throws SystemException {
202
203 List<Theme> themes = getThemes(companyId);
204
205 themes = PluginUtil.restrictPlugins(themes, companyId, userId);
206
207 Iterator<Theme> itr = themes.iterator();
208
209 while (itr.hasNext()) {
210 Theme theme = itr.next();
211
212 if (theme.getThemeId().equals("controlpanel") ||
213 !theme.isGroupAvailable(groupId) ||
214 (theme.isWapTheme() != wapTheme)) {
215
216 itr.remove();
217 }
218 }
219
220 return themes;
221 }
222
223 @Override
224 public List<Theme> getWARThemes() {
225 List<Theme> themes = ListUtil.fromMapValues(_themes);
226
227 Iterator<Theme> itr = themes.iterator();
228
229 while (itr.hasNext()) {
230 Theme theme = itr.next();
231
232 if (!theme.isWARFile()) {
233 itr.remove();
234 }
235 }
236
237 return themes;
238 }
239
240 @Override
241 public List<Theme> init(
242 ServletContext servletContext, String themesPath,
243 boolean loadFromServletContext, String[] xmls,
244 PluginPackage pluginPackage) {
245
246 return init(
247 null, servletContext, themesPath, loadFromServletContext, xmls,
248 pluginPackage);
249 }
250
251 @Override
252 public List<Theme> init(
253 String servletContextName, ServletContext servletContext,
254 String themesPath, boolean loadFromServletContext, String[] xmls,
255 PluginPackage pluginPackage) {
256
257 List<Theme> themes = new UniqueList<Theme>();
258
259 try {
260 for (String xml : xmls) {
261 themes.addAll(
262 _readThemes(
263 servletContextName, servletContext, themesPath,
264 loadFromServletContext, xml, pluginPackage));
265 }
266 }
267 catch (Exception e) {
268 _log.error(e, e);
269 }
270
271 _themesPool.clear();
272
273 return themes;
274 }
275
276 @Override
277 public void uninstallThemes(List<Theme> themes) {
278 for (Theme theme : themes) {
279 String themeId = theme.getThemeId();
280
281 _themes.remove(themeId);
282
283 layoutTemplateLocalService.uninstallLayoutTemplates(themeId);
284 }
285
286 _themesPool.clear();
287 }
288
289 private List<ThemeCompanyId> _getCompanyLimitExcludes(Element element) {
290 List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
291
292 if (element == null) {
293 return includes;
294 }
295
296 List<Element> companyIdsElements = element.elements("company-id");
297
298 for (int i = 0; i < companyIdsElements.size(); i++) {
299 Element companyIdElement = companyIdsElements.get(i);
300
301 String name = companyIdElement.attributeValue("name");
302 String pattern = companyIdElement.attributeValue("pattern");
303
304 ThemeCompanyId themeCompanyId = null;
305
306 if (Validator.isNotNull(name)) {
307 themeCompanyId = new ThemeCompanyId(name, false);
308 }
309 else if (Validator.isNotNull(pattern)) {
310 themeCompanyId = new ThemeCompanyId(pattern, true);
311 }
312
313 if (themeCompanyId != null) {
314 includes.add(themeCompanyId);
315 }
316 }
317
318 return includes;
319 }
320
321 private List<ThemeCompanyId> _getCompanyLimitIncludes(Element element) {
322 return _getCompanyLimitExcludes(element);
323 }
324
325 private List<ThemeGroupId> _getGroupLimitExcludes(Element element) {
326 List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
327
328 if (element == null) {
329 return includes;
330 }
331
332 List<Element> groupIdsElements = element.elements("group-id");
333
334 for (int i = 0; i < groupIdsElements.size(); i++) {
335 Element groupIdElement = groupIdsElements.get(i);
336
337 String name = groupIdElement.attributeValue("name");
338 String pattern = groupIdElement.attributeValue("pattern");
339
340 ThemeGroupId themeGroupId = null;
341
342 if (Validator.isNotNull(name)) {
343 themeGroupId = new ThemeGroupId(name, false);
344 }
345 else if (Validator.isNotNull(pattern)) {
346 themeGroupId = new ThemeGroupId(pattern, true);
347 }
348
349 if (themeGroupId != null) {
350 includes.add(themeGroupId);
351 }
352 }
353
354 return includes;
355 }
356
357 private List<ThemeGroupId> _getGroupLimitIncludes(Element element) {
358 return _getGroupLimitExcludes(element);
359 }
360
361 private Map<String, Theme> _getThemes(long companyId) {
362 Map<String, Theme> themes = _themesPool.get(companyId);
363
364 if (themes != null) {
365 return themes;
366 }
367
368 themes = new ConcurrentHashMap<String, Theme>();
369
370 for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
371 String themeId = entry.getKey();
372 Theme theme = entry.getValue();
373
374 if (theme.isCompanyAvailable(companyId)) {
375 themes.put(themeId, theme);
376 }
377 }
378
379 _themesPool.put(companyId, themes);
380
381 return themes;
382 }
383
384 private Version _getVersion(String version) {
385 if (version.equals("${current-version}")) {
386 version = ReleaseInfo.getVersion();
387 }
388
389 return Version.getInstance(version);
390 }
391
392 private void _readColorSchemes(
393 Element themeElement, Map<String, ColorScheme> colorSchemes,
394 ContextReplace themeContextReplace) {
395
396 List<Element> colorSchemeElements = themeElement.elements(
397 "color-scheme");
398
399 for (Element colorSchemeElement : colorSchemeElements) {
400 ContextReplace colorSchemeContextReplace =
401 (ContextReplace)themeContextReplace.clone();
402
403 String id = colorSchemeElement.attributeValue("id");
404
405 colorSchemeContextReplace.addValue("color-scheme-id", id);
406
407 ColorScheme colorSchemeModel = colorSchemes.get(id);
408
409 if (colorSchemeModel == null) {
410 colorSchemeModel = ColorSchemeFactoryUtil.getColorScheme(id);
411 }
412
413 String name = GetterUtil.getString(
414 colorSchemeElement.attributeValue("name"),
415 colorSchemeModel.getName());
416
417 name = colorSchemeContextReplace.replace(name);
418
419 boolean defaultCs = GetterUtil.getBoolean(
420 colorSchemeElement.elementText("default-cs"),
421 colorSchemeModel.isDefaultCs());
422
423 String cssClass = GetterUtil.getString(
424 colorSchemeElement.elementText("css-class"),
425 colorSchemeModel.getCssClass());
426
427 cssClass = colorSchemeContextReplace.replace(cssClass);
428
429 colorSchemeContextReplace.addValue("css-class", cssClass);
430
431 String colorSchemeImagesPath = GetterUtil.getString(
432 colorSchemeElement.elementText("color-scheme-images-path"),
433 colorSchemeModel.getColorSchemeImagesPath());
434
435 colorSchemeImagesPath = colorSchemeContextReplace.replace(
436 colorSchemeImagesPath);
437
438 colorSchemeContextReplace.addValue(
439 "color-scheme-images-path", colorSchemeImagesPath);
440
441 colorSchemeModel.setName(name);
442 colorSchemeModel.setDefaultCs(defaultCs);
443 colorSchemeModel.setCssClass(cssClass);
444 colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
445
446 colorSchemes.put(id, colorSchemeModel);
447 }
448 }
449
450 private Set<Theme> _readThemes(
451 String servletContextName, ServletContext servletContext,
452 String themesPath, boolean loadFromServletContext, String xml,
453 PluginPackage pluginPackage)
454 throws Exception {
455
456 Set<Theme> themes = new HashSet<Theme>();
457
458 if (xml == null) {
459 return themes;
460 }
461
462 Document document = SAXReaderUtil.read(xml, true);
463
464 Element rootElement = document.getRootElement();
465
466 Version portalVersion = _getVersion(ReleaseInfo.getVersion());
467
468 boolean compatible = false;
469
470 Element compatibilityElement = rootElement.element("compatibility");
471
472 if (compatibilityElement != null) {
473 List<Element> versionElements = compatibilityElement.elements(
474 "version");
475
476 for (Element versionElement : versionElements) {
477 Version version = _getVersion(versionElement.getTextTrim());
478
479 if (version.includes(portalVersion)) {
480 compatible = true;
481
482 break;
483 }
484 }
485 }
486
487 if (!compatible) {
488 _log.error(
489 "Themes in this WAR are not compatible with " +
490 ReleaseInfo.getServerInfo());
491
492 return themes;
493 }
494
495 ThemeCompanyLimit companyLimit = null;
496
497 Element companyLimitElement = rootElement.element("company-limit");
498
499 if (companyLimitElement != null) {
500 companyLimit = new ThemeCompanyLimit();
501
502 Element companyIncludesElement = companyLimitElement.element(
503 "company-includes");
504
505 if (companyIncludesElement != null) {
506 companyLimit.setIncludes(
507 _getCompanyLimitIncludes(companyIncludesElement));
508 }
509
510 Element companyExcludesElement = companyLimitElement.element(
511 "company-excludes");
512
513 if (companyExcludesElement != null) {
514 companyLimit.setExcludes(
515 _getCompanyLimitExcludes(companyExcludesElement));
516 }
517 }
518
519 ThemeGroupLimit groupLimit = null;
520
521 Element groupLimitElement = rootElement.element("group-limit");
522
523 if (groupLimitElement != null) {
524 groupLimit = new ThemeGroupLimit();
525
526 Element groupIncludesElement = groupLimitElement.element(
527 "group-includes");
528
529 if (groupIncludesElement != null) {
530 groupLimit.setIncludes(
531 _getGroupLimitIncludes(groupIncludesElement));
532 }
533
534 Element groupExcludesElement = groupLimitElement.element(
535 "group-excludes");
536
537 if (groupExcludesElement != null) {
538 groupLimit.setExcludes(
539 _getGroupLimitExcludes(groupExcludesElement));
540 }
541 }
542
543 long timestamp = ServletContextUtil.getLastModified(servletContext);
544
545 List<Element> themeElements = rootElement.elements("theme");
546
547 for (Element themeElement : themeElements) {
548 ContextReplace themeContextReplace = new ContextReplace();
549
550 themeContextReplace.addValue("themes-path", themesPath);
551
552 String themeId = themeElement.attributeValue("id");
553
554 if (servletContextName != null) {
555 themeId =
556 themeId + PortletConstants.WAR_SEPARATOR +
557 servletContextName;
558 }
559
560 themeId = PortalUtil.getJsSafePortletId(themeId);
561
562 themeContextReplace.addValue("theme-id", themeId);
563
564 Theme theme = _themes.get(themeId);
565
566 if (theme == null) {
567 theme = ThemeFactoryUtil.getTheme(themeId);
568 }
569
570 theme.setTimestamp(timestamp);
571
572 PluginSetting pluginSetting =
573 pluginSettingLocalService.getDefaultPluginSetting();
574
575 theme.setPluginPackage(pluginPackage);
576 theme.setDefaultPluginSetting(pluginSetting);
577
578 theme.setThemeCompanyLimit(companyLimit);
579 theme.setThemeGroupLimit(groupLimit);
580
581 if (servletContextName != null) {
582 theme.setServletContextName(servletContextName);
583 }
584
585 theme.setLoadFromServletContext(loadFromServletContext);
586
587 String name = GetterUtil.getString(
588 themeElement.attributeValue("name"), theme.getName());
589
590 String rootPath = GetterUtil.getString(
591 themeElement.elementText("root-path"), theme.getRootPath());
592
593 rootPath = themeContextReplace.replace(rootPath);
594
595 themeContextReplace.addValue("root-path", rootPath);
596
597 String templatesPath = GetterUtil.getString(
598 themeElement.elementText("templates-path"),
599 theme.getTemplatesPath());
600
601 templatesPath = themeContextReplace.replace(templatesPath);
602 templatesPath = StringUtil.safePath(templatesPath);
603
604 themeContextReplace.addValue("templates-path", templatesPath);
605
606 String cssPath = GetterUtil.getString(
607 themeElement.elementText("css-path"), theme.getCssPath());
608
609 cssPath = themeContextReplace.replace(cssPath);
610 cssPath = StringUtil.safePath(cssPath);
611
612 themeContextReplace.addValue("css-path", cssPath);
613
614 String imagesPath = GetterUtil.getString(
615 themeElement.elementText("images-path"), theme.getImagesPath());
616
617 imagesPath = themeContextReplace.replace(imagesPath);
618 imagesPath = StringUtil.safePath(imagesPath);
619
620 themeContextReplace.addValue("images-path", imagesPath);
621
622 String javaScriptPath = GetterUtil.getString(
623 themeElement.elementText("javascript-path"),
624 theme.getJavaScriptPath());
625
626 javaScriptPath = themeContextReplace.replace(javaScriptPath);
627 javaScriptPath = StringUtil.safePath(javaScriptPath);
628
629 themeContextReplace.addValue("javascript-path", javaScriptPath);
630
631 String virtualPath = GetterUtil.getString(
632 themeElement.elementText("virtual-path"),
633 theme.getVirtualPath());
634
635 String templateExtension = GetterUtil.getString(
636 themeElement.elementText("template-extension"),
637 theme.getTemplateExtension());
638
639 theme.setName(name);
640 theme.setRootPath(rootPath);
641 theme.setTemplatesPath(templatesPath);
642 theme.setCssPath(cssPath);
643 theme.setImagesPath(imagesPath);
644 theme.setJavaScriptPath(javaScriptPath);
645 theme.setVirtualPath(virtualPath);
646 theme.setTemplateExtension(templateExtension);
647
648 Element settingsElement = themeElement.element("settings");
649
650 if (settingsElement != null) {
651 List<Element> settingElements = settingsElement.elements(
652 "setting");
653
654 for (Element settingElement : settingElements) {
655 boolean configurable = GetterUtil.getBoolean(
656 settingElement.attributeValue("configurable"));
657 String key = settingElement.attributeValue("key");
658 String[] options = StringUtil.split(
659 settingElement.attributeValue("options"));
660 String type = settingElement.attributeValue("type", "text");
661 String value = settingElement.attributeValue(
662 "value", StringPool.BLANK);
663 String script = settingElement.getTextTrim();
664
665 theme.addSetting(
666 key, value, configurable, type, options, script);
667 }
668 }
669
670 theme.setWapTheme(
671 GetterUtil.getBoolean(
672 themeElement.elementText("wap-theme"), theme.isWapTheme()));
673
674 Element rolesElement = themeElement.element("roles");
675
676 if (rolesElement != null) {
677 List<Element> roleNameElements = rolesElement.elements(
678 "role-name");
679
680 for (Element roleNameElement : roleNameElements) {
681 pluginSetting.addRole(roleNameElement.getText());
682 }
683 }
684
685 _readColorSchemes(
686 themeElement, theme.getColorSchemesMap(), themeContextReplace);
687 _readColorSchemes(
688 themeElement, theme.getColorSchemesMap(), themeContextReplace);
689
690 Element layoutTemplatesElement = themeElement.element(
691 "layout-templates");
692
693 if (layoutTemplatesElement != null) {
694 Element standardElement = layoutTemplatesElement.element(
695 "standard");
696
697 if (standardElement != null) {
698 layoutTemplateLocalService.readLayoutTemplate(
699 servletContextName, servletContext, null,
700 standardElement, true, themeId, pluginPackage);
701 }
702
703 Element customElement = layoutTemplatesElement.element(
704 "custom");
705
706 if (customElement != null) {
707 layoutTemplateLocalService.readLayoutTemplate(
708 servletContextName, servletContext, null, customElement,
709 false, themeId, pluginPackage);
710 }
711 }
712
713 if (!theme.isWapTheme()) {
714 _setSpriteImages(servletContext, theme, imagesPath);
715 }
716
717 if (!_themes.containsKey(themeId)) {
718 _themes.put(themeId, theme);
719 }
720
721 themes.add(theme);
722 }
723
724 return themes;
725 }
726
727 private void _setSpriteImages(
728 ServletContext servletContext, Theme theme, String resourcePath)
729 throws Exception {
730
731 if (!resourcePath.startsWith(StringPool.SLASH)) {
732 resourcePath = StringPool.SLASH.concat(resourcePath);
733 }
734
735 Set<String> resourcePaths = servletContext.getResourcePaths(
736 resourcePath);
737
738 if ((resourcePaths == null) || resourcePaths.isEmpty()) {
739 return;
740 }
741
742 List<URL> imageURLs = new ArrayList<URL>(resourcePaths.size());
743
744 for (String curResourcePath : resourcePaths) {
745 if (curResourcePath.endsWith(StringPool.SLASH)) {
746 _setSpriteImages(servletContext, theme, curResourcePath);
747 }
748 else if (curResourcePath.endsWith(".png")) {
749 URL imageURL = servletContext.getResource(curResourcePath);
750
751 if (imageURL != null) {
752 imageURLs.add(imageURL);
753 }
754 else {
755 _log.error(
756 "Resource URL for " + curResourcePath + " is null");
757 }
758 }
759 }
760
761 String spriteRootDirName = PropsValues.SPRITE_ROOT_DIR;
762 String spriteFileName = resourcePath.concat(
763 PropsValues.SPRITE_FILE_NAME);
764 String spritePropertiesFileName = resourcePath.concat(
765 PropsValues.SPRITE_PROPERTIES_FILE_NAME);
766 String rootPath = ServletContextUtil.getRootPath(servletContext);
767
768 Properties spriteProperties = SpriteProcessorUtil.generate(
769 servletContext, imageURLs, spriteRootDirName, spriteFileName,
770 spritePropertiesFileName, rootPath, 16, 16, 10240);
771
772 if (spriteProperties == null) {
773 return;
774 }
775
776 String contextPath = ContextPathUtil.getContextPath(servletContext);
777
778 spriteFileName = contextPath.concat(SpriteProcessor.PATH).concat(
779 spriteFileName);
780
781 theme.setSpriteImages(spriteFileName, spriteProperties);
782 }
783
784 private static Log _log = LogFactoryUtil.getLog(
785 ThemeLocalServiceImpl.class);
786
787 private static Map<String, Theme> _themes =
788 new ConcurrentHashMap<String, Theme>();
789 private static Map<Long, Map<String, Theme>> _themesPool =
790 new ConcurrentHashMap<Long, Map<String, Theme>>();
791
792 }