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