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