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