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.workplace.search;
029
030import org.opencms.db.CmsUserSettings.CmsSearchResultStyle;
031import org.opencms.jsp.CmsJspActionElement;
032import org.opencms.main.CmsIllegalStateException;
033import org.opencms.main.OpenCms;
034import org.opencms.search.CmsSearchParameters;
035import org.opencms.search.I_CmsSearchIndex;
036import org.opencms.search.fields.CmsLuceneField;
037import org.opencms.search.fields.CmsSearchField;
038import org.opencms.util.CmsStringUtil;
039import org.opencms.widgets.A_CmsWidget;
040import org.opencms.widgets.CmsCalendarWidget;
041import org.opencms.widgets.CmsCheckboxWidget;
042import org.opencms.widgets.CmsInputWidget;
043import org.opencms.widgets.CmsMultiSelectWidget;
044import org.opencms.widgets.CmsSelectOnChangeReloadWidget;
045import org.opencms.widgets.CmsSelectWidget;
046import org.opencms.widgets.CmsSelectWidgetOption;
047import org.opencms.workplace.CmsDialog;
048import org.opencms.workplace.CmsWidgetDialog;
049import org.opencms.workplace.CmsWidgetDialogParameter;
050import org.opencms.workplace.CmsWorkplaceSettings;
051import org.opencms.workplace.list.A_CmsListDialog;
052import org.opencms.workplace.list.A_CmsListExplorerDialog;
053import org.opencms.workplace.list.CmsListMetadata;
054import org.opencms.workplace.tools.CmsToolDialog;
055
056import java.util.ArrayList;
057import java.util.HashMap;
058import java.util.Iterator;
059import java.util.List;
060import java.util.Map;
061
062import javax.servlet.http.HttpServletRequest;
063import javax.servlet.http.HttpServletResponse;
064import javax.servlet.jsp.PageContext;
065
066/**
067 * Provides a GUI for the workplace search feature.<p>
068 *
069 * @since 6.2.0
070 */
071public class CmsSearchDialog extends CmsWidgetDialog {
072
073    /** Localization key label infix for fields. */
074    public static final String LABEL_FIELD_INFIX = "field.";
075
076    /** the dialog type. */
077    private static final String DIALOG_TYPE = "search";
078
079    /** Defines which pages are valid for this dialog. */
080    private static final String[] PAGES = {"page1"};
081
082    /** the search data. */
083    private CmsSearchWorkplaceBean m_search;
084
085    /**
086     * Default constructor.<p>
087     *
088     * @param jsp an initialized JSP action element
089     */
090    public CmsSearchDialog(CmsJspActionElement jsp) {
091
092        super(jsp);
093    }
094
095    /**
096     * Public constructor with JSP variables.<p>
097     *
098     * @param context the JSP page context
099     * @param req the JSP request
100     * @param res the JSP response
101     */
102    public CmsSearchDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {
103
104        this(new CmsJspActionElement(context, req, res));
105    }
106
107    /**
108     * @see org.opencms.workplace.CmsWidgetDialog#actionCommit()
109     */
110    @Override
111    public void actionCommit() {
112
113        List<Throwable> errors = new ArrayList<Throwable>();
114        try {
115            Map<String, String[]> params = new HashMap<String, String[]>();
116            params.put(CmsDialog.PARAM_ACTION, new String[] {A_CmsListDialog.LIST_SELECT_PAGE});
117            params.put(A_CmsListDialog.PARAM_PAGE, new String[] {"1"});
118            params.put(CmsToolDialog.PARAM_ROOT, new String[] {"explorer"});
119            if (getSettings().getUserSettings().getWorkplaceSearchViewStyle() == CmsSearchResultStyle.STYLE_EXPLORER) {
120                getSettings().setExplorerPage(1);
121                params.put(A_CmsListExplorerDialog.PARAM_SHOW_EXPLORER, new String[] {Boolean.TRUE.toString()});
122            } else {
123                CmsSearchResultsList resultsList = new CmsSearchResultsList(getJsp());
124                CmsListMetadata metadata = resultsList.getMetadata(
125                    CmsSearchResultsList.class.getName(),
126                    resultsList.getListId());
127                boolean withExcerpts = (getSettings().getUserSettings().getWorkplaceSearchViewStyle() == CmsSearchResultStyle.STYLE_LIST_WITH_EXCERPTS);
128                if (metadata == null) {
129                    if (!withExcerpts) {
130                        // prevent the excerpts to be displayed by default
131                        params.put(CmsDialog.PARAM_ACTION, new String[] {A_CmsListDialog.LIST_INDEPENDENT_ACTION});
132                        params.put(
133                            A_CmsListDialog.PARAM_LIST_ACTION,
134                            new String[] {CmsSearchResultsList.LIST_DETAIL_EXCERPT});
135                    }
136                } else {
137                    // toggle excerpts
138                    metadata.getItemDetailDefinition(CmsSearchResultsList.LIST_DETAIL_EXCERPT).setVisible(withExcerpts);
139                }
140                params.put(A_CmsListExplorerDialog.PARAM_SHOW_EXPLORER, new String[] {Boolean.FALSE.toString()});
141            }
142            getToolManager().jspForwardTool(this, "/search/results", params);
143        } catch (Exception e) {
144            errors.add(e);
145        }
146        setCommitErrors(errors);
147    }
148
149    /**
150     * Returns the list of searchable fields used in the workplace search index.<p>
151     *
152     * @return the list of searchable fields used in the workplace search index
153     */
154    public List<CmsLuceneField> getFields() {
155
156        I_CmsSearchIndex index = getIndex();
157        List<CmsLuceneField> result = new ArrayList<CmsLuceneField>();
158        Iterator<CmsSearchField> i = index.getFieldConfiguration().getFields().iterator();
159        while (i.hasNext()) {
160            CmsLuceneField field = (CmsLuceneField)i.next();
161            if (field.isIndexed() && field.isDisplayed()) {
162                // only include indexed (ie. searchable) fields
163                result.add(field);
164            }
165        }
166        return result;
167    }
168
169    /**
170     * Creates the dialog HTML for all defined widgets of the named dialog (page).<p>
171     *
172     * This overwrites the method from the super class to create a layout variation for the widgets.<p>
173     *
174     * @param dialog the dialog (page) to get the HTML for
175     * @return the dialog HTML for all defined widgets of the named dialog (page)
176     */
177    @Override
178    protected String createDialogHtml(String dialog) {
179
180        // check if the configured search index exists
181        I_CmsSearchIndex index = getIndex();
182        if (index == null) {
183            throw new CmsIllegalStateException(
184                Messages.get().container(
185                    Messages.ERR_INDEX_INVALID_1,
186                    getSettings().getUserSettings().getWorkplaceSearchIndexName()));
187        }
188
189        StringBuffer result = new StringBuffer(1024);
190
191        // create widget table
192        result.append(createWidgetTableStart());
193
194        // show error header once if there were validation errors
195        result.append(createWidgetErrorHeader());
196
197        if (dialog.equals(PAGES[0])) {
198            // content
199            result.append(createWidgetBlockStart(key(Messages.GUI_SEARCH_QUERY_TITLE_0)));
200            result.append(createDialogRowsHtml(0, 3));
201            result.append(createWidgetBlockEnd());
202            // fields for limiting time ranges
203            // result.append(createWidgetBlockStart(key(Messages.GUI_SEARCH_TIME_RANGES_0)));
204            // result.append(createDialogRowsHtml(4, 7));
205            // result.append(createWidgetBlockEnd());
206            result.append(createWidgetBlockStart(key(Messages.GUI_SEARCH_FIELDS_TITLE_0)));
207            result.append(createDialogRowsHtml(8, 8));
208            result.append(createWidgetBlockEnd());
209        }
210        // close widget table
211        result.append(createWidgetTableEnd());
212
213        return result.toString();
214    }
215
216    /**
217     * @see org.opencms.workplace.CmsWidgetDialog#defineWidgets()
218     */
219    @Override
220    protected void defineWidgets() {
221
222        initParams();
223        addWidget(
224            new CmsWidgetDialogParameter(
225                m_search,
226                "indexName",
227                PAGES[0],
228                new CmsSelectOnChangeReloadWidget(getSortNamesIndex())));
229        addWidget(new CmsWidgetDialogParameter(m_search, "query", PAGES[0], new CmsInputWidget()));
230        addWidget(
231            new CmsWidgetDialogParameter(m_search, "sortOrder", PAGES[0], new CmsSelectWidget(getSortNamesConf())));
232        addWidget(new CmsWidgetDialogParameter(m_search, "restrictSearch", PAGES[0], new CmsCheckboxWidget()));
233        addWidget(new CmsWidgetDialogParameter(m_search, "minDateCreated", PAGES[0], new CmsCalendarWidget()));
234        addWidget(new CmsWidgetDialogParameter(m_search, "maxDateCreated", PAGES[0], new CmsCalendarWidget()));
235        addWidget(new CmsWidgetDialogParameter(m_search, "minDateLastModified", PAGES[0], new CmsCalendarWidget()));
236        addWidget(new CmsWidgetDialogParameter(m_search, "maxDateLastModified", PAGES[0], new CmsCalendarWidget()));
237        addWidget(
238            new CmsWidgetDialogParameter(m_search, "fields", PAGES[0], new CmsMultiSelectWidget(getFieldList(), true)));
239    }
240
241    /**
242     * @see org.opencms.workplace.CmsWidgetDialog#getPageArray()
243     */
244    @Override
245    protected String[] getPageArray() {
246
247        return PAGES;
248    }
249
250    /**
251     * @see org.opencms.workplace.CmsWorkplace#initMessages()
252     */
253    @Override
254    protected void initMessages() {
255
256        // add specific dialog resource bundle
257        addMessages(Messages.get().getBundleName());
258        // add default resource bundles
259        super.initMessages();
260    }
261
262    /**
263     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
264     */
265    @Override
266    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
267
268        // set the dialog type
269        setParamDialogtype(DIALOG_TYPE);
270        super.initWorkplaceRequestValues(settings, request);
271        // save the current state of the parameters (may be changed because of the widget values)
272        setDialogObject(m_search);
273    }
274
275    /**
276     * Returns a list of <code>{@link CmsSelectWidgetOption}</code> objects for field list selection.<p>
277     *
278     * @return a list of <code>{@link CmsSelectWidgetOption}</code> objects
279     */
280    private List<CmsSelectWidgetOption> getFieldList() {
281
282        List<CmsSelectWidgetOption> retVal = new ArrayList<CmsSelectWidgetOption>();
283        try {
284            Iterator<CmsLuceneField> i = getFields().iterator();
285            while (i.hasNext()) {
286                CmsLuceneField field = i.next();
287                if (isInitialCall()) {
288                    // search form is in the initial state
289                    retVal.add(
290                        new CmsSelectWidgetOption(
291                            field.getName(),
292                            true,
293                            getMacroResolver().resolveMacros(field.getDisplayName())));
294                } else {
295                    // search form is not in the initial state
296                    retVal.add(
297                        new CmsSelectWidgetOption(
298                            field.getName(),
299                            false,
300                            getMacroResolver().resolveMacros(field.getDisplayName())));
301                }
302            }
303        } catch (Exception e) {
304            // noop
305        }
306        return retVal;
307    }
308
309    /** Gets the index to use in the search.
310     *
311     * @return  the index to use in the search
312     */
313    private I_CmsSearchIndex getIndex() {
314
315        I_CmsSearchIndex index = null;
316        // get the configured index or the selected index
317        if (isInitialCall()) {
318            // the search form is in the initial state
319            // get the configured index
320            index = OpenCms.getSearchManager().getIndex(getSettings().getUserSettings().getWorkplaceSearchIndexName());
321        } else {
322            // the search form is not in the inital state, the submit button was used already or the
323            // search index was changed already
324            // get the selected index in the search dialog
325            index = OpenCms.getSearchManager().getIndex(getJsp().getRequest().getParameter("indexName.0"));
326        }
327        return index;
328    }
329
330    /**
331     * Creates the select widget configuration for the sort names.<p>
332     *
333     * @return the select widget configuration for the sort names
334     */
335    private List<CmsSelectWidgetOption> getSortNamesConf() {
336
337        List<CmsSelectWidgetOption> retVal = new ArrayList<CmsSelectWidgetOption>();
338        try {
339            String[] names = CmsSearchParameters.SORT_NAMES;
340            for (int i = 0; i < names.length; i++) {
341                retVal.add(
342                    new CmsSelectWidgetOption(
343                        names[i],
344                        (i == 0),
345                        key(A_CmsWidget.LABEL_PREFIX + names[i].toLowerCase())));
346            }
347        } catch (Exception e) {
348            // noop
349        }
350        return retVal;
351    }
352
353    /**
354     * Creates the select widget configuration for the index names.<p>
355     *
356     * @return the select widget configuration for the index names
357     */
358    private List<CmsSelectWidgetOption> getSortNamesIndex() {
359
360        List<CmsSelectWidgetOption> retVal = new ArrayList<CmsSelectWidgetOption>();
361        try {
362            List<String> names = OpenCms.getSearchManager().getIndexNames();
363            for (int i = 0; i < names.size(); i++) {
364                String indexName = names.get(i);
365                String wpIndexName = getSettings().getUserSettings().getWorkplaceSearchIndexName();
366                boolean isDefault = indexName.toLowerCase().equals(wpIndexName.toLowerCase());
367                retVal.add(new CmsSelectWidgetOption(names.get(i), isDefault, names.get(i)));
368            }
369        } catch (Exception e) {
370            // noop
371        }
372        return retVal;
373    }
374
375    /**
376     * Initializes the parameters.<p>
377     */
378    private void initParams() {
379
380        Object o;
381
382        if (CmsStringUtil.isEmpty(getParamAction()) || CmsDialog.DIALOG_INITIAL.equals(getParamAction())) {
383            // read params from config
384            o = null;
385        } else {
386            // this is not the initial call, get params from session
387            o = getDialogObject();
388        }
389        if (!(o instanceof CmsSearchWorkplaceBean)) {
390            String oldExplorerMode = getSettings().getExplorerMode();
391            getSettings().setExplorerMode(null);
392            String explorerResource = getSettings().getExplorerResource();
393            getSettings().setExplorerMode(oldExplorerMode);
394            m_search = new CmsSearchWorkplaceBean(explorerResource);
395        } else {
396            // reuse params stored in session
397            m_search = (CmsSearchWorkplaceBean)o;
398        }
399    }
400
401    /**
402     * Gets the information if the search form is in the inital state.<p>
403     *
404     * @return true, the search form is in the inital state. otherwise false
405     */
406    private boolean isInitialCall() {
407
408        // return false in case the form was submitted already or the submit button was pressed or the index was changed
409        return !(((getJsp().getRequest().getParameter(CmsDialog.PARAM_ACTION) != null)
410            && (getJsp().getRequest().getParameter(CmsDialog.PARAM_ACTION).equals(
411                CmsDialog.PARAM_ACTION_VALUE_FOR_CHANGED_INDEX)))
412            || ((getJsp().getRequest().getParameter("indexName.0")) != null));
413    }
414}