001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.service.impl;
016    
017    import com.liferay.portal.kernel.image.SpriteProcessor;
018    import com.liferay.portal.kernel.image.SpriteProcessorUtil;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.model.ColorScheme;
022    import com.liferay.portal.kernel.model.PluginSetting;
023    import com.liferay.portal.kernel.model.PortletConstants;
024    import com.liferay.portal.kernel.model.PortletDecorator;
025    import com.liferay.portal.kernel.model.Theme;
026    import com.liferay.portal.kernel.plugin.PluginPackage;
027    import com.liferay.portal.kernel.plugin.Version;
028    import com.liferay.portal.kernel.servlet.ServletContextUtil;
029    import com.liferay.portal.kernel.theme.PortletDecoratorFactoryUtil;
030    import com.liferay.portal.kernel.theme.ThemeCompanyId;
031    import com.liferay.portal.kernel.theme.ThemeCompanyLimit;
032    import com.liferay.portal.kernel.theme.ThemeGroupId;
033    import com.liferay.portal.kernel.theme.ThemeGroupLimit;
034    import com.liferay.portal.kernel.util.ColorSchemeFactoryUtil;
035    import com.liferay.portal.kernel.util.GetterUtil;
036    import com.liferay.portal.kernel.util.ListUtil;
037    import com.liferay.portal.kernel.util.PortalUtil;
038    import com.liferay.portal.kernel.util.ReleaseInfo;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.StringUtil;
041    import com.liferay.portal.kernel.util.ThemeFactoryUtil;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.kernel.xml.Document;
044    import com.liferay.portal.kernel.xml.Element;
045    import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
046    import com.liferay.portal.plugin.PluginUtil;
047    import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
048    import com.liferay.portal.util.PropsValues;
049    import com.liferay.util.ContextReplace;
050    
051    import java.net.URL;
052    
053    import java.util.ArrayList;
054    import java.util.HashSet;
055    import java.util.Iterator;
056    import java.util.LinkedHashSet;
057    import java.util.List;
058    import java.util.Map;
059    import java.util.Properties;
060    import java.util.Set;
061    import java.util.concurrent.ConcurrentHashMap;
062    
063    import javax.servlet.ServletContext;
064    
065    /**
066     * @author Brian Wing Shun Chan
067     * @author Jorge Ferrer
068     * @author Raymond Aug??
069     */
070    public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
071    
072            @Override
073            public ColorScheme fetchColorScheme(
074                    long companyId, String themeId, String colorSchemeId) {
075    
076                    colorSchemeId = GetterUtil.getString(colorSchemeId);
077    
078                    Theme theme = fetchTheme(companyId, themeId);
079    
080                    if (theme == null) {
081                            return null;
082                    }
083    
084                    Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
085    
086                    return colorSchemesMap.get(colorSchemeId);
087            }
088    
089            @Override
090            public PortletDecorator fetchPortletDecorator(
091                    long companyId, String themeId, String colorSchemeId) {
092    
093                    colorSchemeId = GetterUtil.getString(colorSchemeId);
094    
095                    Theme theme = fetchTheme(companyId, themeId);
096    
097                    if (theme == null) {
098                            return null;
099                    }
100    
101                    Map<String, PortletDecorator> portletDecoratorsMap =
102                            theme.getPortletDecoratorsMap();
103    
104                    return portletDecoratorsMap.get(colorSchemeId);
105            }
106    
107            @Override
108            public Theme fetchTheme(long companyId, String themeId) {
109                    themeId = GetterUtil.getString(themeId);
110    
111                    Map<String, Theme> themes = _getThemes(companyId);
112    
113                    return themes.get(themeId);
114            }
115    
116            @Override
117            public ColorScheme getColorScheme(
118                    long companyId, String themeId, String colorSchemeId) {
119    
120                    colorSchemeId = GetterUtil.getString(colorSchemeId);
121    
122                    Theme theme = getTheme(companyId, themeId);
123    
124                    Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
125    
126                    ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
127    
128                    if (colorScheme != null) {
129                            return colorScheme;
130                    }
131    
132                    List<ColorScheme> colorSchemes = theme.getColorSchemes();
133    
134                    if (!colorSchemes.isEmpty()) {
135                            for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
136                                    colorScheme = colorSchemes.get(i);
137    
138                                    if (colorScheme.isDefaultCs()) {
139                                            return colorScheme;
140                                    }
141                            }
142                    }
143    
144                    if (colorScheme == null) {
145                            colorScheme = ColorSchemeFactoryUtil.getDefaultRegularColorScheme();
146                    }
147    
148                    return colorScheme;
149            }
150    
151            @Override
152            public List<Theme> getControlPanelThemes(long companyId, long userId) {
153                    List<Theme> themes = getThemes(companyId);
154    
155                    themes = PluginUtil.restrictPlugins(themes, companyId, userId);
156    
157                    Iterator<Theme> itr = themes.iterator();
158    
159                    while (itr.hasNext()) {
160                            Theme theme = itr.next();
161    
162                            if (!theme.isControlPanelTheme()) {
163                                    itr.remove();
164                            }
165                    }
166    
167                    return themes;
168            }
169    
170            @Override
171            public List<Theme> getPageThemes(
172                    long companyId, long groupId, long userId) {
173    
174                    List<Theme> themes = getThemes(companyId);
175    
176                    themes = PluginUtil.restrictPlugins(themes, companyId, userId);
177    
178                    Iterator<Theme> itr = themes.iterator();
179    
180                    while (itr.hasNext()) {
181                            Theme theme = itr.next();
182    
183                            if (!theme.isPageTheme() || !theme.isGroupAvailable(groupId)) {
184                                    itr.remove();
185                            }
186                    }
187    
188                    return themes;
189            }
190    
191            @Override
192            public PortletDecorator getPortletDecorator(
193                    long companyId, String themeId, String portletDecoratorId) {
194    
195                    Theme theme = fetchTheme(companyId, themeId);
196    
197                    Map<String, PortletDecorator> portletDecoratorsMap =
198                            theme.getPortletDecoratorsMap();
199    
200                    portletDecoratorId = GetterUtil.getString(portletDecoratorId);
201    
202                    PortletDecorator portletDecorator = portletDecoratorsMap.get(
203                            portletDecoratorId);
204    
205                    if (portletDecorator != null) {
206                            return portletDecorator;
207                    }
208    
209                    List<PortletDecorator> portletDecorators = theme.getPortletDecorators();
210    
211                    if (!portletDecorators.isEmpty()) {
212                            for (int i = (portletDecorators.size() - 1); i >= 0; i--) {
213                                    portletDecorator = portletDecorators.get(i);
214    
215                                    if (portletDecorator.isDefaultPortletDecorator()) {
216                                            return portletDecorator;
217                                    }
218                            }
219                    }
220    
221                    return PortletDecoratorFactoryUtil.getDefaultPortletDecorator();
222            }
223    
224            @Override
225            public Theme getTheme(long companyId, String themeId) {
226                    themeId = GetterUtil.getString(themeId);
227    
228                    Map<String, Theme> themes = _getThemes(companyId);
229    
230                    Theme theme = themes.get(themeId);
231    
232                    if (theme != null) {
233                            return theme;
234                    }
235    
236                    if (_log.isWarnEnabled()) {
237                            _log.warn(
238                                    "No theme found for specified theme id " + themeId +
239                                            ". Returning the default theme.");
240                    }
241    
242                    themeId = ThemeFactoryUtil.getDefaultRegularThemeId(companyId);
243    
244                    theme = _themes.get(themeId);
245    
246                    if (theme != null) {
247                            return theme;
248                    }
249    
250                    if (_themes.isEmpty()) {
251                            if (_log.isDebugEnabled()) {
252                                    _log.debug("No themes are installed");
253                            }
254    
255                            return null;
256                    }
257    
258                    if (!themeId.contains(PortletConstants.WAR_SEPARATOR)) {
259                            _log.error(
260                                    "No theme found for default theme id " + themeId +
261                                            ". Returning a random theme.");
262                    }
263    
264                    for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
265                            theme = entry.getValue();
266    
267                            if ((theme != null) && !theme.isControlPanelTheme()) {
268                                    return theme;
269                            }
270                    }
271    
272                    return null;
273            }
274    
275            @Override
276            public List<Theme> getThemes(long companyId) {
277                    Map<String, Theme> themes = _getThemes(companyId);
278    
279                    List<Theme> themesList = ListUtil.fromMapValues(themes);
280    
281                    return ListUtil.sort(themesList);
282            }
283    
284            /**
285             * @deprecated As of 7.0.0, replaced by {@link #getPageThemes}
286             */
287            @Deprecated
288            @Override
289            public List<Theme> getThemes(
290                    long companyId, long groupId, long userId, boolean wapTheme) {
291    
292                    return getPageThemes(companyId, groupId, userId);
293            }
294    
295            @Override
296            public List<Theme> getWARThemes() {
297                    List<Theme> themes = ListUtil.fromMapValues(_themes);
298    
299                    Iterator<Theme> itr = themes.iterator();
300    
301                    while (itr.hasNext()) {
302                            Theme theme = itr.next();
303    
304                            if (!theme.isWARFile()) {
305                                    itr.remove();
306                            }
307                    }
308    
309                    return themes;
310            }
311    
312            @Override
313            public List<Theme> init(
314                    ServletContext servletContext, String themesPath,
315                    boolean loadFromServletContext, String[] xmls,
316                    PluginPackage pluginPackage) {
317    
318                    return init(
319                            null, servletContext, themesPath, loadFromServletContext, xmls,
320                            pluginPackage);
321            }
322    
323            @Override
324            public List<Theme> init(
325                    String servletContextName, ServletContext servletContext,
326                    String themesPath, boolean loadFromServletContext, String[] xmls,
327                    PluginPackage pluginPackage) {
328    
329                    Set<Theme> themes = new LinkedHashSet<>();
330    
331                    try {
332                            for (String xml : xmls) {
333                                    themes.addAll(
334                                            _readThemes(
335                                                    servletContextName, servletContext, themesPath,
336                                                    loadFromServletContext, xml, pluginPackage));
337                            }
338                    }
339                    catch (Exception e) {
340                            _log.error(e, e);
341                    }
342    
343                    _themesPool.clear();
344    
345                    return new ArrayList<>(themes);
346            }
347    
348            @Override
349            public void uninstallThemes(List<Theme> themes) {
350                    for (Theme theme : themes) {
351                            String themeId = theme.getThemeId();
352    
353                            _themes.remove(themeId);
354    
355                            layoutTemplateLocalService.uninstallLayoutTemplates(themeId);
356                    }
357    
358                    _themesPool.clear();
359            }
360    
361            private List<ThemeCompanyId> _getCompanyLimitExcludes(Element element) {
362                    List<ThemeCompanyId> includes = new ArrayList<>();
363    
364                    if (element == null) {
365                            return includes;
366                    }
367    
368                    List<Element> companyIdsElements = element.elements("company-id");
369    
370                    for (int i = 0; i < companyIdsElements.size(); i++) {
371                            Element companyIdElement = companyIdsElements.get(i);
372    
373                            String name = companyIdElement.attributeValue("name");
374                            String pattern = companyIdElement.attributeValue("pattern");
375    
376                            ThemeCompanyId themeCompanyId = null;
377    
378                            if (Validator.isNotNull(name)) {
379                                    themeCompanyId = new ThemeCompanyId(name, false);
380                            }
381                            else if (Validator.isNotNull(pattern)) {
382                                    themeCompanyId = new ThemeCompanyId(pattern, true);
383                            }
384    
385                            if (themeCompanyId != null) {
386                                    includes.add(themeCompanyId);
387                            }
388                    }
389    
390                    return includes;
391            }
392    
393            private List<ThemeCompanyId> _getCompanyLimitIncludes(Element element) {
394                    return _getCompanyLimitExcludes(element);
395            }
396    
397            private List<ThemeGroupId> _getGroupLimitExcludes(Element element) {
398                    List<ThemeGroupId> includes = new ArrayList<>();
399    
400                    if (element == null) {
401                            return includes;
402                    }
403    
404                    List<Element> groupIdsElements = element.elements("group-id");
405    
406                    for (int i = 0; i < groupIdsElements.size(); i++) {
407                            Element groupIdElement = groupIdsElements.get(i);
408    
409                            String name = groupIdElement.attributeValue("name");
410                            String pattern = groupIdElement.attributeValue("pattern");
411    
412                            ThemeGroupId themeGroupId = null;
413    
414                            if (Validator.isNotNull(name)) {
415                                    themeGroupId = new ThemeGroupId(name, false);
416                            }
417                            else if (Validator.isNotNull(pattern)) {
418                                    themeGroupId = new ThemeGroupId(pattern, true);
419                            }
420    
421                            if (themeGroupId != null) {
422                                    includes.add(themeGroupId);
423                            }
424                    }
425    
426                    return includes;
427            }
428    
429            private List<ThemeGroupId> _getGroupLimitIncludes(Element element) {
430                    return _getGroupLimitExcludes(element);
431            }
432    
433            private Map<String, Theme> _getThemes(long companyId) {
434                    Map<String, Theme> themes = _themesPool.get(companyId);
435    
436                    if (themes != null) {
437                            return themes;
438                    }
439    
440                    themes = new ConcurrentHashMap<>();
441    
442                    for (Map.Entry<String, Theme> entry : _themes.entrySet()) {
443                            String themeId = entry.getKey();
444                            Theme theme = entry.getValue();
445    
446                            if (theme.isCompanyAvailable(companyId)) {
447                                    themes.put(themeId, theme);
448                            }
449                    }
450    
451                    _themesPool.put(companyId, themes);
452    
453                    return themes;
454            }
455    
456            private Version _getVersion(String version) {
457                    if (version.equals("${current-version}")) {
458                            version = ReleaseInfo.getVersion();
459                    }
460    
461                    return Version.getInstance(version);
462            }
463    
464            private void _readColorSchemes(
465                    Element themeElement, Map<String, ColorScheme> colorSchemes,
466                    ContextReplace themeContextReplace) {
467    
468                    List<Element> colorSchemeElements = themeElement.elements(
469                            "color-scheme");
470    
471                    for (Element colorSchemeElement : colorSchemeElements) {
472                            ContextReplace colorSchemeContextReplace =
473                                    (ContextReplace)themeContextReplace.clone();
474    
475                            String id = colorSchemeElement.attributeValue("id");
476    
477                            colorSchemeContextReplace.addValue("color-scheme-id", id);
478    
479                            ColorScheme colorSchemeModel = colorSchemes.get(id);
480    
481                            if (colorSchemeModel == null) {
482                                    colorSchemeModel = ColorSchemeFactoryUtil.getColorScheme(id);
483                            }
484    
485                            String name = GetterUtil.getString(
486                                    colorSchemeElement.attributeValue("name"),
487                                    colorSchemeModel.getName());
488    
489                            name = colorSchemeContextReplace.replace(name);
490    
491                            boolean defaultCs = GetterUtil.getBoolean(
492                                    colorSchemeElement.elementText("default-cs"),
493                                    colorSchemeModel.isDefaultCs());
494    
495                            String cssClass = GetterUtil.getString(
496                                    colorSchemeElement.elementText("css-class"),
497                                    colorSchemeModel.getCssClass());
498    
499                            cssClass = colorSchemeContextReplace.replace(cssClass);
500    
501                            colorSchemeContextReplace.addValue("css-class", cssClass);
502    
503                            String colorSchemeImagesPath = GetterUtil.getString(
504                                    colorSchemeElement.elementText("color-scheme-images-path"),
505                                    colorSchemeModel.getColorSchemeImagesPath());
506    
507                            colorSchemeImagesPath = colorSchemeContextReplace.replace(
508                                    colorSchemeImagesPath);
509    
510                            colorSchemeContextReplace.addValue(
511                                    "color-scheme-images-path", colorSchemeImagesPath);
512    
513                            colorSchemeModel.setName(name);
514                            colorSchemeModel.setDefaultCs(defaultCs);
515                            colorSchemeModel.setCssClass(cssClass);
516                            colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
517    
518                            colorSchemes.put(id, colorSchemeModel);
519                    }
520            }
521    
522            private void _readPortletDecorators(
523                    Element themeElement, Map<String, PortletDecorator> portletDecorators,
524                    ContextReplace themeContextReplace) {
525    
526                    List<Element> portletDecoratorElements = themeElement.elements(
527                            "portlet-decorator");
528    
529                    for (Element portletDecoratorElement : portletDecoratorElements) {
530                            ContextReplace portletDecoratorContextReplace =
531                                    (ContextReplace)themeContextReplace.clone();
532    
533                            String id = portletDecoratorElement.attributeValue("id");
534    
535                            portletDecoratorContextReplace.addValue("portlet-decorator-id", id);
536    
537                            PortletDecorator portletDecoratorModel = portletDecorators.get(id);
538    
539                            if (portletDecoratorModel == null) {
540                                    portletDecoratorModel =
541                                            PortletDecoratorFactoryUtil.getPortletDecorator(id);
542                            }
543    
544                            String name = GetterUtil.getString(
545                                    portletDecoratorElement.attributeValue("name"),
546                                    portletDecoratorModel.getName());
547    
548                            name = portletDecoratorContextReplace.replace(name);
549    
550                            boolean defaultPortletDecorator = GetterUtil.getBoolean(
551                                    portletDecoratorElement.elementText(
552                                            "default-portlet-decorator"),
553                                    portletDecoratorModel.isDefaultPortletDecorator());
554    
555                            String cssClass = GetterUtil.getString(
556                                    portletDecoratorElement.elementText(
557                                            "portlet-decorator-css-class"),
558                                    portletDecoratorModel.getCssClass());
559    
560                            cssClass = portletDecoratorContextReplace.replace(cssClass);
561    
562                            portletDecoratorContextReplace.addValue(
563                                    "portlet-decorator-css-class", cssClass);
564    
565                            String portletDecoratorThumbnailPath = GetterUtil.getString(
566                                    portletDecoratorElement.elementText(
567                                            "portlet-decorator-thumbnail-path"),
568                                    portletDecoratorModel.getPortletDecoratorThumbnailPath());
569    
570                            portletDecoratorThumbnailPath =
571                                    portletDecoratorContextReplace.replace(
572                                            portletDecoratorThumbnailPath);
573    
574                            portletDecoratorContextReplace.addValue(
575                                    "portlet-decorator-thumbnail-path",
576                                    portletDecoratorThumbnailPath);
577    
578                            portletDecoratorModel.setName(name);
579                            portletDecoratorModel.setDefaultPortletDecorator(
580                                    defaultPortletDecorator);
581                            portletDecoratorModel.setCssClass(cssClass);
582                            portletDecoratorModel.setPortletDecoratorThumbnailPath(
583                                    portletDecoratorThumbnailPath);
584    
585                            portletDecorators.put(id, portletDecoratorModel);
586                    }
587            }
588    
589            private Set<Theme> _readThemes(
590                            String servletContextName, ServletContext servletContext,
591                            String themesPath, boolean loadFromServletContext, String xml,
592                            PluginPackage pluginPackage)
593                    throws Exception {
594    
595                    Set<Theme> themes = new HashSet<>();
596    
597                    if (xml == null) {
598                            return themes;
599                    }
600    
601                    Document document = UnsecureSAXReaderUtil.read(xml, true);
602    
603                    Element rootElement = document.getRootElement();
604    
605                    Version portalVersion = _getVersion(ReleaseInfo.getVersion());
606    
607                    boolean compatible = false;
608    
609                    Element compatibilityElement = rootElement.element("compatibility");
610    
611                    if (compatibilityElement != null) {
612                            List<Element> versionElements = compatibilityElement.elements(
613                                    "version");
614    
615                            for (Element versionElement : versionElements) {
616                                    Version version = _getVersion(versionElement.getTextTrim());
617    
618                                    if (version.includes(portalVersion)) {
619                                            compatible = true;
620    
621                                            break;
622                                    }
623                            }
624                    }
625    
626                    if (!compatible) {
627                            _log.error(
628                                    "Themes in this WAR are not compatible with " +
629                                            ReleaseInfo.getServerInfo());
630    
631                            return themes;
632                    }
633    
634                    ThemeCompanyLimit companyLimit = null;
635    
636                    Element companyLimitElement = rootElement.element("company-limit");
637    
638                    if (companyLimitElement != null) {
639                            companyLimit = new ThemeCompanyLimit();
640    
641                            Element companyIncludesElement = companyLimitElement.element(
642                                    "company-includes");
643    
644                            if (companyIncludesElement != null) {
645                                    companyLimit.setIncludes(
646                                            _getCompanyLimitIncludes(companyIncludesElement));
647                            }
648    
649                            Element companyExcludesElement = companyLimitElement.element(
650                                    "company-excludes");
651    
652                            if (companyExcludesElement != null) {
653                                    companyLimit.setExcludes(
654                                            _getCompanyLimitExcludes(companyExcludesElement));
655                            }
656                    }
657    
658                    ThemeGroupLimit groupLimit = null;
659    
660                    Element groupLimitElement = rootElement.element("group-limit");
661    
662                    if (groupLimitElement != null) {
663                            groupLimit = new ThemeGroupLimit();
664    
665                            Element groupIncludesElement = groupLimitElement.element(
666                                    "group-includes");
667    
668                            if (groupIncludesElement != null) {
669                                    groupLimit.setIncludes(
670                                            _getGroupLimitIncludes(groupIncludesElement));
671                            }
672    
673                            Element groupExcludesElement = groupLimitElement.element(
674                                    "group-excludes");
675    
676                            if (groupExcludesElement != null) {
677                                    groupLimit.setExcludes(
678                                            _getGroupLimitExcludes(groupExcludesElement));
679                            }
680                    }
681    
682                    long timestamp = ServletContextUtil.getLastModified(servletContext);
683    
684                    List<Element> themeElements = rootElement.elements("theme");
685    
686                    for (Element themeElement : themeElements) {
687                            ContextReplace themeContextReplace = new ContextReplace();
688    
689                            themeContextReplace.addValue("themes-path", themesPath);
690    
691                            String themeId = themeElement.attributeValue("id");
692    
693                            if (servletContextName != null) {
694                                    themeId =
695                                            themeId + PortletConstants.WAR_SEPARATOR +
696                                                    servletContextName;
697                            }
698    
699                            themeId = PortalUtil.getJsSafePortletId(themeId);
700    
701                            themeContextReplace.addValue("theme-id", themeId);
702    
703                            Theme theme = _themes.get(themeId);
704    
705                            if (theme == null) {
706                                    theme = ThemeFactoryUtil.getTheme(themeId);
707                            }
708    
709                            theme.setTimestamp(timestamp);
710    
711                            PluginSetting pluginSetting =
712                                    pluginSettingLocalService.getDefaultPluginSetting();
713    
714                            theme.setPluginPackage(pluginPackage);
715                            theme.setDefaultPluginSetting(pluginSetting);
716    
717                            theme.setThemeCompanyLimit(companyLimit);
718                            theme.setThemeGroupLimit(groupLimit);
719    
720                            if (servletContextName != null) {
721                                    theme.setServletContextName(servletContextName);
722                            }
723    
724                            theme.setLoadFromServletContext(loadFromServletContext);
725    
726                            String name = GetterUtil.getString(
727                                    themeElement.attributeValue("name"), theme.getName());
728    
729                            String rootPath = GetterUtil.getString(
730                                    themeElement.elementText("root-path"), theme.getRootPath());
731    
732                            rootPath = themeContextReplace.replace(rootPath);
733    
734                            themeContextReplace.addValue("root-path", rootPath);
735    
736                            String templatesPath = GetterUtil.getString(
737                                    themeElement.elementText("templates-path"),
738                                    theme.getTemplatesPath());
739    
740                            templatesPath = themeContextReplace.replace(templatesPath);
741                            templatesPath = StringUtil.safePath(templatesPath);
742    
743                            themeContextReplace.addValue("templates-path", templatesPath);
744    
745                            String cssPath = GetterUtil.getString(
746                                    themeElement.elementText("css-path"), theme.getCssPath());
747    
748                            cssPath = themeContextReplace.replace(cssPath);
749                            cssPath = StringUtil.safePath(cssPath);
750    
751                            themeContextReplace.addValue("css-path", cssPath);
752    
753                            String imagesPath = GetterUtil.getString(
754                                    themeElement.elementText("images-path"), theme.getImagesPath());
755    
756                            imagesPath = themeContextReplace.replace(imagesPath);
757                            imagesPath = StringUtil.safePath(imagesPath);
758    
759                            themeContextReplace.addValue("images-path", imagesPath);
760    
761                            String javaScriptPath = GetterUtil.getString(
762                                    themeElement.elementText("javascript-path"),
763                                    theme.getJavaScriptPath());
764    
765                            javaScriptPath = themeContextReplace.replace(javaScriptPath);
766                            javaScriptPath = StringUtil.safePath(javaScriptPath);
767    
768                            themeContextReplace.addValue("javascript-path", javaScriptPath);
769    
770                            String virtualPath = GetterUtil.getString(
771                                    themeElement.elementText("virtual-path"),
772                                    theme.getVirtualPath());
773    
774                            String templateExtension = GetterUtil.getString(
775                                    themeElement.elementText("template-extension"),
776                                    theme.getTemplateExtension());
777    
778                            theme.setName(name);
779                            theme.setRootPath(rootPath);
780                            theme.setTemplatesPath(templatesPath);
781                            theme.setCssPath(cssPath);
782                            theme.setImagesPath(imagesPath);
783                            theme.setJavaScriptPath(javaScriptPath);
784                            theme.setVirtualPath(virtualPath);
785                            theme.setTemplateExtension(templateExtension);
786    
787                            Element settingsElement = themeElement.element("settings");
788    
789                            if (settingsElement != null) {
790                                    List<Element> settingElements = settingsElement.elements(
791                                            "setting");
792    
793                                    for (Element settingElement : settingElements) {
794                                            boolean configurable = GetterUtil.getBoolean(
795                                                    settingElement.attributeValue("configurable"));
796                                            String key = settingElement.attributeValue("key");
797                                            String[] options = StringUtil.split(
798                                                    settingElement.attributeValue("options"));
799                                            String type = settingElement.attributeValue("type", "text");
800                                            String value = settingElement.attributeValue(
801                                                    "value", StringPool.BLANK);
802                                            String script = settingElement.getTextTrim();
803    
804                                            theme.addSetting(
805                                                    key, value, configurable, type, options, script);
806                                    }
807                            }
808    
809                            theme.setControlPanelTheme(
810                                    GetterUtil.getBoolean(
811                                            themeElement.elementText("control-panel-theme")));
812                            theme.setPageTheme(
813                                    GetterUtil.getBoolean(
814                                            themeElement.elementText("page-theme"), true));
815    
816                            Element rolesElement = themeElement.element("roles");
817    
818                            if (rolesElement != null) {
819                                    List<Element> roleNameElements = rolesElement.elements(
820                                            "role-name");
821    
822                                    for (Element roleNameElement : roleNameElements) {
823                                            pluginSetting.addRole(roleNameElement.getText());
824                                    }
825                            }
826    
827                            _readColorSchemes(
828                                    themeElement, theme.getColorSchemesMap(), themeContextReplace);
829                            _readColorSchemes(
830                                    themeElement, theme.getColorSchemesMap(), themeContextReplace);
831    
832                            Element layoutTemplatesElement = themeElement.element(
833                                    "layout-templates");
834    
835                            if (layoutTemplatesElement != null) {
836                                    Element standardElement = layoutTemplatesElement.element(
837                                            "standard");
838    
839                                    if (standardElement != null) {
840                                            layoutTemplateLocalService.readLayoutTemplate(
841                                                    servletContextName, servletContext, null,
842                                                    standardElement, true, themeId, pluginPackage);
843                                    }
844    
845                                    Element customElement = layoutTemplatesElement.element(
846                                            "custom");
847    
848                                    if (customElement != null) {
849                                            layoutTemplateLocalService.readLayoutTemplate(
850                                                    servletContextName, servletContext, null, customElement,
851                                                    false, themeId, pluginPackage);
852                                    }
853                            }
854    
855                            _setSpriteImages(servletContext, theme, imagesPath);
856    
857                            if (!_themes.containsKey(themeId)) {
858                                    _themes.put(themeId, theme);
859                            }
860    
861                            _readPortletDecorators(
862                                    themeElement, theme.getPortletDecoratorsMap(),
863                                    themeContextReplace);
864                            _readPortletDecorators(
865                                    themeElement, theme.getPortletDecoratorsMap(),
866                                    themeContextReplace);
867    
868                            themes.add(theme);
869                    }
870    
871                    return themes;
872            }
873    
874            private void _setSpriteImages(
875                            ServletContext servletContext, Theme theme, String resourcePath)
876                    throws Exception {
877    
878                    if (!resourcePath.startsWith(StringPool.SLASH)) {
879                            resourcePath = StringPool.SLASH.concat(resourcePath);
880                    }
881    
882                    Set<String> resourcePaths = servletContext.getResourcePaths(
883                            resourcePath);
884    
885                    if ((resourcePaths == null) || resourcePaths.isEmpty()) {
886                            return;
887                    }
888    
889                    List<URL> imageURLs = new ArrayList<>(resourcePaths.size());
890    
891                    for (String curResourcePath : resourcePaths) {
892                            if (curResourcePath.endsWith(StringPool.SLASH)) {
893                                    _setSpriteImages(servletContext, theme, curResourcePath);
894                            }
895                            else if (curResourcePath.endsWith(".png")) {
896                                    URL imageURL = servletContext.getResource(curResourcePath);
897    
898                                    if (imageURL != null) {
899                                            imageURLs.add(imageURL);
900                                    }
901                                    else {
902                                            _log.error(
903                                                    "Resource URL for " + curResourcePath + " is null");
904                                    }
905                            }
906                    }
907    
908                    String spriteRootDirName = PropsValues.SPRITE_ROOT_DIR;
909                    String spriteFileName = resourcePath.concat(
910                            PropsValues.SPRITE_FILE_NAME);
911                    String spritePropertiesFileName = resourcePath.concat(
912                            PropsValues.SPRITE_PROPERTIES_FILE_NAME);
913                    String rootPath = ServletContextUtil.getRootPath(servletContext);
914    
915                    Properties spriteProperties = SpriteProcessorUtil.generate(
916                            servletContext, imageURLs, spriteRootDirName, spriteFileName,
917                            spritePropertiesFileName, rootPath, 16, 16, 10240);
918    
919                    if (spriteProperties == null) {
920                            return;
921                    }
922    
923                    String contextPath = servletContext.getContextPath();
924    
925                    spriteFileName = contextPath.concat(SpriteProcessor.PATH).concat(
926                            spriteFileName);
927    
928                    theme.setSpriteImages(spriteFileName, spriteProperties);
929            }
930    
931            private static final Log _log = LogFactoryUtil.getLog(
932                    ThemeLocalServiceImpl.class);
933    
934            private static final Map<String, Theme> _themes = new ConcurrentHashMap<>();
935            private static final Map<Long, Map<String, Theme>> _themesPool =
936                    new ConcurrentHashMap<>();
937    
938    }