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;
016    
017    import com.liferay.portal.exception.NoSuchGroupException;
018    import com.liferay.portal.exception.NoSuchLayoutException;
019    import com.liferay.portal.kernel.language.LanguageUtil;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.servlet.PortalMessages;
023    import com.liferay.portal.kernel.servlet.SessionMessages;
024    import com.liferay.portal.kernel.struts.LastPath;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.HttpUtil;
028    import com.liferay.portal.kernel.util.LocaleUtil;
029    import com.liferay.portal.kernel.util.ParamUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.kernel.util.WebKeys;
035    import com.liferay.portal.model.Group;
036    import com.liferay.portal.model.Layout;
037    import com.liferay.portal.model.LayoutConstants;
038    import com.liferay.portal.model.LayoutFriendlyURL;
039    import com.liferay.portal.model.LayoutFriendlyURLComposite;
040    import com.liferay.portal.model.User;
041    import com.liferay.portal.service.GroupLocalServiceUtil;
042    import com.liferay.portal.service.LayoutFriendlyURLLocalServiceUtil;
043    import com.liferay.portal.service.LayoutLocalServiceUtil;
044    import com.liferay.portal.service.ServiceContext;
045    import com.liferay.portal.service.ServiceContextFactory;
046    import com.liferay.portal.service.ServiceContextThreadLocal;
047    import com.liferay.portal.service.UserLocalServiceUtil;
048    import com.liferay.portal.util.Portal;
049    import com.liferay.portal.util.PortalInstances;
050    import com.liferay.portal.util.PortalUtil;
051    
052    import java.io.IOException;
053    
054    import java.util.HashMap;
055    import java.util.List;
056    import java.util.Locale;
057    import java.util.Map;
058    
059    import javax.servlet.RequestDispatcher;
060    import javax.servlet.ServletConfig;
061    import javax.servlet.ServletContext;
062    import javax.servlet.ServletException;
063    import javax.servlet.http.HttpServlet;
064    import javax.servlet.http.HttpServletRequest;
065    import javax.servlet.http.HttpServletResponse;
066    
067    /**
068     * @author Brian Wing Shun Chan
069     * @author Jorge Ferrer
070     * @author Shuyang Zhou
071     */
072    public class FriendlyURLServlet extends HttpServlet {
073    
074            @Override
075            public void init(ServletConfig servletConfig) throws ServletException {
076                    super.init(servletConfig);
077    
078                    _private = GetterUtil.getBoolean(
079                            servletConfig.getInitParameter("private"));
080                    _user = GetterUtil.getBoolean(servletConfig.getInitParameter("user"));
081    
082                    if (_private) {
083                            if (_user) {
084                                    _friendlyURLPathPrefix =
085                                            PortalUtil.getPathFriendlyURLPrivateUser();
086                            }
087                            else {
088                                    _friendlyURLPathPrefix =
089                                            PortalUtil.getPathFriendlyURLPrivateGroup();
090                            }
091                    }
092                    else {
093                            _friendlyURLPathPrefix = PortalUtil.getPathFriendlyURLPublic();
094                    }
095            }
096    
097            @Override
098            public void service(
099                            HttpServletRequest request, HttpServletResponse response)
100                    throws IOException, ServletException {
101    
102                    // Do not set the entire full main path. See LEP-456.
103    
104                    //String mainPath = (String)ctx.getAttribute(WebKeys.MAIN_PATH);
105                    String mainPath = Portal.PATH_MAIN;
106    
107                    String redirect = mainPath;
108    
109                    String pathInfo = getPathInfo(request);
110    
111                    request.setAttribute(WebKeys.FRIENDLY_URL, getFriendlyURL(pathInfo));
112    
113                    Object[] redirectArray = null;
114    
115                    boolean forcePermanentRedirect = false;
116    
117                    try {
118                            redirectArray = getRedirect(
119                                    request, pathInfo, mainPath, request.getParameterMap());
120    
121                            redirect = (String)redirectArray[0];
122                            forcePermanentRedirect = (Boolean)redirectArray[1];
123    
124                            if (request.getAttribute(WebKeys.LAST_PATH) == null) {
125                                    LastPath lastPath = null;
126    
127                                    String lifecycle = ParamUtil.getString(
128                                            request, "p_p_lifecycle");
129    
130                                    if (lifecycle.equals("1")) {
131                                            lastPath = new LastPath(_friendlyURLPathPrefix, pathInfo);
132                                    }
133                                    else {
134                                            lastPath = new LastPath(
135                                                    _friendlyURLPathPrefix, pathInfo,
136                                                    HttpUtil.parameterMapToString(
137                                                            request.getParameterMap()));
138                                    }
139    
140                                    request.setAttribute(WebKeys.LAST_PATH, lastPath);
141                            }
142                    }
143                    catch (Exception e) {
144                            if (_log.isWarnEnabled()) {
145                                    _log.warn(e);
146                            }
147    
148                            if ((e instanceof NoSuchGroupException) ||
149                                    (e instanceof NoSuchLayoutException)) {
150    
151                                    PortalUtil.sendError(
152                                            HttpServletResponse.SC_NOT_FOUND, e, request, response);
153    
154                                    return;
155                            }
156                    }
157    
158                    if (Validator.isNull(redirect)) {
159                            redirect = mainPath;
160                    }
161    
162                    if (_log.isDebugEnabled()) {
163                            _log.debug("Redirect " + redirect);
164                    }
165    
166                    if ((redirect.charAt(0) == CharPool.SLASH) && !forcePermanentRedirect) {
167                            ServletContext servletContext = getServletContext();
168    
169                            RequestDispatcher requestDispatcher =
170                                    servletContext.getRequestDispatcher(redirect);
171    
172                            if (requestDispatcher != null) {
173                                    requestDispatcher.forward(request, response);
174                            }
175                    }
176                    else {
177                            if (forcePermanentRedirect) {
178                                    response.setHeader("Location", redirect);
179                                    response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
180                            }
181                            else {
182                                    response.sendRedirect(redirect);
183                            }
184                    }
185            }
186    
187            protected String getFriendlyURL(String pathInfo) {
188                    String friendlyURL = _friendlyURLPathPrefix;
189    
190                    if (Validator.isNotNull(pathInfo)) {
191                            friendlyURL = friendlyURL.concat(pathInfo);
192                    }
193    
194                    return friendlyURL;
195            }
196    
197            protected String getPathInfo(HttpServletRequest request) {
198                    String requestURI = request.getRequestURI();
199    
200                    int pos = requestURI.indexOf(Portal.JSESSIONID);
201    
202                    if (pos != -1) {
203                            requestURI = requestURI.substring(0, pos);
204                    }
205    
206                    String pathProxy = PortalUtil.getPathProxy();
207    
208                    pos = _friendlyURLPathPrefix.length() - pathProxy.length();
209    
210                    return requestURI.substring(pos);
211            }
212    
213            protected Object[] getRedirect(
214                            HttpServletRequest request, String path, String mainPath,
215                            Map<String, String[]> params)
216                    throws Exception {
217    
218                    if (Validator.isNull(path) || (path.charAt(0) != CharPool.SLASH)) {
219                            return new Object[] {mainPath, false};
220                    }
221    
222                    // Group friendly URL
223    
224                    String friendlyURL = null;
225    
226                    int pos = path.indexOf(CharPool.SLASH, 1);
227    
228                    if (pos != -1) {
229                            friendlyURL = path.substring(0, pos);
230                    }
231                    else if (path.length() > 1) {
232                            friendlyURL = path;
233                    }
234    
235                    if (Validator.isNull(friendlyURL)) {
236                            return new Object[] {mainPath, Boolean.FALSE};
237                    }
238    
239                    long companyId = PortalInstances.getCompanyId(request);
240    
241                    Group group = GroupLocalServiceUtil.fetchFriendlyURLGroup(
242                            companyId, friendlyURL);
243    
244                    if (group == null) {
245                            String screenName = friendlyURL.substring(1);
246    
247                            if (_user || !Validator.isNumber(screenName)) {
248                                    User user = UserLocalServiceUtil.fetchUserByScreenName(
249                                            companyId, screenName);
250    
251                                    if (user != null) {
252                                            group = user.getGroup();
253                                    }
254                                    else if (_log.isWarnEnabled()) {
255                                            _log.warn("No user exists with friendly URL " + screenName);
256                                    }
257                            }
258                            else {
259                                    long groupId = GetterUtil.getLong(screenName);
260    
261                                    group = GroupLocalServiceUtil.fetchGroup(groupId);
262    
263                                    if (group == null) {
264                                            if (_log.isDebugEnabled()) {
265                                                    _log.debug(
266                                                            "No group exists with friendly URL " + groupId +
267                                                                    ". Try fetching by screen name instead.");
268                                            }
269    
270                                            User user = UserLocalServiceUtil.fetchUserByScreenName(
271                                                    companyId, screenName);
272    
273                                            if (user != null) {
274                                                    group = user.getGroup();
275                                            }
276                                            else if (_log.isWarnEnabled()) {
277                                                    _log.warn(
278                                                            "No user or group exists with friendly URL " +
279                                                                    groupId);
280                                            }
281                                    }
282                            }
283                    }
284    
285                    if (group == null) {
286                            StringBundler sb = new StringBundler(5);
287    
288                            sb.append("{companyId=");
289                            sb.append(companyId);
290                            sb.append(", friendlyURL=");
291                            sb.append(friendlyURL);
292                            sb.append("}");
293    
294                            throw new NoSuchGroupException(sb.toString());
295                    }
296    
297                    // Layout friendly URL
298    
299                    friendlyURL = null;
300    
301                    if ((pos != -1) && ((pos + 1) != path.length())) {
302                            friendlyURL = path.substring(pos);
303                    }
304    
305                    if (Validator.isNull(friendlyURL)) {
306                            request.setAttribute(
307                                    WebKeys.REDIRECT_TO_DEFAULT_LAYOUT, Boolean.TRUE);
308                    }
309    
310                    Map<String, Object> requestContext = new HashMap<>();
311    
312                    requestContext.put("request", request);
313    
314                    ServiceContext serviceContext =
315                            ServiceContextThreadLocal.getServiceContext();
316    
317                    if (serviceContext == null) {
318                            serviceContext = ServiceContextFactory.getInstance(request);
319    
320                            ServiceContextThreadLocal.pushServiceContext(serviceContext);
321                    }
322    
323                    try {
324                            LayoutFriendlyURLComposite layoutFriendlyURLComposite =
325                                    PortalUtil.getLayoutFriendlyURLComposite(
326                                            group.getGroupId(), _private, friendlyURL, params,
327                                            requestContext);
328    
329                            Layout layout = layoutFriendlyURLComposite.getLayout();
330    
331                            request.setAttribute(WebKeys.LAYOUT, layout);
332    
333                            Locale locale = PortalUtil.getLocale(request);
334    
335                            String layoutFriendlyURLCompositeFriendlyURL =
336                                    layoutFriendlyURLComposite.getFriendlyURL();
337    
338                            if (Validator.isNull(layoutFriendlyURLCompositeFriendlyURL)) {
339                                    layoutFriendlyURLCompositeFriendlyURL = layout.getFriendlyURL(
340                                            locale);
341                            }
342    
343                            pos = layoutFriendlyURLCompositeFriendlyURL.indexOf(
344                                    Portal.FRIENDLY_URL_SEPARATOR);
345    
346                            if (pos != 0) {
347                                    if (pos != -1) {
348                                            layoutFriendlyURLCompositeFriendlyURL =
349                                                    layoutFriendlyURLCompositeFriendlyURL.substring(0, pos);
350                                    }
351    
352                                    if (isI18nRedirect(request, group.getGroupId()) ||
353                                            !StringUtil.equalsIgnoreCase(
354                                                    layoutFriendlyURLCompositeFriendlyURL,
355                                                    layout.getFriendlyURL(locale))) {
356    
357                                            Locale originalLocale = setAlternativeLayoutFriendlyURL(
358                                                    request, layout, layoutFriendlyURLCompositeFriendlyURL);
359    
360                                            String redirect = PortalUtil.getLocalizedFriendlyURL(
361                                                    request, layout, locale, originalLocale);
362    
363                                            return new Object[] {redirect, Boolean.TRUE};
364                                    }
365                            }
366                    }
367                    catch (NoSuchLayoutException nsle) {
368                            List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
369                                    group.getGroupId(), _private,
370                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
371    
372                            for (Layout layout : layouts) {
373                                    if (layout.matches(request, friendlyURL)) {
374                                            String redirect = PortalUtil.getLayoutActualURL(
375                                                    layout, mainPath);
376    
377                                            return new Object[] {redirect, Boolean.FALSE};
378                                    }
379                            }
380    
381                            throw nsle;
382                    }
383    
384                    String actualURL = PortalUtil.getActualURL(
385                            group.getGroupId(), _private, mainPath, friendlyURL, params,
386                            requestContext);
387    
388                    return new Object[] {actualURL, Boolean.FALSE};
389            }
390    
391            protected boolean isI18nRedirect(HttpServletRequest request, long groupId) {
392                    String i18nPath = (String)request.getAttribute(WebKeys.I18N_PATH);
393    
394                    if (Validator.isNull(i18nPath)) {
395                            return false;
396                    }
397    
398                    int pos = i18nPath.indexOf(StringPool.SLASH);
399    
400                    String i18nLanguageId = i18nPath.substring(pos + 1);
401    
402                    Locale i18nLocale = LanguageUtil.getLocale(i18nLanguageId);
403    
404                    if (i18nLocale == null) {
405                            i18nLocale = LocaleUtil.fromLanguageId(i18nLanguageId, true, false);
406                    }
407    
408                    if (LanguageUtil.isAvailableLocale(groupId, i18nLocale)) {
409                            return false;
410                    }
411    
412                    return true;
413            }
414    
415            protected Locale setAlternativeLayoutFriendlyURL(
416                            HttpServletRequest request, Layout layout, String friendlyURL)
417                    throws Exception {
418    
419                    List<LayoutFriendlyURL> layoutFriendlyURLs =
420                            LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURLs(
421                                    layout.getPlid(), friendlyURL, 0, 1);
422    
423                    if (layoutFriendlyURLs.isEmpty()) {
424                            return null;
425                    }
426    
427                    LayoutFriendlyURL layoutFriendlyURL = layoutFriendlyURLs.get(0);
428    
429                    Locale locale = LocaleUtil.fromLanguageId(
430                            layoutFriendlyURL.getLanguageId());
431    
432                    String alternativeLayoutFriendlyURL =
433                            PortalUtil.getLocalizedFriendlyURL(request, layout, locale, locale);
434    
435                    SessionMessages.add(
436                            request, "alternativeLayoutFriendlyURL",
437                            alternativeLayoutFriendlyURL);
438    
439                    PortalMessages.add(
440                            request, PortalMessages.KEY_JSP_PATH,
441                            "/html/common/themes/layout_friendly_url_redirect.jsp");
442    
443                    return locale;
444            }
445    
446            private static final Log _log = LogFactoryUtil.getLog(
447                    FriendlyURLServlet.class);
448    
449            private String _friendlyURLPathPrefix;
450            private boolean _private;
451            private boolean _user;
452    
453    }