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