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