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.servlet.filters.dynamiccss;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.CharPool;
020    import com.liferay.portal.kernel.util.ParamUtil;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.kernel.util.WebKeys;
026    import com.liferay.portal.model.PortletConstants;
027    import com.liferay.portal.model.Theme;
028    import com.liferay.portal.service.ThemeLocalServiceUtil;
029    import com.liferay.portal.theme.ThemeDisplay;
030    import com.liferay.portal.util.PortalUtil;
031    
032    import java.net.URLDecoder;
033    
034    import java.util.regex.Matcher;
035    import java.util.regex.Pattern;
036    
037    import javax.servlet.ServletContext;
038    import javax.servlet.http.HttpServletRequest;
039    
040    /**
041     * @author Raymond Aug??
042     * @author Sergio S??nchez
043     * @author David Truong
044     */
045    public class DynamicCSSUtil {
046    
047            public static String replaceToken(
048                            ServletContext servletContext, HttpServletRequest request,
049                            String content)
050                    throws Exception {
051    
052                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
053                            WebKeys.THEME_DISPLAY);
054    
055                    Theme theme = _getTheme(request);
056    
057                    if (theme == null) {
058                            return content;
059                    }
060    
061                    return replaceToken(
062                            servletContext, request, themeDisplay, theme, content);
063            }
064    
065            public static String replaceToken(
066                            ServletContext servletContext, HttpServletRequest request,
067                            ThemeDisplay themeDisplay, Theme theme, String parsedContent)
068                    throws Exception {
069    
070                    String portalContextPath = PortalUtil.getPathContext();
071    
072                    String baseURL = servletContext.getContextPath();
073    
074                    if (baseURL.endsWith(StringPool.SLASH)) {
075                            baseURL = baseURL.substring(0, baseURL.length() - 1);
076                    }
077    
078                    parsedContent = StringUtil.replace(
079                            parsedContent,
080                            new String[] {"@base_url@", "@portal_ctx@", "@theme_image_path@"},
081                            new String[] {
082                                    baseURL, portalContextPath,
083                                    _getThemeImagesPath(request, themeDisplay, theme)
084                            });
085    
086                    return parsedContent;
087            }
088    
089            /**
090             * @see com.liferay.portal.servlet.filters.aggregate.AggregateFilter#aggregateCss(
091             *      com.liferay.portal.servlet.filters.aggregate.ServletPaths, String)
092             */
093            protected static String propagateQueryString(
094                    String content, String queryString) {
095    
096                    StringBuilder sb = new StringBuilder(content.length());
097    
098                    int pos = 0;
099    
100                    while (true) {
101                            int importX = content.indexOf(_CSS_IMPORT_BEGIN, pos);
102                            int importY = content.indexOf(
103                                    _CSS_IMPORT_END, importX + _CSS_IMPORT_BEGIN.length());
104    
105                            if ((importX == -1) || (importY == -1)) {
106                                    sb.append(content.substring(pos));
107    
108                                    break;
109                            }
110    
111                            sb.append(content.substring(pos, importX));
112                            sb.append(_CSS_IMPORT_BEGIN);
113    
114                            String url = content.substring(
115                                    importX + _CSS_IMPORT_BEGIN.length(), importY);
116    
117                            char firstChar = url.charAt(0);
118    
119                            if (firstChar == CharPool.APOSTROPHE) {
120                                    sb.append(CharPool.APOSTROPHE);
121                            }
122                            else if (firstChar == CharPool.QUOTE) {
123                                    sb.append(CharPool.QUOTE);
124                            }
125    
126                            url = StringUtil.unquote(url);
127    
128                            sb.append(url);
129    
130                            if (url.indexOf(CharPool.QUESTION) != -1) {
131                                    sb.append(CharPool.AMPERSAND);
132                            }
133                            else {
134                                    sb.append(CharPool.QUESTION);
135                            }
136    
137                            sb.append(queryString);
138    
139                            if (firstChar == CharPool.APOSTROPHE) {
140                                    sb.append(CharPool.APOSTROPHE);
141                            }
142                            else if (firstChar == CharPool.QUOTE) {
143                                    sb.append(CharPool.QUOTE);
144                            }
145    
146                            sb.append(_CSS_IMPORT_END);
147    
148                            pos = importY + _CSS_IMPORT_END.length();
149                    }
150    
151                    return sb.toString();
152            }
153    
154            private static Theme _getTheme(HttpServletRequest request)
155                    throws Exception {
156    
157                    long companyId = PortalUtil.getCompanyId(request);
158    
159                    String themeId = ParamUtil.getString(request, "themeId");
160    
161                    if (Validator.isNotNull(themeId)) {
162                            try {
163                                    Theme theme = ThemeLocalServiceUtil.getTheme(
164                                            companyId, themeId, false);
165    
166                                    return theme;
167                            }
168                            catch (Exception e) {
169                                    _log.error(e, e);
170                            }
171                    }
172    
173                    String requestURI = URLDecoder.decode(
174                            request.getRequestURI(), StringPool.UTF8);
175    
176                    Matcher portalThemeMatcher = _portalThemePattern.matcher(requestURI);
177    
178                    if (portalThemeMatcher.find()) {
179                            String themePathId = portalThemeMatcher.group(1);
180    
181                            themePathId = StringUtil.replace(
182                                    themePathId, StringPool.UNDERLINE, StringPool.BLANK);
183    
184                            themeId = PortalUtil.getJsSafePortletId(themePathId);
185                    }
186                    else {
187                            Matcher pluginThemeMatcher = _pluginThemePattern.matcher(
188                                    requestURI);
189    
190                            if (pluginThemeMatcher.find()) {
191                                    String themePathId = pluginThemeMatcher.group(1);
192    
193                                    themePathId = StringUtil.replace(
194                                            themePathId, StringPool.UNDERLINE, StringPool.BLANK);
195    
196                                    StringBundler sb = new StringBundler(4);
197    
198                                    sb.append(themePathId);
199                                    sb.append(PortletConstants.WAR_SEPARATOR);
200                                    sb.append(themePathId);
201                                    sb.append("theme");
202    
203                                    themePathId = sb.toString();
204    
205                                    themeId = PortalUtil.getJsSafePortletId(themePathId);
206                            }
207                    }
208    
209                    if (Validator.isNull(themeId)) {
210                            return null;
211                    }
212    
213                    try {
214                            Theme theme = ThemeLocalServiceUtil.getTheme(
215                                    companyId, themeId, false);
216    
217                            return theme;
218                    }
219                    catch (Exception e) {
220                            _log.error(e, e);
221                    }
222    
223                    return null;
224            }
225    
226            private static String _getThemeImagesPath(
227                            HttpServletRequest request, ThemeDisplay themeDisplay, Theme theme)
228                    throws Exception {
229    
230                    String themeImagesPath = null;
231    
232                    if (themeDisplay != null) {
233                            themeImagesPath = themeDisplay.getPathThemeImages();
234                    }
235                    else {
236                            String cdnHost = PortalUtil.getCDNHost(request);
237                            String themeStaticResourcePath = theme.getStaticResourcePath();
238    
239                            themeImagesPath =
240                                    cdnHost + themeStaticResourcePath + theme.getImagesPath();
241                    }
242    
243                    return themeImagesPath;
244            }
245    
246            private static final String _CSS_IMPORT_BEGIN = "@import url(";
247    
248            private static final String _CSS_IMPORT_END = ");";
249    
250            private static final Log _log = LogFactoryUtil.getLog(DynamicCSSUtil.class);
251    
252            private static final Pattern _pluginThemePattern = Pattern.compile(
253                    "\\/([^\\/]+)-theme\\/", Pattern.CASE_INSENSITIVE);
254            private static final Pattern _portalThemePattern = Pattern.compile(
255                    "themes\\/([^\\/]+)\\/css", Pattern.CASE_INSENSITIVE);
256    
257    }