001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.search.controller;
029
030import org.opencms.file.CmsObject;
031import org.opencms.i18n.CmsEncoder;
032import org.opencms.i18n.CmsLocaleManager;
033import org.opencms.jsp.search.config.I_CmsSearchConfigurationCommon;
034import org.opencms.jsp.search.state.CmsSearchStateCommon;
035import org.opencms.jsp.search.state.I_CmsSearchStateCommon;
036import org.opencms.search.solr.CmsSolrQuery;
037import org.opencms.util.CmsRequestUtil;
038
039import java.util.Arrays;
040import java.util.HashMap;
041import java.util.Map;
042import java.util.Map.Entry;
043import java.util.TimeZone;
044import java.util.regex.Pattern;
045
046import org.apache.solr.client.solrj.util.ClientUtils;
047import org.apache.solr.common.params.CommonParams;
048
049/** Search controller for the common search options. */
050public class CmsSearchControllerCommon implements I_CmsSearchControllerCommon {
051
052    /** Value macro. */
053    private static final String MACRO_VALUE = "value";
054    /** Site root macro. */
055    private static final String MACRO_SITE_ROOT = "site_root";
056    /** Locale macro. */
057    private static final String MACRO_LOCALE = "locale";
058    /** Query macro. */
059    private static final String MACRO_QUERY = "query";
060    /** Configuration of common search options. */
061    private final I_CmsSearchConfigurationCommon m_config;
062    /** State of the common search options. */
063    private final I_CmsSearchStateCommon m_state;
064
065    /** Constructor taking the managed configuration.
066     * @param config The configuration to manage by the controller.
067     */
068    public CmsSearchControllerCommon(final I_CmsSearchConfigurationCommon config) {
069
070        m_config = config;
071        m_state = new CmsSearchStateCommon();
072    }
073
074    /**
075     * @see org.opencms.jsp.search.controller.I_CmsSearchController#addParametersForCurrentState(java.util.Map)
076     */
077    @Override
078    public void addParametersForCurrentState(final Map<String, String[]> parameters) {
079
080        if (!m_state.getQuery().isEmpty()) {
081            parameters.put(m_config.getQueryParam(), new String[] {m_state.getQuery()});
082        }
083        parameters.put(m_config.getReloadedParam(), null);
084        for (Entry<String, String> e : m_state.getAdditionalParameters().entrySet()) {
085            parameters.put(e.getKey(), new String[] {e.getValue()});
086        }
087    }
088
089    /**
090     * @see org.opencms.jsp.search.controller.I_CmsSearchController#addQueryParts(CmsSolrQuery, CmsObject)
091     */
092    @Override
093    public void addQueryParts(CmsSolrQuery query, CmsObject cms) {
094
095        String queryString = m_state.getQuery();
096        if (!m_config.getIgnoreQueryParam()) {
097            if (m_config.getEscapeQueryChars()) {
098                queryString = ClientUtils.escapeQueryChars(queryString);
099            }
100            if (queryString.isEmpty() && m_config.getSearchForEmptyQueryParam()) {
101                queryString = "*";
102            }
103            String modifiedQuery = m_config.getModifiedQuery(queryString);
104            if (modifiedQuery.startsWith("{!")) {
105                modifiedQuery = "{!tag=q " + modifiedQuery.substring(2);
106            } else {
107                modifiedQuery = "{!tag=q}" + modifiedQuery;
108            }
109            query.set("q", modifiedQuery);
110        }
111
112        if (m_config.getSolrIndex() != null) {
113            query.set("index", m_config.getSolrIndex());
114        }
115        if (m_config.getSolrCore() != null) {
116            query.set("core", m_config.getSolrCore());
117        }
118
119        if (!m_config.getExtraSolrParams().isEmpty()) {
120            String currentSiteRoot = null == cms ? null : cms.getRequestContext().getSiteRoot();
121            if ((null != currentSiteRoot) && !currentSiteRoot.endsWith("/")) {
122                currentSiteRoot = currentSiteRoot + "/";
123            }
124            String currentLocale = (null == cms
125            ? CmsLocaleManager.getDefaultLocale()
126            : cms.getRequestContext().getLocale()).toString();
127            String extraParams = m_config.getExtraSolrParams();
128            extraParams = resolveMacro(extraParams, MACRO_SITE_ROOT, currentSiteRoot);
129            extraParams = resolveMacro(extraParams, MACRO_LOCALE, currentLocale);
130            extraParams = resolveMacro(extraParams, MACRO_QUERY, queryString);
131            Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(extraParams, true, null);
132            for (String key : extraParamsMap.keySet()) {
133                for (String value : Arrays.asList(extraParamsMap.get(key))) {
134                    if (SET_VARIABLES.contains(key)) {
135                        if (key.equals(CommonParams.FL)) {
136                            query.setReturnFields(value);
137                        } else {
138                            query.set(key, value);
139                        }
140                    } else {
141                        query.add(key, value);
142                    }
143                }
144            }
145        }
146        for (String additionalParam : m_state.getAdditionalParameters().keySet()) {
147            String solrValue = m_config.getAdditionalParameters().get(additionalParam);
148            if (null != solrValue) {
149                String additionalParamString = resolveMacro(
150                    solrValue,
151                    MACRO_VALUE,
152                    CmsEncoder.encode(m_state.getAdditionalParameters().get(additionalParam), null));
153                Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(
154                    additionalParamString,
155                    true,
156                    null);
157                for (String key : extraParamsMap.keySet()) {
158                    for (String value : Arrays.asList(extraParamsMap.get(key))) {
159                        if (SET_VARIABLES.contains(key)) {
160                            query.set(key, value);
161                        } else {
162                            query.add(key, value);
163                        }
164                    }
165                }
166            }
167        }
168        // Add timezone query parameter to allow for correct date/time handling if not already present.
169        if (!query.getMap().keySet().contains("TZ")) {
170            query.add("TZ", TimeZone.getDefault().getID());
171        }
172    }
173
174    /**
175     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getConfig()
176     */
177    @Override
178    public I_CmsSearchConfigurationCommon getConfig() {
179
180        return m_config;
181    }
182
183    /**
184     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getState()
185     */
186    @Override
187    public I_CmsSearchStateCommon getState() {
188
189        return m_state;
190    }
191
192    /**
193     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateForQueryChange()
194     */
195    @Override
196    public void updateForQueryChange() {
197
198        // Nothing to do
199
200    }
201
202    /**
203     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateFromRequestParameters(java.util.Map, boolean)
204     */
205    @Override
206    public void updateFromRequestParameters(final Map<String, String[]> parameters, boolean isReloaded) {
207
208        m_state.setQuery("");
209
210        if (parameters.containsKey(m_config.getQueryParam())) {
211            final String[] queryStrings = parameters.get(m_config.getQueryParam());
212            if (queryStrings.length > 0) {
213                m_state.setQuery(queryStrings[0]);
214            }
215        }
216        if (parameters.containsKey(m_config.getLastQueryParam())) {
217            final String[] queryStrings = parameters.get(m_config.getLastQueryParam());
218            if (queryStrings.length > 0) {
219                m_state.setLastQuery(queryStrings[0]);
220            }
221        }
222        // Set state for additional query parameters
223        Map<String, String> additionalParameters = new HashMap<String, String>(
224            m_config.getAdditionalParameters().size());
225        for (String key : m_config.getAdditionalParameters().keySet()) {
226            if (parameters.containsKey(key)
227                && ((parameters.get(key).length > 0) && (parameters.get(key)[0].length() > 0))) {
228                additionalParameters.put(key, parameters.get(key)[0]);
229            }
230        }
231        m_state.setAdditionalParameters(additionalParameters);
232    }
233
234    /**
235     * Replaces the macro with the value, unless the value is <code>null</code>, then the macro is kept.
236     *
237     * @param string The String where the macros should be replaced.
238     * @param value The value used for the replacement.
239     * @param macroName The name of the macro to resolve.
240     *
241     * @return The original String with %(value) macros replaced.
242     */
243    private String resolveMacro(final String string, final String macroName, final String value) {
244
245        return null != value ? string.replaceAll("\\%\\(" + Pattern.quote(macroName) + "\\)", value) : string;
246    }
247}