Package org.jahia.bin

Source Code of org.jahia.bin.Render

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/

package org.jahia.bin;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.lang.StringUtils;
import org.jahia.api.Constants;
import org.jahia.bin.errors.DefaultErrorHandler;
import org.jahia.bin.errors.ErrorHandler;
import org.jahia.exceptions.JahiaForbiddenAccessException;
import org.jahia.exceptions.JahiaUnauthorizedException;
import org.jahia.params.ParamBean;
import org.jahia.params.ProcessingContext;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.applications.pluto.JahiaPortalURLParserImpl;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.JCRPropertyWrapper;
import org.jahia.services.content.JCRSessionFactory;
import org.jahia.services.content.JCRSessionWrapper;
import org.jahia.services.content.decorator.JCRSiteNode;
import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition;
import org.jahia.services.logging.MetricsLoggingService;
import org.jahia.services.render.*;
import org.jahia.services.templates.JahiaTemplateManagerService;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.services.usermanager.JahiaUserManagerService;
import org.jahia.settings.SettingsBean;
import org.jahia.tools.files.FileUpload;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.jcr.*;
import javax.servlet.ServletConfig;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
* Rendering controller. Resolves the node and the template, and renders it by executing the appropriate script.
*/
public class Render extends HttpServlet implements Controller, ServletConfigAware {

    private static final long serialVersionUID = 5377039107890340659L;

    public static final String METHOD_DELETE = "DELETE";
    public static final String METHOD_HEAD = "HEAD";
    public static final String METHOD_GET = "GET";
    public static final String METHOD_OPTIONS = "OPTIONS";
    public static final String METHOD_POST = "POST";
    public static final String METHOD_PUT = "PUT";
    public static final String METHOD_TRACE = "TRACE";

    protected static final String HEADER_IFMODSINCE = "If-Modified-Since";
    protected static final String HEADER_LASTMOD = "Last-Modified";

    protected static final Set<String> reservedParameters;

    private static Logger logger = org.slf4j.LoggerFactory.getLogger(Render.class);

    // Here we define the constants for the reserved keywords for post methods
    public static final String NODE_TYPE = "jcrNodeType";
    public static final String NODE_NAME = "jcrNodeName";
    public static final String NODE_NAME_PROPERTY = "jcrNodeNameProperty";
    public static final String NEW_NODE_OUTPUT_FORMAT = "jcrNewNodeOutputFormat";
    public static final String REDIRECT_TO = "jcrRedirectTo";
    public static final String REDIRECT_HTTP_RESPONSE_CODE = "jcrRedirectResponseCode";
    public static final String METHOD_TO_CALL = "jcrMethodToCall";
    public static final String AUTO_CHECKIN = "jcrAutoCheckin";
    public static final String CAPTCHA = "jcrCaptcha";
    public static final String TARGETDIRECTORY = "jcrTargetDirectory";
    public static final String TARGETNAME = "jcrTargetName";
    public static final String NORMALIZE_NODE_NAME = "jcrNormalizeNodeName";
    public static final String VERSION = "jcrVersion";
    public static final String SUBMIT = "jcrSubmit";
    public static final String AUTO_ASSIGN_ROLE = "jcrAutoAssignRole";
    public static final String ALIAS_USER = "alias";
    public static final String PARENT_TYPE = "jcrParentType";
    public static final String RETURN_CONTENTTYPE = "jcrReturnContentType";
    public static final String RETURN_CONTENTTYPE_OVERRIDE = "jcrReturnContentTypeOverride";
    public static final String RESOURCE_ID = "jcrResourceID";
    public static final String REMOVE_MIXIN = "jcrRemoveMixin";
    public static final String COOKIE_VALUE = "jcrCookieValue";
    public static final String COOKIE_NAME = "jcrCookieName";
    public static final String COOKIE_PATH = "jcrCookiePath";
    public static final String CONTRIBUTE_POST = "jcrContributePost";

    private static final List<String> REDIRECT_CODE_MOVED_PERMANENTLY = new ArrayList<String>(
            Arrays.asList(new String[]{String.valueOf(HttpServletResponse.SC_MOVED_PERMANENTLY)}));
    private static final List<String> LIST_WITH_EMPTY_STRING = new ArrayList<String>(Arrays.asList(new String[]{StringUtils.EMPTY}));

    private MetricsLoggingService loggingService;
    private JahiaTemplateManagerService templateService;
    private Action defaultPostAction;
    private Action defaultDeleteAction = new DefaultDeleteAction();

    protected SettingsBean settingsBean;
    private RenderService renderService;
    private JCRSessionFactory jcrSessionFactory;
    private URLResolverFactory urlResolverFactory;

    private Integer sessionExpiryTime = null;
    private Integer cookieExpirationInDays = 1;

    private Set<String> allowedMethods = new HashSet<String>();

    static {
        reservedParameters = new HashSet<String>();
        reservedParameters.add(NODE_TYPE);
        reservedParameters.add(NODE_NAME);
        reservedParameters.add(NODE_NAME_PROPERTY);
        reservedParameters.add(NEW_NODE_OUTPUT_FORMAT);
        reservedParameters.add(REDIRECT_TO);
        reservedParameters.add(METHOD_TO_CALL);
        reservedParameters.add(AUTO_CHECKIN);
        reservedParameters.add(CAPTCHA);
        reservedParameters.add(TARGETDIRECTORY);
        reservedParameters.add(TARGETNAME);
        reservedParameters.add(Constants.JCR_MIXINTYPES);
        reservedParameters.add(NORMALIZE_NODE_NAME);
        reservedParameters.add(VERSION);
        reservedParameters.add(SUBMIT);
        reservedParameters.add(AUTO_ASSIGN_ROLE);
        reservedParameters.add(PARENT_TYPE);
        reservedParameters.add(RETURN_CONTENTTYPE);
        reservedParameters.add(RETURN_CONTENTTYPE_OVERRIDE);
        reservedParameters.add(COOKIE_NAME);
        reservedParameters.add(COOKIE_VALUE);
        reservedParameters.add(COOKIE_PATH);
        reservedParameters.add(CONTRIBUTE_POST);
    }

    private transient ServletConfig servletConfig;


    /**
     * Returns the time the <code>HttpServletRequest</code> object was last modified, in milliseconds since midnight January 1, 1970 GMT. If
     * the time is unknown, this method returns a negative number (the default).
     * <p/>
     * <p/>
     * Servlets that support HTTP GET requests and can quickly determine their last modification time should override this method. This
     * makes browser and proxy caches work more effectively, reducing the load on server and network resources.
     *
     * @return a <code>long</code> integer specifying the time the <code>HttpServletRequest</code> object was last modified, in milliseconds
     *         since midnight, January 1, 1970 GMT, or -1 if the time is not known
     */
    protected long getLastModified(Resource resource, RenderContext renderContext)
            throws RepositoryException, IOException {
        // Node node = resource.getNode();
        // if (node.hasProperty("jcr:lastModified")) {
        // return node.getProperty("jcr:lastModified").getDate().getTime().getTime();
        // }
        return -1;
    }

    /**
     * Sets the Last-Modified entity header field, if it has not already been set and if the value is meaningful. Called before doGet, to
     * ensure that headers are set before response data is written. A subclass might have set this header already, so we check.
     */
    protected void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
        if (resp.containsHeader(HEADER_LASTMOD)) {
            return;
        }
        if (lastModified >= 0) {
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
        }
    }

    protected RenderContext createRenderContext(HttpServletRequest req, HttpServletResponse resp, JahiaUser user) {
        RenderContext context = new RenderContext(req, resp, user);
        context.setServletPath(getRenderServletPath());
        return context;
    }

    protected Date getVersionDate(HttpServletRequest req) {
        // we assume here that the date has been passed as milliseconds.
        String msString = req.getParameter("v");
        if (msString == null) {
            return null;
        }
        try {
            long msLong = Long.parseLong(msString);
            if (logger.isDebugEnabled()) {
                logger.debug("Display version of date : " + SimpleDateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(msLong)));
            }
            return new Date(msLong);
        } catch (NumberFormatException nfe) {
            logger.warn("Invalid version date found in URL " + msString);
            return null;
        }
    }

    protected String getVersionLabel(HttpServletRequest req) {
        return req.getParameter("l");
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
                         Resource resource, long startTime) throws RepositoryException, RenderException, IOException {
        loggingService.startProfiler("MAIN");
        resp.setCharacterEncoding(settingsBean.getCharacterEncoding());
        String out = renderService.render(resource, renderContext).trim();
        if (renderContext.getRedirect() != null && !resp.isCommitted()) {
            resp.sendRedirect(renderContext.getRedirect());
        } else {
            resp.setContentType(
                    renderContext.getContentType() != null ? renderContext.getContentType() : "text/html; charset=UTF-8");
//            resp.setContentLength(out.getBytes("UTF-8").length);
            resp.getWriter().print(out);
//            resp.getWriter().close();
        }
        String sessionID = "";
        HttpSession httpSession = req.getSession(false);
        if (httpSession != null) {
            sessionID = httpSession.getId();
        }
        loggingService.stopProfiler("MAIN");
        loggingService.logContentEvent(renderContext.getUser().getName(), req.getRemoteAddr(), sessionID,
                resource.getNode().getIdentifier(), resource.getNode().getPath(), resource.getNode().getPrimaryNodeType().getName(), "pageViewed",
                req.getHeader("User-Agent"), req.getHeader("Referer"), Long.toString(System.currentTimeMillis() - startTime));
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
                         URLResolver urlResolver) throws RepositoryException, IOException {
        JCRSessionWrapper session = jcrSessionFactory.getCurrentUserSession(urlResolver.getWorkspace(), urlResolver.getLocale());
        JCRNodeWrapper node = session.getNode(urlResolver.getPath());
        session.checkout(node);
        @SuppressWarnings("unchecked")
        Map<String, String[]> parameters = req.getParameterMap();
        if (parameters.containsKey(REMOVE_MIXIN)) {
            String[] mixinTypes = (String[]) parameters.get(REMOVE_MIXIN);
            for (String mixinType : mixinTypes) {
                node.removeMixin(mixinType);
            }
        }
        if (parameters.containsKey(Constants.JCR_MIXINTYPES)) {
            String[] mixinTypes = (String[]) parameters.get(Constants.JCR_MIXINTYPES);
            for (String mixinType : mixinTypes) {
                node.addMixin(mixinType);
            }
        }
        Set<Map.Entry<String, String[]>> set = parameters.entrySet();
        for (Map.Entry<String, String[]> entry : set) {
            String key = entry.getKey();
            if (!reservedParameters.contains(key)) {
                String[] values = entry.getValue();
                final ExtendedPropertyDefinition propertyDefinition =
                        ((JCRNodeWrapper) node).getApplicablePropertyDefinition(key);
                if (propertyDefinition == null) {
                    continue;
                }
                if (propertyDefinition.isMultiple()) {
                    node.setProperty(key, values);
                } else if (propertyDefinition.getRequiredType() == PropertyType.DATE) {
                    // Expecting ISO date yyyy-MM-dd'T'HH:mm:ss
                    DateTime dateTime = ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(values[0]);
                    node.setProperty(key, dateTime.toCalendar(Locale.ENGLISH));
                } else {
                    node.setProperty(key, values[0]);
                }
            }
        }
        session.save();
        if (req.getParameter(AUTO_CHECKIN) != null && req.getParameter(AUTO_CHECKIN).length() > 0) {
            session.getWorkspace().getVersionManager().checkpoint(node.getPath());
        }
        final String requestWith = req.getHeader("x-requested-with");
        if (req.getHeader("accept").contains("application/json") && requestWith != null &&
                requestWith.equals("XMLHttpRequest")) {
            try {
                serializeNodeToJSON(node).write(resp.getWriter());
            } catch (JSONException e) {
                logger.error(e.getMessage(), e);
            }
        } else {
            performRedirect(null, null, req, resp, toParameterMapOfListOfString(req), false);
        }
        if (req.getParameter(COOKIE_NAME) != null && req.getParameter(COOKIE_VALUE) != null) {
            Cookie cookie = new Cookie(req.getParameter(COOKIE_NAME), req.getParameter(COOKIE_VALUE));
            cookie.setMaxAge(60 * 60 * 24 * cookieExpirationInDays);
            if (req.getParameter(COOKIE_PATH) != null)
                cookie.setPath(req.getParameter(COOKIE_PATH));
            else {
                cookie.setPath("/");
            }
            resp.addCookie(cookie);
        }
        String sessionID = "";
        HttpSession httpSession = req.getSession(false);
        if (httpSession != null) {
            sessionID = httpSession.getId();
        }
        loggingService.logContentEvent(renderContext.getUser().getName(), req.getRemoteAddr(), sessionID,
                node.getIdentifier(), urlResolver.getPath(), node.getPrimaryNodeType().getName(), "nodeUpdated",
                new JSONObject(req.getParameterMap()).toString());
    }

    public static JSONObject serializeNodeToJSON(JCRNodeWrapper node)
            throws RepositoryException, IOException, JSONException {
        final PropertyIterator stringMap = node.getProperties();
        Map<String, String> map = new HashMap<String, String>();
        while (stringMap.hasNext()) {
            JCRPropertyWrapper propertyWrapper = (JCRPropertyWrapper) stringMap.next();
            final int type = propertyWrapper.getType();
            final String name = propertyWrapper.getName().replace(":", "_");
            if (!Constants.forbiddenPropertiesToSerialize.contains(propertyWrapper.getDefinition().getName())) {
                if (type == PropertyType.WEAKREFERENCE || type == PropertyType.REFERENCE) {
                    if (!propertyWrapper.isMultiple()) {
                        map.put(name, ((JCRNodeWrapper) propertyWrapper.getNode()).getUrl());
                    }
                } else {
                    if (!propertyWrapper.isMultiple()) {
                        map.put(name, propertyWrapper.getValue().getString());
                    }
                }
            }
        }
        JSONObject nodeJSON = new JSONObject(map);
        return nodeJSON;
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
                          URLResolver urlResolver) throws Exception {
        if (req.getParameter(JahiaPortalURLParserImpl.PORTLET_INFO) != null) {
            Resource resource = urlResolver.getResource();
            renderContext.setMainResource(resource);
            JCRSiteNode site = resource.getNode().getResolveSite();
            renderContext.setSite(site);
            doGet(req, resp, renderContext, resource, System.currentTimeMillis());
            return;
        }
        Map<String, List<String>> parameters = new HashMap<String, List<String>>();
        if (checkForUploadedFiles(req, resp, urlResolver.getWorkspace(), urlResolver.getLocale(), parameters, urlResolver)) {
            if (parameters.isEmpty()) {
                return;
            }
        }
        if (parameters.isEmpty()) {
            parameters = toParameterMapOfListOfString(req);
        }

        req.getSession().removeAttribute("formDatas");
        req.getSession().removeAttribute("formError");

        Action action;
        Resource resource = null;
        if (urlResolver.getPath().endsWith(".do")) {
            resource = urlResolver.getResource();
            renderContext.setMainResource(resource);
            try {
                JCRSiteNode site = resource.getNode().getResolveSite();
                renderContext.setSite(site);
            } catch (RepositoryException e) {
            }

            action = templateService.getActions().get(resource.getResolvedTemplate());
        } else {
            String path = urlResolver.getPath();
            resource = urlResolver.getResource((path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/") : path) + ".html");
            renderContext.setMainResource(resource);
            try {
                JCRSiteNode site = resource.getNode().getResolveSite();
                renderContext.setSite(site);
            } catch (RepositoryException e) {
            }
            action = defaultPostAction;
        }
        if (action == null) {
            if (urlResolver.getPath().endsWith(".do")) {
                logger.error("Couldn't resolve action named [" + resource.getResolvedTemplate() + "]");
            }
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
        } else {
            doAction(req, resp, urlResolver, renderContext, resource, action, parameters);
        }
    }

    private Map<String, List<String>> toParameterMapOfListOfString(HttpServletRequest req) {
        Map<String, List<String>> parameters = new HashMap<String, List<String>>();
        for (Object key : req.getParameterMap().keySet()) {
            if (key != null) {
                parameters.put((String) key, new ArrayList<String>(Arrays.asList((String[]) req.getParameterMap().get(key))));
            }
        }
        return parameters;
    }

    private boolean checkForUploadedFiles(HttpServletRequest req, HttpServletResponse resp, String workspace,
                                          Locale locale, Map<String, List<String>> parameters,
                                          URLResolver urlResolver)
            throws RepositoryException, IOException {

        if (isMultipartRequest(req)) {
            // multipart is processed only if it's not a portlet request.
            // otherwise it's the task the portlet
            if (!isPortletRequest(req)) {
                final String savePath = settingsBean.getTmpContentDiskPath();
                final File tmp = new File(savePath);
                if (!tmp.exists()) {
                    tmp.mkdirs();
                }
                try {
                    final FileUpload fileUpload = new FileUpload(req, savePath, Integer.MAX_VALUE);
                    req.setAttribute(FileUpload.FILEUPLOAD_ATTRIBUTE, fileUpload);
                    if (fileUpload.getFileItems() != null && fileUpload.getFileItems().size() > 0) {
                        boolean isTargetDirectoryDefined = fileUpload.getParameterNames().contains(TARGETDIRECTORY);
                        boolean isContributePost = fileUpload.getParameterNames().contains(CONTRIBUTE_POST);
                        final String requestWith = req.getHeader("x-requested-with");
                        boolean isAjaxRequest =
                                req.getHeader("accept").contains("application/json") && requestWith != null &&
                                        requestWith.equals("XMLHttpRequest") || fileUpload.getParameterMap().isEmpty();
                        List<String> uuids = new LinkedList<String>();
                        List<String> files = new ArrayList<String>();
                        List<String> urls = new LinkedList<String>();
                        // If target directory is defined or if it is an ajax request then save the file now
                        // otherwise we delay the save of the file to the node creation
                        if (isContributePost || isTargetDirectoryDefined || isAjaxRequest) {
                            JCRSessionWrapper session =
                                    jcrSessionFactory.getCurrentUserSession(workspace, locale);
                            String target;
                            if (isTargetDirectoryDefined) {
                                target = (fileUpload.getParameterValues(TARGETDIRECTORY))[0];
                            } else if (isContributePost) {
                                String path = urlResolver.getPath();
                                path = (path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/") : path);
                                JCRNodeWrapper sessionNode = session.getNode(path);
                                JCRSiteNode siteNode = sessionNode.getResolveSite();
                                if (siteNode != null) {
                                    String s = sessionNode.getResolveSite().getPath() + "/files/contributed/";
                                    String name = sessionNode.getPrimaryNodeTypeName().replaceAll(":", "_") + "_" + sessionNode.getName();
                                    target = s + name;
                                    try {
                                        session.getNode(target);
                                    } catch (RepositoryException e) {
                                        JCRNodeWrapper node = session.getNode(s);
                                        session.checkout(node);
                                        node.addNode(name, "jnt:folder");
                                        session.save();
                                    }
                                } else {
                                    target = sessionNode.getPath() + "/files";
                                    if (!sessionNode.hasNode("files")) {
                                        session.checkout(sessionNode);
                                        sessionNode.addNode("files", "jnt:folder");
                                        session.save();
                                    }
                                }
                            } else {
                                String path = urlResolver.getPath();
                                target = (path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/") : path);
                            }
                            final JCRNodeWrapper targetDirectory = session.getNode(target);

                            boolean isVersionActivated = fileUpload.getParameterNames().contains(VERSION) ?
                                    (fileUpload.getParameterValues(VERSION))[0].equals("true") : false;

                            final Map<String, DiskFileItem> stringDiskFileItemMap = fileUpload.getFileItems();
                            for (Map.Entry<String, DiskFileItem> itemEntry : stringDiskFileItemMap.entrySet()) {
                                //if node exists, do a checkout before
                                String name = itemEntry.getValue().getName();

                                if (fileUpload.getParameterNames().contains(TARGETNAME)) {
                                    name = (fileUpload.getParameterValues(TARGETNAME))[0];
                                }

                                JCRNodeWrapper fileNode = targetDirectory.hasNode(name) ?
                                        targetDirectory.getNode(name) : null;
                                if (fileNode != null && isVersionActivated) {
                                    session.checkout(fileNode);
                                }
                                // checkout parent directory
                                session.getWorkspace().getVersionManager().checkout(targetDirectory.getPath());
                                final JCRNodeWrapper wrapper = targetDirectory
                                        .uploadFile(name,
                                                itemEntry.getValue().getInputStream(),
                                                itemEntry.getValue().getContentType());
                                uuids.add(wrapper.getIdentifier());
                                urls.add(wrapper.getAbsoluteUrl(req));
                                files.add(itemEntry.getValue().getName());
                                if (isVersionActivated) {
                                    if (!wrapper.isVersioned()) {
                                        wrapper.versionFile();
                                    }
                                    session.save();
                                    wrapper.checkpoint();
                                }
                            }
                            fileUpload.markFilesAsConsumed();
                            session.save();
                        }

                        if (!isAjaxRequest && !isContributePost) {
                            parameters.putAll(fileUpload.getParameterMap());
                            if (isTargetDirectoryDefined) {
                                parameters.put(NODE_NAME, files);
                            }
                            return true;
                        } else {
                            try {
                                resp.setStatus(HttpServletResponse.SC_CREATED);
                                Map<String, Object> map = new LinkedHashMap<String, Object>();
                                map.put("uuids", uuids);
                                map.put("urls", urls);
                                JSONObject nodeJSON = new JSONObject(map);
                                nodeJSON.write(resp.getWriter());
                                return true;
                            } catch (JSONException e) {
                                logger.error(e.getMessage(), e);
                            }
                        }
                    }
                    if (fileUpload.getParameterMap() != null && !fileUpload.getParameterMap().isEmpty()) {
                        parameters.putAll(fileUpload.getParameterMap());
                    }
                } catch (IOException e) {
                    logger.error("Cannot parse multipart data !", e);
                }
            } else {
                logger.debug("Mulipart request is not processed. It's the task of the portlet");
            }
        }

        return false;
    }

    protected void doDelete(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
                            URLResolver urlResolver) throws Exception {
        doAction(req, resp, urlResolver, renderContext, null, defaultDeleteAction, toParameterMapOfListOfString(req));
    }

    public boolean isMultipartRequest(final HttpServletRequest req) {
        final String contentType = req.getHeader("Content-Type");

        return ((contentType != null) && (contentType.indexOf("multipart/form-data") >= 0));
    }

    /**
     * If the request is a portlet request, it returns true, otherwise returns false.
     *
     * @param req An HttpServletRequest.
     * @return True if request is a portlet request.
     */
    public boolean isPortletRequest(final HttpServletRequest req) {
        String pathInfo = req.getPathInfo();
        if (pathInfo != null) {
            StringTokenizer st = new StringTokenizer(pathInfo, "/", false);
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                // remder/resource url
                if (token.startsWith(ProcessingContext.PLUTO_PREFIX + ProcessingContext.PLUTO_RESOURCE)) {
                    return true;
                } else if (token.startsWith(ProcessingContext.PLUTO_PREFIX + ProcessingContext.PLUTO_ACTION)) {
                    return true;
                }
            }
        }
        return false;

    }

    /**
     * This method allows you to define where you want to redircet the user after request.
     *
     * @param url
     * @param path
     * @param req
     * @param resp
     * @param parameters
     * @param bypassCache If true we will append a parameter to the URL that should match the id of the resource to refresh
     * @throws IOException
     */
    public static void performRedirect(String url, String path, HttpServletRequest req, HttpServletResponse resp,
                                       Map<String, List<String>> parameters, boolean bypassCache) throws IOException {
        String renderedURL = null;

        List<String> stringList = parameters.get(NEW_NODE_OUTPUT_FORMAT);
        String outputFormat =
                !CollectionUtils.isEmpty(stringList) && stringList.get(0) != null ? stringList.get(0) : "html";

        stringList = parameters.get(REDIRECT_HTTP_RESPONSE_CODE);
        int responseCode = !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(0)) ?
                Integer.parseInt(stringList.get(0)) : HttpServletResponse.SC_SEE_OTHER;

        stringList = parameters.get(REDIRECT_TO);
        String stayOnPage =
                !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(0)) ? StringUtils.substringBeforeLast(stringList.get(0),";") :
                        "";

        if (!StringUtils.isEmpty(stayOnPage)) {
            renderedURL = stayOnPage + (!StringUtils.isEmpty(outputFormat) ? "." + outputFormat : "");
        } else if (!StringUtils.isEmpty(url)) {
            String requestedURL = req.getRequestURI();
//            String encodedPath = URLEncoder.encode(path, "UTF-8").replace("%2F", "/").replace("+", "%20");
            String decodedURL = URLDecoder.decode(requestedURL, "UTF-8");

            int index = decodedURL.indexOf(path);

            renderedURL = decodedURL.substring(0, index) + url +
                    (!StringUtils.isEmpty(outputFormat) ? "." + outputFormat : "");
        }
        if (bypassCache) {
            stringList = parameters.get(RESOURCE_ID);
            String formuuid = !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(
                    0)) ? stringList.get(0) : UUID.randomUUID().toString();
            renderedURL = renderedURL + "?ec=" + formuuid;
        }
        if (!StringUtils.isEmpty(renderedURL)) {
            if (StringUtils.isEmpty(stayOnPage)) {
                resp.setHeader("Location", resp.encodeRedirectURL(renderedURL));
            } else if (responseCode == HttpServletResponse.SC_SEE_OTHER) {
                resp.setHeader("Location", resp.encodeRedirectURL(renderedURL));
            }
            if (responseCode == HttpServletResponse.SC_FOUND) {
                resp.sendRedirect(resp.encodeRedirectURL(renderedURL));
            } else {
                resp.setStatus(responseCode);
            }
        }
    }

    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        if (isDisabled()) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        }
        String method = req.getMethod();
        if (req.getParameter(METHOD_TO_CALL) != null) {
            method = req.getParameter(METHOD_TO_CALL).toUpperCase();
        }
        if (!isMethodAllowed(method)) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return null;
        }
        long startTime = System.currentTimeMillis();
        String sessionId = null;
        try {
            final HttpSession session = req.getSession();
            if (logger.isInfoEnabled()) {
                sessionId = session.getId();
            }
            Date date = getVersionDate(req);
            String versionLabel = getVersionLabel(req);
            URLResolver urlResolver = urlResolverFactory.createURLResolver(req.getPathInfo(), req.getServerName(), req);
            urlResolver.setVersionDate(date);
            urlResolver.setVersionLabel(versionLabel);

            req.setAttribute("urlResolver", urlResolver);

            // check permission
            try {
                if (!hasAccess(urlResolver.getNode())) {
                    if (JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                        throw new JahiaUnauthorizedException();
                    } else {
                        throw new JahiaForbiddenAccessException();
                    }
                }
            } catch (PathNotFoundException e) {

            }

            session.setAttribute("workspace", urlResolver.getWorkspace());

            if (sessionExpiryTime != null && session.getMaxInactiveInterval() != sessionExpiryTime * 60) {
                session.setMaxInactiveInterval(sessionExpiryTime * 60);
            }

            RenderContext renderContext =
                    createRenderContext(req, resp, jcrSessionFactory.getCurrentUser());
            renderContext.setLiveMode(Constants.LIVE_WORKSPACE.equals(urlResolver.getWorkspace()));
            renderContext.setPreviewMode(!renderContext.isEditMode() && !renderContext.isContributionMode() && !renderContext.isLiveMode());
            urlResolver.setRenderContext(renderContext);
            req.getSession().setAttribute(ParamBean.SESSION_LOCALE, urlResolver.getLocale());
            jcrSessionFactory.setCurrentLocale(urlResolver.getLocale());
            if (renderContext.isPreviewMode() && req.getParameter(ALIAS_USER) != null && !JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                jcrSessionFactory.setCurrentAliasedUser(ServicesRegistry.getInstance().getJahiaUserManagerService().lookupUser(req.getParameter(ALIAS_USER)));
            }
            if (method.equals(METHOD_GET)) {
                if (!StringUtils.isEmpty(urlResolver.getRedirectUrl())) {
                    Map<String, List<String>> parameters = new HashMap<String, List<String>>();
                    parameters.put(NEW_NODE_OUTPUT_FORMAT, LIST_WITH_EMPTY_STRING);
                    parameters.put(REDIRECT_HTTP_RESPONSE_CODE, REDIRECT_CODE_MOVED_PERMANENTLY);

                    performRedirect(urlResolver.getRedirectUrl(), StringUtils.isEmpty(urlResolver.getVanityUrl()) ?
                            "/" + urlResolver.getLocale().toString() + urlResolver.getPath() :
                            urlResolver.getVanityUrl(), req, resp, parameters, false);
                } else {
                    Resource resource;

                    resource = urlResolver.getResource();
                    renderContext.setMainResource(resource);

                    JCRSiteNode site = resource.getNode().getResolveSite();
                    if ((site == null && resource.getNode().getPath().startsWith("/sites/")) || (site != null
                            && (renderContext.getEditModeConfigName() == null
                            || !renderContext.getEditModeConfigName().equals(Studio.STUDIO_MODE))
                            && !(renderContext.isLiveMode() ? site.getActiveLanguagesAsLocales()
                                    .contains(urlResolver.getLocale()) : site
                                    .getLanguagesAsLocales().contains(urlResolver.getLocale())))) {
                        throw new PathNotFoundException("This language does not exist on this site");
                    }
                    renderContext.setSite(site);
//                    resource.pushWrapper("wrapper.fullpage");
//                    resource.pushBodyWrapper();

                    if (urlResolver.getPath().endsWith(".do")) {
                        Action action = templateService.getActions().get(resource.getResolvedTemplate());
                        Map<String, List<String>> parameters = toParameterMapOfListOfString(req);
                        doAction(req, resp, urlResolver, renderContext, resource, action, parameters);
                    } else {
                        long lastModified = getLastModified(resource, renderContext);

                        if (lastModified == -1) {
                            // servlet doesn't support if-modified-since, no reason
                            // to go through further expensive logic
                            doGet(req, resp, renderContext, resource, startTime);
                        } else {
                            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                                // If the servlet mod time is later, call doGet()
                                // Round down to the nearest second for a proper compare
                                // A ifModifiedSince of -1 will always be less
                                maybeSetLastModified(resp, lastModified);
                                doGet(req, resp, renderContext, resource, startTime);
                            } else {
                                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                            }
                        }
                    }
                }
            } else if (method.equals(METHOD_HEAD)) {
                doHead(req, resp);

            } else if (method.equals(METHOD_POST)) {
                doPost(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_PUT)) {
                doPut(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_DELETE)) {
                doDelete(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_OPTIONS)) {
                doOptions(req, resp);

            } else if (method.equals(METHOD_TRACE)) {
                doTrace(req, resp);

            } else {
                //
                // Note that this means NO servlet supports whatever
                // method was requested, anywhere on this server.
                //
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            }
        } catch (Exception e) {
            List<ErrorHandler> handlers = templateService.getErrorHandler();
            for (ErrorHandler handler : handlers) {
                if (handler.handle(e, req, resp)) {
                    return null;
                }
            }
            DefaultErrorHandler.getInstance().handle(e, req, resp);
        } finally {
            if (logger.isInfoEnabled()) {
                StringBuilder sb = new StringBuilder(100);
                sb.append("Rendered [").append(req.getRequestURI());
                if (jcrSessionFactory.getCurrentUser() != null) {
                    sb.append("] user=[").append(jcrSessionFactory.getCurrentUser().getUsername());
                }
                sb.append("] ip=[").append(req.getRemoteAddr()).append("] sessionID=[")
                        .append(sessionId).append("] in [")
                        .append(System.currentTimeMillis() - startTime).append("ms]");
                logger.info(sb.toString());
            }
        }
        return null;
    }

    protected boolean isDisabled() {
        return false;
    }

    private void doAction(HttpServletRequest req, HttpServletResponse resp, URLResolver urlResolver,
                          RenderContext renderContext, Resource resource, Action action,
                          Map<String, List<String>> parameters) throws Exception {

        String token = parameters.get("form-token")!=null?parameters.get("form-token").get(0):null;
        if (token != null) {
            final String requestWith = req.getHeader("x-requested-with");
            boolean isAjaxRequest =
                    req.getHeader("accept").contains("application/json") && requestWith != null &&
                            requestWith.equals("XMLHttpRequest");

            @SuppressWarnings("unchecked")
            Map<String, Map<String, List<String>>> toks = (Map<String, Map<String, List<String>>>) req.getSession().getAttribute("form-tokens");
            if (toks != null && toks.containsKey(token)) {
                Map<String, List<String>> m = toks.remove(token);
                if (m == null) {
                    Map<String, String[]> formDatas = new HashMap<String, String[]>();
                    Set<Map.Entry<String, List<String>>> set = parameters.entrySet();
                    for (Map.Entry<String, List<String>> params : set) {
                        formDatas.put(params.getKey(), params.getValue().toArray(new String[params.getValue().size()]));
                    }
                    if (!isAjaxRequest) {
                        req.getSession().setAttribute("formDatas", formDatas);
                        req.getSession().setAttribute("formError", "Your captcha is invalid");
                        performRedirect(urlResolver.getRedirectUrl(), urlResolver.getPath(), req, resp, parameters, true);
                    } else {
                        resp.setContentType("application/json");
                        Map<String,String> res = new HashMap<String,String>();
                        res.put("status","Your captcha is invalid");
                        new JSONObject(res).write(resp.getWriter());
                    }
                    return;
                }
                Map<String, List<String>> values = new HashMap<String, List<String>>(m);

                // Validate form token
                List<String> stringList1 = values.remove("form-action");
                String formAction = stringList1.isEmpty()?null:stringList1.get(0);
                String characterEncoding = SettingsBean.getInstance().getCharacterEncoding();
                if (formAction == null ||
                        (!URLDecoder.decode(req.getRequestURI(), characterEncoding).equals(URLDecoder.decode(formAction, characterEncoding)) &&
                        !URLDecoder.decode(resp.encodeURL(req.getRequestURI()), characterEncoding).equals(URLDecoder.decode(formAction, characterEncoding)))
                        ) {
                    throw new AccessDeniedException();
                }
                if (!req.getMethod().equalsIgnoreCase(values.remove("form-method").get(0))) {
                    throw new AccessDeniedException();
                }
                for (Map.Entry<String, List<String>> entry : values.entrySet()) {
                    List<String> stringList = entry.getValue();
                    List<String> parameterValues = parameters.get(entry.getKey());
                    if (!CollectionUtils.isEqualCollection(stringList, parameterValues)) {
                        if (entry.getKey().equals("jcrCaptcha")) {
                            Map<String, String[]> formDatas = new HashMap<String, String[]>();
                            Set<Map.Entry<String, List<String>>> set = parameters.entrySet();
                            for (Map.Entry<String, List<String>> params : set) {
                                formDatas.put(params.getKey(), params.getValue().toArray(new String[params.getValue().size()]));
                            }
                            if (!isAjaxRequest) {
                                req.getSession().setAttribute("formDatas", formDatas);
                                req.getSession().setAttribute("formError", "Your captcha is invalid");
                                performRedirect(renderContext.getMainResource().getNode().getPath(), urlResolver.getPath(), req, resp, parameters,
                                        true);
                            } else {
                                resp.setContentType("application/json");
                                Map<String,String> res = new HashMap<String,String>();
                                res.put("status","Your captcha is invalid");
                                new JSONObject(res).write(resp.getWriter());
                            }
                            return;
                        }
                        throw new AccessDeniedException();
                    }
                }

                final Action originalAction = action;
                action = new SystemAction() {
                    @Override
                    public ActionResult doExecuteAsSystem(HttpServletRequest req, RenderContext renderContext, JCRSessionWrapper systemSession, Resource resource, Map<String, List<String>> parameters, URLResolver urlResolver) throws Exception {
                        return originalAction.doExecute(req, renderContext, resource, systemSession, parameters, urlResolver);
                    }
                };
            }
        }

        if (!(action instanceof SystemAction)) {
            if (action.getRequiredWorkspace() != null
                    && !action.getRequiredWorkspace().equals(urlResolver.getWorkspace())) {
                throw new PathNotFoundException("Action is not supported for this workspace");
            }
            if (action.isRequireAuthenticatedUser() && !renderContext.isLoggedIn()) {
                throw new AccessDeniedException("Action '" + action.getName() + "' requires an authenticated user");
            }
            if (!action.isPermitted(urlResolver.getNode())) {
                throw new AccessDeniedException("Action '" + action.getName() + "' requires '" + action.getRequiredPermission() + "' permission.");
            }
        }

        JCRSessionWrapper session = null;
        if (resource != null) {
            session = resource.getNode().getSession();
        } else {
            session = JCRSessionFactory.getInstance().getCurrentUserSession(urlResolver.getWorkspace(),
                    urlResolver.getLocale());
        }
        ActionResult result = action.doExecute(req, renderContext, resource, session, parameters, urlResolver);
        if (result != null) {
            if (result.getResultCode() < 300) {
                resp.setStatus(result.getResultCode());
               
                if (result.getJson() != null &&
                        ("json".equals(parameters.get(RETURN_CONTENTTYPE) != null ? parameters.get(RETURN_CONTENTTYPE).get(0) : "")
                                || req.getHeader("accept") != null && req.getHeader("accept").contains("application/json"))) {
                    try {
                        resp.setContentType(parameters.get(RETURN_CONTENTTYPE_OVERRIDE) != null ? parameters.get(RETURN_CONTENTTYPE_OVERRIDE).get(0) : "application/json");
                        result.getJson().write(resp.getWriter());
                    } catch (JSONException e) {
                        logger.error(e.getMessage(), e);
                    }
                } else {
                    if (!result.isAbsoluteUrl()) {
                        performRedirect(result.getUrl(), urlResolver.getPath(), req, resp, parameters, false);
                    } else {
                        resp.sendRedirect(resp.encodeRedirectURL(result.getUrl()));
                    }
                }
            } else {
                resp.sendError(result.getResultCode());
            }
        }
    }

    protected boolean isMethodAllowed(String method) {
        return allowedMethods.isEmpty() || allowedMethods.contains(method);
    }

    protected boolean hasAccess(JCRNodeWrapper node) {
        return true;
    }

    public void setServletConfig(ServletConfig servletConfig) {
        this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public String getServletName() {
        return getServletConfig().getServletName();
    }

    public static String getRenderServletPath() {
        // TODO move this into configuration
        return "/cms/render";
    }

    public void setLoggingService(MetricsLoggingService loggingService) {
        this.loggingService = loggingService;
    }

    public void setTemplateService(JahiaTemplateManagerService templateService) {
        this.templateService = templateService;
    }

    public void setSessionExpiryTime(int sessionExpiryTime) {
        this.sessionExpiryTime = sessionExpiryTime;
    }

    public void setDefaultPostAction(Action defaultPostActionResult) {
        this.defaultPostAction = defaultPostActionResult;
    }

    public static Set<String> getReservedParameters() {
        return reservedParameters;
    }

    /**
     * @param settingsBean the settingsBean to set
     */
    public void setSettingsBean(SettingsBean settingsBean) {
        this.settingsBean = settingsBean;
    }

    /**
     * @param renderService the renderService to set
     */
    public void setRenderService(RenderService renderService) {
        this.renderService = renderService;
    }

    /**
     * @param jcrSessionFactory the jcrSessionFactory to set
     */
    public void setJcrSessionFactory(JCRSessionFactory jcrSessionFactory) {
        this.jcrSessionFactory = jcrSessionFactory;
    }

    public void setCookieExpirationInDays(Integer cookieExpirationInDays) {
        this.cookieExpirationInDays = cookieExpirationInDays;
    }

    public void setUrlResolverFactory(URLResolverFactory urlResolverFactory) {
        this.urlResolverFactory = urlResolverFactory;
    }

    /**
     * Specifies the set of allowed HTTP methods.
     *
     * @param allowedMethods the set of allowed HTTP methods
     */
    public void setAllowedMethods(Set<String> allowedMethods) {
        this.allowedMethods = new HashSet<String>(allowedMethods.size());
        for (String method : allowedMethods) {
            this.allowedMethods.add(method.toUpperCase());
        }
    }
}
TOP

Related Classes of org.jahia.bin.Render

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.