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