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.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.request.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<>();
421        addFoldersToSearchIn(m_folders);
422        addFoldersToSearchIn(m_galleries);
423        setSearchFolders(cms);
424        query.addFilterQuery(CmsSearchField.FIELD_PARENT_FOLDERS, new ArrayList<>(m_foldersToSearchIn), false, true);
425
426        if (!m_ignoreSearchExclude) {
427            // Reference for the values: CmsGallerySearchIndex.java, field EXCLUDE_PROPERTY_VALUES
428            query.addFilterQuery(
429                "-" + CmsSearchField.FIELD_SEARCH_EXCLUDE,
430                Arrays.asList(
431                    new String[] {
432                        A_CmsSearchIndex.PROPERTY_SEARCH_EXCLUDE_VALUE_ALL,
433                        A_CmsSearchIndex.PROPERTY_SEARCH_EXCLUDE_VALUE_GALLERY}),
434                false,
435                true);
436        }
437
438        // set matches per page
439        query.setRows(Integer.valueOf(m_matchesPerPage));
440
441        // set resource types
442        if (null != m_resourceTypes) {
443            List<String> resourceTypes = new ArrayList<>(m_resourceTypes);
444            if (m_resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)
445                && !m_resourceTypes.contains(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION)) {
446                resourceTypes.add(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION);
447            }
448            query.setResourceTypes(resourceTypes);
449        }
450
451        // set result page
452        query.setStart(Integer.valueOf((m_resultPage - 1) * m_matchesPerPage));
453
454        // set search locale
455        if (null != m_locale) {
456            Locale l = CmsLocaleManager.getLocale(m_locale);
457            List<Locale> locales = new ArrayList<>(3);
458            locales.add(l);
459            if (!l.getVariant().isEmpty()) {
460                locales.add(Locale.of(l.getLanguage(), l.getCountry()));
461            }
462            if (!l.getCountry().isEmpty()) {
463                locales.add(Locale.of(l.getLanguage()));
464            }
465            query.setLocales(locales);
466        }
467
468        // set search words
469        if (null != m_words) {
470            query.setQuery(m_words);
471        }
472
473        // set sort order
474        query.setSort(getSort().getFirst(), getSort().getSecond());
475
476        // set result collapsing by id
477        // add sort criteria to possibly speed up performance and prevent sorting by score - not sure if a good solution
478        query.addFilterQuery(
479            "{!collapse field=id sort='"
480                + CmsSearchField.FIELD_INSTANCEDATE
481                + CmsSearchField.FIELD_POSTFIX_DATE
482                + " asc'}");
483
484        query.setFields(CmsGallerySearchResult.getRequiredSolrFields());
485
486        if ((m_functionAvailability != null) && m_functionAvailability.isDefined()) {
487            String notFunction = "(*:* AND -type:("
488                + CmsXmlDynamicFunctionHandler.TYPE_FUNCTION
489                + " OR "
490                + CmsResourceTypeFunctionConfig.TYPE_NAME
491                + "))";
492            Collection<CmsUUID> whitelist = m_functionAvailability.getWhitelist();
493            Collection<CmsUUID> blacklist = new ArrayList<>(m_functionAvailability.getBlacklist());
494            CmsUUID dummyId = CmsUUID.getNullUUID();
495            blacklist.add(dummyId);
496            String idClause;
497            if (whitelist != null) {
498                whitelist = new ArrayList<>(whitelist);
499                whitelist.add(dummyId);
500                String whitelistIdCondition = whitelist.stream().map(id -> id.toString()).collect(
501                    Collectors.joining(" OR "));
502                idClause = "id:(" + whitelistIdCondition + ")";
503            } else {
504                idClause = "*:* AND -id:("
505                    + blacklist.stream().map(id -> id.toString()).collect(Collectors.joining(" OR "))
506                    + ") ";
507            }
508            String functionFilter = "(" + notFunction + " OR (" + idClause + "))";
509            query.addFilterQuery(functionFilter);
510        }
511
512        if ((m_resourceTypes != null) && m_resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
513            if ((m_excludedFunctions != null) && (m_excludedFunctions.size() > 0)) {
514                List<CmsUUID> excludedFunctions = new ArrayList<>(m_excludedFunctions);
515                Collections.sort(excludedFunctions);
516                String orList = Joiner.on(" OR ").join(excludedFunctions);
517                String filter = "*:* AND -id:(" + orList + ")";
518                query.addFilterQuery(filter);
519            }
520            if (m_includedFunctions != null) {
521                List<CmsUUID> includedFunctions = new ArrayList<>(m_includedFunctions);
522                // not sure if order of terms matters for filter query caching in Solr, so normalize order just in case
523                Collections.sort(includedFunctions);
524                List<String> conditions = new ArrayList<>();
525                String notFunction = "(*:* AND -type:("
526                    + CmsXmlDynamicFunctionHandler.TYPE_FUNCTION
527                    + " OR "
528                    + CmsResourceTypeFunctionConfig.TYPE_NAME
529                    + "))";
530                conditions.add(notFunction);
531                for (CmsUUID id : includedFunctions) {
532                    conditions.add("id:" + id);
533                }
534                String includedFunctionsFilter = Joiner.on(" OR ").join(conditions);
535                query.addFilterQuery(includedFunctionsFilter);
536            }
537        }
538
539        if (m_templateCompatibility != null) {
540            if (m_templateCompatibility.matches("^[0-9a-zA-Z_]+$")) {
541                String fieldName = CmsPropertyDefinition.PROPERTY_TEMPLATE_COMPATILIBITY + "_prop";
542                query.addFilterQuery("(*:* NOT " + fieldName + ":*) OR " + fieldName + ":" + m_templateCompatibility);
543            } else {
544                LOG.warn(
545                    "Invalid template compatibility value: "
546                        + m_templateCompatibility
547                        + ". Must only contain digits, letters (a-z) or underscores.");
548            }
549        }
550
551        // include expired/unreleased
552        if (m_includeExpired) {
553            query.removeExpiration();
554        }
555
556        return query;
557    }
558
559    /**
560     * Gets the reference path.<p>
561     *
562     * @return the gallery reference path
563     */
564    public String getReferencePath() {
565
566        return m_referencePath;
567    }
568
569    /**
570     * Returns the names of the resource types that have been included in the search result.<p>
571     *
572     * If no resource types have been set, then <code>null</code> is returned.<p>
573     *
574     * @return the names of the resource types that have been included in the search result
575     */
576    public List<String> getResourceTypes() {
577
578        return m_resourceTypes;
579    }
580
581    /**
582     * Returns the index of the requested result page.<p>
583     *
584     * @return the index of the requested result page
585     *
586     * @see #setResultPage(int)
587     * @see #getMatchesPerPage()
588     * @see #setMatchesPerPage(int)
589     */
590    public int getResultPage() {
591
592        return m_resultPage;
593    }
594
595    /**
596     * The gallery search scope.<p>
597     *
598     * @return the gallery search scope
599     */
600    public CmsGallerySearchScope getScope() {
601
602        if (m_scope == null) {
603            return OpenCms.getWorkplaceManager().getGalleryDefaultScope();
604        }
605        return m_scope;
606    }
607
608    /**
609     * Returns the words (terms) that have been used for the full text search.<p>
610     *
611     * If no search words have been set, then <code>null</code> is returned.<p>
612     *
613     * @return the words (terms) that have been used for the full text search
614     */
615    public String getSearchWords() {
616
617        return m_words;
618    }
619
620    /**
621     * Returns the sort order that has been used in the search.<p>
622     *
623     * If the sort parameter has not been set the default sort order
624     * defined by {@link CmsGallerySortParam#DEFAULT} is used.<p>
625     *
626     * @return the sort order that has been used in the search
627     */
628    public CmsGallerySortParam getSortOrder() {
629
630        if (m_sortOrder == null) {
631
632            m_sortOrder = CmsGallerySortParam.DEFAULT;
633        }
634        return m_sortOrder;
635    }
636
637    /**
638     * Gets the template compatibility.
639     *
640     * <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).
641     *
642     * @return the template compatibility
643     */
644    public String getTemplateCompatibility() {
645
646        return m_templateCompatibility;
647    }
648
649    /**
650     * If this returns true, an empty search result should be returned, regardless of other settings.
651     *
652     * @return true if an empty search result should be forced
653     */
654    public boolean isForceEmptyResult() {
655
656        return m_forceEmptyResult;
657    }
658
659    /**
660     * Returns the search exclude property ignore flag.<p>
661     *
662     * @return the search exclude property ignore flag
663     */
664    public boolean isIgnoreSearchExclude() {
665
666        return m_ignoreSearchExclude;
667    }
668
669    /**
670     * Returns a flag, indicating if release and expiration date should be ignored.<p>
671     *
672     * @return a flag, indicating if release and expiration date should be ignored
673     */
674    public boolean isIncludeExpired() {
675
676        return m_includeExpired;
677    }
678
679    /**
680     * Sets the categories for the search.<p>
681     *
682     * Results are found only if they are contained in at least one of the given categories.
683     *
684     * @param categories the categories to set
685     */
686    public void setCategories(List<String> categories) {
687
688        m_categories = categories;
689    }
690
691    /**
692     * Sets the time range for the date of resource creation to consider in the search.<p>
693     *
694     * @param startTime the start time of the time range
695     * @param endTime the end time of the time range
696     */
697    public void setDateCreatedTimeRange(long startTime, long endTime) {
698
699        if (m_dateCreatedTimeRange == null) {
700            m_dateCreatedTimeRange = new CmsGallerySearchTimeRange(startTime, endTime);
701        }
702    }
703
704    /**
705     * Sets the time range for the date of resource last modification to consider in the search.<p>
706     *
707     * @param startTime the start time of the time range
708     * @param endTime the end time of the time range
709     */
710    public void setDateLastModifiedTimeRange(long startTime, long endTime) {
711
712        if (m_dateLastModifiedTimeRange == null) {
713            m_dateLastModifiedTimeRange = new CmsGallerySearchTimeRange(startTime, endTime);
714        }
715    }
716
717    /**
718     * Sets the structure IDs of functions to exclude from the search results.
719     *
720     * @param excludedFunctions the structure IDs of functions to exclude
721     */
722    public void setExcludedFunctions(Set<CmsUUID> excludedFunctions) {
723
724        m_excludedFunctions = excludedFunctions;
725
726    }
727
728    /**
729     * Sets the folders to search in.<p>
730     *
731     * @param folders the list of VFS folders
732     */
733    public void setFolders(List<String> folders) {
734
735        m_folders = folders;
736    }
737
738    /**
739     * Enables/disables the 'force empty result' flag.
740     *
741     * If this is set to true, an empty search result should be returned regardless of the other parameters.
742     *
743     * @param forceEmptyResult if true, force an empty search result
744     */
745    public void setForceEmptyResult(boolean forceEmptyResult) {
746
747        m_forceEmptyResult = forceEmptyResult;
748    }
749
750    /**
751     * Sets the dynamic function availability.
752     *
753     * @param dynamicFunctionAvailability the dynamic function availability
754     */
755    public void setFunctionAvailability(CmsFunctionAvailability dynamicFunctionAvailability) {
756
757        m_functionAvailability = dynamicFunctionAvailability;
758    }
759
760    /**
761     * Sets the galleries for the search.<p>
762     *
763     * Results are found only if they are contained in one of the given galleries.
764     * If no gallery is set, results from all galleries will be returned in the search result.<p>
765     *
766     * @param galleries the galleries to set
767     */
768    public void setGalleries(List<String> galleries) {
769
770        m_galleries = galleries;
771    }
772
773    /**
774     * Sets the search exclude property ignore flag.<p>
775     *
776     * @param excludeForPageEditor the search exclude property ignore flag
777     */
778    public void setIgnoreSearchExclude(boolean excludeForPageEditor) {
779
780        m_ignoreSearchExclude = excludeForPageEditor;
781    }
782
783    /**
784     * Sets the ids of functions to include.
785     *
786     * @param includedFunctions the ids of functions to include
787     */
788    public void setIncludedFunctions(Set<CmsUUID> includedFunctions) {
789
790        m_includedFunctions = includedFunctions;
791    }
792
793    /**
794     * Set the flag, determining if expired and unreleased resources should be shown.
795     * @param includeExpired iff <code>true</code> expired and unreleased resources are shown.
796     */
797    public void setIncludeExpired(boolean includeExpired) {
798
799        m_includeExpired = includeExpired;
800
801    }
802
803    /**
804     * Sets the maximum number of matches per result page.<p>
805     *
806     * Use this together with {@link #setResultPage(int)} in order to split the result
807     * in more than one page.<p>
808     *
809     * @param matchesPerPage the the maximum number of matches per result page to set
810     *
811     * @see #getMatchesPerPage()
812     * @see #setResultPage(int)
813     */
814    public void setMatchesPerPage(int matchesPerPage) {
815
816        m_matchesPerPage = matchesPerPage;
817    }
818
819    /**
820     * Sets the gallery reference path.<p>
821     *
822     * @param referencePath the gallery reference path
823     */
824    public void setReferencePath(String referencePath) {
825
826        m_referencePath = referencePath;
827    }
828
829    /**
830     * Sets the names of the resource types to include in the search result.<p>
831     *
832     * Results are found only if they resources match one of the given resource type names.
833     * If no resource type name is set, all resource types will be returned in the search result.<p>
834     *
835     * @param resourceTypes the names of the resource types to include in the search result
836     */
837    public void setResourceTypes(List<String> resourceTypes) {
838
839        m_resourceTypes = resourceTypes;
840    }
841
842    /**
843     * Sets the index of the result page that should be returned.<p>
844     *
845     * Use this together with {@link #setMatchesPerPage(int)} in order to split the result
846     * in more than one page.<p>
847     *
848     * @param resultPage the index of the result page to return
849     *
850     * @see #getResultPage()
851     * @see #getMatchesPerPage()
852     * @see #setMatchesPerPage(int)
853     */
854    public void setResultPage(int resultPage) {
855
856        m_resultPage = resultPage;
857    }
858
859    /**
860     * Sets the search scope.<p>
861     *
862     * @param scope the search scope
863     */
864    public void setScope(CmsGallerySearchScope scope) {
865
866        m_scope = scope;
867    }
868
869    /**
870     * Sets the locale for the search.<p>
871     *
872     * Results are found only if they match the given locale.
873     * If no locale is set, results for all locales will be returned in the search result.<p>
874     *
875     * @param locale the locale to set
876     */
877    public void setSearchLocale(String locale) {
878
879        m_locale = locale;
880    }
881
882    /**
883     * Sets the words (terms) for the full text search.<p>
884     *
885     * Results are found only if they text extraction for the resource contains all given search words.
886     * If no search word is set, all resources will be returned in the search result.<p>
887     *
888     * Please note that this should be a list of words separated by white spaces.
889     * Simple Lucene modifiers such as (+), (-) and (*) are allowed, but anything more complex then this
890     * will be removed.<p>
891     *
892     * @param words the words (terms) for the full text search to set
893     */
894    public void setSearchWords(String words) {
895
896        m_words = words;
897    }
898
899    /**
900     * Sets the sort order for the search.<p>
901     *
902     * @param sortOrder the sort order to set
903     */
904    public void setSortOrder(CmsGallerySortParam sortOrder) {
905
906        m_sortOrder = sortOrder;
907    }
908
909    /**
910     * Sets the template compatibility string.
911     *
912     * @param compatibility the template compatibility string
913     */
914    public void setTemplateCompatibility(String compatibility) {
915
916        m_templateCompatibility = compatibility;
917    }
918
919    /**
920     * Adds folders to perform the search in.
921     * @param folders Folders to search in.
922     */
923    private void addFoldersToSearchIn(final List<String> folders) {
924
925        if (null == folders) {
926            return;
927        }
928
929        for (String folder : folders) {
930            if (!CmsResource.isFolder(folder)) {
931                folder += "/";
932            }
933
934            m_foldersToSearchIn.add(folder);
935        }
936    }
937
938    /**
939     * Checks if the given list of resource type names contains a function-like type.<p>
940     *
941     * @param resourceTypes the collection of resource types
942     * @return true if the list contains a function-like type
943     */
944    private boolean containsFunctionType(List<String> resourceTypes) {
945
946        if (resourceTypes.contains(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION)) {
947            return true;
948        }
949        if (resourceTypes.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
950            return true;
951        }
952        return false;
953    }
954
955    /**
956     * Returns the Lucene sort indicated by the selected sort order.<p>
957     *
958     * @return the Lucene sort indicated by the selected sort order
959     *
960     * @see #getSortOrder()
961     */
962    private CmsPair<String, org.apache.solr.client.solrj.request.SolrQuery.ORDER> getSort() {
963
964        final String sortTitle = CmsSearchFieldConfiguration.getLocaleExtendedName(
965            CmsSearchField.FIELD_DISPTITLE,
966            getLocale()) + "_sort";
967
968        switch (getSortOrder()) {
969            case dateCreated_asc:
970                return CmsPair.create(CmsSearchField.FIELD_DATE_CREATED, ORDER.asc);
971            case dateCreated_desc:
972                return CmsPair.create(CmsSearchField.FIELD_DATE_CREATED, ORDER.desc);
973            case dateExpired_asc:
974                return CmsPair.create(CmsSearchField.FIELD_DATE_EXPIRED, ORDER.asc);
975            case dateExpired_desc:
976                return CmsPair.create(CmsSearchField.FIELD_DATE_EXPIRED, ORDER.desc);
977            case dateLastModified_asc:
978                return CmsPair.create(CmsSearchField.FIELD_DATE_LASTMODIFIED, ORDER.asc);
979            case dateLastModified_desc:
980                return CmsPair.create(CmsSearchField.FIELD_DATE_LASTMODIFIED, ORDER.desc);
981            case dateReleased_asc:
982                return CmsPair.create(CmsSearchField.FIELD_DATE_RELEASED, ORDER.asc);
983            case dateReleased_desc:
984                return CmsPair.create(CmsSearchField.FIELD_DATE_RELEASED, ORDER.desc);
985            case length_asc:
986                return CmsPair.create(CmsSearchField.FIELD_SIZE, ORDER.asc);
987            case length_desc:
988                return CmsPair.create(CmsSearchField.FIELD_SIZE, ORDER.desc);
989            case path_asc:
990                return CmsPair.create(CmsSearchField.FIELD_PATH, ORDER.asc);
991            case path_desc:
992                return CmsPair.create(CmsSearchField.FIELD_PATH, ORDER.desc);
993            case score:
994                return CmsPair.create(CmsSearchField.FIELD_SCORE, ORDER.desc);
995            case state_asc:
996                return CmsPair.create(CmsSearchField.FIELD_STATE, ORDER.asc);
997            case state_desc:
998                return CmsPair.create(CmsSearchField.FIELD_STATE, ORDER.desc);
999            case title_asc:
1000                return CmsPair.create(sortTitle, ORDER.asc);
1001            case title_desc:
1002                return CmsPair.create(sortTitle, ORDER.desc);
1003            case type_asc:
1004                return CmsPair.create(CmsSearchField.FIELD_TYPE, ORDER.asc);
1005            case type_desc:
1006                return CmsPair.create(CmsSearchField.FIELD_TYPE, ORDER.desc);
1007            case userCreated_asc:
1008                return CmsPair.create(CmsSearchField.FIELD_USER_CREATED, ORDER.asc);
1009            case userCreated_desc:
1010                return CmsPair.create(CmsSearchField.FIELD_USER_CREATED, ORDER.desc);
1011            case userLastModified_asc:
1012                return CmsPair.create(CmsSearchField.FIELD_USER_LAST_MODIFIED, ORDER.asc);
1013            case userLastModified_desc:
1014                return CmsPair.create(CmsSearchField.FIELD_USER_LAST_MODIFIED, ORDER.desc);
1015            default:
1016                return CmsPair.create(sortTitle, ORDER.asc);
1017        }
1018    }
1019
1020    /**
1021     * Applies the defined search folders to the Solr query.
1022     *
1023     * @param obj The current CmsObject object.
1024     */
1025    private void setSearchFolders(CmsObject obj) {
1026
1027        // check if parentFolders to search in have been set
1028        // if this evaluates false, the search folders have already been set, so
1029        // there's no need to add a scope filter
1030        if (m_foldersToSearchIn.isEmpty()) {
1031            // only append scope filter if no no folders or galleries given
1032            setSearchScopeFilter(obj);
1033        }
1034    }
1035
1036    /**
1037     * Sets the search scope.
1038     *
1039     * @param cms The current CmsObject object.
1040     */
1041    private void setSearchScopeFilter(CmsObject cms) {
1042
1043        final List<String> searchRoots = CmsSearchUtil.computeScopeFolders(cms, this);
1044
1045        // If the resource types contain the type "function" also
1046        // add "/system/modules/" to the search path
1047
1048        if ((null != getResourceTypes()) && containsFunctionType(getResourceTypes())) {
1049            searchRoots.add("/system/modules/");
1050        }
1051
1052        addFoldersToSearchIn(searchRoots);
1053    }
1054}