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