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 GmbH & Co. KG, 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.tools.searchindex;
029
030import org.opencms.i18n.CmsMessages;
031import org.opencms.jsp.CmsJspActionElement;
032import org.opencms.main.CmsIllegalStateException;
033import org.opencms.main.OpenCms;
034import org.opencms.search.CmsSearch;
035import org.opencms.search.CmsSearchIndex;
036import org.opencms.search.CmsSearchParameters;
037import org.opencms.search.I_CmsSearchIndex;
038import org.opencms.search.fields.CmsLuceneField;
039import org.opencms.search.fields.CmsSearchField;
040import org.opencms.util.CmsStringUtil;
041import org.opencms.widgets.CmsCalendarWidget;
042import org.opencms.widgets.CmsCheckboxWidget;
043import org.opencms.widgets.CmsDisplayWidget;
044import org.opencms.widgets.CmsInputWidget;
045import org.opencms.widgets.CmsMultiSelectWidget;
046import org.opencms.widgets.CmsSelectWidget;
047import org.opencms.widgets.CmsSelectWidgetOption;
048import org.opencms.widgets.CmsVfsFileWidget;
049import org.opencms.workplace.CmsDialog;
050import org.opencms.workplace.CmsWidgetDialogParameter;
051import org.opencms.workplace.CmsWorkplaceSettings;
052
053import java.util.ArrayList;
054import java.util.Iterator;
055import java.util.LinkedList;
056import java.util.List;
057import java.util.Map;
058
059import javax.servlet.http.HttpServletRequest;
060import javax.servlet.http.HttpServletResponse;
061import javax.servlet.jsp.PageContext;
062
063/**
064 * A <code>{@link org.opencms.workplace.CmsWidgetDialog}</code> that performs a
065 * search on the <code>{@link org.opencms.search.CmsSearchIndex}</code> identified
066 * by request parameter
067 * <code>{@link org.opencms.workplace.tools.searchindex.A_CmsEditSearchIndexDialog#PARAM_INDEXNAME}</code>
068 * using an instance of <code>{@link org.opencms.search.CmsSearchParameters}</code>
069 * as widget object to fill.
070 * <p>
071 *
072 * @since 6.0.0
073 */
074public class CmsSearchWidgetDialog extends A_CmsEditSearchIndexDialog {
075
076    /** The request parameter for the search object. **/
077    public static final String PARAM_SEARCH_OBJECT = "searchobject";
078
079    /** The request parameter for the search parameters. **/
080    public static final String PARAM_SEARCH_PARAMS = "searchparams";
081
082    /** The search instance used with the search parameters. **/
083    protected CmsSearch m_search;
084
085    /** The search parameter instance used for storing widget values and performing search.    */
086    protected CmsSearchParameters m_searchParams;
087
088    /**
089     * Public constructor with JSP action element.<p>
090     *
091     * @param jsp an initialized JSP action element
092     */
093    public CmsSearchWidgetDialog(CmsJspActionElement jsp) {
094
095        super(jsp);
096
097    }
098
099    /**
100     * Public constructor with JSP variables.<p>
101     *
102     * @param context the JSP page context
103     * @param req the JSP request
104     * @param res the JSP response
105     */
106    public CmsSearchWidgetDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {
107
108        this(new CmsJspActionElement(context, req, res));
109
110    }
111
112    /**
113     * Overrides this action that is performed in case an element
114     * has been added to a widget parameter
115     * (<code>{@link org.opencms.workplace.CmsWidgetDialog#ACTION_ELEMENT_ADD}</code>)
116     * or removed
117     * (<code>{@link org.opencms.workplace.CmsWidgetDialog#ACTION_ELEMENT_REMOVE}</code>)
118     * from a widget parameter to additionally commit these values to the
119     * underlying lists.<p>
120     *
121     * This is necessary because this dialog performs a search for every request
122     * (not only if OK is pressed, also if a category or field is added/removed).
123     * The search directly uses the underlying Lists of categories, fields,... .
124     * More precise: The very same lists that are in the search parameter instance used
125     * for search are contained as base collections of the widget parameters.
126     * Therefore before every search the changes of categories, fields,... have to
127     * be committed here.<p>
128     *
129     * @see org.opencms.workplace.CmsWidgetDialog#actionToggleElement()
130     */
131    @Override
132    public void actionToggleElement() {
133
134        super.actionToggleElement();
135        commitWidgetValues();
136    }
137
138    /**
139     * Builds the standard javascript for submitting the dialog.<p>
140     *
141     * Overridden to allow additional validation and encoding of the
142     * search query. <p>
143     *
144     * @return the standard javascript for submitting the dialog
145     */
146    @Override
147    public String dialogScriptSubmit() {
148
149        StringBuffer html = new StringBuffer(512);
150        html.append(submitJS());
151        html.append("function submitAction(actionValue, theForm, formName) {\n");
152        html.append("\tif (theForm == null) {\n");
153        html.append("\t\ttheForm = document.forms[formName];\n");
154        html.append("\t}\n");
155        html.append("\tvar queryOK = validateQuery();\n");
156        html.append("\ttheForm." + CmsDialog.PARAM_FRAMENAME + ".value = window.name;\n");
157        html.append("\tif (queryOK) {\n");
158        html.append("\t\tif (actionValue == '" + CmsDialog.DIALOG_OK + "') {\n");
159        //        html.append("\t\t\talert('action :'+actionValue);\n");
160        html.append("\t\t\tloadingOn();\n");
161        html.append("\t\t\treturn queryOK;\n");
162        html.append("\t\t}\n");
163        html.append("\t\ttheForm." + CmsDialog.PARAM_ACTION + ".value = actionValue;\n");
164        html.append("\t\tsubmitForm(theForm);\n");
165        html.append("\t}\n");
166        html.append("\treturn queryOK;\n");
167        html.append("}\n");
168        return html.toString();
169    }
170
171    /**
172     * Returns the fields parameter value.<p>
173     *
174     * @return the fields parameter value
175     */
176    public String getFields() {
177
178        return CmsStringUtil.collectionAsString(m_searchParams.getFields(), ",");
179    }
180
181    /**
182     * Returns the creation date the resources have to have as maximum.<p>
183     *
184     * @return the creation date the resources have to have as maximum
185     */
186    public String getMaxDateCreated() {
187
188        if (m_searchParams.getMaxDateCreated() == Long.MAX_VALUE) {
189            return "";
190        }
191        return Long.toString(m_searchParams.getMaxDateCreated());
192    }
193
194    /**
195     * Returns the last modification date the resources have to have as maximum.<p>
196     *
197     * @return the last modification date the resources have to have as maximum
198     */
199    public String getMaxDateLastModified() {
200
201        if (m_searchParams.getMaxDateLastModified() == Long.MAX_VALUE) {
202            return "";
203        }
204        return Long.toString(m_searchParams.getMaxDateLastModified());
205    }
206
207    /**
208     * Returns the creation date the resources have to have as minimum.<p>
209     *
210     * @return the creation date the resources have to have as minimum
211     */
212    public String getMinDateCreated() {
213
214        if (m_searchParams.getMinDateCreated() == Long.MIN_VALUE) {
215            return "";
216        }
217        return Long.toString(m_searchParams.getMinDateCreated());
218    }
219
220    /**
221     * Returns the last modification date the resources have to have as minimum.<p>
222     *
223     * @return the last modification date the resources have to have as minimum
224     */
225    public String getMinDateLastModified() {
226
227        if (m_searchParams.getMinDateLastModified() == Long.MIN_VALUE) {
228            return "";
229        }
230        return Long.toString(m_searchParams.getMinDateLastModified());
231    }
232
233    /**
234     * Returns the list of searchable fields used in the workplace search index.<p>
235     *
236     * @return the list of searchable fields used in the workplace search index
237     */
238    public List<CmsSearchField> getSearchFields() {
239
240        I_CmsSearchIndex index = OpenCms.getSearchManager().getIndex(getParamIndexName());
241        List<CmsSearchField> result = new ArrayList<CmsSearchField>();
242        Iterator<CmsSearchField> i = index.getFieldConfiguration().getFields().iterator();
243        while (i.hasNext()) {
244            CmsLuceneField field = (CmsLuceneField)i.next();
245            if (field.isIndexed() && field.isDisplayed()) {
246                // only include indexed (ie. searchable) fields
247                result.add(field);
248            }
249        }
250        return result;
251    }
252
253    /**
254     * Sets the fields parameter value.<p>
255     *
256     * @param fields the fields parameter value to set
257     */
258    public void setFields(String fields) {
259
260        String searchPage = getJsp().getRequest().getParameter("searchPage");
261        if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchPage) && CmsStringUtil.isEmptyOrWhitespaceOnly(fields)) {
262            throw new CmsIllegalStateException(
263                org.opencms.workplace.search.Messages.get().container(
264                    org.opencms.workplace.search.Messages.ERR_VALIDATE_SEARCH_PARAMS_0));
265        }
266        m_searchParams.setFields(CmsStringUtil.splitAsList(fields, ","));
267    }
268
269    /**
270     * Sets the creation date the resources have to have as maximum.<p>
271     *
272     * @param maxCreationDate the creation date the resources have to have as maximum to set
273     */
274    public void setMaxDateCreated(String maxCreationDate) {
275
276        if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(maxCreationDate)) && (!maxCreationDate.equals("0"))) {
277            m_searchParams.setMaxDateCreated(Long.parseLong(maxCreationDate));
278        } else {
279            m_searchParams.setMaxDateCreated(Long.MAX_VALUE);
280        }
281    }
282
283    /**
284     * Sets the last modification date the resources have to have as maximum.<p>
285     *
286     * @param maxDateLastModified the last modification date the resources have to have as maximum to set
287     */
288    public void setMaxDateLastModified(String maxDateLastModified) {
289
290        if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(maxDateLastModified)) && (!maxDateLastModified.equals("0"))) {
291            m_searchParams.setMaxDateLastModified(Long.parseLong(maxDateLastModified));
292        } else {
293            m_searchParams.setMaxDateLastModified(Long.MAX_VALUE);
294        }
295    }
296
297    /**
298     * Sets the creation date the resources have to have as minimum.<p>
299     *
300     * @param minCreationDate the creation date the resources have to have as minimum to set
301     */
302    public void setMinDateCreated(String minCreationDate) {
303
304        if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(minCreationDate)) && (!minCreationDate.equals("0"))) {
305            m_searchParams.setMinDateCreated(Long.parseLong(minCreationDate));
306        } else {
307            m_searchParams.setMinDateCreated(Long.MIN_VALUE);
308        }
309    }
310
311    /**
312     * Sets the last modification date the resources have to have as minimum.<p>
313     *
314     * @param minDateLastModified the last modification date the resources have to have as minimum to set
315     */
316    public void setMinDateLastModified(String minDateLastModified) {
317
318        if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(minDateLastModified)) && (!minDateLastModified.equals("0"))) {
319            m_searchParams.setMinDateLastModified(Long.parseLong(minDateLastModified));
320        } else {
321            m_searchParams.setMinDateLastModified(Long.MIN_VALUE);
322        }
323    }
324
325    /**
326     * This dialog does not return on commit but stay for many search requests until it is
327     * exited with cancel or up in the workplace. <p>
328     *
329     * @return false always to ensure the dialog is not left
330     *
331     * @see org.opencms.workplace.CmsWidgetDialog#closeDialogOnCommit()
332     */
333    @Override
334    protected boolean closeDialogOnCommit() {
335
336        return false;
337    }
338
339    /**
340     * @see org.opencms.workplace.CmsWidgetDialog#createDialogHtml(java.lang.String)
341     */
342    @Override
343    protected String createDialogHtml(String dialog) {
344
345        StringBuffer result = new StringBuffer(1024);
346
347        result.append(createWidgetTableStart());
348        // show error header once if there were validation errors
349        result.append(createWidgetErrorHeader());
350
351        if (dialog.equals(PAGES[0])) {
352
353            // first block "Query for index...."
354            result.append(dialogBlockStart(key(Messages.GUI_LABEL_SEARCHINDEX_BLOCK_SEARCH_QUERY_0)));
355            result.append(createWidgetTableStart());
356            result.append(createDialogRowsHtml(0, 5));
357            result.append(createWidgetTableEnd());
358            result.append(dialogBlockEnd());
359
360            // 2nd block with limiting to time ranges
361            result.append(dialogBlockStart(key(Messages.GUI_LABEL_SEARCHINDEX_BLOCK_SEARCH_TIME_RANGES_0)));
362            result.append(createWidgetTableStart());
363            result.append(createDialogRowsHtml(6, 9));
364            result.append(createWidgetTableEnd());
365            result.append(dialogBlockEnd());
366
367            // 3rd block "Fields to search in"
368            result.append(dialogBlockStart(key(Messages.GUI_LABEL_SEARCHINDEX_BLOCK_SEARCH_FIELDS_0)));
369            result.append(createWidgetTableStart());
370            result.append(createDialogRowsHtml(10, 10));
371            result.append(createWidgetTableEnd());
372            result.append(dialogBlockEnd());
373        }
374
375        result.append(createWidgetTableEnd());
376
377        return result.toString();
378    }
379
380    /**
381     * Returns the html code for the default action content.<p>
382     *
383     * Overrides <code> {@link org.opencms.workplace.CmsWidgetDialog#defaultActionHtml()}</code>
384     * in order to put additional forms for page links and search results after OK - CANCEL buttons
385     * and outside the main form.<p>
386     *
387     * @return html code
388     */
389    @Override
390    protected String defaultActionHtmlContent() {
391
392        StringBuffer result = new StringBuffer(2048);
393        result.append("<form name=\"EDITOR\" id=\"EDITOR\" method=\"post\" action=\"").append(getDialogUri());
394        result.append("\" class=\"nomargin\" onsubmit=\"return submitAction('").append(DIALOG_OK).append(
395            "', null, 'EDITOR');\">\n");
396        result.append(dialogContentStart(null));
397        result.append(buildDialogForm());
398        result.append(dialogContentEnd());
399        result.append(dialogButtonsCustom());
400        result.append(paramsAsHidden());
401        if (getParamFramename() == null) {
402            result.append("\n<input type=\"hidden\" name=\"").append(PARAM_FRAMENAME).append("\" value=\"\">\n");
403        }
404
405        // add script for filtering categories
406        result.append(filterCategoryJS());
407        result.append("</form>\n");
408
409        // get search results if query was more than nothing
410        // we have to retrieve them before category search results are available because
411        // those are collected as a side effect of search iteration.
412        String searchResults = createSearchResults();
413
414        // append category search results if there...
415        result.append(createCategorySearchResultHtml());
416        result.append(searchResults);
417
418        result.append(getWidgetHtmlEnd());
419        // Normalize the previous encoded query value on client side
420        result.append(normalizePreviousQueryJS());
421
422        return result.toString();
423    }
424
425    /**
426     *
427     * @see org.opencms.workplace.CmsWidgetDialog#defineWidgets()
428     */
429    @Override
430    protected void defineWidgets() {
431
432        // initialization -> initUserObject
433        super.defineWidgets();
434        // first block "Query for search index..."
435        addWidget(new CmsWidgetDialogParameter(m_searchParams, "index", "", PAGES[0], new CmsDisplayWidget(), 1, 1));
436        addWidget(new CmsWidgetDialogParameter(m_searchParams, "query", "", PAGES[0], new CmsInputWidget(), 1, 1));
437        addWidget(
438            new CmsWidgetDialogParameter(
439                m_searchParams,
440                "sortName",
441                "",
442                PAGES[0],
443                new CmsSelectWidget(getSortWidgetConfiguration()),
444                0,
445                1));
446        addWidget(
447            new CmsWidgetDialogParameter(
448                m_searchParams.getRoots(),
449                "roots",
450                "/",
451                PAGES[0],
452                new CmsVfsFileWidget(),
453                1,
454                10));
455        addWidget(
456            new CmsWidgetDialogParameter(
457                m_searchParams.getCategories(),
458                "categories",
459                "",
460                PAGES[0],
461                new CmsInputWidget(),
462                0,
463                6));
464
465        addWidget(new CmsWidgetDialogParameter(m_searchParams, "calculateCategories", new CmsCheckboxWidget()));
466
467        // 2nd block with limiting to time ranges
468        addWidget(new CmsWidgetDialogParameter(this, "minDateCreated", PAGES[0], new CmsCalendarWidget()));
469        addWidget(new CmsWidgetDialogParameter(this, "maxDateCreated", PAGES[0], new CmsCalendarWidget()));
470        addWidget(new CmsWidgetDialogParameter(this, "minDateLastModified", PAGES[0], new CmsCalendarWidget()));
471        addWidget(new CmsWidgetDialogParameter(this, "maxDateLastModified", PAGES[0], new CmsCalendarWidget()));
472
473        // 3rd block "fields to search in"
474        addWidget(
475            new CmsWidgetDialogParameter(this, "fields", PAGES[0], new CmsMultiSelectWidget(getFieldList(), true)));
476    }
477
478    /**
479     * Overridden to additionally get a hold on the widget object of type
480     * <code>{@link CmsSearchParameters}</code>.<p>
481     *
482     * @see org.opencms.workplace.tools.searchindex.A_CmsEditSearchIndexDialog#initUserObject()
483     */
484    @SuppressWarnings("rawtypes")
485    @Override
486    protected void initUserObject() {
487
488        super.initUserObject();
489        Object o = getDialogObject();
490        if (o == null) {
491            m_searchParams = new CmsSearchParameters();
492            // implant a hook upon modifications of the list
493            // this will set the search page to 1 if a restriction to the set of categories is performed
494            m_searchParams.setCategories(new CmsHookListSearchCategory(m_searchParams, m_searchParams.getCategories()));
495            m_search = new CmsSearch();
496        } else {
497            Map dialogObject = (Map)o;
498            m_searchParams = (CmsSearchParameters)dialogObject.get(PARAM_SEARCH_PARAMS);
499            if (m_searchParams == null) {
500                m_searchParams = new CmsSearchParameters();
501            }
502            m_search = (CmsSearch)dialogObject.get(PARAM_SEARCH_OBJECT);
503            if (m_search == null) {
504                m_search = new CmsSearch();
505            }
506        }
507        m_searchParams.setSearchIndex((CmsSearchIndex)getSearchIndexIndex());
508    }
509
510    /**
511     * Additionally saves <code>{@link #PARAM_SEARCH_PARAMS}</code> to the dialog object map.<p>
512     *
513     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
514     */
515    @SuppressWarnings({"unchecked", "rawtypes"})
516    @Override
517    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
518
519        super.initWorkplaceRequestValues(settings, request);
520        Map dialogMap = (Map)getDialogObject();
521        if (dialogMap != null) {
522            dialogMap.put(PARAM_SEARCH_PARAMS, m_searchParams);
523            dialogMap.put(PARAM_SEARCH_OBJECT, m_search);
524        }
525    }
526
527    /**
528     * Returns the hmtl for the category search results.<p>
529     *
530     * Note that a valid search (<code>{@link CmsSearch#getSearchResult()}</code> with
531     * correct settings and inited) has to be triggered before this call or an empty
532     * String will be returned. <p>
533     *
534     * @return the hmtl for the category search results
535     */
536    private String createCategorySearchResultHtml() {
537
538        StringBuffer result = new StringBuffer();
539        if (m_searchParams.isCalculateCategories()) {
540            // trigger calculation of categories, even if we don't need search results
541            // this is cached unless more set operation on CmsSearch are performed
542            Map<String, Integer> categoryMap = m_search.getSearchResultCategories();
543            if (categoryMap != null) {
544                result.append(dialogContentStart(null));
545                result.append(result.append(createWidgetTableStart()));
546                // first block "Query for index...."
547                result.append(
548                    dialogBlockStart(
549                        key(
550                            Messages.GUI_LABEL_SEARCHINDEX_BLOCK_SEARCH_CATEGORIES_1,
551                            new Object[] {m_searchParams.getQuery()})));
552                result.append(createWidgetTableStart());
553
554                // categories:
555                result.append("\n<p>\n");
556                for (Map.Entry<String, Integer> entry : categoryMap.entrySet()) {
557                    result.append("  ").append("<a class=\"searchcategory\" href=\"#\" onClick=\"filterCategory('");
558                    result.append(entry.getKey()).append("')\")>");
559                    result.append(entry.getKey());
560                    result.append("</a> : ");
561                    result.append(entry.getValue()).append("<br>\n");
562                }
563                result.append("</p>\n");
564
565                result.append(createWidgetTableEnd());
566                result.append(dialogBlockEnd());
567                result.append(createWidgetTableEnd());
568                result.append(dialogContentEnd());
569            }
570        }
571        return result.toString();
572    }
573
574    /**
575     * Returns the HTML for the search results.<p>
576     *
577     * @return the HTML for the search results
578     */
579    private String createSearchResults() {
580
581        String query = m_searchParams.getQuery();
582        StringBuffer result = new StringBuffer();
583        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(query) && (query.length() > 3)) {
584            CmsSearchResultView resultView = new CmsSearchResultView(getJsp());
585            // proprietary workplace admin link for pagelinks of search:
586            resultView.setSearchRessourceUrl(
587                getJsp().link(
588                    "/system/workplace/views/admin/admin-main.jsp?path=/searchindex/singleindex/search&indexname="
589                        + getSearchIndexIndex().getName()));
590            m_search.init(getCms());
591
592            // custom parameters (non-widget controlled)
593            // these are from generated search page links
594            String page = getJsp().getRequest().getParameter("searchPage");
595            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(page)) {
596                m_searchParams.setSearchPage(Integer.parseInt(page));
597            }
598            String categories = getJsp().getRequest().getParameter("searchCategories");
599            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(categories)) {
600                m_searchParams.setCategories(CmsStringUtil.splitAsList(categories, ','));
601            }
602
603            String searchRoots = getJsp().getRequest().getParameter("searchRoots");
604            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(searchRoots)) {
605                m_searchParams.setSearchRoots(searchRoots);
606            }
607            m_search.setParameters(m_searchParams);
608            result.append("<div style=\"padding:12px;\">\n").append(resultView.displaySearchResult(m_search)).append(
609                "\n</div>\n");
610        } else {
611            // Just don't perform search
612        }
613        return result.toString();
614    }
615
616    /**
617     * Generates the JavaScript to filter categories.<p>
618     *
619     * @return the JavaScript
620     */
621    private String filterCategoryJS() {
622
623        StringBuffer result = new StringBuffer();
624        // fool the widget with a hidden categories input field
625        result.append("<input name=\"dummysearchcategory\" id=\"dummysearchcategory\" type=\"hidden\" value=\"\">\n");
626        result.append("<input name=\"dummysearchpage\" id=\"dummysearchpage\" type=\"hidden\" value=\"\">\n");
627        // delete all other chosen "cateogries.x" values and put a value here
628        result.append("<script >\n");
629        result.append("  function filterCategory(category) {\n");
630        result.append("    var searchform = document.forms['EDITOR'];\n");
631        result.append("    var inputFields = searchform.elements;\n");
632        result.append("    for (var i=0; i<inputFields.length; i++) {\n");
633        result.append("      if(inputFields[i].name != null && inputFields[i].name.indexOf('categories') != -1) {\n");
634        result.append("        inputFields[i].value='';\n");
635        result.append("        inputFields[i].name='invalidsearchcategory';\n");
636        result.append("      }\n");
637        result.append("    }\n");
638        // if we found a category field use it, if not, set the name of our
639        // fooling field to "categories.0" or the widget filling will fail
640        result.append("    var categoryField = inputFields['dummysearchcategory'];\n");
641        result.append("    categoryField.name = 'categories.0';\n");
642        result.append("    categoryField.value = category;\n");
643        // additionally set searchpage to zero because filtered results may / will be smaller
644        result.append("    inputFields['dummysearchpage'].value = '0';\n");
645        result.append("    inputFields['dummysearchpage'].name = 'searchpage.0';\n");
646        result.append("    validateQuery();\n");
647        result.append("    searchform.submit();\n");
648        result.append("  }\n");
649        result.append("</script>\n");
650        return result.toString();
651    }
652
653    /**
654     * Returns a list of <code>{@link CmsSelectWidgetOption}</code> objects for field list selection.<p>
655     *
656     * @return a list of <code>{@link CmsSelectWidgetOption}</code> objects
657     */
658    private List<CmsSelectWidgetOption> getFieldList() {
659
660        List<CmsSelectWidgetOption> retVal = new ArrayList<CmsSelectWidgetOption>();
661        try {
662            Iterator<CmsSearchField> i = getSearchFields().iterator();
663            while (i.hasNext()) {
664                CmsLuceneField field = (CmsLuceneField)i.next();
665                retVal.add(
666                    new CmsSelectWidgetOption(
667                        field.getName(),
668                        true,
669                        getMacroResolver().resolveMacros(field.getDisplayName())));
670            }
671        } catch (Exception e) {
672            // noop
673        }
674        return retVal;
675    }
676
677    /**
678     * Returns the different select options for sort search result criteria. <p>
679     *
680     * @return the different select options for sort search result criteria
681     */
682    private List<CmsSelectWidgetOption> getSortWidgetConfiguration() {
683
684        List<CmsSelectWidgetOption> result = new LinkedList<CmsSelectWidgetOption>();
685        CmsMessages messages = Messages.get().getBundle(getLocale());
686        result.add(
687            new CmsSelectWidgetOption(
688                CmsSearchParameters.SORT_NAMES[0],
689                true,
690                messages.key(Messages.GUI_SELECT_LABEL_SEARCH_SORT_SCORE_0)));
691        result.add(
692            new CmsSelectWidgetOption(
693                CmsSearchParameters.SORT_NAMES[1],
694                false,
695                messages.key(Messages.GUI_SELECT_LABEL_SEARCH_SORT_DATE_CREATED_0)));
696        result.add(
697            new CmsSelectWidgetOption(
698                CmsSearchParameters.SORT_NAMES[2],
699                false,
700                messages.key(Messages.GUI_SELECT_LABEL_SEARCH_SORT_DATE_LAST_MODIFIED_0)));
701        result.add(
702            new CmsSelectWidgetOption(
703                CmsSearchParameters.SORT_NAMES[3],
704                false,
705                messages.key(Messages.GUI_SELECT_LABEL_SEARCH_SORT_TITLE_0)));
706        return result;
707    }
708
709    /**
710     * Normalizes the JavaScript for the previous search query.<p>
711     *
712     * @return the normalized JavaScript
713     */
714    private String normalizePreviousQueryJS() {
715
716        StringBuffer result = new StringBuffer();
717        result.append("<script >\n");
718        result.append("  function normalizeQueryValue() {\n");
719        result.append("    var searchform = document.forms['EDITOR'];\n");
720        result.append("    var query = searchform.elements['query.0'].value;\n");
721        result.append("    query = decodeURI(query);\n");
722        result.append("    searchform.elements['query.0'].value = query;\n");
723        result.append("    return true;\n");
724        result.append("  }\n");
725        result.append("  normalizeQueryValue();\n");
726        result.append("</script>\n");
727        return result.toString();
728    }
729
730    /**
731     * Returns the JavaScript for submitting the search form.<p>
732     *
733     * @return the JavaScript for submitting the search form
734     */
735    private String submitJS() {
736
737        StringBuffer result = new StringBuffer();
738        result.append("  function validateQuery() {\n");
739        result.append("    var searchform = document.getElementById(\"EDITOR\");\n");
740        result.append("    var query = searchform.elements['query.0'].value;\n");
741        result.append("    searchform.elements['query.0'].value = query;\n");
742        result.append("    return true;\n");
743        result.append("  }\n");
744        return result.toString();
745    }
746}