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