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