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