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.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsResource;
034import org.opencms.file.types.CmsResourceTypeFunctionConfig;
035import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
036import org.opencms.i18n.CmsLocaleManager;
037import org.opencms.loader.CmsLoaderException;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.search.I_CmsSearchDocument;
042import org.opencms.search.fields.CmsSearchField;
043import org.opencms.search.fields.CmsSearchFieldConfiguration;
044import org.opencms.util.CmsStringUtil;
045import org.opencms.util.CmsUUID;
046
047import java.util.ArrayList;
048import java.util.Arrays;
049import java.util.Date;
050import java.util.List;
051import java.util.Locale;
052import java.util.Map;
053
054import org.apache.commons.logging.Log;
055
056/**
057 * Contains a single search result from the gallery search index.<p>
058 *
059 * @since 8.0.0
060 */
061/**
062 *
063 */
064public class CmsGallerySearchResult implements Comparable<CmsGallerySearchResult> {
065
066    /** The logger instance for this class. */
067    public static final Log LOG = CmsLog.getLog(CmsGallerySearchResult.class);
068
069    /** List of Solr fields that are read during the initialization of the search result. */
070    private static String[] m_requiredSolrFields;
071
072    /** The additional information for the gallery search index. */
073    protected String m_additonalInfo;
074
075    /** The supported container types of this search result. */
076    protected List<String> m_containerTypes;
077
078    /** The creation date of this search result. */
079    protected Date m_dateCreated;
080
081    /** The expiration date of this search result. */
082    protected Date m_dateExpired;
083
084    /** The last modification date of this search result. */
085    protected Date m_dateLastModified;
086
087    /** The release date of this search result. */
088    protected Date m_dateReleased;
089
090    /** The description of this search result. */
091    protected String m_description;
092
093    /** The excerpt of this search result. */
094    protected String m_excerpt;
095
096    /** The length of the search result. */
097    protected int m_length;
098
099    /** The locales in which the content is available. */
100    protected List<String> m_locales;
101
102    /** The resource path of this search result. */
103    protected String m_path;
104
105    /** The resource type of the search result. */
106    protected String m_resourceType;
107
108    /** The score of this search result. */
109    protected int m_score;
110
111    /** The state of the search result. */
112    protected int m_state;
113
114    /** The structure UUID of the resource. */
115    protected String m_structureId;
116
117    /** The title of this search result. */
118    protected String m_title;
119
120    /** The user who created the search result resource. */
121    protected String m_userCreated;
122
123    /** The user who last modified the search result resource. */
124    protected String m_userLastModified;
125
126    /**
127     * Creates a fake gallery search result by reading the necessary data from a VFS resource.<p>
128     *
129     * @param cms the current CMS context
130     * @param res the resource from which the data should be read
131     */
132    public CmsGallerySearchResult(CmsObject cms, CmsResource res) {
133
134        try {
135            Map<String, String> props = CmsProperty.toMap(
136                cms.readPropertyObjects(res, CmsResourceTypeXmlContainerPage.isContainerPage(res)));
137            m_title = props.get(CmsPropertyDefinition.PROPERTY_TITLE);
138            m_description = props.get(CmsPropertyDefinition.PROPERTY_DESCRIPTION);
139        } catch (CmsException e) {
140            LOG.error(e.getLocalizedMessage(), e);
141        }
142        if (m_description == null) {
143            m_description = "";
144        }
145        if (m_title == null) {
146            m_title = res.getName();
147        }
148        m_dateCreated = new Date(res.getDateCreated());
149        m_dateExpired = new Date(res.getDateExpired());
150        m_dateLastModified = new Date(res.getDateLastModified());
151        m_dateReleased = new Date(res.getDateReleased());
152        m_length = res.getLength();
153        m_locales = null;
154        m_path = res.getRootPath();
155        try {
156            m_resourceType = OpenCms.getResourceManager().getResourceType(res.getTypeId()).getTypeName();
157        } catch (CmsLoaderException e) {
158            LOG.warn(e.getLocalizedMessage(), e);
159        }
160        m_state = res.getState().getState();
161        m_structureId = res.getStructureId().toString();
162        try {
163            m_userCreated = cms.readUser(res.getUserCreated()).getFullName();
164        } catch (CmsException e) {
165            LOG.warn(e.getLocalizedMessage(), e);
166        }
167        try {
168            m_userLastModified = cms.readUser(res.getUserLastModified()).getFullName();
169        } catch (CmsException e) {
170            LOG.warn(e.getLocalizedMessage(), e);
171        }
172    }
173
174    /**
175     * Creates a new gallery search result.
176     *
177     * @param cms the current CMS context (used for reading information missing from the index)
178     * @param doc the I_CmsSearchResult document to extract information from.
179     * @param score the score of this search result
180     * @param locale the locale to create the result for
181     */
182    public CmsGallerySearchResult(I_CmsSearchDocument doc, CmsObject cms, int score, Locale locale) {
183
184        if (null == doc) {
185            throw new IllegalArgumentException();
186        }
187
188        if (doc.getType().equals(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
189            locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
190        }
191
192        m_score = score; //(int)doc.getScore();
193
194        m_path = doc.getFieldValueAsString(CmsSearchField.FIELD_PATH);
195
196        if (null == locale) {
197            OpenCms.getLocaleManager();
198            locale = CmsLocaleManager.getDefaultLocale();
199        }
200
201        // For title and description, try various fields and use the first which is not empty
202
203        String mainTitleField = CmsSearchFieldConfiguration.getLocaleExtendedName(
204            CmsSearchField.FIELD_TITLE_UNSTORED,
205            locale.toString()) + "_s";
206        String localizedTitlePropertyField = CmsSearchFieldConfiguration.getLocaleExtendedName(
207            CmsPropertyDefinition.PROPERTY_TITLE,
208            locale.toString()) + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT + "_s";
209        String titlePropertyField = CmsPropertyDefinition.PROPERTY_TITLE
210            + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT
211            + "_s";
212
213        for (String fieldName : Arrays.asList(mainTitleField, localizedTitlePropertyField, titlePropertyField)) {
214            m_title = doc.getFieldValueAsString(fieldName);
215            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_title)) {
216                break;
217            }
218        }
219
220        String mainDescField = CmsSearchFieldConfiguration.getLocaleExtendedName(
221            CmsSearchField.FIELD_DESCRIPTION,
222            locale.toString()) + "_s";
223        String localizedDescPropertyField = CmsSearchFieldConfiguration.getLocaleExtendedName(
224            CmsPropertyDefinition.PROPERTY_DESCRIPTION,
225            locale.toString()) + CmsSearchField.FIELD_DYNAMIC_PROPERTIES + "_s";
226        String descPropertyField = CmsPropertyDefinition.PROPERTY_DESCRIPTION
227            + CmsSearchField.FIELD_DYNAMIC_PROPERTIES
228            + "_s";
229
230        for (String fieldName : Arrays.asList(mainDescField, localizedDescPropertyField, descPropertyField)) {
231            m_description = doc.getFieldValueAsString(fieldName);
232            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_description)) {
233                break;
234            }
235        }
236
237        m_resourceType = doc.getFieldValueAsString(CmsSearchField.FIELD_TYPE);
238
239        m_dateCreated = doc.getFieldValueAsDate(CmsSearchField.FIELD_DATE_CREATED);
240
241        m_dateLastModified = doc.getFieldValueAsDate(CmsSearchField.FIELD_DATE_LASTMODIFIED);
242
243        m_dateExpired = doc.getFieldValueAsDate(CmsSearchField.FIELD_DATE_EXPIRED);
244
245        m_dateReleased = doc.getFieldValueAsDate(CmsSearchField.FIELD_DATE_RELEASED);
246
247        m_length = 0;
248        final String s_length = doc.getFieldValueAsString(CmsSearchField.FIELD_SIZE);
249        if (null != s_length) {
250            try {
251                m_length = Integer.parseInt(s_length);
252            } catch (NumberFormatException exc) {
253                // NOOP, default is 0
254            }
255        }
256
257        m_state = 0;
258        final String s_state = doc.getFieldValueAsString(CmsSearchField.FIELD_STATE);
259        if (s_state != null) {
260            try {
261                m_state = Integer.parseInt(s_state);
262            } catch (NumberFormatException exc) {
263                // NOOP, default is 0
264            }
265        }
266
267        m_userCreated = doc.getFieldValueAsString(CmsSearchField.FIELD_USER_CREATED);
268
269        m_structureId = doc.getFieldValueAsString(CmsSearchField.FIELD_ID);
270
271        m_userLastModified = doc.getFieldValueAsString(CmsSearchField.FIELD_USER_LAST_MODIFIED);
272
273        m_additonalInfo = doc.getFieldValueAsString(CmsSearchField.FIELD_ADDITIONAL_INFO);
274        final String s_containerTypes = doc.getFieldValueAsString(CmsSearchField.FIELD_CONTAINER_TYPES);
275        if (s_containerTypes != null) {
276            m_containerTypes = CmsStringUtil.splitAsList(s_containerTypes, ' ');
277        } else {
278            m_containerTypes = new ArrayList<String>(0);
279        }
280
281        final String s_locales = doc.getFieldValueAsString(CmsSearchField.FIELD_RESOURCE_LOCALES);
282        if (null != s_locales) {
283            m_locales = CmsStringUtil.splitAsList(s_locales, ' ');
284        } else {
285            m_locales = new ArrayList<String>(0);
286        }
287
288        if ((null != cms) && (null != m_structureId)) {
289            initializeMissingFieldsFromVfs(cms, new CmsUUID(m_structureId));
290        }
291    }
292
293    /**
294     * Returns the list of Solr fields a search result must have to initialize the gallery search result correctly.
295     * @return the list of Solr fields.
296     */
297    public static final String[] getRequiredSolrFields() {
298
299        if (null == m_requiredSolrFields) {
300            List<Locale> locales = OpenCms.getLocaleManager().getAvailableLocales();
301            m_requiredSolrFields = new String[14 + (locales.size() * 6)];
302            int count = 0;
303            m_requiredSolrFields[count++] = CmsSearchField.FIELD_PATH;
304            m_requiredSolrFields[count++] = CmsSearchField.FIELD_TYPE;
305            m_requiredSolrFields[count++] = CmsSearchField.FIELD_DATE_CREATED;
306            m_requiredSolrFields[count++] = CmsSearchField.FIELD_DATE_LASTMODIFIED;
307            m_requiredSolrFields[count++] = CmsSearchField.FIELD_DATE_EXPIRED;
308            m_requiredSolrFields[count++] = CmsSearchField.FIELD_DATE_RELEASED;
309            m_requiredSolrFields[count++] = CmsSearchField.FIELD_SIZE;
310            m_requiredSolrFields[count++] = CmsSearchField.FIELD_STATE;
311            m_requiredSolrFields[count++] = CmsSearchField.FIELD_USER_CREATED;
312            m_requiredSolrFields[count++] = CmsSearchField.FIELD_ID;
313            m_requiredSolrFields[count++] = CmsSearchField.FIELD_USER_LAST_MODIFIED;
314            m_requiredSolrFields[count++] = CmsSearchField.FIELD_ADDITIONAL_INFO;
315            m_requiredSolrFields[count++] = CmsSearchField.FIELD_CONTAINER_TYPES;
316            m_requiredSolrFields[count++] = CmsSearchField.FIELD_RESOURCE_LOCALES;
317            for (Locale locale : locales) {
318                m_requiredSolrFields[count++] = CmsSearchFieldConfiguration.getLocaleExtendedName(
319                    CmsSearchField.FIELD_TITLE_UNSTORED,
320                    locale.toString()) + "_s";
321                m_requiredSolrFields[count++] = CmsSearchFieldConfiguration.getLocaleExtendedName(
322                    CmsPropertyDefinition.PROPERTY_TITLE,
323                    locale.toString()) + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT + "_s";
324                m_requiredSolrFields[count++] = CmsPropertyDefinition.PROPERTY_TITLE
325                    + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT
326                    + "_s";
327                m_requiredSolrFields[count++] = CmsSearchFieldConfiguration.getLocaleExtendedName(
328                    CmsSearchField.FIELD_DESCRIPTION,
329                    locale.toString()) + "_s";
330                m_requiredSolrFields[count++] = CmsSearchFieldConfiguration.getLocaleExtendedName(
331                    CmsPropertyDefinition.PROPERTY_DESCRIPTION,
332                    locale.toString()) + CmsSearchField.FIELD_DYNAMIC_PROPERTIES + "_s";
333                m_requiredSolrFields[count++] = CmsPropertyDefinition.PROPERTY_DESCRIPTION
334                    + CmsSearchField.FIELD_DYNAMIC_PROPERTIES
335                    + "_s";
336            }
337        }
338        return m_requiredSolrFields;
339    }
340
341    /**
342     * Compares two search results based on the score of the result.<p>
343     *
344     * @param other the result to compare this result with
345     * @return the comparison result
346     *
347     * @see java.lang.Comparable#compareTo(java.lang.Object)
348     */
349    public int compareTo(CmsGallerySearchResult other) {
350
351        if (other == this) {
352            return 0;
353        }
354        return other.m_score - m_score;
355    }
356
357    /**
358     * @see java.lang.Object#equals(java.lang.Object)
359     */
360    @Override
361    public boolean equals(Object obj) {
362
363        if (obj == this) {
364            return true;
365        }
366        if (obj instanceof CmsGallerySearchResult) {
367            CmsGallerySearchResult other = (CmsGallerySearchResult)obj;
368            return m_path.equals(other.m_path);
369        }
370        return false;
371    }
372
373    /**
374     * Returns the additional information stored for this search result in the gallery search index.<p>
375     *
376     * @return the additional information stored for this search result in the gallery search index
377     */
378    public String getAdditonalInfo() {
379
380        return m_additonalInfo;
381    }
382
383    /**
384     * Returns the containers supported by this resource.<p>
385     *
386     * @return the containers supported by this resource
387     */
388    public List<String> getContainerTypes() {
389
390        return m_containerTypes;
391    }
392
393    /**
394     * Returns the date created.<p>
395     *
396     * @return the date created
397     */
398    public Date getDateCreated() {
399
400        return m_dateCreated;
401    }
402
403    /**
404     * Returns the date the resource expires.<p>
405     *
406     * @return the date the resource expires
407     *
408     * @see org.opencms.file.CmsResource#getDateExpired()
409     */
410    public Date getDateExpired() {
411
412        return m_dateExpired;
413    }
414
415    /**
416     * Returns the date last modified.<p>
417     *
418     * @return the date last modified
419     */
420    public Date getDateLastModified() {
421
422        return m_dateLastModified;
423    }
424
425    /**
426     * Returns the date the resource is released.<p>
427     *
428     * @return the date the resource is released
429     *
430     * @see  org.opencms.file.CmsResource#getDateReleased()
431     */
432    public Date getDateReleased() {
433
434        return m_dateReleased;
435    }
436
437    /**
438     * Returns the description.<p>
439     *
440     * @return the description
441     */
442    public String getDescription() {
443
444        return m_description;
445    }
446
447    /**
448     * Returns the excerpt.<p>
449     *
450     * @return the excerpt
451     */
452    public String getExcerpt() {
453
454        return m_excerpt;
455    }
456
457    /**
458     * Returns the length of the resource.<p>
459     *
460     * @return the length of the resource
461     *
462     * @see org.opencms.file.CmsResource#getLength()
463     */
464    public int getLength() {
465
466        return m_length;
467    }
468
469    /**
470     * Returns the list of locales this search result is available for.<p>
471     *
472     * @return the list of locales this search result is available for
473     */
474    public List<String> getLocales() {
475
476        return m_locales;
477    }
478
479    /**
480     * Returns the resource root path.<p>
481     *
482     * @return the resource root path
483     *
484     * @see org.opencms.file.CmsResource#getRootPath()
485     */
486    public String getPath() {
487
488        return m_path;
489    }
490
491    /**
492     * Returns the resource type of the search result document.<p>
493     *
494     * @return the resource type of the search result document
495     *
496     * @see org.opencms.loader.CmsResourceManager#getResourceType(String)
497     */
498    public String getResourceType() {
499
500        return m_resourceType;
501    }
502
503    /**
504     * Returns the Lucene search score for this result.<p>
505     *
506     * @return the Lucene search score for this result
507     */
508    public int getScore() {
509
510        return m_score;
511    }
512
513    /**
514     * Returns the state of the resource.<p>
515     *
516     * @return the state of the resource
517     *
518     * @see org.opencms.file.CmsResource#getState()
519     */
520    public int getState() {
521
522        return m_state;
523    }
524
525    /**
526     * Returns the structure id of the resource.<p>
527     *
528     * @return the structure id of the resource
529     */
530    public String getStructureId() {
531
532        return m_structureId;
533    }
534
535    /**
536     * Returns the title of the resource.<p>
537     *
538     * @return the title of the resource
539     */
540    public String getTitle() {
541
542        return m_title;
543    }
544
545    /**
546     * Returns the name of the user who created the resource.<p>
547     *
548     * @return the name of the user who created the resource
549     *
550     * @see org.opencms.file.CmsResource#getUserCreated()
551     */
552    public String getUserCreated() {
553
554        return m_userCreated;
555    }
556
557    /**
558     * Returns the name of the user who last modified the resource.<p>
559     *
560     * @return the name of the user who last modified the resource
561     *
562     * @see org.opencms.file.CmsResource#getUserLastModified()
563     */
564    public String getUserLastModified() {
565
566        return m_userLastModified;
567    }
568
569    /**
570     * @see java.lang.Object#hashCode()
571     */
572    @Override
573    public int hashCode() {
574
575        return m_path.hashCode();
576    }
577
578    /**
579     * Returns if the related resource is released and not expired.<p>
580     *
581     * @param cms the cms context
582     *
583     * @return <code>true</code> if the related resource is released and not expired
584     */
585    public boolean isReleaseAndNotExpired(CmsObject cms) {
586
587        long time = cms.getRequestContext().getRequestTime();
588        return (time == CmsResource.DATE_RELEASED_EXPIRED_IGNORE)
589            || ((time > m_dateReleased.getTime()) && (time < m_dateExpired.getTime()));
590    }
591
592    /**
593     * Initializes missing fields by reading the information from the VFS.<p>
594     *
595     * @param cms the current CMS context
596     * @param structureId the current structure id
597     */
598    protected void initializeMissingFieldsFromVfs(CmsObject cms, CmsUUID structureId) {
599
600        if (structureId == null) {
601            return;
602        }
603        if ((m_title != null) && (m_description != null)) {
604            return;
605        }
606
607        try {
608            CmsResource res = cms.readResource(structureId);
609            if (m_description == null) {
610                CmsProperty descProp = cms.readPropertyObject(
611                    res,
612                    CmsPropertyDefinition.PROPERTY_DESCRIPTION,
613                    CmsResourceTypeXmlContainerPage.isContainerPage(res));
614                m_description = descProp.getValue();
615            }
616
617            if (m_title == null) {
618                CmsProperty titleProp = cms.readPropertyObject(
619                    res,
620                    CmsPropertyDefinition.PROPERTY_TITLE,
621                    CmsResourceTypeXmlContainerPage.isContainerPage(res));
622                m_title = titleProp.getValue();
623                if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_title)) {
624                    m_title = res.getName();
625                }
626            }
627        } catch (CmsException e) {
628            LOG.error(e.getLocalizedMessage(), e);
629            return;
630        }
631    }
632}