001/* 002 * File : $Source$ 003 * Date : $Date$ 004 * Version: $Revision$ 005 * 006 * This library is part of OpenCms - 007 * the Open Source Content Management System 008 * 009 * Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com) 010 * 011 * This library is free software; you can redistribute it and/or 012 * modify it under the terms of the GNU Lesser General Public 013 * License as published by the Free Software Foundation; either 014 * version 2.1 of the License, or (at your option) any later version. 015 * 016 * This library is distributed in the hope that it will be useful, 017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 * Lesser General Public License for more details. 020 * 021 * For further information about Alkacon Software, please see the 022 * company website: http://www.alkacon.com 023 * 024 * For further information about OpenCms, please see the 025 * project website: http://www.opencms.org 026 * 027 * You should have received a copy of the GNU Lesser General Public 028 * License along with this library; if not, write to the Free Software 029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 030 */ 031 032package org.opencms.search.solr; 033 034import org.opencms.acacia.shared.I_CmsSerialDateValue; 035import org.opencms.ade.containerpage.CmsDetailOnlyContainerUtil; 036import org.opencms.file.CmsFile; 037import org.opencms.file.CmsObject; 038import org.opencms.file.CmsPropertyDefinition; 039import org.opencms.file.CmsResource; 040import org.opencms.file.types.CmsResourceTypeXmlContent; 041import org.opencms.i18n.CmsLocaleManager; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsLog; 044import org.opencms.main.OpenCms; 045import org.opencms.search.CmsIndexException; 046import org.opencms.search.CmsSearchUtil; 047import org.opencms.search.I_CmsSearchIndex; 048import org.opencms.search.documents.A_CmsVfsDocument; 049import org.opencms.search.documents.CmsIndexNoContentException; 050import org.opencms.search.documents.Messages; 051import org.opencms.search.extractors.CmsExtractionResult; 052import org.opencms.search.extractors.I_CmsExtractionResult; 053import org.opencms.search.fields.CmsSearchField; 054import org.opencms.search.fields.CmsSearchFieldConfiguration; 055import org.opencms.search.galleries.CmsGalleryNameMacroResolver; 056import org.opencms.util.CmsStringUtil; 057import org.opencms.util.CmsUUID; 058import org.opencms.widgets.serialdate.CmsSerialDateBeanFactory; 059import org.opencms.widgets.serialdate.CmsSerialDateValue; 060import org.opencms.widgets.serialdate.I_CmsSerialDateBean; 061import org.opencms.xml.A_CmsXmlDocument; 062import org.opencms.xml.CmsXmlContentDefinition; 063import org.opencms.xml.CmsXmlUtils; 064import org.opencms.xml.content.CmsXmlContent; 065import org.opencms.xml.content.CmsXmlContentFactory; 066import org.opencms.xml.content.I_CmsXmlContentHandler; 067import org.opencms.xml.types.CmsXmlDateTimeValue; 068import org.opencms.xml.types.CmsXmlHtmlValue; 069import org.opencms.xml.types.CmsXmlNestedContentDefinition; 070import org.opencms.xml.types.CmsXmlSerialDateValue; 071import org.opencms.xml.types.I_CmsXmlContentValue; 072import org.opencms.xml.types.I_CmsXmlSchemaType; 073 074import java.util.ArrayList; 075import java.util.Collections; 076import java.util.HashMap; 077import java.util.HashSet; 078import java.util.LinkedHashMap; 079import java.util.List; 080import java.util.Locale; 081import java.util.Map; 082import java.util.Set; 083import java.util.function.Consumer; 084 085import org.apache.commons.logging.Log; 086 087import com.google.common.collect.Sets; 088 089/** 090 * Special document text extraction factory for Solr index.<p> 091 * 092 * @since 8.5.0 093 */ 094public class CmsSolrDocumentXmlContent extends A_CmsVfsDocument { 095 096 /** 097 * The gallery name is determined by resolving the macros in a string which can either come from a field mapped 098 * to the gallery name, or the title, or from default values for those fields. This class is used to select the 099 * value to use and performs the macro substitution. 100 */ 101 private static class GalleryNameChooser { 102 103 /** CMS context for this instance. */ 104 private CmsObject m_cms; 105 106 /** Current XML content. */ 107 private A_CmsXmlDocument m_content; 108 109 /** Default value of field mapped to gallery name. */ 110 private String m_defaultGalleryNameValue; 111 112 /** Default value of field mapped to title. */ 113 private String m_defaultTitleValue; 114 115 /** Current locale. */ 116 private Locale m_locale; 117 118 /** Content value mapped to Description property. */ 119 private String m_mappedDescriptionValue; 120 121 /** Content value mapped to gallery description. */ 122 private String m_mappedGalleryDescriptionValue; 123 124 /** Content value mapped to gallery name. */ 125 private String m_mappedGalleryNameValue; 126 127 /** Content value mapped to title. */ 128 private String m_mappedTitleValue; 129 130 /** 131 * Creates a new instance.<p> 132 * 133 * @param cms the CMS context 134 * @param content the XML content 135 * @param locale the locale in the XML content 136 */ 137 public GalleryNameChooser(CmsObject cms, A_CmsXmlDocument content, Locale locale) { 138 139 m_cms = cms; 140 m_content = content; 141 m_locale = locale; 142 } 143 144 /** 145 * Selects the description displayed in the gallery.<p> 146 * 147 * This method assumes that all the available values have been set via the setters of this class. 148 * 149 * @return the description 150 * 151 * @throws CmsException of something goes wrong 152 */ 153 public String getDescription() throws CmsException { 154 155 return getDescription(m_locale); 156 } 157 158 /** 159 * Selects the description displayed in the gallery.<p> 160 * 161 * This method assumes that all the available values have been set via the setters of this class. 162 * 163 * @param locale the locale to get the description in 164 * 165 * @return the description 166 * 167 * @throws CmsException of something goes wrong 168 */ 169 public String getDescription(Locale locale) throws CmsException { 170 171 String result = null; 172 for (String resultCandidateWithMacros : new String[] { 173 m_mappedGalleryDescriptionValue, 174 m_mappedDescriptionValue}) { 175 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(resultCandidateWithMacros)) { 176 CmsGalleryNameMacroResolver resolver = new CmsGalleryNameMacroResolver(m_cms, m_content, locale); 177 result = resolver.resolveMacros(resultCandidateWithMacros); 178 return result; 179 } 180 } 181 result = m_cms.readPropertyObject( 182 m_content.getFile(), 183 CmsPropertyDefinition.PROPERTY_DESCRIPTION, 184 false).getValue(); 185 return result; 186 } 187 188 /** 189 * Selects the gallery name.<p> 190 * 191 * This method assumes that all the available values have been set via the setters of this class. 192 * 193 * @return the gallery name 194 * 195 * @throws CmsException of something goes wrong 196 */ 197 public String getGalleryName() throws CmsException { 198 199 return getGalleryName(m_locale); 200 } 201 202 /** 203 * Selects the gallery name.<p> 204 * 205 * This method assumes that all the available values have been set via the setters of this class. 206 * 207 * @param locale the locale to get the gallery name in 208 * 209 * @return the gallery name 210 * 211 * @throws CmsException of something goes wrong 212 */ 213 public String getGalleryName(Locale locale) throws CmsException { 214 215 String result = null; 216 for (String resultCandidateWithMacros : new String[] { 217 // Prioritize gallery name over title, and actual content values over defaults 218 m_mappedGalleryNameValue, 219 m_defaultGalleryNameValue, 220 m_mappedTitleValue, 221 m_defaultTitleValue}) { 222 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(resultCandidateWithMacros)) { 223 CmsGalleryNameMacroResolver resolver = new CmsGalleryNameMacroResolver(m_cms, m_content, locale); 224 result = resolver.resolveMacros(resultCandidateWithMacros); 225 return result; 226 } 227 } 228 result = m_cms.readPropertyObject( 229 m_content.getFile(), 230 CmsPropertyDefinition.PROPERTY_TITLE, 231 false).getValue(); 232 return result; 233 } 234 235 /** 236 * Sets the defaultGalleryNameValue.<p> 237 * 238 * @param defaultGalleryNameValue the defaultGalleryNameValue to set 239 */ 240 public void setDefaultGalleryNameValue(String defaultGalleryNameValue) { 241 242 m_defaultGalleryNameValue = defaultGalleryNameValue; 243 } 244 245 /** 246 * Sets the defaultTitleValue.<p> 247 * 248 * @param defaultTitleValue the defaultTitleValue to set 249 */ 250 public void setDefaultTitleValue(String defaultTitleValue) { 251 252 m_defaultTitleValue = defaultTitleValue; 253 } 254 255 /** 256 * Sets the mapped description value.<p> 257 * 258 * @param mappedDescriptionValue the mappedDescriptionValue to set 259 */ 260 public void setMappedDescriptionValue(String mappedDescriptionValue) { 261 262 m_mappedDescriptionValue = mappedDescriptionValue; 263 } 264 265 /** 266 * Sets the name from a value mapped via 'galleryDescription'. 267 * 268 * @param mappedGalleryDescriptionValue the value that has been mapped 269 */ 270 public void setMappedGalleryDescriptionValue(String mappedGalleryDescriptionValue) { 271 272 m_mappedGalleryDescriptionValue = mappedGalleryDescriptionValue; 273 } 274 275 /** 276 * Sets the mappedGalleryNameValue.<p> 277 * 278 * @param mappedGalleryNameValue the mappedGalleryNameValue to set 279 */ 280 public void setMappedGalleryNameValue(String mappedGalleryNameValue) { 281 282 m_mappedGalleryNameValue = mappedGalleryNameValue; 283 } 284 285 /** 286 * Sets the mappedTitleValue.<p> 287 * 288 * @param mappedTitleValue the mappedTitleValue to set 289 */ 290 public void setMappedTitleValue(String mappedTitleValue) { 291 292 m_mappedTitleValue = mappedTitleValue; 293 } 294 } 295 296 /** Mapping name used to indicate that the value should be used for the gallery description. */ 297 public static final String MAPPING_GALLERY_DESCRIPTION = "galleryDescription"; 298 299 /** Mapping name used to indicate that the value should be used for the gallery name. */ 300 public static final String MAPPING_GALLERY_NAME = "galleryName"; 301 302 /** The log object for this class. */ 303 private static final Log LOG = CmsLog.getLog(CmsSolrDocumentXmlContent.class); 304 305 /** 306 * Public constructor.<p> 307 * 308 * @param name the name for the document type 309 */ 310 public CmsSolrDocumentXmlContent(String name) { 311 312 super(name); 313 } 314 315 /** 316 * Collects a list of all possible XPaths for a content definition.<p> 317 * 318 * @param cms the CMS context to use 319 * @param def the content definition 320 * @param path the path of the given content definition 321 * @param result the set used to collect the XPaths 322 */ 323 public static void collectSchemaXpathsForSimpleValues( 324 CmsObject cms, 325 CmsXmlContentDefinition def, 326 String path, 327 Set<String> result) { 328 329 List<I_CmsXmlSchemaType> nestedTypes = def.getTypeSequence(); 330 for (I_CmsXmlSchemaType nestedType : nestedTypes) { 331 String subPath = path + "/" + nestedType.getName(); 332 if (nestedType instanceof CmsXmlNestedContentDefinition) { 333 CmsXmlContentDefinition nestedDef = ((CmsXmlNestedContentDefinition)nestedType).getNestedContentDefinition(); 334 collectSchemaXpathsForSimpleValues(cms, nestedDef, subPath, result); 335 } else { 336 result.add(subPath); 337 } 338 } 339 } 340 341 /** 342 * Extracts the content of a single XML content resource.<p> 343 * 344 * @param cms the cms context 345 * @param resource the resource 346 * @param index the used index 347 * 348 * @return the extraction result 349 * 350 * @throws CmsException in case reading or unmarshalling the content fails 351 */ 352 public static CmsExtractionResult extractXmlContent(CmsObject cms, CmsResource resource, I_CmsSearchIndex index) 353 throws CmsException { 354 355 return extractXmlContent(cms, resource, index, null); 356 } 357 358 /** 359 * Extracts the content of a single XML content resource.<p> 360 * 361 * @param cms the cms context 362 * @param resource the resource 363 * @param index the used index 364 * @param forceLocale if set, only the content values for the given locale will be extracted 365 * 366 * @return the extraction result 367 * 368 * @throws CmsException in case reading or unmarshalling the content fails 369 */ 370 public static CmsExtractionResult extractXmlContent( 371 CmsObject cms, 372 CmsResource resource, 373 I_CmsSearchIndex index, 374 Locale forceLocale) 375 throws CmsException { 376 377 return extractXmlContent(cms, resource, index, forceLocale, null); 378 } 379 380 /** 381 * Extracts the content of a single XML content resource.<p> 382 * 383 * @param cms the cms context 384 * @param resource the resource 385 * @param index the used index 386 * @param forceLocale if set, only the content values for the given locale will be extracted 387 * @param alreadyExtracted keeps track of ids of contents which have already been extracted 388 * 389 * @return the extraction result 390 * 391 * @throws CmsException in case reading or unmarshalling the content fails 392 */ 393 public static CmsExtractionResult extractXmlContent( 394 CmsObject cms, 395 CmsResource resource, 396 I_CmsSearchIndex index, 397 Locale forceLocale, 398 Set<CmsUUID> alreadyExtracted) 399 throws CmsException { 400 401 return extractXmlContent( 402 cms, 403 resource, 404 index, 405 forceLocale, 406 alreadyExtracted, 407 content -> {/*do nothing with the content*/}); 408 409 } 410 411 /** 412 * Extracts the content of a single XML content resource.<p> 413 * 414 * @param cms the cms context 415 * @param resource the resource 416 * @param index the used index 417 * @param forceLocale if set, only the content values for the given locale will be extracted 418 * @param alreadyExtracted keeps track of ids of contents which have already been extracted 419 * @param contentConsumer gets called with the unmarshalled content object 420 * 421 * @return the extraction result 422 * 423 * @throws CmsException in case reading or unmarshalling the content fails 424 */ 425 public static CmsExtractionResult extractXmlContent( 426 CmsObject cms, 427 CmsResource resource, 428 I_CmsSearchIndex index, 429 Locale forceLocale, 430 Set<CmsUUID> alreadyExtracted, 431 Consumer<A_CmsXmlDocument> contentConsumer) 432 throws CmsException { 433 434 if (null == alreadyExtracted) { 435 alreadyExtracted = Collections.emptySet(); 436 } 437 // un-marshal the content 438 CmsFile file = cms.readFile(resource); 439 if (file.getLength() <= 0) { 440 throw new CmsIndexNoContentException( 441 Messages.get().container(Messages.ERR_NO_CONTENT_1, resource.getRootPath())); 442 } 443 A_CmsXmlDocument xmlContent = CmsXmlContentFactory.unmarshal(cms, file); 444 if (contentConsumer != null) { 445 contentConsumer.accept(xmlContent); 446 } 447 448 // initialize some variables 449 Map<Locale, LinkedHashMap<String, String>> items = new HashMap<Locale, LinkedHashMap<String, String>>(); 450 Map<String, String> fieldMappings = new HashMap<String, String>(); 451 List<Locale> contentLocales = forceLocale != null 452 ? Collections.singletonList(forceLocale) 453 : xmlContent.getLocales(); 454 Locale resourceLocale = index.getLocaleForResource(cms, resource, contentLocales); 455 456 LinkedHashMap<String, String> localeItems = null; 457 GalleryNameChooser galleryNameChooser = null; 458 // loop over the locales of the content 459 for (Locale locale : contentLocales) { 460 galleryNameChooser = new GalleryNameChooser(cms, xmlContent, locale); 461 localeItems = new LinkedHashMap<String, String>(); 462 StringBuffer textContent = new StringBuffer(); 463 // store the locales of the content as space separated field 464 // loop over the available element paths of the current content locale 465 List<String> paths = xmlContent.getNames(locale); 466 for (String xpath : paths) { 467 468 // try to get the value extraction for the current element path 469 String extracted = null; 470 I_CmsXmlContentValue value = xmlContent.getValue(xpath, locale); 471 try { 472 //the new DatePointField.createField dose not support milliseconds 473 if (value instanceof CmsXmlDateTimeValue) { 474 extracted = CmsSearchUtil.getDateAsIso8601(((CmsXmlDateTimeValue)value).getDateTimeValue()); 475 } else { 476 extracted = value.getPlainText(cms); 477 if (CmsStringUtil.isEmptyOrWhitespaceOnly(extracted) 478 && value.isSimpleType() 479 && !(value instanceof CmsXmlHtmlValue)) { 480 // no text value for simple type, so take the string value as item 481 // prevent this for elements of type "OpenCmsHtml", since this causes problematic values 482 // being indexed, e.g., <iframe ...></iframe> 483 // TODO: Why is this special handling needed at all??? 484 extracted = value.getStringValue(cms); 485 } 486 } 487 } catch (Exception e) { 488 // it can happen that a exception is thrown while extracting a single value 489 LOG.warn(Messages.get().container(Messages.LOG_EXTRACT_VALUE_2, xpath, resource), e); 490 } 491 492 // put the extraction to the items and to the textual content 493 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(extracted)) { 494 localeItems.put(xpath, extracted); 495 } 496 switch (xmlContent.getHandler().getSearchContentType(value)) { 497 case TRUE: 498 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(extracted)) { 499 textContent.append(extracted); 500 textContent.append('\n'); 501 } 502 break; 503 case CONTENT: 504 // TODO: Potentially extend to allow for indexing of non-xml-contents as well. 505 String potentialLinkValue = value.getStringValue(cms); 506 try { 507 if ((null != potentialLinkValue) 508 && !potentialLinkValue.isEmpty() 509 && cms.existsResource(potentialLinkValue)) { 510 CmsResource linkedRes = cms.readResource(potentialLinkValue); 511 if (CmsResourceTypeXmlContent.isXmlContent(linkedRes) 512 && !alreadyExtracted.contains(linkedRes.getStructureId())) { 513 Set<CmsUUID> newAlreadyExtracted = new HashSet<>(alreadyExtracted); 514 newAlreadyExtracted.add(resource.getStructureId()); 515 I_CmsExtractionResult exRes = CmsSolrDocumentXmlContent.extractXmlContent( 516 cms, 517 linkedRes, 518 index, 519 locale, 520 newAlreadyExtracted); 521 String exContent = exRes.getContent(locale); 522 if ((exContent != null) && !exContent.trim().isEmpty()) { 523 textContent.append(exContent.trim()); 524 textContent.append('\n'); 525 break; // Success - we break here to not repeatedly programm a warning. 526 } 527 } 528 } 529 if (LOG.isInfoEnabled()) { 530 LOG.info( 531 "When indexing resource " 532 + resource.getRootPath() 533 + ", the elements value " 534 + value.getPath() 535 + " in locale " 536 + locale 537 + " does not contain a link to an XML content. Hence, the linked element's content is not added to the content indexed for the resource itself."); 538 } 539 } catch (Throwable t) { 540 LOG.error( 541 "Failed to add content of resource (site path) " 542 + potentialLinkValue 543 + " to content of resource (root path) " 544 + resource.getRootPath() 545 + " when indexing the resource for locale " 546 + locale 547 + ". Skipping this content part.", 548 t); 549 } 550 break; 551 default: 552 // we do not index the content element for the content field. 553 break; 554 } 555 556 List<String> mappings = xmlContent.getHandler().getMappings(value.getPath()); 557 if (mappings.size() > 0) { 558 // mappings are defined, lets check if we have mappings that interest us 559 for (String mapping : mappings) { 560 if (mapping.startsWith(I_CmsXmlContentHandler.MAPTO_PROPERTY)) { 561 // this is a property mapping 562 String propertyName = mapping.substring(mapping.lastIndexOf(':') + 1); 563 if (CmsPropertyDefinition.PROPERTY_TITLE.equals(propertyName) 564 || CmsPropertyDefinition.PROPERTY_DESCRIPTION.equals(propertyName)) { 565 566 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(extracted)) { 567 if (CmsPropertyDefinition.PROPERTY_TITLE.equals(propertyName)) { 568 galleryNameChooser.setMappedTitleValue(extracted); 569 } else { 570 // if field is not title, it must be description 571 galleryNameChooser.setMappedDescriptionValue(extracted); 572 } 573 } 574 } 575 } else if (mapping.equals(MAPPING_GALLERY_NAME)) { 576 galleryNameChooser.setMappedGalleryNameValue(value.getPlainText(cms)); 577 } else if (mapping.equals(MAPPING_GALLERY_DESCRIPTION)) { 578 galleryNameChooser.setMappedGalleryDescriptionValue(value.getPlainText(cms)); 579 } 580 } 581 } 582 if (value instanceof CmsXmlSerialDateValue) { 583 if ((null != extracted) && !extracted.isEmpty()) { 584 I_CmsSerialDateValue serialDateValue = new CmsSerialDateValue(extracted); 585 I_CmsSerialDateBean serialDateBean = CmsSerialDateBeanFactory.createSerialDateBean( 586 serialDateValue); 587 if (null != serialDateBean) { 588 StringBuffer values = new StringBuffer(); 589 StringBuffer endValues = new StringBuffer(); 590 StringBuffer currentTillValues = new StringBuffer(); 591 for (Long eventDate : serialDateBean.getDatesAsLong()) { 592 values.append("\n").append(eventDate.toString()); 593 long endDate = null != serialDateBean.getEventDuration() 594 ? eventDate.longValue() + serialDateBean.getEventDuration().longValue() 595 : eventDate.longValue(); 596 endValues.append("\n").append(Long.toString(endDate)); 597 // Special treatment for events that end at 00:00: 598 // To not show them at the day after they ended, one millisecond is removed from the end time 599 // for the "currenttill"-time 600 currentTillValues.append("\n").append( 601 serialDateValue.isCurrentTillEnd() 602 ? Long.valueOf( 603 serialDateValue.endsAtMidNight() && (endDate > eventDate.longValue()) 604 ? endDate - 1L 605 : endDate) 606 : eventDate); 607 } 608 fieldMappings.put(CmsSearchField.FIELD_SERIESDATES, values.substring(1)); 609 fieldMappings.put(CmsSearchField.FIELD_SERIESDATES_END, endValues.substring(1)); 610 fieldMappings.put( 611 CmsSearchField.FIELD_SERIESDATES_CURRENT_TILL, 612 currentTillValues.substring(1)); 613 fieldMappings.put( 614 CmsSearchField.FIELD_SERIESDATES_TYPE, 615 serialDateValue.getDateType().toString()); 616 } else { 617 LOG.warn( 618 "Serial date value \"" 619 + value.getStringValue(cms) 620 + "\" at element \"" 621 + value.getPath() 622 + "\" is invalid. No dates are indexed for resource \"" 623 + resource.getRootPath() 624 + "\"."); 625 } 626 } 627 } 628 } 629 630 Set<String> xpaths = Sets.newHashSet(); 631 collectSchemaXpathsForSimpleValues(cms, xmlContent.getContentDefinition(), "", xpaths); 632 for (String xpath : xpaths) { 633 // mappings always are stored with indexes, so we add them to the xpath 634 List<String> mappings = xmlContent.getHandler().getMappings(CmsXmlUtils.createXpath(xpath, 1)); 635 for (String mapping : mappings) { 636 637 if (mapping.equals(MAPPING_GALLERY_NAME) 638 || mapping.equals( 639 I_CmsXmlContentHandler.MAPTO_PROPERTY + CmsPropertyDefinition.PROPERTY_TITLE)) { 640 String defaultValue = xmlContent.getHandler().getDefault( 641 cms, 642 xmlContent.getFile(), 643 null, 644 xpath, 645 locale); 646 if (mapping.equals(MAPPING_GALLERY_NAME)) { 647 galleryNameChooser.setDefaultGalleryNameValue(defaultValue); 648 } else { 649 galleryNameChooser.setDefaultTitleValue(defaultValue); 650 } 651 } 652 } 653 } 654 655 final String galleryTitleFieldKey = CmsSearchFieldConfiguration.getLocaleExtendedName( 656 CmsSearchField.FIELD_TITLE_UNSTORED, 657 locale) + "_s"; 658 final String galleryNameValue = galleryNameChooser.getGalleryName(); 659 fieldMappings.put(galleryTitleFieldKey, galleryNameValue); 660 fieldMappings.put( 661 CmsSearchFieldConfiguration.getLocaleExtendedName(CmsSearchField.FIELD_DESCRIPTION, locale) + "_s", 662 galleryNameChooser.getDescription()); 663 664 // handle the textual content 665 if (textContent.length() > 0) { 666 // add the textual content with a localized key to the items 667 //String key = CmsSearchFieldConfiguration.getLocaleExtendedName(CmsSearchField.FIELD_CONTENT, locale); 668 //items.put(key, textContent.toString()); 669 // use the default locale of this resource as general text content for the extraction result 670 localeItems.put(I_CmsExtractionResult.ITEM_CONTENT, textContent.toString()); 671 } 672 items.put(locale, localeItems); 673 } 674 // if the content is locale independent, it should have only one content locale, but that should be indexed for all available locales. 675 // TODO: One could think of different indexing behavior, i.e., index only for getDefaultLocales(cms,resource) 676 // But using getAvailableLocales(cms,resource) does not work, because locale-available is set to "en" for all that content. 677 if ((xmlContent instanceof CmsXmlContent) && ((CmsXmlContent)xmlContent).isLocaleIndependent()) { 678 if (forceLocale != null) { 679 items.put(forceLocale, localeItems); 680 } else { 681 for (Locale l : OpenCms.getLocaleManager().getAvailableLocales()) { 682 items.put(l, localeItems); 683 if (null != galleryNameChooser) { 684 final String galleryTitleFieldKey = CmsSearchFieldConfiguration.getLocaleExtendedName( 685 CmsSearchField.FIELD_TITLE_UNSTORED, 686 l) + "_s"; 687 fieldMappings.put(galleryTitleFieldKey, galleryNameChooser.getGalleryName(l)); 688 fieldMappings.put( 689 CmsSearchFieldConfiguration.getLocaleExtendedName(CmsSearchField.FIELD_DESCRIPTION, l) 690 + "_s", 691 galleryNameChooser.getDescription(l)); 692 } 693 } 694 } 695 } 696 // add the locales that have been indexed for this document as item and return the extraction result 697 // fieldMappings.put(CmsSearchField.FIELD_RESOURCE_LOCALES, locales.toString().trim()); 698 return new CmsExtractionResult(resourceLocale, items, fieldMappings); 699 700 } 701 702 /** 703 * @see org.opencms.search.documents.CmsDocumentXmlContent#extractContent(org.opencms.file.CmsObject, org.opencms.file.CmsResource, org.opencms.search.I_CmsSearchIndex) 704 */ 705 @Override 706 public I_CmsExtractionResult extractContent(CmsObject cms, CmsResource resource, I_CmsSearchIndex index) 707 throws CmsException { 708 709 logContentExtraction(resource, index); 710 711 try { 712 I_CmsExtractionResult result = null; 713 List<I_CmsExtractionResult> ex = new ArrayList<I_CmsExtractionResult>(); 714 for (CmsResource detailContainers : CmsDetailOnlyContainerUtil.getDetailOnlyResources(cms, resource)) { 715 CmsSolrDocumentContainerPage containerpageExtractor = new CmsSolrDocumentContainerPage(""); 716 String localeTemp = detailContainers.getRootPath(); 717 localeTemp = CmsResource.getParentFolder(localeTemp); 718 localeTemp = CmsResource.getName(localeTemp); 719 localeTemp = localeTemp.substring(0, localeTemp.length() - 1); 720 Locale locale = CmsLocaleManager.getLocale(localeTemp); 721 if (CmsDetailOnlyContainerUtil.useSingleLocaleDetailContainers( 722 OpenCms.getSiteManager().getSiteRoot(resource.getRootPath())) 723 && locale.equals(CmsLocaleManager.getDefaultLocale())) { 724 // in case of single locale detail containers do not force the locale 725 locale = null; 726 } 727 I_CmsExtractionResult containersExtractionResult = containerpageExtractor.extractContent( 728 cms, 729 detailContainers, 730 index, 731 locale); 732 // only use the locales of the resource itself, not the ones of the detail containers page 733 containersExtractionResult.getContentItems().remove(CmsSearchField.FIELD_RESOURCE_LOCALES); 734 735 ex.add(containersExtractionResult); 736 } 737 result = extractXmlContent(cms, resource, index); 738 result = result.merge(ex); 739 return result; 740 741 } catch (Throwable t) { 742 throw new CmsIndexException(Messages.get().container(Messages.ERR_TEXT_EXTRACTION_1, resource), t); 743 } 744 } 745 746 /** 747 * Solr index content is stored in multiple languages, so the result is NOT locale dependent.<p> 748 * 749 * @see org.opencms.search.documents.I_CmsDocumentFactory#isLocaleDependend() 750 */ 751 public boolean isLocaleDependend() { 752 753 return false; 754 } 755 756 /** 757 * @see org.opencms.search.documents.I_CmsDocumentFactory#isUsingCache() 758 */ 759 @Override 760 public boolean isOnlyDependentOnContent() { 761 762 return false; 763 } 764 765 /** 766 * @see org.opencms.search.documents.I_CmsDocumentFactory#isUsingCache() 767 */ 768 public boolean isUsingCache() { 769 770 return false; 771 } 772}