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            Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(
128                m_config.getExtraSolrParams(),
129                true,
130                null);
131            for (String key : extraParamsMap.keySet()) {
132                for (String value : Arrays.asList(extraParamsMap.get(key))) {
133                    value = resolveMacro(value, MACRO_SITE_ROOT, currentSiteRoot);
134                    value = resolveMacro(value, MACRO_LOCALE, currentLocale);
135                    value = resolveMacro(value, MACRO_QUERY, queryString);
136                    if (SET_VARIABLES.contains(key)) {
137                        if (key.equals(CommonParams.FL)) {
138                            query.setReturnFields(value);
139                        } else {
140                            query.set(key, value);
141                        }
142                    } else {
143                        query.add(key, value);
144                    }
145                }
146            }
147        }
148        for (String additionalParam : m_state.getAdditionalParameters().keySet()) {
149            String solrValue = m_config.getAdditionalParameters().get(additionalParam);
150            if (null != solrValue) {
151                String additionalParamString = resolveMacro(
152                    solrValue,
153                    MACRO_VALUE,
154                    CmsEncoder.encode(m_state.getAdditionalParameters().get(additionalParam), null));
155                Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(
156                    additionalParamString,
157                    true,
158                    null);
159                for (String key : extraParamsMap.keySet()) {
160                    for (String value : Arrays.asList(extraParamsMap.get(key))) {
161                        if (SET_VARIABLES.contains(key)) {
162                            query.set(key, value);
163                        } else {
164                            query.add(key, value);
165                        }
166                    }
167                }
168            }
169        }
170        // Add timezone query parameter to allow for correct date/time handling if not already present.
171        if (!query.getMap().keySet().contains("TZ")) {
172            query.add("TZ", TimeZone.getDefault().getID());
173        }
174    }
175
176    /**
177     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getConfig()
178     */
179    @Override
180    public I_CmsSearchConfigurationCommon getConfig() {
181
182        return m_config;
183    }
184
185    /**
186     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getState()
187     */
188    @Override
189    public I_CmsSearchStateCommon getState() {
190
191        return m_state;
192    }
193
194    /**
195     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateForQueryChange()
196     */
197    @Override
198    public void updateForQueryChange() {
199
200        // Nothing to do
201
202    }
203
204    /**
205     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateFromRequestParameters(java.util.Map, boolean)
206     */
207    @Override
208    public void updateFromRequestParameters(final Map<String, String[]> parameters, boolean isReloaded) {
209
210        m_state.setQuery("");
211
212        if (parameters.containsKey(m_config.getQueryParam())) {
213            final String[] queryStrings = parameters.get(m_config.getQueryParam());
214            if (queryStrings.length > 0) {
215                m_state.setQuery(queryStrings[0]);
216            }
217        }
218        if (parameters.containsKey(m_config.getLastQueryParam())) {
219            final String[] queryStrings = parameters.get(m_config.getLastQueryParam());
220            if (queryStrings.length > 0) {
221                m_state.setLastQuery(queryStrings[0]);
222            }
223        }
224        // Set state for additional query parameters
225        Map<String, String> additionalParameters = new HashMap<String, String>(
226            m_config.getAdditionalParameters().size());
227        for (String key : m_config.getAdditionalParameters().keySet()) {
228            if (parameters.containsKey(key)
229                && ((parameters.get(key).length > 0) && (parameters.get(key)[0].length() > 0))) {
230                additionalParameters.put(key, parameters.get(key)[0]);
231            }
232        }
233        m_state.setAdditionalParameters(additionalParameters);
234    }
235
236    /**
237     * Replaces the macro with the value, unless the value is <code>null</code>, then the macro is kept.
238     *
239     * @param string The String where the macros should be replaced.
240     * @param value The value used for the replacement.
241     * @param macroName The name of the macro to resolve.
242     *
243     * @return The original String with %(value) macros replaced.
244     */
245    private String resolveMacro(final String string, final String macroName, final String value) {
246
247        return null != value ? string.replaceAll("\\%\\(" + Pattern.quote(macroName) + "\\)", value) : string;
248    }
249}