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