1
19
20 package com.liferay.portal.service.impl;
21
22 import com.liferay.portal.SystemException;
23 import com.liferay.portal.kernel.image.SpriteProcessorUtil;
24 import com.liferay.portal.kernel.log.Log;
25 import com.liferay.portal.kernel.log.LogFactoryUtil;
26 import com.liferay.portal.kernel.plugin.PluginPackage;
27 import com.liferay.portal.kernel.servlet.ServletContextUtil;
28 import com.liferay.portal.kernel.util.GetterUtil;
29 import com.liferay.portal.kernel.util.ListUtil;
30 import com.liferay.portal.kernel.util.ReleaseInfo;
31 import com.liferay.portal.kernel.util.ServerDetector;
32 import com.liferay.portal.kernel.util.StringPool;
33 import com.liferay.portal.kernel.util.StringUtil;
34 import com.liferay.portal.kernel.util.Validator;
35 import com.liferay.portal.kernel.xml.Document;
36 import com.liferay.portal.kernel.xml.Element;
37 import com.liferay.portal.kernel.xml.SAXReaderUtil;
38 import com.liferay.portal.model.ColorScheme;
39 import com.liferay.portal.model.PluginSetting;
40 import com.liferay.portal.model.PortletConstants;
41 import com.liferay.portal.model.Theme;
42 import com.liferay.portal.model.impl.ColorSchemeImpl;
43 import com.liferay.portal.model.impl.ThemeImpl;
44 import com.liferay.portal.plugin.PluginUtil;
45 import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
46 import com.liferay.portal.theme.ThemeCompanyId;
47 import com.liferay.portal.theme.ThemeCompanyLimit;
48 import com.liferay.portal.theme.ThemeGroupId;
49 import com.liferay.portal.theme.ThemeGroupLimit;
50 import com.liferay.portal.util.PortalUtil;
51 import com.liferay.util.ContextReplace;
52 import com.liferay.util.Version;
53
54 import java.io.File;
55
56 import java.util.ArrayList;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Properties;
62 import java.util.Set;
63 import java.util.concurrent.ConcurrentHashMap;
64
65 import javax.servlet.ServletContext;
66
67
74 public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
75
76 public ColorScheme getColorScheme(
77 long companyId, String themeId, String colorSchemeId,
78 boolean wapTheme)
79 throws SystemException {
80
81 colorSchemeId = GetterUtil.getString(colorSchemeId);
82
83 Theme theme = getTheme(companyId, themeId, wapTheme);
84
85 Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
86
87 ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
88
89 if (colorScheme == null) {
90 List<ColorScheme> colorSchemes = theme.getColorSchemes();
91
92 if (colorSchemes.size() > 0) {
93 for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
94 colorScheme = colorSchemes.get(i);
95
96 if (colorScheme.isDefaultCs()) {
97 break;
98 }
99 }
100 }
101 }
102
103 if (colorScheme == null) {
104 if (wapTheme) {
105 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
106 }
107 else {
108 colorSchemeId =
109 ColorSchemeImpl.getDefaultRegularColorSchemeId();
110 }
111 }
112
113 if (colorScheme == null) {
114 colorScheme = ColorSchemeImpl.getNullColorScheme();
115 }
116
117 return colorScheme;
118 }
119
120 public Theme getTheme(long companyId, String themeId, boolean wapTheme)
121 throws SystemException {
122
123 themeId = GetterUtil.getString(themeId);
124
125 Theme theme = _getThemes(companyId).get(themeId);
126
127 if (theme == null) {
128 if (_log.isWarnEnabled()) {
129 _log.warn(
130 "No theme found for specified theme id " + themeId +
131 ". Returning the default theme.");
132 }
133
134 if (wapTheme) {
135 themeId = ThemeImpl.getDefaultWapThemeId(companyId);
136 }
137 else {
138 themeId = ThemeImpl.getDefaultRegularThemeId(companyId);
139 }
140
141 theme = _themes.get(themeId);
142 }
143
144 if (theme == null) {
145 if (_themes.isEmpty()) {
146 if (_log.isDebugEnabled()) {
147 _log.debug("No themes are installed");
148 }
149
150 return null;
151 }
152
153 _log.error(
154 "No theme found for default theme id " + themeId +
155 ". Returning a random theme.");
156
157 Iterator<Map.Entry<String, Theme>> itr =
158 _themes.entrySet().iterator();
159
160 while (itr.hasNext()) {
161 Map.Entry<String, Theme> entry = itr.next();
162
163 theme = entry.getValue();
164 }
165 }
166
167 return theme;
168 }
169
170 public List<Theme> getThemes(long companyId) {
171 List<Theme> themes = ListUtil.fromCollection(
172 _getThemes(companyId).values());
173
174 return ListUtil.sort(themes);
175 }
176
177 public List<Theme> getThemes(
178 long companyId, long groupId, long userId, boolean wapTheme)
179 throws SystemException {
180
181 List<Theme> themes = getThemes(companyId);
182
183 themes = PluginUtil.restrictPlugins(themes, companyId, userId);
184
185 Iterator<Theme> itr = themes.iterator();
186
187 while (itr.hasNext()) {
188 Theme theme = itr.next();
189
190 if ((theme.getThemeId().equals("controlpanel")) ||
191 (!theme.isGroupAvailable(groupId)) ||
192 (theme.isWapTheme() != wapTheme)) {
193
194 itr.remove();
195 }
196 }
197
198 return themes;
199 }
200
201 public List<Theme> getWARThemes() {
202 List<Theme> themes = ListUtil.fromCollection(_themes.values());
203
204 Iterator<Theme> itr = themes.iterator();
205
206 while (itr.hasNext()) {
207 Theme theme = itr.next();
208
209 if (!theme.isWARFile()) {
210 itr.remove();
211 }
212 }
213
214 return themes;
215 }
216
217 public List<String> init(
218 ServletContext servletContext, String themesPath,
219 boolean loadFromServletContext, String[] xmls,
220 PluginPackage pluginPackage) {
221
222 return init(
223 null, servletContext, themesPath, loadFromServletContext, xmls,
224 pluginPackage);
225 }
226
227 public List<String> init(
228 String servletContextName, ServletContext servletContext,
229 String themesPath, boolean loadFromServletContext, String[] xmls,
230 PluginPackage pluginPackage) {
231
232 List<String> themeIds = new ArrayList<String>();
233
234 try {
235 for (int i = 0; i < xmls.length; i++) {
236 Set<String> themes = _readThemes(
237 servletContextName, servletContext, themesPath,
238 loadFromServletContext, xmls[i], pluginPackage);
239
240 Iterator<String> itr = themes.iterator();
241
242 while (itr.hasNext()) {
243 String themeId = itr.next();
244
245 if (!themeIds.contains(themeId)) {
246 themeIds.add(themeId);
247 }
248 }
249 }
250 }
251 catch (Exception e) {
252 e.printStackTrace();
253 }
254
255 _themesPool.clear();
256
257 return themeIds;
258 }
259
260 public void uninstallThemes(List<String> themeIds) {
261 for (int i = 0; i < themeIds.size(); i++) {
262 String themeId = themeIds.get(i);
263
264 _themes.remove(themeId);
265
266 layoutTemplateLocalService.uninstallLayoutTemplates(themeId);
267 }
268
269 _themesPool.clear();
270 }
271
272 private List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
273 List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
274
275 if (el != null) {
276 List<Element> companyIds = el.elements("company-id");
277
278 for (int i = 0; i < companyIds.size(); i++) {
279 Element companyIdEl = companyIds.get(i);
280
281 String name = companyIdEl.attributeValue("name");
282 String pattern = companyIdEl.attributeValue("pattern");
283
284 ThemeCompanyId themeCompanyId = null;
285
286 if (Validator.isNotNull(name)) {
287 themeCompanyId = new ThemeCompanyId(name, false);
288 }
289 else if (Validator.isNotNull(pattern)) {
290 themeCompanyId = new ThemeCompanyId(pattern, true);
291 }
292
293 if (themeCompanyId != null) {
294 includes.add(themeCompanyId);
295 }
296 }
297 }
298
299 return includes;
300 }
301
302 private List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
303 return _getCompanyLimitExcludes(el);
304 }
305
306 private List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
307 List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
308
309 if (el != null) {
310 List<Element> groupIds = el.elements("group-id");
311
312 for (int i = 0; i < groupIds.size(); i++) {
313 Element groupIdEl = groupIds.get(i);
314
315 String name = groupIdEl.attributeValue("name");
316 String pattern = groupIdEl.attributeValue("pattern");
317
318 ThemeGroupId themeGroupId = null;
319
320 if (Validator.isNotNull(name)) {
321 themeGroupId = new ThemeGroupId(name, false);
322 }
323 else if (Validator.isNotNull(pattern)) {
324 themeGroupId = new ThemeGroupId(pattern, true);
325 }
326
327 if (themeGroupId != null) {
328 includes.add(themeGroupId);
329 }
330 }
331 }
332
333 return includes;
334 }
335
336 private List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
337 return _getGroupLimitExcludes(el);
338 }
339
340 private Map<String, Theme> _getThemes(long companyId) {
341 Map<String, Theme> themes = _themesPool.get(companyId);
342
343 if (themes == null) {
344 themes = new ConcurrentHashMap<String, Theme>();
345
346 Iterator<Map.Entry<String, Theme>> itr =
347 _themes.entrySet().iterator();
348
349 while (itr.hasNext()) {
350 Map.Entry<String, Theme> entry = itr.next();
351
352 String themeId = entry.getKey();
353 Theme theme = entry.getValue();
354
355 if (theme.isCompanyAvailable(companyId)) {
356 themes.put(themeId, theme);
357 }
358 }
359
360 _themesPool.put(companyId, themes);
361 }
362
363 return themes;
364 }
365
366 private Version _getVersion(String version) {
367 if (version.equals("${current-version}")) {
368 version = ReleaseInfo.getVersion();
369 }
370
371 return Version.getInstance(version);
372 }
373
374 private void _readColorSchemes(
375 Element theme, Map<String, ColorScheme> colorSchemes,
376 ContextReplace themeContextReplace) {
377
378 Iterator<Element> itr = theme.elements("color-scheme").iterator();
379
380 while (itr.hasNext()) {
381 Element colorScheme = itr.next();
382
383 ContextReplace colorSchemeContextReplace =
384 (ContextReplace)themeContextReplace.clone();
385
386 String id = colorScheme.attributeValue("id");
387
388 colorSchemeContextReplace.addValue("color-scheme-id", id);
389
390 ColorScheme colorSchemeModel = colorSchemes.get(id);
391
392 if (colorSchemeModel == null) {
393 colorSchemeModel = new ColorSchemeImpl(id);
394 }
395
396 String name = GetterUtil.getString(
397 colorScheme.attributeValue("name"), colorSchemeModel.getName());
398
399 name = colorSchemeContextReplace.replace(name);
400
401 boolean defaultCs = GetterUtil.getBoolean(
402 colorScheme.elementText("default-cs"),
403 colorSchemeModel.isDefaultCs());
404
405 String cssClass = GetterUtil.getString(
406 colorScheme.elementText("css-class"),
407 colorSchemeModel.getCssClass());
408
409 cssClass = colorSchemeContextReplace.replace(cssClass);
410
411 colorSchemeContextReplace.addValue("css-class", cssClass);
412
413 String colorSchemeImagesPath = GetterUtil.getString(
414 colorScheme.elementText("color-scheme-images-path"),
415 colorSchemeModel.getColorSchemeImagesPath());
416
417 colorSchemeImagesPath = colorSchemeContextReplace.replace(
418 colorSchemeImagesPath);
419
420 colorSchemeContextReplace.addValue(
421 "color-scheme-images-path", colorSchemeImagesPath);
422
423 colorSchemeModel.setName(name);
424 colorSchemeModel.setDefaultCs(defaultCs);
425 colorSchemeModel.setCssClass(cssClass);
426 colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
427
428 colorSchemes.put(id, colorSchemeModel);
429 }
430 }
431
432 private Set<String> _readThemes(
433 String servletContextName, ServletContext servletContext,
434 String themesPath, boolean loadFromServletContext, String xml,
435 PluginPackage pluginPackage)
436 throws Exception {
437
438 Set<String> themeIds = new HashSet<String>();
439
440 if (xml == null) {
441 return themeIds;
442 }
443
444 Document doc = SAXReaderUtil.read(xml, true);
445
446 Element root = doc.getRootElement();
447
448 Version portalVersion = _getVersion(ReleaseInfo.getVersion());
449
450 boolean compatible = false;
451
452 Element compatibilityEl = root.element("compatibility");
453
454 if (compatibilityEl != null) {
455 Iterator<Element> itr = compatibilityEl.elements(
456 "version").iterator();
457
458 while (itr.hasNext()) {
459 Element versionEl = itr.next();
460
461 Version version = _getVersion(versionEl.getTextTrim());
462
463 if (version.includes(portalVersion)) {
464 compatible = true;
465
466 break;
467 }
468 }
469 }
470
471 if (!compatible) {
472 _log.error(
473 "Themes in this WAR are not compatible with " +
474 ReleaseInfo.getServerInfo());
475
476 return themeIds;
477 }
478
479 ThemeCompanyLimit companyLimit = null;
480
481 Element companyLimitEl = root.element("company-limit");
482
483 if (companyLimitEl != null) {
484 companyLimit = new ThemeCompanyLimit();
485
486 Element companyIncludesEl =
487 companyLimitEl.element("company-includes");
488
489 if (companyIncludesEl != null) {
490 companyLimit.setIncludes(
491 _getCompanyLimitIncludes(companyIncludesEl));
492 }
493
494 Element companyExcludesEl =
495 companyLimitEl.element("company-excludes");
496
497 if (companyExcludesEl != null) {
498 companyLimit.setExcludes(
499 _getCompanyLimitExcludes(companyExcludesEl));
500 }
501 }
502
503 ThemeGroupLimit groupLimit = null;
504
505 Element groupLimitEl = root.element("group-limit");
506
507 if (groupLimitEl != null) {
508 groupLimit = new ThemeGroupLimit();
509
510 Element groupIncludesEl = groupLimitEl.element("group-includes");
511
512 if (groupIncludesEl != null) {
513 groupLimit.setIncludes(_getGroupLimitIncludes(groupIncludesEl));
514 }
515
516 Element groupExcludesEl =
517 groupLimitEl.element("group-excludes");
518
519 if (groupExcludesEl != null) {
520 groupLimit.setExcludes(_getGroupLimitExcludes(groupExcludesEl));
521 }
522 }
523
524 long timestamp = ServletContextUtil.getLastModified(servletContext);
525
526 Iterator<Element> itr1 = root.elements("theme").iterator();
527
528 while (itr1.hasNext()) {
529 Element theme = itr1.next();
530
531 ContextReplace themeContextReplace = new ContextReplace();
532
533 themeContextReplace.addValue("themes-path", themesPath);
534
535 String themeId = theme.attributeValue("id");
536
537 if (servletContextName != null) {
538 themeId =
539 themeId + PortletConstants.WAR_SEPARATOR +
540 servletContextName;
541 }
542
543 themeId = PortalUtil.getJsSafePortletId(themeId);
544
545 themeContextReplace.addValue("theme-id", themeId);
546
547 themeIds.add(themeId);
548
549 Theme themeModel = _themes.get(themeId);
550
551 if (themeModel == null) {
552 themeModel = new ThemeImpl(themeId);
553
554 _themes.put(themeId, themeModel);
555 }
556
557 themeModel.setTimestamp(timestamp);
558
559 PluginSetting pluginSetting =
560 pluginSettingLocalService.getDefaultPluginSetting();
561
562 themeModel.setPluginPackage(pluginPackage);
563 themeModel.setDefaultPluginSetting(pluginSetting);
564
565 themeModel.setThemeCompanyLimit(companyLimit);
566 themeModel.setThemeGroupLimit(groupLimit);
567
568 if (servletContextName != null) {
569 themeModel.setServletContextName(servletContextName);
570 }
571
572 themeModel.setLoadFromServletContext(loadFromServletContext);
573
574 String name = GetterUtil.getString(
575 theme.attributeValue("name"), themeModel.getName());
576
577 String rootPath = GetterUtil.getString(
578 theme.elementText("root-path"), themeModel.getRootPath());
579
580 rootPath = themeContextReplace.replace(rootPath);
581
582 themeContextReplace.addValue("root-path", rootPath);
583
584 String templatesPath = GetterUtil.getString(
585 theme.elementText("templates-path"),
586 themeModel.getTemplatesPath());
587
588 templatesPath = themeContextReplace.replace(templatesPath);
589 templatesPath = StringUtil.safePath(templatesPath);
590
591 themeContextReplace.addValue("templates-path", templatesPath);
592
593 String cssPath = GetterUtil.getString(
594 theme.elementText("css-path"), themeModel.getCssPath());
595
596 cssPath = themeContextReplace.replace(cssPath);
597 cssPath = StringUtil.safePath(cssPath);
598
599 themeContextReplace.addValue("css-path", cssPath);
600
601 String imagesPath = GetterUtil.getString(
602 theme.elementText("images-path"),
603 themeModel.getImagesPath());
604
605 imagesPath = themeContextReplace.replace(imagesPath);
606 imagesPath = StringUtil.safePath(imagesPath);
607
608 themeContextReplace.addValue("images-path", imagesPath);
609
610 String javaScriptPath = GetterUtil.getString(
611 theme.elementText("javascript-path"),
612 themeModel.getJavaScriptPath());
613
614 javaScriptPath = themeContextReplace.replace(javaScriptPath);
615 javaScriptPath = StringUtil.safePath(javaScriptPath);
616
617 themeContextReplace.addValue("javascript-path", javaScriptPath);
618
619 String virtualPath = GetterUtil.getString(
620 theme.elementText("virtual-path"), themeModel.getVirtualPath());
621
622 String templateExtension = GetterUtil.getString(
623 theme.elementText("template-extension"),
624 themeModel.getTemplateExtension());
625
626 themeModel.setName(name);
627 themeModel.setRootPath(rootPath);
628 themeModel.setTemplatesPath(templatesPath);
629 themeModel.setCssPath(cssPath);
630 themeModel.setImagesPath(imagesPath);
631 themeModel.setJavaScriptPath(javaScriptPath);
632 themeModel.setVirtualPath(virtualPath);
633 themeModel.setTemplateExtension(templateExtension);
634
635 Element settingsEl = theme.element("settings");
636
637 if (settingsEl != null) {
638 Iterator<Element> itr2 = settingsEl.elements(
639 "setting").iterator();
640
641 while (itr2.hasNext()) {
642 Element settingEl = itr2.next();
643
644 String key = settingEl.attributeValue("key");
645 String value = settingEl.attributeValue("value");
646
647 themeModel.setSetting(key, value);
648 }
649 }
650
651 themeModel.setWapTheme(GetterUtil.getBoolean(
652 theme.elementText("wap-theme"), themeModel.isWapTheme()));
653
654 Element rolesEl = theme.element("roles");
655
656 if (rolesEl != null) {
657 Iterator<Element> itr2 = rolesEl.elements(
658 "role-name").iterator();
659
660 while (itr2.hasNext()) {
661 Element roleNameEl = itr2.next();
662
663 pluginSetting.addRole(roleNameEl.getText());
664 }
665 }
666
667 _readColorSchemes(
668 theme, themeModel.getColorSchemesMap(), themeContextReplace);
669 _readColorSchemes(
670 theme, themeModel.getColorSchemesMap(), themeContextReplace);
671
672 Element layoutTemplatesEl = theme.element("layout-templates");
673
674 if (layoutTemplatesEl != null) {
675 Element standardEl = layoutTemplatesEl.element("standard");
676
677 if (standardEl != null) {
678 layoutTemplateLocalService.readLayoutTemplate(
679 servletContextName, servletContext, null,
680 standardEl, true, themeId, pluginPackage);
681 }
682
683 Element customEl = layoutTemplatesEl.element("custom");
684
685 if (customEl != null) {
686 layoutTemplateLocalService.readLayoutTemplate(
687 servletContextName, servletContext, null,
688 customEl, false, themeId, pluginPackage);
689 }
690 }
691
692 if (!themeModel.isWapTheme()) {
693 _setSpriteImages(servletContext, themeModel, imagesPath);
694 }
695 }
696
697 return themeIds;
698 }
699
700 private void _setSpriteImages(
701 ServletContext servletContext, Theme theme, String resourcePath)
702 throws Exception {
703
704 Set<String> resourcePaths = servletContext.getResourcePaths(
705 resourcePath);
706
707 if (resourcePaths == null) {
708 return;
709 }
710
711 List<File> images = new ArrayList<File>(resourcePaths.size());
712
713 for (String curResourcePath : resourcePaths) {
714 if (curResourcePath.endsWith(StringPool.SLASH)) {
715 _setSpriteImages(servletContext, theme, curResourcePath);
716 }
717 else if (curResourcePath.endsWith(".png")) {
718 String realPath = ServletContextUtil.getRealPath(
719 servletContext, curResourcePath);
720
721 if (realPath != null) {
722 images.add(new File(realPath));
723 }
724 else {
725 if (ServerDetector.isTomcat()) {
726 if (_log.isInfoEnabled()) {
727 _log.info(ServletContextUtil.LOG_INFO_SPRITES);
728 }
729 }
730 else {
731 _log.error(
732 "Real path for " + curResourcePath + " is null");
733 }
734 }
735 }
736 }
737
738 String spriteFileName = ".sprite.png";
739 String spritePropertiesFileName = ".sprite.properties";
740 String spritePropertiesRootPath = ServletContextUtil.getRealPath(
741 servletContext, theme.getImagesPath());
742
743 Properties spriteProperties = SpriteProcessorUtil.generate(
744 images, spriteFileName, spritePropertiesFileName,
745 spritePropertiesRootPath, 16, 16, 10240);
746
747 if (spriteProperties == null) {
748 return;
749 }
750
751 spriteFileName =
752 resourcePath.substring(
753 theme.getImagesPath().length(), resourcePath.length()) +
754 spriteFileName;
755
756 theme.setSpriteImages(spriteFileName, spriteProperties);
757 }
758
759 private static Log _log =
760 LogFactoryUtil.getLog(ThemeLocalServiceImpl.class);
761
762 private static Map<String, Theme> _themes =
763 new ConcurrentHashMap<String, Theme>();
764 private static Map<Long, Map<String, Theme>> _themesPool =
765 new ConcurrentHashMap<Long, Map<String, Theme>>();
766
767 }