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.search.galleries;
029
030import org.opencms.ade.configuration.CmsFunctionAvailability;
031import org.opencms.ade.galleries.shared.CmsGallerySearchScope;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsPropertyDefinition;
034import org.opencms.file.CmsResource;
035import org.opencms.file.types.CmsResourceTypeFunctionConfig;
036import org.opencms.i18n.CmsLocaleManager;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.search.A_CmsSearchIndex;
040import org.opencms.search.CmsSearchUtil;
041import org.opencms.search.fields.CmsSearchField;
042import org.opencms.search.fields.CmsSearchFieldConfiguration;
043import org.opencms.search.solr.CmsSolrQuery;
044import org.opencms.util.CmsPair;
045import org.opencms.util.CmsUUID;
046import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
047
048import java.util.ArrayList;
049import java.util.Arrays;
050import java.util.Collection;
051import java.util.Collections;
052import java.util.List;
053import java.util.Locale;
054import java.util.Set;
055import java.util.stream.Collectors;
056
057import org.apache.commons.logging.Log;
058import org.apache.solr.client.solrj.SolrQuery.ORDER;
059
060import com.google.common.base.Joiner;
061
062/**
063 * Parameters used for the gallery search index.<p>
064 *
065 * @since 8.0.0
066 */
067public class CmsGallerySearchParameters {
068
069    /** Sort parameter constants. */
070    public enum CmsGallerySortParam {
071
072        /** Sort by date created ascending. */
073        dateCreated_asc,
074
075        /** Sort by date created descending. */
076        dateCreated_desc,
077
078        /** Sort date expired ascending. */
079        dateExpired_asc,
080
081        /** Sort date expired descending. */
082        dateExpired_desc,
083
084        /** Sort by date modified ascending. */
085        dateLastModified_asc,
086
087        /** Sort by date modified descending. */
088        dateLastModified_desc,
089
090        /** Sort date released ascending. */
091        dateReleased_asc,
092
093        /** Sort date released descending. */
094        dateReleased_desc,
095
096        /** Sort by length ascending. */
097        length_asc,
098
099        /** Sort by length descending. */
100        length_desc,
101
102        /** Sort by VFS root path ascending. */
103        path_asc,
104
105        /** Sort by VFS root path descending. */
106        path_desc,
107
108        /** Sort by score descending. */
109        score,
110
111        /** Sort state ascending. */
112        state_asc,
113
114        /** Sort state descending. */
115        state_desc,
116
117        /** Sort by title ascending. */
118        title_asc,
119
120        /** Sort by title ascending. */
121        title_desc,
122
123        /** Sort by type ascending. */
124        type_asc,
125
126        /** Sort by type descending. */
127        type_desc,
128
129        /** Sort created by ascending. */
130        userCreated_asc,
131
132        /** Sort created by descending. */
133        userCreated_desc,
134
135        /** Sort modified by ascending. */
136        userLastModified_asc,
137
138        /** Sort modified by descending. */
139        userLastModified_desc;
140
141        /** The default sort parameter. */
142        public static final CmsGallerySortParam DEFAULT = title_asc;
143    }
144
145    /**
146     * Helper class to store a time range.<p>
147     */
148    class CmsGallerySearchTimeRange {
149
150        /** The end time of the time range. */
151        long m_endTime;
152
153        /** The start time of the time range. */
154        long m_startTime;
155
156        /**
157         * Default constructor.<p>
158         *
159         * This will create an object where the start date is equal to
160         * {@link Long#MIN_VALUE} and the end date is equal to {@link Long#MAX_VALUE}.<p>
161         */
162        public CmsGallerySearchTimeRange() {
163
164            m_startTime = Long.MIN_VALUE;
165            m_endTime = Long.MAX_VALUE;
166        }
167
168        /**
169         * Constructor with start and end time.<p>
170         *
171         * @param startTime the start time of the time range
172         * @param endTime the end time of the time range
173         */
174        public CmsGallerySearchTimeRange(long startTime, long endTime) {
175
176            m_startTime = startTime;
177            m_endTime = endTime;
178        }
179
180        /**
181         * Returns the end time of the time range.<p>
182         *
183         * @return the end time of the time range
184         */
185        public long getEndTime() {
186
187            return m_endTime;
188        }
189
190        /**
191         * Returns the start time of the time range.<p>
192         *
193         * @return the start time of the time range
194         */
195        public long getStartTime() {
196
197            return m_startTime;
198        }
199    }
200
201    /** Logge instance for this class. */
202    private static final Log LOG = CmsLog.getLog(CmsGallerySearchParameters.class);
203
204    /** The categories to search in. */
205    private List<String> m_categories;
206
207    /** The time range for the date of resource creation to consider in the search. */
208    private CmsGallerySearchTimeRange m_dateCreatedTimeRange;
209
210    /** The time range for the date of resource last modification to consider in the search. */
211    private CmsGallerySearchTimeRange m_dateLastModifiedTimeRange;
212
213    /** The set of functions to be excluded from the search result (may be null). */
214    private Set<CmsUUID> m_excludedFunctions;
215
216    /** The list of folders to search in. */
217    private List<String> m_folders;
218
219    /** Enlists all VFS folders to perform a search in. */
220    private List<String> m_foldersToSearchIn;
221
222    /** If true, empty search result should be returned regardless of other settings. */
223    private boolean m_forceEmptyResult;
224
225    /** Function availability. */
226    private CmsFunctionAvailability m_functionAvailability;
227
228    /** The galleries to search in. */
229    private List<String> m_galleries;
230
231    /** Indicates the search exclude property should be ignored. */
232    private boolean m_ignoreSearchExclude;
233
234    /** Set of functions to include (whitelist). */
235    private Set<CmsUUID> m_includedFunctions;
236
237    /** Indicates if expired and unreleased resources should be included in the search. */
238    private boolean m_includeExpired;
239
240    /** The locale for the search. */
241    private String m_locale;
242
243    /** The number of search results per page. */
244    private int m_matchesPerPage;
245
246    /** The sitemap reference path. */
247    private String m_referencePath;
248
249    /** The resource types to search for. */
250    private List<String> m_resourceTypes;
251
252    /** The requested page of the result. */
253    private int m_resultPage;
254
255    /** The gallery search scope. */
256    private CmsGallerySearchScope m_scope;
257
258    /** The sort order for the search result. */
259    private CmsGallerySortParam m_sortOrder;
260
261    /** The template compatibility. */
262    private String m_templateCompatibility;
263
264    /** Search words to search for. */
265    private String m_words;
266
267    /**
268     * Default constructor.<p>
269     */
270    public CmsGallerySearchParameters() {
271
272        m_resultPage = 1;
273        m_matchesPerPage = 10;
274    }
275
276    /**
277     * Returns the categories that have been included in the search.<p>
278     *
279     * If no categories have been set, then <code>null</code> is returned.<p>
280     *
281     * @return the categories that have been included in the search
282     */
283    public List<String> getCategories() {
284
285        return m_categories;
286    }
287
288    /**
289     * Returns the time range for the date of creation that has been used for the search result.<p>
290     *
291     * In case this time range has not been set, this will return an object
292     * where the start date is equal to {@link Long#MIN_VALUE} and the end date is equal to {@link Long#MAX_VALUE}.<p>
293     *
294     * @return the time range for the date of creation that has been used for the search result
295     */
296    public CmsGallerySearchTimeRange getDateCreatedRange() {
297
298        if (m_dateCreatedTimeRange == null) {
299            m_dateCreatedTimeRange = new CmsGallerySearchTimeRange();
300        }
301        return m_dateCreatedTimeRange;
302    }
303
304    /**
305     * Returns the time range for the dadelete examplete of last modification that has been used for the search result.<p>
306     *
307     * In case this time range has not been set, this will return an object
308     * where the start date is equal to {@link Long#MIN_VALUE} and the end date is equal to {@link Long#MAX_VALUE}.<p>
309     *
310     * @return the time range for the date of last modification that has been used for the search result
311     */
312    public CmsGallerySearchTimeRange getDateLastModifiedRange() {
313
314        if (m_dateLastModifiedTimeRange == null) {
315            m_dateLastModifiedTimeRange = new CmsGallerySearchTimeRange();
316        }
317        return m_dateLastModifiedTimeRange;
318    }
319
320    /**
321     * Gets the set of structure IDs of functions to exclude from the search result.
322     *
323     * @return the set of structure IDs to exclude
324     */
325    public Set<CmsUUID> getExcludedFunctions() {
326
327        return m_excludedFunctions;
328    }
329
330    /**
331     * Returns the list of folders to search in.<p>
332     *
333     * @return a list of paths of VFS folders
334     */
335    public List<String> getFolders() {
336
337        return m_folders;
338    }
339
340    /**
341     * Returns the galleries that have been included in the search.<p>
342     *
343     * If no galleries have been set, then <code>null</code> is returned.<p>
344     *
345     * @return the galleries that have been included in the search
346     */
347    public List<String> getGalleries() {
348
349        return m_galleries;
350    }
351
352    /**
353     * Gets the set of ids of functions to include.
354     *
355     * <p>Note: If the id of a function is returned in the ID set returned by thsi method,
356     * the function may still be excluded from search results based on other parameters.
357     *
358     * @return the included functions
359     */
360    public Set<CmsUUID> getIncludedFunctions() {
361
362        return m_includedFunctions;
363    }
364
365    /**
366     * Returns the locale that has been used for the search.<p>
367     *
368     * If no locale has been set, then <code>null</code> is returned.<p>
369     *
370     * @return the locale that has been used for the search
371     */
372    public String getLocale() {
373
374        if (m_locale == null) {
375            m_locale = CmsLocaleManager.getDefaultLocale().toString();
376        }
377        return m_locale;
378    }
379
380    /**
381     * Returns the maximum number of matches per result page.<p>
382     *
383     * @return the the maximum number of matches per result page
384     *
385     * @see #getMatchesPerPage()
386     * @see #setResultPage(int)
387     */
388    public int getMatchesPerPage() {
389
390        return m_matchesPerPage;
391    }
392
393    /**
394     * Returns a CmsSolrQuery representation of this class.
395     * @param cms the openCms object.
396     * @return CmsSolrQuery representation of this class.
397     */
398    public CmsSolrQuery getQuery(CmsObject cms) {
399
400        final CmsSolrQuery query = new CmsSolrQuery();
401
402        // set categories
403        query.setCategories(m_categories);
404
405        // Set date created time filter
406        query.addFilterQuery(
407            CmsSearchUtil.getDateCreatedTimeRangeFilterQuery(
408                CmsSearchField.FIELD_DATE_CREATED,
409                getDateCreatedRange().m_startTime,
410                getDateCreatedRange().m_endTime));
411
412        // Set date last modified time filter
413        query.addFilterQuery(
414            CmsSearchUtil.getDateCreatedTimeRangeFilterQuery(
415                CmsSearchField.FIELD_DATE_LASTMODIFIED,
416                getDateLastModifiedRange().m_startTime,
417                getDateLastModifiedRange().m_endTime));
418
419        // set scope / folders to search in
420        m_foldersToSearchIn = new ArrayList<String>();
421        addFoldersToSearchIn(m_folders);
422        addFoldersToSearchIn(m_galleries);
423        setSearchFolders(cms);
424        query.addFilterQuery(
425            CmsSearchField.FIELD_PARENT_FOLDERS,
426            new ArrayList<String>(m_foldersToSearchIn),
427            false,
428            true);
429
430        if (!m_ignoreSearchExclude) {
431            // Reference for the values: CmsGallerySearchIndex.java, field EXCLUDE_PROPERTY_VALUES
432            query.addFilterQuery(
433                "-" + CmsSearchField.FIELD_SEARCH_EXCLUDE,
434                Arrays.asList(
435                    new String[] {
436                        A_CmsSearchIndex.PROPERTY_SEARCH_EXCLUDE_VALUE_ALL,
437                        A_CmsSearchIndex.PROPERTY_SEARCH_EXCLUDE_VALUE_GALLERY}),
438                false,
439                true);
440        }
441
442        // set matches per page
443        query.setRows(Integer.valueOf(m_matchesPerPage));
444
445        // set resource types
446        if (null != m_resourceTypes) {
447            List<String> resourceTypes = new ArrayList<>(m_resourceTypes);
448            if (m_resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)
449                && !m_resourceTypes.contains(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION)) {
450                resourceTypes.add(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION);
451            }
452            query.setResourceTypes(resourceTypes);
453        }
454
455        // set result page
456        query.setStart(Integer.valueOf((m_resultPage - 1) * m_matchesPerPage));
457
458        // set search locale
459        if (null != m_locale) {
460            Locale l = CmsLocaleManager.getLocale(m_locale);
461            List<Locale> locales = new ArrayList<>(3);
462            locales.add(l);
463            if (!l.getVariant().isEmpty()) {
464                locales.add(new Locale(l.getLanguage(), l.getCountry()));
465            }
466            if (!l.getCountry().isEmpty()) {
467                locales.add(new Locale(l.getLanguage()));
468            }
469            query.setLocales(locales);
470        }
471
472        // set search words
473        if (null != m_words) {
474            query.setQuery(m_words);
475        }
476
477        // set sort order
478        query.setSort(getSort().getFirst(), getSort().getSecond());
479
480        // set result collapsing by id
481        // add sort criteria to possibly speed up performance and prevent sorting by score - not sure if a good solution
482        query.addFilterQuery(
483            "{!collapse field=id sort='"
484                + CmsSearchField.FIELD_INSTANCEDATE
485                + CmsSearchField.FIELD_POSTFIX_DATE
486                + " asc'}");
487
488        query.setFields(CmsGallerySearchResult.getRequiredSolrFields());
489
490        if ((m_functionAvailability != null) && m_functionAvailability.isDefined()) {
491            String notFunction = "(*:* AND -type:("
492                + CmsXmlDynamicFunctionHandler.TYPE_FUNCTION
493                + " OR "
494                + CmsResourceTypeFunctionConfig.TYPE_NAME
495                + "))";
496            Collection<CmsUUID> whitelist = m_functionAvailability.getWhitelist();
497            Collection<CmsUUID> blacklist = new ArrayList<>(m_functionAvailability.getBlacklist());
498            CmsUUID dummyId = CmsUUID.getNullUUID();
499            blacklist.add(dummyId);
500            String idClause;
501            if (whitelist != null) {
502                whitelist = new ArrayList<>(whitelist);
503                whitelist.add(dummyId);
504                String whitelistIdCondition = whitelist.stream().map(id -> id.toString()).collect(
505                    Collectors.joining(" OR "));
506                idClause = "id:(" + whitelistIdCondition + ")";
507            } else {
508                idClause = "*:* AND -id:("
509                    + blacklist.stream().map(id -> id.toString()).collect(Collectors.joining(" OR "))
510                    + ") ";
511            }
512            String functionFilter = "(" + notFunction + " OR (" + idClause + "))";
513            query.addFilterQuery(functionFilter);
514        }
515
516        if ((m_resourceTypes != null) && m_resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
517            if ((m_excludedFunctions != null) && (m_excludedFunctions.size() > 0)) {
518                List<CmsUUID> excludedFunctions = new ArrayList<>(m_excludedFunctions);
519                Collections.sort(excludedFunctions);
520                String orList = Joiner.on(" OR ").join(excludedFunctions);
521                String filter = "*:* AND -id:(" + orList + ")";
522                query.addFilterQuery(filter);
523            }
524            if (m_includedFunctions != null) {
525                List<CmsUUID> includedFunctions = new ArrayList<>(m_includedFunctions);
526                // not sure if order of terms matters for filter query caching in Solr, so normalize order just in case
527                Collections.sort(includedFunctions);
528                List<String> conditions = new ArrayList<>();
529                String notFunction = "(*:* AND -type:("
530                    + CmsXmlDynamicFunctionHandler.TYPE_FUNCTION
531                    + " OR "
532                    + CmsResourceTypeFunctionConfig.TYPE_NAME
533                    + "))";
534                conditions.add(notFunction);
535                for (CmsUUID id : includedFunctions) {
536                    conditions.add("id:" + id);
537                }
538                String includedFunctionsFilter = Joiner.on(" OR ").join(conditions);
539                query.addFilterQuery(includedFunctionsFilter);
540            }
541        }
542
543        if (m_templateCompatibility != null) {
544            if (m_templateCompatibility.matches("^[0-9a-zA-Z_]+$")) {
545                String fieldName = CmsPropertyDefinition.PROPERTY_TEMPLATE_COMPATILIBITY + "_prop";
546                query.addFilterQuery("(*:* NOT " + fieldName + ":*) OR " + fieldName + ":" + m_templateCompatibility);
547            } else {
548                LOG.warn(
549                    "Invalid template compatibility value: "
550                        + m_templateCompatibility
551                        + ". Must only contain digits, letters (a-z) or underscores.");
552            }
553        }
554
555        // include expired/unreleased
556        if (m_includeExpired) {
557            query.removeExpiration();
558        }
559
560        return query;
561    }
562
563    /**
564     * Gets the reference path.<p>
565     *
566     * @return the gallery reference path
567     */
568    public String getReferencePath() {
569
570        return m_referencePath;
571    }
572
573    /**
574     * Returns the names of the resource types that have been included in the search result.<p>
575     *
576     * If no resource types have been set, then <code>null</code> is returned.<p>
577     *
578     * @return the names of the resource types that have been included in the search result
579     */
580    public List<String> getResourceTypes() {
581
582        return m_resourceTypes;
583    }
584
585    /**
586     * Returns the index of the requested result page.<p>
587     *
588     * @return the index of the requested result page
589     *
590     * @see #setResultPage(int)
591     * @see #getMatchesPerPage()
592     * @see #setMatchesPerPage(int)
593     */
594    public int getResultPage() {
595
596        return m_resultPage;
597    }
598
599    /**
600     * The gallery search scope.<p>
601     *
602     * @return the gallery search scope
603     */
604    public CmsGallerySearchScope getScope() {
605
606        if (m_scope == null) {
607            return OpenCms.getWorkplaceManager().getGalleryDefaultScope();
608        }
609        return m_scope;
610    }
611
612    /**
613     * Returns the words (terms) that have been used for the full text search.<p>
614     *
615     * If no search words have been set, then <code>null</code> is returned.<p>
616     *
617     * @return the words (terms) that have been used for the full text search
618     */
619    public String getSearchWords() {
620
621        return m_words;
622    }
623
624    /**
625     * Returns the sort order that has been used in the search.<p>
626     *
627     * If the sort parameter has not been set the default sort order
628     * defined by {@link CmsGallerySortParam#DEFAULT} is used.<p>
629     *
630     * @return the sort order that has been used in the search
631     */
632    public CmsGallerySortParam getSortOrder() {
633
634        if (m_sortOrder == null) {
635
636            m_sortOrder = CmsGallerySortParam.DEFAULT;
637        }
638        return m_sortOrder;
639    }
640
641    /**
642     * Gets the template compatibility.
643     *
644     * <p>If set, matches those resources whose template.compatibility property is either empty or contains the value (possibly together with other values, separated by whitespace).
645     *
646     * @return the template compatibility
647     */
648    public String getTemplateCompatibility() {
649
650        return m_templateCompatibility;
651    }
652
653    /**
654     * If this returns true, an empty search result should be returned, regardless of other settings.
655     *
656     * @return true if an empty search result should be forced
657     */
658    public boolean isForceEmptyResult() {
659
660        return m_forceEmptyResult;
661    }
662
663    /**
664     * Returns the search exclude property ignore flag.<p>
665     *
666     * @return the search exclude property ignore flag
667     */
668    public boolean isIgnoreSearchExclude() {
669
670        return m_ignoreSearchExclude;
671    }
672
673    /**
674     * Returns a flag, indicating if release and expiration date should be ignored.<p>
675     *
676     * @return a flag, indicating if release and expiration date should be ignored
677     */
678    public boolean isIncludeExpired() {
679
680        return m_includeExpired;
681    }
682
683    /**
684     * Sets the categories for the search.<p>
685     *
686     * Results are found only if they are contained in at least one of the given categories.
687     *
688     * @param categories the categories to set
689     */
690    public void setCategories(List<String> categories) {
691
692        m_categories = categories;
693    }
694
695    /**
696     * Sets the time range for the date of resource creation to consider in the search.<p>
697     *
698     * @param startTime the start time of the time range
699     * @param endTime the end time of the time range
700     */
701    public void setDateCreatedTimeRange(long startTime, long endTime) {
702
703        if (m_dateCreatedTimeRange == null) {
704            m_dateCreatedTimeRange = new CmsGallerySearchTimeRange(startTime, endTime);
705        }
706    }
707
708    /**
709     * Sets the time range for the date of resource last modification to consider in the search.<p>
710     *
711     * @param startTime the start time of the time range
712     * @param endTime the end time of the time range
713     */
714    public void setDateLastModifiedTimeRange(long startTime, long endTime) {
715
716        if (m_dateLastModifiedTimeRange == null) {
717            m_dateLastModifiedTimeRange = new CmsGallerySearchTimeRange(startTime, endTime);
718        }
719    }
720
721    /**
722     * Sets the structure IDs of functions to exclude from the search results.
723     *
724     * @param excludedFunctions the structure IDs of functions to exclude
725     */
726    public void setExcludedFunctions(Set<CmsUUID> excludedFunctions) {
727
728        m_excludedFunctions = excludedFunctions;
729
730    }
731
732    /**
733     * Sets the folders to search in.<p>
734     *
735     * @param folders the list of VFS folders
736     */
737    public void setFolders(List<String> folders) {
738
739        m_folders = folders;
740    }
741
742    /**
743     * Enables/disables the 'force empty result' flag.
744     *
745     * If this is set to true, an empty search result should be returned regardless of the other parameters.
746     *
747     * @param forceEmptyResult if true, force an empty search result
748     */
749    public void setForceEmptyResult(boolean forceEmptyResult) {
750
751        m_forceEmptyResult = forceEmptyResult;
752    }
753
754    /**
755     * Sets the dynamic function availability.
756     *
757     * @param dynamicFunctionAvailability the dynamic function availability
758     */
759    public void setFunctionAvailability(CmsFunctionAvailability dynamicFunctionAvailability) {
760
761        m_functionAvailability = dynamicFunctionAvailability;
762    }
763
764    /**
765     * Sets the galleries for the search.<p>
766     *
767     * Results are found only if they are contained in one of the given galleries.
768     * If no gallery is set, results from all galleries will be returned in the search result.<p>
769     *
770     * @param galleries the galleries to set
771     */
772    public void setGalleries(List<String> galleries) {
773
774        m_galleries = galleries;
775    }
776
777    /**
778     * Sets the search exclude property ignore flag.<p>
779     *
780     * @param excludeForPageEditor the search exclude property ignore flag
781     */
782    public void setIgnoreSearchExclude(boolean excludeForPageEditor) {
783
784        m_ignoreSearchExclude = excludeForPageEditor;
785    }
786
787    /**
788     * Sets the ids of functions to include.
789     *
790     * @param includedFunctions the ids of functions to include
791     */
792    public void setIncludedFunctions(Set<CmsUUID> includedFunctions) {
793
794        m_includedFunctions = includedFunctions;
795    }
796
797    /**
798     * Set the flag, determining if expired and unreleased resources should be shown.
799     * @param includeExpired iff <code>true</code> expired and unreleased resources are shown.
800     */
801    public void setIncludeExpired(boolean includeExpired) {
802
803        m_includeExpired = includeExpired;
804
805    }
806
807    /**
808     * Sets the maximum number of matches per result page.<p>
809     *
810     * Use this together with {@link #setResultPage(int)} in order to split the result
811     * in more than one page.<p>
812     *
813     * @param matchesPerPage the the maximum number of matches per result page to set
814     *
815     * @see #getMatchesPerPage()
816     * @see #setResultPage(int)
817     */
818    public void setMatchesPerPage(int matchesPerPage) {
819
820        m_matchesPerPage = matchesPerPage;
821    }
822
823    /**
824     * Sets the gallery reference path.<p>
825     *
826     * @param referencePath the gallery reference path
827     */
828    public void setReferencePath(String referencePath) {
829
830        m_referencePath = referencePath;
831    }
832
833    /**
834     * Sets the names of the resource types to include in the search result.<p>
835     *
836     * Results are found only if they resources match one of the given resource type names.
837     * If no resource type name is set, all resource types will be returned in the search result.<p>
838     *
839     * @param resourceTypes the names of the resource types to include in the search result
840     */
841    public void setResourceTypes(List<String> resourceTypes) {
842
843        m_resourceTypes = resourceTypes;
844    }
845
846    /**
847     * Sets the index of the result page that should be returned.<p>
848     *
849     * Use this together with {@link #setMatchesPerPage(int)} in order to split the result
850     * in more than one page.<p>
851     *
852     * @param resultPage the index of the result page to return
853     *
854     * @see #getResultPage()
855     * @see #getMatchesPerPage()
856     * @see #setMatchesPerPage(int)
857     */
858    public void setResultPage(int resultPage) {
859
860        m_resultPage = resultPage;
861    }
862
863    /**
864     * Sets the search scope.<p>
865     *
866     * @param scope the search scope
867     */
868    public void setScope(CmsGallerySearchScope scope) {
869
870        m_scope = scope;
871    }
872
873    /**
874     * Sets the locale for the search.<p>
875     *
876     * Results are found only if they match the given locale.
877     * If no locale is set, results for all locales will be returned in the search result.<p>
878     *
879     * @param locale the locale to set
880     */
881    public void setSearchLocale(String locale) {
882
883        m_locale = locale;
884    }
885
886    /**
887     * Sets the words (terms) for the full text search.<p>
888     *
889     * Results are found only if they text extraction for the resource contains all given search words.
890     * If no search word is set, all resources will be returned in the search result.<p>
891     *
892     * Please note that this should be a list of words separated by white spaces.
893     * Simple Lucene modifiers such as (+), (-) and (*) are allowed, but anything more complex then this
894     * will be removed.<p>
895     *
896     * @param words the words (terms) for the full text search to set
897     */
898    public void setSearchWords(String words) {
899
900        m_words = words;
901    }
902
903    /**
904     * Sets the sort order for the search.<p>
905     *
906     * @param sortOrder the sort order to set
907     */
908    public void setSortOrder(CmsGallerySortParam sortOrder) {
909
910        m_sortOrder = sortOrder;
911    }
912
913    /**
914     * Sets the template compatibility string.
915     *
916     * @param compatibility the template compatibility string
917     */
918    public void setTemplateCompatibility(String compatibility) {
919
920        m_templateCompatibility = compatibility;
921    }
922
923    /**
924     * Adds folders to perform the search in.
925     * @param folders Folders to search in.
926     */
927    private void addFoldersToSearchIn(final List<String> folders) {
928
929        if (null == folders) {
930            return;
931        }
932
933        for (String folder : folders) {
934            if (!CmsResource.isFolder(folder)) {
935                folder += "/";
936            }
937
938            m_foldersToSearchIn.add(folder);
939        }
940    }
941
942    /**
943     * Checks if the given list of resource type names contains a function-like type.<p>
944     *
945     * @param resourceTypes the collection of resource types
946     * @return true if the list contains a function-like type
947     */
948    private boolean containsFunctionType(List<String> resourceTypes) {
949
950        if (resourceTypes.contains(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION)) {
951            return true;
952        }
953        if (resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
954            return true;
955        }
956        return false;
957    }
958
959    /**
960     * Returns the Lucene sort indicated by the selected sort order.<p>
961     *
962     * @return the Lucene sort indicated by the selected sort order
963     *
964     * @see #getSortOrder()
965     */
966    private CmsPair<String, org.apache.solr.client.solrj.SolrQuery.ORDER> getSort() {
967
968        final String sortTitle = CmsSearchFieldConfiguration.getLocaleExtendedName(
969            CmsSearchField.FIELD_DISPTITLE,
970            getLocale()) + "_sort";
971
972        switch (getSortOrder()) {
973            case dateCreated_asc:
974                return CmsPair.create(CmsSearchField.FIELD_DATE_CREATED, ORDER.asc);
975            case dateCreated_desc:
976                return CmsPair.create(CmsSearchField.FIELD_DATE_CREATED, ORDER.desc);
977            case dateExpired_asc:
978                return CmsPair.create(CmsSearchField.FIELD_DATE_EXPIRED, ORDER.asc);
979            case dateExpired_desc:
980                return CmsPair.create(CmsSearchField.FIELD_DATE_EXPIRED, ORDER.desc);
981            case dateLastModified_asc:
982                return CmsPair.create(CmsSearchField.FIELD_DATE_LASTMODIFIED, ORDER.asc);
983            case dateLastModified_desc:
984                return CmsPair.create(CmsSearchField.FIELD_DATE_LASTMODIFIED, ORDER.desc);
985            case dateReleased_asc:
986                return CmsPair.create(CmsSearchField.FIELD_DATE_RELEASED, ORDER.asc);
987            case dateReleased_desc:
988                return CmsPair.create(CmsSearchField.FIELD_DATE_RELEASED, ORDER.desc);
989            case length_asc:
990                return CmsPair.create(CmsSearchField.FIELD_SIZE, ORDER.asc);
991            case length_desc:
992                return CmsPair.create(CmsSearchField.FIELD_SIZE, ORDER.desc);
993            case path_asc:
994                return CmsPair.create(CmsSearchField.FIELD_PATH, ORDER.asc);
995            case path_desc:
996                return CmsPair.create(CmsSearchField.FIELD_PATH, ORDER.desc);
997            case score:
998                return CmsPair.create(CmsSearchField.FIELD_SCORE, ORDER.desc);
999            case state_asc:
1000                return CmsPair.create(CmsSearchField.FIELD_STATE, ORDER.asc);
1001            case state_desc:
1002                return CmsPair.create(CmsSearchField.FIELD_STATE, ORDER.desc);
1003            case title_asc:
1004                return CmsPair.create(sortTitle, ORDER.asc);
1005            case title_desc:
1006                return CmsPair.create(sortTitle, ORDER.desc);
1007            case type_asc:
1008                return CmsPair.create(CmsSearchField.FIELD_TYPE, ORDER.asc);
1009            case type_desc:
1010                return CmsPair.create(CmsSearchField.FIELD_TYPE, ORDER.desc);
1011            case userCreated_asc:
1012                return CmsPair.create(CmsSearchField.FIELD_USER_CREATED, ORDER.asc);
1013            case userCreated_desc:
1014                return CmsPair.create(CmsSearchField.FIELD_USER_CREATED, ORDER.desc);
1015            case userLastModified_asc:
1016                return CmsPair.create(CmsSearchField.FIELD_USER_LAST_MODIFIED, ORDER.asc);
1017            case userLastModified_desc:
1018                return CmsPair.create(CmsSearchField.FIELD_USER_LAST_MODIFIED, ORDER.desc);
1019            default:
1020                return CmsPair.create(sortTitle, ORDER.asc);
1021        }
1022    }
1023
1024    /**
1025     * Applies the defined search folders to the Solr query.
1026     *
1027     * @param obj The current CmsObject object.
1028     */
1029    private void setSearchFolders(CmsObject obj) {
1030
1031        // check if parentFolders to search in have been set
1032        // if this evaluates false, the search folders have already been set, so
1033        // there's no need to add a scope filter
1034        if (m_foldersToSearchIn.isEmpty()) {
1035            // only append scope filter if no no folders or galleries given
1036            setSearchScopeFilter(obj);
1037        }
1038    }
1039
1040    /**
1041     * Sets the search scope.
1042     *
1043     * @param cms The current CmsObject object.
1044     */
1045    private void setSearchScopeFilter(CmsObject cms) {
1046
1047        final List<String> searchRoots = CmsSearchUtil.computeScopeFolders(cms, this);
1048
1049        // If the resource types contain the type "function" also
1050        // add "/system/modules/" to the search path
1051
1052        if ((null != getResourceTypes()) && containsFunctionType(getResourceTypes())) {
1053            searchRoots.add("/system/modules/");
1054        }
1055
1056        addFoldersToSearchIn(searchRoots);
1057    }
1058}