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.ContextPathUtil;
026 import com.liferay.portal.kernel.util.GetterUtil;
027 import com.liferay.portal.kernel.util.ListUtil;
028 import com.liferay.portal.kernel.util.ReleaseInfo;
029 import com.liferay.portal.kernel.util.StringPool;
030 import com.liferay.portal.kernel.util.StringUtil;
031 import com.liferay.portal.kernel.util.Validator;
032 import com.liferay.portal.kernel.xml.Document;
033 import com.liferay.portal.kernel.xml.Element;
034 import com.liferay.portal.kernel.xml.SAXReaderUtil;
035 import com.liferay.portal.model.ColorScheme;
036 import com.liferay.portal.model.PluginSetting;
037 import com.liferay.portal.model.PortletConstants;
038 import com.liferay.portal.model.Theme;
039 import com.liferay.portal.model.impl.ColorSchemeImpl;
040 import com.liferay.portal.model.impl.ThemeImpl;
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 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
125 }
126 else {
127 colorSchemeId =
128 ColorSchemeImpl.getDefaultRegularColorSchemeId();
129 }
130 }
131
132 if (colorScheme == null) {
133 colorScheme = ColorSchemeImpl.getNullColorScheme();
134 }
135
136 return colorScheme;
137 }
138
139 public Theme getTheme(long companyId, String themeId, boolean wapTheme)
140 throws SystemException {
141
142 themeId = GetterUtil.getString(themeId);
143
144 Map<String, Theme> themes = _getThemes(companyId);
145
146 Theme theme = themes.get(themeId);
147
148 if (theme == null) {
149 if (_log.isWarnEnabled()) {
150 _log.warn(
151 "No theme found for specified theme id " + themeId +
152 ". Returning the default theme.");
153 }
154
155 if (wapTheme) {
156 themeId = ThemeImpl.getDefaultWapThemeId(companyId);
157 }
158 else {
159 themeId = ThemeImpl.getDefaultRegularThemeId(companyId);
160 }
161
162 theme = _themes.get(themeId);
163 }
164
165 if (theme == null) {
166 if (_themes.isEmpty()) {
167 if (_log.isDebugEnabled()) {
168 _log.debug("No themes are installed");
169 }
170
171 return null;
172 }
173
174 if (!themeId.contains(PortletConstants.WAR_SEPARATOR)) {
175 _log.error(
176 "No theme found for default theme id " + themeId +
177 ". Returning a random theme.");
178 }
179
180 for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
181 theme = entry.getValue();
182 }
183 }
184
185 return theme;
186 }
187
188 public List<Theme> getThemes(long companyId) {
189 Map<String, Theme> themes = _getThemes(companyId);
190
191 List<Theme> themesList = ListUtil.fromMapValues(themes);
192
193 return ListUtil.sort(themesList);
194 }
195
196 public List<Theme> getThemes(
197 long companyId, long groupId, long userId, boolean wapTheme)
198 throws SystemException {
199
200 List<Theme> themes = getThemes(companyId);
201
202 themes = PluginUtil.restrictPlugins(themes, companyId, userId);
203
204 Iterator<Theme> itr = themes.iterator();
205
206 while (itr.hasNext()) {
207 Theme theme = itr.next();
208
209 if (theme.getThemeId().equals("controlpanel") ||
210 !theme.isGroupAvailable(groupId) ||
211 (theme.isWapTheme() != wapTheme)) {
212
213 itr.remove();
214 }
215 }
216
217 return themes;
218 }
219
220 public List<Theme> getWARThemes() {
221 List<Theme> themes = ListUtil.fromMapValues(_themes);
222
223 Iterator<Theme> itr = themes.iterator();
224
225 while (itr.hasNext()) {
226 Theme theme = itr.next();
227
228 if (!theme.isWARFile()) {
229 itr.remove();
230 }
231 }
232
233 return themes;
234 }
235
236 public List<String> init(
237 ServletContext servletContext, String themesPath,
238 boolean loadFromServletContext, String[] xmls,
239 PluginPackage pluginPackage) {
240
241 return init(
242 null, servletContext, themesPath, loadFromServletContext, xmls,
243 pluginPackage);
244 }
245
246 public List<String> init(
247 String servletContextName, ServletContext servletContext,
248 String themesPath, boolean loadFromServletContext, String[] xmls,
249 PluginPackage pluginPackage) {
250
251 List<String> themeIdsList = new ArrayList<String>();
252
253 try {
254 for (String xml : xmls) {
255 Set<String> themeIds = _readThemes(
256 servletContextName, servletContext, themesPath,
257 loadFromServletContext, xml, pluginPackage);
258
259 for (String themeId : themeIds) {
260 if (!themeIdsList.contains(themeId)) {
261 themeIdsList.add(themeId);
262 }
263 }
264 }
265 }
266 catch (Exception e) {
267 e.printStackTrace();
268 }
269
270 _themesPool.clear();
271
272 return themeIdsList;
273 }
274
275 public void uninstallThemes(List<String> themeIds) {
276 for (int i = 0; i < themeIds.size(); i++) {
277 String themeId = themeIds.get(i);
278
279 _themes.remove(themeId);
280
281 layoutTemplateLocalService.uninstallLayoutTemplates(themeId);
282 }
283
284 _themesPool.clear();
285 }
286
287 private List<ThemeCompanyId> _getCompanyLimitExcludes(Element element) {
288 List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
289
290 if (element == null) {
291 return includes;
292 }
293
294 List<Element> companyIdsElements = element.elements("company-id");
295
296 for (int i = 0; i < companyIdsElements.size(); i++) {
297 Element companyIdElement = companyIdsElements.get(i);
298
299 String name = companyIdElement.attributeValue("name");
300 String pattern = companyIdElement.attributeValue("pattern");
301
302 ThemeCompanyId themeCompanyId = null;
303
304 if (Validator.isNotNull(name)) {
305 themeCompanyId = new ThemeCompanyId(name, false);
306 }
307 else if (Validator.isNotNull(pattern)) {
308 themeCompanyId = new ThemeCompanyId(pattern, true);
309 }
310
311 if (themeCompanyId != null) {
312 includes.add(themeCompanyId);
313 }
314 }
315
316 return includes;
317 }
318
319 private List<ThemeCompanyId> _getCompanyLimitIncludes(Element element) {
320 return _getCompanyLimitExcludes(element);
321 }
322
323 private List<ThemeGroupId> _getGroupLimitExcludes(Element element) {
324 List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
325
326 if (element == null) {
327 return includes;
328 }
329
330 List<Element> groupIdsElements = element.elements("group-id");
331
332 for (int i = 0; i < groupIdsElements.size(); i++) {
333 Element groupIdElement = groupIdsElements.get(i);
334
335 String name = groupIdElement.attributeValue("name");
336 String pattern = groupIdElement.attributeValue("pattern");
337
338 ThemeGroupId themeGroupId = null;
339
340 if (Validator.isNotNull(name)) {
341 themeGroupId = new ThemeGroupId(name, false);
342 }
343 else if (Validator.isNotNull(pattern)) {
344 themeGroupId = new ThemeGroupId(pattern, true);
345 }
346
347 if (themeGroupId != null) {
348 includes.add(themeGroupId);
349 }
350 }
351
352 return includes;
353 }
354
355 private List<ThemeGroupId> _getGroupLimitIncludes(Element element) {
356 return _getGroupLimitExcludes(element);
357 }
358
359 private Map<String, Theme> _getThemes(long companyId) {
360 Map<String, Theme> themes = _themesPool.get(companyId);
361
362 if (themes != null) {
363 return themes;
364 }
365
366 themes = new ConcurrentHashMap<String, Theme>();
367
368 for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
369 String themeId = entry.getKey();
370 Theme theme = entry.getValue();
371
372 if (theme.isCompanyAvailable(companyId)) {
373 themes.put(themeId, theme);
374 }
375 }
376
377 _themesPool.put(companyId, themes);
378
379 return themes;
380 }
381
382 private Version _getVersion(String version) {
383 if (version.equals("${current-version}")) {
384 version = ReleaseInfo.getVersion();
385 }
386
387 return Version.getInstance(version);
388 }
389
390 private void _readColorSchemes(
391 Element themeElement, Map<String, ColorScheme> colorSchemes,
392 ContextReplace themeContextReplace) {
393
394 List<Element> colorSchemeElements = themeElement.elements(
395 "color-scheme");
396
397 for (Element colorSchemeElement : colorSchemeElements) {
398 ContextReplace colorSchemeContextReplace =
399 (ContextReplace)themeContextReplace.clone();
400
401 String id = colorSchemeElement.attributeValue("id");
402
403 colorSchemeContextReplace.addValue("color-scheme-id", id);
404
405 ColorScheme colorSchemeModel = colorSchemes.get(id);
406
407 if (colorSchemeModel == null) {
408 colorSchemeModel = new ColorSchemeImpl(id);
409 }
410
411 String name = GetterUtil.getString(
412 colorSchemeElement.attributeValue("name"),
413 colorSchemeModel.getName());
414
415 name = colorSchemeContextReplace.replace(name);
416
417 boolean defaultCs = GetterUtil.getBoolean(
418 colorSchemeElement.elementText("default-cs"),
419 colorSchemeModel.isDefaultCs());
420
421 String cssClass = GetterUtil.getString(
422 colorSchemeElement.elementText("css-class"),
423 colorSchemeModel.getCssClass());
424
425 cssClass = colorSchemeContextReplace.replace(cssClass);
426
427 colorSchemeContextReplace.addValue("css-class", cssClass);
428
429 String colorSchemeImagesPath = GetterUtil.getString(
430 colorSchemeElement.elementText("color-scheme-images-path"),
431 colorSchemeModel.getColorSchemeImagesPath());
432
433 colorSchemeImagesPath = colorSchemeContextReplace.replace(
434 colorSchemeImagesPath);
435
436 colorSchemeContextReplace.addValue(
437 "color-scheme-images-path", colorSchemeImagesPath);
438
439 colorSchemeModel.setName(name);
440 colorSchemeModel.setDefaultCs(defaultCs);
441 colorSchemeModel.setCssClass(cssClass);
442 colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
443
444 colorSchemes.put(id, colorSchemeModel);
445 }
446 }
447
448 private Set<String> _readThemes(
449 String servletContextName, ServletContext servletContext,
450 String themesPath, boolean loadFromServletContext, String xml,
451 PluginPackage pluginPackage)
452 throws Exception {
453
454 Set<String> themeIds = new HashSet<String>();
455
456 if (xml == null) {
457 return themeIds;
458 }
459
460 Document document = SAXReaderUtil.read(xml, true);
461
462 Element rootElement = document.getRootElement();
463
464 Version portalVersion = _getVersion(ReleaseInfo.getVersion());
465
466 boolean compatible = false;
467
468 Element compatibilityElement = rootElement.element("compatibility");
469
470 if (compatibilityElement != null) {
471 List<Element> versionElements = compatibilityElement.elements(
472 "version");
473
474 for (Element versionElement : versionElements) {
475 Version version = _getVersion(versionElement.getTextTrim());
476
477 if (version.includes(portalVersion)) {
478 compatible = true;
479
480 break;
481 }
482 }
483 }
484
485 if (!compatible) {
486 _log.error(
487 "Themes in this WAR are not compatible with " +
488 ReleaseInfo.getServerInfo());
489
490 return themeIds;
491 }
492
493 ThemeCompanyLimit companyLimit = null;
494
495 Element companyLimitElement = rootElement.element("company-limit");
496
497 if (companyLimitElement != null) {
498 companyLimit = new ThemeCompanyLimit();
499
500 Element companyIncludesElement = companyLimitElement.element(
501 "company-includes");
502
503 if (companyIncludesElement != null) {
504 companyLimit.setIncludes(
505 _getCompanyLimitIncludes(companyIncludesElement));
506 }
507
508 Element companyExcludesElement = companyLimitElement.element(
509 "company-excludes");
510
511 if (companyExcludesElement != null) {
512 companyLimit.setExcludes(
513 _getCompanyLimitExcludes(companyExcludesElement));
514 }
515 }
516
517 ThemeGroupLimit groupLimit = null;
518
519 Element groupLimitElement = rootElement.element("group-limit");
520
521 if (groupLimitElement != null) {
522 groupLimit = new ThemeGroupLimit();
523
524 Element groupIncludesElement = groupLimitElement.element(
525 "group-includes");
526
527 if (groupIncludesElement != null) {
528 groupLimit.setIncludes(
529 _getGroupLimitIncludes(groupIncludesElement));
530 }
531
532 Element groupExcludesElement = groupLimitElement.element(
533 "group-excludes");
534
535 if (groupExcludesElement != null) {
536 groupLimit.setExcludes(
537 _getGroupLimitExcludes(groupExcludesElement));
538 }
539 }
540
541 long timestamp = ServletContextUtil.getLastModified(servletContext);
542
543 List<Element> themeElements = rootElement.elements("theme");
544
545 for (Element themeElement : themeElements) {
546 ContextReplace themeContextReplace = new ContextReplace();
547
548 themeContextReplace.addValue("themes-path", themesPath);
549
550 String themeId = themeElement.attributeValue("id");
551
552 if (servletContextName != null) {
553 themeId =
554 themeId + PortletConstants.WAR_SEPARATOR +
555 servletContextName;
556 }
557
558 themeId = PortalUtil.getJsSafePortletId(themeId);
559
560 themeContextReplace.addValue("theme-id", themeId);
561
562 themeIds.add(themeId);
563
564 Theme theme = _themes.get(themeId);
565
566 if (theme == null) {
567 theme = new ThemeImpl(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");
661 String value = settingElement.attributeValue("value");
662 String script = settingElement.getTextTrim();
663
664 theme.addSetting(
665 key, value, configurable, type, options, script);
666 }
667 }
668
669 theme.setWapTheme(
670 GetterUtil.getBoolean(
671 themeElement.elementText("wap-theme"), theme.isWapTheme()));
672
673 Element rolesElement = themeElement.element("roles");
674
675 if (rolesElement != null) {
676 List<Element> roleNameElements = rolesElement.elements(
677 "role-name");
678
679 for (Element roleNameElement : roleNameElements) {
680 pluginSetting.addRole(roleNameElement.getText());
681 }
682 }
683
684 _readColorSchemes(
685 themeElement, theme.getColorSchemesMap(), themeContextReplace);
686 _readColorSchemes(
687 themeElement, theme.getColorSchemesMap(), themeContextReplace);
688
689 Element layoutTemplatesElement = themeElement.element(
690 "layout-templates");
691
692 if (layoutTemplatesElement != null) {
693 Element standardElement = layoutTemplatesElement.element(
694 "standard");
695
696 if (standardElement != null) {
697 layoutTemplateLocalService.readLayoutTemplate(
698 servletContextName, servletContext, null,
699 standardElement, true, themeId, pluginPackage);
700 }
701
702 Element customElement = layoutTemplatesElement.element(
703 "custom");
704
705 if (customElement != null) {
706 layoutTemplateLocalService.readLayoutTemplate(
707 servletContextName, servletContext, null, customElement,
708 false, themeId, pluginPackage);
709 }
710 }
711
712 if (!theme.isWapTheme()) {
713 _setSpriteImages(servletContext, theme, imagesPath);
714 }
715
716 if (!_themes.containsKey(themeId)) {
717 _themes.put(themeId, theme);
718 }
719 }
720
721 return themeIds;
722 }
723
724 private void _setSpriteImages(
725 ServletContext servletContext, Theme theme, String resourcePath)
726 throws Exception {
727
728 if (!resourcePath.startsWith(StringPool.SLASH)) {
729 resourcePath = StringPool.SLASH.concat(resourcePath);
730 }
731
732 Set<String> resourcePaths = servletContext.getResourcePaths(
733 resourcePath);
734
735 if ((resourcePaths == null) || resourcePaths.isEmpty()) {
736 return;
737 }
738
739 List<URL> imageURLs = new ArrayList<URL>(resourcePaths.size());
740
741 for (String curResourcePath : resourcePaths) {
742 if (curResourcePath.endsWith(StringPool.SLASH)) {
743 _setSpriteImages(servletContext, theme, curResourcePath);
744 }
745 else if (curResourcePath.endsWith(".png")) {
746 URL imageURL = servletContext.getResource(curResourcePath);
747
748 if (imageURL != null) {
749 imageURLs.add(imageURL);
750 }
751 else {
752 _log.error(
753 "Resource URL for " + curResourcePath + " is null");
754 }
755 }
756 }
757
758 String spriteRootDirName = PropsValues.SPRITE_ROOT_DIR;
759 String spriteFileName = resourcePath.concat(
760 PropsValues.SPRITE_FILE_NAME);
761 String spritePropertiesFileName = resourcePath.concat(
762 PropsValues.SPRITE_PROPERTIES_FILE_NAME);
763 String rootPath = ServletContextUtil.getRootPath(servletContext);
764
765 Properties spriteProperties = SpriteProcessorUtil.generate(
766 servletContext, imageURLs, spriteRootDirName, spriteFileName,
767 spritePropertiesFileName, rootPath, 16, 16, 10240);
768
769 if (spriteProperties == null) {
770 return;
771 }
772
773 String contextPath = ContextPathUtil.getContextPath(servletContext);
774
775 spriteFileName = contextPath.concat(SpriteProcessor.PATH).concat(
776 spriteFileName);
777
778 theme.setSpriteImages(spriteFileName, spriteProperties);
779 }
780
781 private static Log _log = LogFactoryUtil.getLog(
782 ThemeLocalServiceImpl.class);
783
784 private static Map<String, Theme> _themes =
785 new ConcurrentHashMap<String, Theme>();
786 private static Map<Long, Map<String, Theme>> _themesPool =
787 new ConcurrentHashMap<Long, Map<String, Theme>>();
788
789 }