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