fr.paris.lutece.portal.web.search.SearchApp.java Source code

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.portal.web.search.SearchApp.java

Source

/*
 * Copyright (c) 2002-2013, Mairie de Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.portal.web.search;

import fr.paris.lutece.portal.business.search.SearchParameterHome;
import fr.paris.lutece.portal.service.html.EncodingService;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.message.SiteMessage;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.message.SiteMessageService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.search.ISponsoredLinksSearchService;
import fr.paris.lutece.portal.service.search.QueryEvent;
import fr.paris.lutece.portal.service.search.QueryListenersService;
import fr.paris.lutece.portal.service.search.SearchEngine;
import fr.paris.lutece.portal.service.search.SearchResult;
import fr.paris.lutece.portal.service.search.SearchService;
import fr.paris.lutece.portal.service.search.SponsoredLinksSearchService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.web.xpages.XPage;
import fr.paris.lutece.portal.web.xpages.XPageApplication;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.html.Paginator;
import fr.paris.lutece.util.http.SecurityUtil;
import fr.paris.lutece.util.url.UrlItem;

import org.apache.commons.lang.StringUtils;

import java.io.UnsupportedEncodingException;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

/**
 * This class provides search results pages.
 */
public class SearchApp implements XPageApplication {
    /**
     * Serial version UID
     */
    private static final long serialVersionUID = -9101183157272256639L;

    ////////////////////////////////////////////////////////////////////////////
    // Constants
    private static final String BEAN_SEARCH_ENGINE = "searchEngine";
    private static final String TEMPLATE_RESULTS = "skin/search/search_results.html";
    private static final String PROPERTY_SEARCH_PAGE_URL = "search.pageSearch.baseUrl";
    private static final String PROPERTY_RESULTS_PER_PAGE = "search.nb.docs.per.page";
    private static final String PROPERTY_PATH_LABEL = "portal.search.search_results.pathLabel";
    private static final String PROPERTY_PAGE_TITLE = "portal.search.search_results.pageTitle";
    private static final String MESSAGE_INVALID_SEARCH_TERMS = "portal.search.message.invalidSearchTerms";
    private static final String MESSAGE_ENCODING_ERROR = "portal.search.message.encodingError";
    private static final String MESSAGE_QUERY_NULL = "portal.search.message.queryNull";
    private static final String DEFAULT_RESULTS_PER_PAGE = "10";
    private static final String DEFAULT_PAGE_INDEX = "1";
    private static final String PARAMETER_PAGE_INDEX = "page_index";
    private static final String PARAMETER_NB_ITEMS_PER_PAGE = "items_per_page";
    private static final String PARAMETER_QUERY = "query";
    private static final String PARAMETER_TAG_FILTER = "tag_filter";
    private static final String MARK_RESULTS_LIST = "results_list";
    private static final String MARK_QUERY = "query";
    private static final String MARK_PAGINATOR = "paginator";
    private static final String MARK_NB_ITEMS_PER_PAGE = "nb_items_per_page";
    private static final String MARK_ERROR = "error";
    private static final String MARK_SPONSOREDLINKS_SET = "sponsoredlinks_set";
    private static final String MARK_LIST_TYPE_AND_LINK = "list_type_and_link";
    private static final String PROPERTY_ENCODE_URI = "search.encode.uri";
    private static final String PROPERTY_ENCODE_URI_ENCODING = "search.encode.uri.encoding";
    private static final String DEFAULT_URI_ENCODING = "ISO-8859-1";
    private static final String CONSTANT_ENCODING_UTF8 = "UTF-8";
    private static final String CONSTANT_HTTP_METHOD_GET = "GET";
    private static final boolean DEFAULT_ENCODE_URI = false;

    /**
     * Returns search results
     *
     * @param request The HTTP request.
     * @param nMode The current mode.
     * @param plugin The plugin
     * @return The HTML code of the page.
     * @throws SiteMessageException If an error occurs
     */
    @Override
    public XPage getPage(HttpServletRequest request, int nMode, Plugin plugin) throws SiteMessageException {
        XPage page = new XPage();
        String strQuery = request.getParameter(PARAMETER_QUERY);
        String strTagFilter = request.getParameter(PARAMETER_TAG_FILTER);

        String strEncoding = AppPropertiesService.getProperty(PROPERTY_ENCODE_URI_ENCODING, DEFAULT_URI_ENCODING);

        if (StringUtils.equalsIgnoreCase(CONSTANT_HTTP_METHOD_GET, request.getMethod())
                && !StringUtils.equalsIgnoreCase(strEncoding, CONSTANT_ENCODING_UTF8)) {
            try {
                if (StringUtils.isNotBlank(strQuery)) {
                    strQuery = new String(strQuery.getBytes(strEncoding), CONSTANT_ENCODING_UTF8);
                }

                if (StringUtils.isNotBlank(strTagFilter)) {
                    strTagFilter = new String(strTagFilter.getBytes(strEncoding), CONSTANT_ENCODING_UTF8);
                }
            } catch (UnsupportedEncodingException e) {
                AppLogService.error(e.getMessage(), e);
            }
        }

        if (StringUtils.isNotEmpty(strTagFilter)) {
            strQuery = strTagFilter;
        }

        boolean bEncodeUri = Boolean.parseBoolean(
                AppPropertiesService.getProperty(PROPERTY_ENCODE_URI, Boolean.toString(DEFAULT_ENCODE_URI)));

        String strSearchPageUrl = AppPropertiesService.getProperty(PROPERTY_SEARCH_PAGE_URL);
        String strError = "";
        Locale locale = request.getLocale();

        // Check XSS characters
        if ((strQuery != null) && (SecurityUtil.containsXssCharacters(request, strQuery))) {
            strError = I18nService.getLocalizedString(MESSAGE_INVALID_SEARCH_TERMS, locale);
            strQuery = "";
        }

        String strNbItemPerPage = request.getParameter(PARAMETER_NB_ITEMS_PER_PAGE);
        String strDefaultNbItemPerPage = AppPropertiesService.getProperty(PROPERTY_RESULTS_PER_PAGE,
                DEFAULT_RESULTS_PER_PAGE);
        strNbItemPerPage = (strNbItemPerPage != null) ? strNbItemPerPage : strDefaultNbItemPerPage;

        int nNbItemsPerPage = Integer.parseInt(strNbItemPerPage);
        String strCurrentPageIndex = request.getParameter(PARAMETER_PAGE_INDEX);
        strCurrentPageIndex = (strCurrentPageIndex != null) ? strCurrentPageIndex : DEFAULT_PAGE_INDEX;

        SearchEngine engine = (SearchEngine) SpringContextService.getBean(BEAN_SEARCH_ENGINE);
        List<SearchResult> listResults = engine.getSearchResults(strQuery, request);

        // The page should not be added to the cache

        // Notify results infos to QueryEventListeners 
        notifyQueryListeners(strQuery, listResults.size(), request);

        UrlItem url = new UrlItem(strSearchPageUrl);
        String strQueryForPaginator = strQuery;

        if (bEncodeUri) {
            strQueryForPaginator = encodeUrl(request, strQuery);
        }

        if (StringUtils.isNotBlank(strTagFilter)) {
            strQuery = "";
        }

        url.addParameter(PARAMETER_QUERY, strQueryForPaginator);
        url.addParameter(PARAMETER_NB_ITEMS_PER_PAGE, nNbItemsPerPage);

        Paginator<SearchResult> paginator = new Paginator<SearchResult>(listResults, nNbItemsPerPage, url.getUrl(),
                PARAMETER_PAGE_INDEX, strCurrentPageIndex);

        Map<String, Object> model = new HashMap<String, Object>();
        model.put(MARK_RESULTS_LIST, paginator.getPageItems());
        model.put(MARK_QUERY, strQuery);
        model.put(MARK_PAGINATOR, paginator);
        model.put(MARK_NB_ITEMS_PER_PAGE, strNbItemPerPage);
        model.put(MARK_ERROR, strError);

        ISponsoredLinksSearchService sponsoredLinksService = new SponsoredLinksSearchService();

        if (sponsoredLinksService.isAvailable()) {
            model.put(MARK_SPONSOREDLINKS_SET, sponsoredLinksService.getHtmlCode(strQuery, locale));
        }

        model.put(MARK_LIST_TYPE_AND_LINK, SearchService.getSearchTypesAndLinks());
        model.putAll(SearchParameterHome.findAll());

        HtmlTemplate template = AppTemplateService.getTemplate(TEMPLATE_RESULTS, locale, model);

        page.setPathLabel(I18nService.getLocalizedString(PROPERTY_PATH_LABEL, locale));
        page.setTitle(I18nService.getLocalizedString(PROPERTY_PAGE_TITLE, locale));
        page.setContent(template.getHtml());

        return page;
    }

    /**
     * Encode an url string
     * @param request The HTTP request
     * @param strSource The string to encode
     * @return The encoded string
     * @throws SiteMessageException If an error occurs
     */
    public static String encodeUrl(HttpServletRequest request, String strSource) throws SiteMessageException {
        if (strSource == null) {
            SiteMessageService.setMessage(request, MESSAGE_QUERY_NULL, SiteMessage.TYPE_ERROR);

            return null;
        }

        String strEncoded = EncodingService.encodeUrl(strSource, PROPERTY_ENCODE_URI_ENCODING,
                DEFAULT_URI_ENCODING);

        if (StringUtils.isBlank(strEncoded) && StringUtils.isNotBlank(strSource)) {
            SiteMessageService.setMessage(request, MESSAGE_ENCODING_ERROR, SiteMessage.TYPE_ERROR);
        }

        return strEncoded;
    }

    /**
     * Notify all query Listeners
     * @param strQuery The query
     * @param nResultsCount The results count
     * @param request The request
     */
    private void notifyQueryListeners(String strQuery, int nResultsCount, HttpServletRequest request) {
        QueryEvent event = new QueryEvent();
        event.setQuery(strQuery);
        event.setResultsCount(nResultsCount);
        event.setRequest(request);
        QueryListenersService.getInstance().notifyListeners(event);
    }
}