001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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;
044
045import org.apache.solr.client.solrj.util.ClientUtils;
046import org.apache.solr.common.params.CommonParams;
047
048/** Search controller for the common search options. */
049public class CmsSearchControllerCommon implements I_CmsSearchControllerCommon {
050
051    /** Value macro. */
052    private static final String MACRO_VALUE = "value";
053    /** Site root macro. */
054    private static final String MACRO_SITE_ROOT = "site_root";
055    /** Locale macro. */
056    private static final String MACRO_LOCALE = "locale";
057    /** Query macro. */
058    private static final String MACRO_QUERY = "query";
059    /** Configuration of common search options. */
060    private final I_CmsSearchConfigurationCommon m_config;
061    /** State of the common search options. */
062    private final I_CmsSearchStateCommon m_state;
063
064    /** Constructor taking the managed configuration.
065     * @param config The configuration to manage by the controller.
066     */
067    public CmsSearchControllerCommon(final I_CmsSearchConfigurationCommon config) {
068
069        m_config = config;
070        m_state = new CmsSearchStateCommon();
071    }
072
073    /**
074     * @see org.opencms.jsp.search.controller.I_CmsSearchController#addParametersForCurrentState(java.util.Map)
075     */
076    @Override
077    public void addParametersForCurrentState(final Map<String, String[]> parameters) {
078
079        if (!m_state.getQuery().isEmpty()) {
080            parameters.put(m_config.getQueryParam(), new String[] {m_state.getQuery()});
081        }
082        parameters.put(m_config.getReloadedParam(), null);
083        for (Entry<String, String> e : m_state.getAdditionalParameters().entrySet()) {
084            parameters.put(e.getKey(), new String[] {e.getValue()});
085        }
086    }
087
088    /**
089     * @see org.opencms.jsp.search.controller.I_CmsSearchController#addQueryParts(CmsSolrQuery, CmsObject)
090     */
091    @Override
092    public void addQueryParts(CmsSolrQuery query, CmsObject cms) {
093
094        String queryString = m_state.getQuery();
095        if (!m_config.getIgnoreQueryParam()) {
096            if (m_config.getEscapeQueryChars()) {
097                queryString = ClientUtils.escapeQueryChars(queryString);
098            }
099            if (queryString.isEmpty() && m_config.getSearchForEmptyQueryParam()) {
100                queryString = "*";
101            }
102            String modifiedQuery = m_config.getModifiedQuery(queryString);
103            if (modifiedQuery.startsWith("{!")) {
104                modifiedQuery = "{!tag=q " + modifiedQuery.substring(2);
105            } else {
106                modifiedQuery = "{!tag=q}" + modifiedQuery;
107            }
108            query.set("q", modifiedQuery);
109        }
110
111        if (m_config.getSolrIndex() != null) {
112            query.set("index", m_config.getSolrIndex());
113        }
114        if (m_config.getSolrCore() != null) {
115            query.set("core", m_config.getSolrCore());
116        }
117
118        if (!m_config.getExtraSolrParams().isEmpty()) {
119            String currentSiteRoot = null == cms ? null : cms.getRequestContext().getSiteRoot();
120            if ((null != currentSiteRoot) && !currentSiteRoot.endsWith("/")) {
121                currentSiteRoot = currentSiteRoot + "/";
122            }
123            String currentLocale = (null == cms
124            ? CmsLocaleManager.getDefaultLocale()
125            : cms.getRequestContext().getLocale()).toString();
126            String extraParams = m_config.getExtraSolrParams();
127            extraParams = resolveMacro(extraParams, MACRO_SITE_ROOT, currentSiteRoot);
128            extraParams = resolveMacro(extraParams, MACRO_LOCALE, currentLocale);
129            extraParams = resolveMacro(extraParams, MACRO_QUERY, queryString);
130            Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(extraParams, true, null);
131            for (String key : extraParamsMap.keySet()) {
132                for (String value : Arrays.asList(extraParamsMap.get(key))) {
133                    if (SET_VARIABLES.contains(key)) {
134                        if (key.equals(CommonParams.FL)) {
135                            query.setReturnFields(value);
136                        } else {
137                            query.set(key, value);
138                        }
139                    } else {
140                        query.add(key, value);
141                    }
142                }
143            }
144        }
145        for (String additionalParam : m_state.getAdditionalParameters().keySet()) {
146            String solrValue = m_config.getAdditionalParameters().get(additionalParam);
147            if (null != solrValue) {
148                String additionalParamString = resolveMacro(
149                    solrValue,
150                    MACRO_VALUE,
151                    CmsEncoder.encode(m_state.getAdditionalParameters().get(additionalParam), null));
152                Map<String, String[]> extraParamsMap = CmsRequestUtil.createParameterMap(
153                    additionalParamString,
154                    true,
155                    null);
156                for (String key : extraParamsMap.keySet()) {
157                    for (String value : Arrays.asList(extraParamsMap.get(key))) {
158                        if (SET_VARIABLES.contains(key)) {
159                            query.set(key, value);
160                        } else {
161                            query.add(key, value);
162                        }
163                    }
164                }
165            }
166        }
167        // Add timezone query parameter to allow for correct date/time handling if not already present.
168        if (!query.getMap().keySet().contains("TZ")) {
169            query.add("TZ", TimeZone.getDefault().getID());
170        }
171    }
172
173    /**
174     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getConfig()
175     */
176    @Override
177    public I_CmsSearchConfigurationCommon getConfig() {
178
179        return m_config;
180    }
181
182    /**
183     * @see org.opencms.jsp.search.controller.I_CmsSearchControllerCommon#getState()
184     */
185    @Override
186    public I_CmsSearchStateCommon getState() {
187
188        return m_state;
189    }
190
191    /**
192     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateForQueryChange()
193     */
194    @Override
195    public void updateForQueryChange() {
196
197        // Nothing to do
198
199    }
200
201    /**
202     * @see org.opencms.jsp.search.controller.I_CmsSearchController#updateFromRequestParameters(java.util.Map, boolean)
203     */
204    @Override
205    public void updateFromRequestParameters(final Map<String, String[]> parameters, boolean isReloaded) {
206
207        m_state.setQuery("");
208
209        if (parameters.containsKey(m_config.getQueryParam())) {
210            final String[] queryStrings = parameters.get(m_config.getQueryParam());
211            if (queryStrings.length > 0) {
212                m_state.setQuery(queryStrings[0]);
213            }
214        }
215        if (parameters.containsKey(m_config.getLastQueryParam())) {
216            final String[] queryStrings = parameters.get(m_config.getLastQueryParam());
217            if (queryStrings.length > 0) {
218                m_state.setLastQuery(queryStrings[0]);
219            }
220        }
221        // Set state for additional query parameters
222        Map<String, String> additionalParameters = new HashMap<String, String>(
223            m_config.getAdditionalParameters().size());
224        for (String key : m_config.getAdditionalParameters().keySet()) {
225            if (parameters.containsKey(key)
226                && ((parameters.get(key).length > 0) && (parameters.get(key)[0].length() > 0))) {
227                additionalParameters.put(key, parameters.get(key)[0]);
228            }
229        }
230        m_state.setAdditionalParameters(additionalParameters);
231    }
232
233    /**
234     * Replaces the macro with the value, unless the value is <code>null</code>, then the macro is kept.
235     *
236     * @param string The String where the macros should be replaced.
237     * @param value The value used for the replacement.
238     * @param macroName The name of the macro to resolve.
239     *
240     * @return The original String with %(value) macros replaced.
241     */
242    private String resolveMacro(final String string, final String macroName, final String value) {
243
244        return null != value ? string.replace("%(" + macroName + ")", value) : string;
245    }
246}