nl.tricode.magnolia.blogs.templates.BlogSearchRenderableDefinition.java Source code

Java tutorial

Introduction

Here is the source code for nl.tricode.magnolia.blogs.templates.BlogSearchRenderableDefinition.java

Source

/**
 *      Tricode Blog module
 *      Is a Blog module for Magnolia CMS.
 *      Copyright (C) 2015  Tricode Business Integrators B.V.
 *
 *      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 3 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, see <http://www.gnu.org/licenses/>.
 */
package nl.tricode.magnolia.blogs.templates;

import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import info.magnolia.cms.util.QueryUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.context.WebContext;
import info.magnolia.jcr.util.ContentMap;
import info.magnolia.jcr.wrapper.I18nNodeWrapper;
import info.magnolia.rendering.model.RenderingModel;
import info.magnolia.rendering.model.RenderingModelImpl;
import info.magnolia.rendering.template.RenderableDefinition;
import info.magnolia.templating.functions.TemplatingFunctions;
import nl.tricode.magnolia.blogs.BlogsNodeTypes;
import nl.tricode.magnolia.blogs.util.BlogWorkspaceUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.query.Query;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by mvdmark on 7-7-2014.
 */
public class BlogSearchRenderableDefinition<RD extends RenderableDefinition> extends RenderingModelImpl<RD> {
    private static final Logger log = LoggerFactory.getLogger(BlogSearchRenderableDefinition.class);

    private static final String SEARCH_TERM = "s";
    private static final String PAGENUMBER = "p";
    private static final String SEARCH_PROXIMITY = "~0.6";

    protected Multimap<String, String> filter;

    private String nodetype;
    private String workspace;
    private int count;
    private int numPages;

    private final TemplatingFunctions templatingFunctions;
    private final WebContext webContext = MgnlContext.getWebContext();

    private List<ContentMap> searchResults = new ArrayList<ContentMap>();

    @Inject
    public BlogSearchRenderableDefinition(Node content, RD definition, RenderingModel<?> parent,
            TemplatingFunctions templatingFunctions) {
        super(content, definition, parent);
        this.templatingFunctions = templatingFunctions;

        setWorkspace(BlogWorkspaceUtil.COLLABORATION);
        setNodetype(BlogsNodeTypes.Blog.NAME);

        filter = LinkedListMultimap.create();
        Set<String> parameters = webContext.getParameters().keySet();
        for (String parameterKey : parameters) {
            if (allowedParameters().contains(parameterKey)) {
                String[] parameterValues = webContext.getParameterValues(parameterKey);
                for (String parameterValue : parameterValues) {
                    if (StringUtils.isNotEmpty(parameterValue)) {
                        filter.get(parameterKey).add(parameterValue);
                    }
                }
            }
            webContext.remove(parameterKey);
        }
        log.debug("Running constructor BlogSearchRenderableDefinition");
    }

    @Override
    public String execute() {
        String queryString = buildQuery(getSearchPath(), true);
        log.debug(MessageFormat.format("BlogSearchRenderableDefinition Query executed: {0}", queryString));

        // Do not cache this response!
        // More info: http://documentation.magnolia-cms.com/display/DOCS/Cache+module#Cachemodule-Cacheheadernegotiation
        webContext.getResponse().setHeader("Cache-Control", "no-cache");

        if (StringUtils.isBlank(queryString)) {
            return null;
        }
        try {
            executePagedNodesQuery(queryString, getMaxResultsPerPage(), getPageNumber(), getWorkspace(),
                    getNodetype());
        } catch (Exception e) {
            log.error(MessageFormat.format("{0} caught while parsing query for search term [{1}] : {2}",
                    e.getClass().getName(), queryString, e.getMessage()), e);
        }
        return StringUtils.EMPTY;
    }

    protected String buildQuery(String path, Boolean useFilters) {
        log.debug("buildQuery path[" + path + "], useFilters [" + useFilters + "].");

        String filters = StringUtils.EMPTY;
        if (useFilters) {
            filters = getPredicate();
        }
        String query = "SELECT p.* FROM [nt:base] AS p " + "WHERE ISDESCENDANTNODE(p, '"
                + StringUtils.defaultIfEmpty(path, "/") + "') " + filters + "ORDER BY " + getOrderString();

        return query;
    }

    protected String getOrderString() {
        return "score() desc";
    }

    /**
     * Fetching paged node items.
     *
     * @param statement SQL2 statement
     * @param maxResultSize Max results returned
     * @param pageNumber paging number
     *
     * @throws javax.jcr.LoginException
     * @throws javax.jcr.RepositoryException
     */
    protected void executePagedNodesQuery(String statement, int maxResultSize, int pageNumber, String workspace,
            String nodeType) throws LoginException, RepositoryException {
        List<Node> nodeList = new ArrayList<Node>(0);
        List<Node> nodeListPaged = new ArrayList<Node>(0);
        NodeIterator items = QueryUtil.search(workspace, statement, Query.JCR_SQL2, nodeType);
        while (items.hasNext()) {
            nodeList.add(new I18nNodeWrapper(items.nextNode()));
        }
        int total = nodeList.size();

        // Paging result set
        int startRow = (maxResultSize * (pageNumber - 1));
        int newLimit = maxResultSize;
        if (total > startRow) {
            if (total < startRow + maxResultSize) {
                newLimit = total - startRow;
            }
            nodeListPaged = nodeList.subList(startRow, startRow + newLimit);
        }

        int calcNumPages = total / maxResultSize;
        if ((total % maxResultSize) > 0) {
            calcNumPages++;
        }
        // Set template model properties
        setCount(total);
        setNumPages(calcNumPages);
        setSearchResults(templatingFunctions.asContentMapList(nodeListPaged));
    }

    protected Set<String> allowedParameters() {
        return Sets.newHashSet(SEARCH_TERM, "r", PAGENUMBER);
    }

    public String getPredicate() {
        String searchTermPredicate = StringUtils.EMPTY;
        if (filter.containsKey(SEARCH_TERM)) {
            String searchString = filter.get(SEARCH_TERM).iterator().next().replaceAll("'", "''");
            searchString = searchString + SEARCH_PROXIMITY;
            searchTermPredicate = MessageFormat.format("AND contains(p.*, ''{0}'') ",
                    new Object[] { searchString });
        }
        return searchTermPredicate;
    }

    /**
     * Get searchPath content property. If not set then "/" (root) is used.
     * @return search path
     */
    protected String getSearchPath() {
        String searchPath = "/";
        try {
            if (content.hasProperty("searchPath")) {
                searchPath = templatingFunctions.nodeById(content.getProperty("searchPath").getString()).getPath();
            }
        } catch (Exception e) {
            log.info("no searchPath property set on content", e);
        }
        return searchPath;
    }

    /**
     * Get request parameter for current page.
     *
     * @return pagenumber
     */
    public int getPageNumber() {
        int pageNumber = 1;
        if (filter.containsKey(PAGENUMBER)) {
            pageNumber = Integer.parseInt(filter.get(PAGENUMBER).iterator().next());
        }
        return pageNumber;
    }

    /**
     * Get Max. results per page content property. If not set then Integer.MAX_VALUE is used.
     *
     * @return maximum results per page number
     */
    protected int getMaxResultsPerPage() {
        int maxResultsPerPage = Integer.MAX_VALUE;
        try {
            if (content.hasProperty("maxResultsPerPage")) {
                maxResultsPerPage = Integer.parseInt(content.getProperty("maxResultsPerPage").getString());
            }
        } catch (Exception e) {
            log.info("no maxResultsPerPage property set on content", e);
        }
        return maxResultsPerPage;
    }

    public String getWorkspace() {
        return workspace;
    }

    public void setWorkspace(String workspace) {
        this.workspace = workspace;
    }

    public String getNodetype() {
        return nodetype;
    }

    public void setNodetype(String nodetype) {
        this.nodetype = nodetype;
    }

    public List<ContentMap> getSearchResults() {
        log.debug("get Search Results  size[" + searchResults.size() + "]");
        return searchResults;
    }

    public void setSearchResults(List<ContentMap> searchResults) {
        this.searchResults = searchResults;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getNumPages() {
        return numPages;
    }

    public void setNumPages(int numPages) {
        this.numPages = numPages;
    }
}