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