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 GmbH & Co. KG, 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.file.types; 029 030import org.opencms.configuration.CmsParameterConfiguration; 031import org.opencms.db.CmsSecurityManager; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsProperty; 035import org.opencms.file.CmsRequestContext; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsResource.CmsResourceDeleteMode; 038import org.opencms.file.CmsResourceFilter; 039import org.opencms.loader.CmsXmlContentLoader; 040import org.opencms.lock.CmsLockActionRecord; 041import org.opencms.lock.CmsLockActionRecord.LockChange; 042import org.opencms.lock.CmsLockUtil; 043import org.opencms.main.CmsException; 044import org.opencms.main.CmsIllegalArgumentException; 045import org.opencms.main.CmsLog; 046import org.opencms.main.OpenCms; 047import org.opencms.relations.CmsLink; 048import org.opencms.relations.CmsRelation; 049import org.opencms.relations.CmsRelationFilter; 050import org.opencms.relations.CmsRelationType; 051import org.opencms.security.CmsPermissionSet; 052import org.opencms.staticexport.CmsLinkTable; 053import org.opencms.util.CmsMacroResolver; 054import org.opencms.util.CmsStringUtil; 055import org.opencms.workplace.editors.I_CmsPreEditorActionDefinition; 056import org.opencms.workplace.editors.directedit.I_CmsEditHandler; 057import org.opencms.xml.CmsXmlContentDefinition; 058import org.opencms.xml.CmsXmlEntityResolver; 059import org.opencms.xml.CmsXmlException; 060import org.opencms.xml.containerpage.CmsFormatterConfiguration; 061import org.opencms.xml.content.CmsDefaultXmlContentHandler; 062import org.opencms.xml.content.CmsXmlContent; 063import org.opencms.xml.content.CmsXmlContentFactory; 064import org.opencms.xml.content.I_CmsXmlContentHandler; 065import org.opencms.xml.types.CmsXmlHtmlValue; 066import org.opencms.xml.types.CmsXmlVarLinkValue; 067import org.opencms.xml.types.CmsXmlVfsFileValue; 068import org.opencms.xml.types.I_CmsXmlContentValue; 069 070import java.util.ArrayList; 071import java.util.Collections; 072import java.util.Iterator; 073import java.util.LinkedHashSet; 074import java.util.List; 075import java.util.Locale; 076import java.util.Set; 077 078import org.apache.commons.logging.Log; 079 080import com.google.common.collect.Lists; 081 082/** 083 * Resource type descriptor for the type "xmlcontent".<p> 084 * 085 * @since 6.0.0 086 */ 087public class CmsResourceTypeXmlContent extends A_CmsResourceTypeLinkParseable { 088 089 /** Configuration key for the (optional) schema. */ 090 public static final String CONFIGURATION_SCHEMA = "schema"; 091 092 /** The name for the choose model file form action. */ 093 public static final String DIALOG_CHOOSEMODEL = "choosemodel"; 094 095 /** The log object for this class. */ 096 private static final Log LOG = CmsLog.getLog(CmsResourceTypeXmlContent.class); 097 098 /** The serial version id. */ 099 private static final long serialVersionUID = 2271469830431937731L; 100 101 /** The (optional) schema of this resource. */ 102 private String m_schema; 103 104 /** 105 * Returns the possible model files for the new resource.<p> 106 * 107 * @param cms the current users context to work with 108 * @param currentFolder the folder 109 * @param newResourceTypeName the resource type name for the new resource to create 110 * @return the possible model files for the new resource 111 */ 112 public static List<CmsResource> getModelFiles(CmsObject cms, String currentFolder, String newResourceTypeName) { 113 114 try { 115 116 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(newResourceTypeName); 117 I_CmsPreEditorActionDefinition preEditorAction = OpenCms.getWorkplaceManager().getPreEditorConditionDefinition( 118 resType); 119 // get the global master folder if configured 120 String masterFolder = preEditorAction.getConfiguration().getString( 121 CmsDefaultXmlContentHandler.APPINFO_MODELFOLDER, 122 null); 123 // get the schema for the resource type to create 124 String schema = resType.getConfiguration().get(CmsResourceTypeXmlContent.CONFIGURATION_SCHEMA); 125 CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema); 126 // get the content handler for the resource type to create 127 I_CmsXmlContentHandler handler = contentDefinition.getContentHandler(); 128 String individualModelFolder = handler.getModelFolder(); 129 if (CmsStringUtil.isNotEmpty(individualModelFolder)) { 130 masterFolder = individualModelFolder; 131 } 132 133 if (CmsStringUtil.isNotEmpty(masterFolder)) { 134 // store the original URI 135 String uri = cms.getRequestContext().getUri(); 136 try { 137 // set URI to current folder 138 cms.getRequestContext().setUri(currentFolder); 139 CmsMacroResolver resolver = CmsMacroResolver.newInstance().setCmsObject(cms); 140 // resolve eventual macros 141 masterFolder = resolver.resolveMacros(masterFolder); 142 } finally { 143 // switch back to stored URI 144 cms.getRequestContext().setUri(uri); 145 } 146 147 if (CmsStringUtil.isNotEmpty(masterFolder) && cms.existsResource(masterFolder)) { 148 // folder for master files exists, get all files of the same resource type 149 CmsResourceFilter filter = CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType( 150 resType.getTypeId()); 151 return cms.readResources(masterFolder, filter, false); 152 } 153 } 154 } catch (Throwable t) { 155 // error determining resource type, should never happen 156 } 157 return Collections.emptyList(); 158 } 159 160 /** 161 * Returns <code>true</code> in case the given resource is an XML content.<p> 162 * 163 * @param resource the resource to check 164 * 165 * @return <code>true</code> in case the given resource is an XML content 166 * 167 * @since 7.0.2 168 */ 169 public static boolean isXmlContent(CmsResource resource) { 170 171 boolean result = false; 172 if (resource != null) { 173 // avoid array index out of bound exception: 174 if (!resource.isFolder()) { 175 result = OpenCms.getResourceManager().getResourceType(resource) instanceof CmsResourceTypeXmlContent; 176 } 177 } 178 return result; 179 } 180 181 /** 182 * @see org.opencms.file.types.A_CmsResourceType#addConfigurationParameter(java.lang.String, java.lang.String) 183 */ 184 @Override 185 public void addConfigurationParameter(String paramName, String paramValue) { 186 187 super.addConfigurationParameter(paramName, paramValue); 188 if (CONFIGURATION_SCHEMA.equalsIgnoreCase(paramName)) { 189 m_schema = paramValue.trim(); 190 } 191 } 192 193 /** 194 * @see org.opencms.file.types.I_CmsResourceType#createResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, java.lang.String, byte[], java.util.List) 195 */ 196 @Override 197 public CmsResource createResource( 198 CmsObject cms, 199 CmsSecurityManager securityManager, 200 String resourcename, 201 byte[] content, 202 List<CmsProperty> properties) 203 throws CmsException { 204 205 boolean hasModelUri = false; 206 CmsXmlContent newContent = null; 207 if ((m_schema != null) && ((content == null) || (content.length == 0))) { 208 // unmarshal the content definition for the new resource 209 CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, m_schema); 210 211 // read the default locale for the new resource 212 Locale locale = getLocaleForNewContent(cms, securityManager, resourcename, properties); 213 String modelUri = (String)cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_MODEL); 214 215 // must set URI of OpenCms user context to parent folder of created resource, 216 // in order to allow reading of properties for default values 217 CmsObject newCms = OpenCms.initCmsObject(cms); 218 newCms.getRequestContext().setUri(CmsResource.getParentFolder(resourcename)); 219 if (modelUri != null) { 220 // create the new content from the model file 221 newContent = CmsXmlContentFactory.createDocument(newCms, locale, modelUri); 222 hasModelUri = true; 223 } else { 224 // create the new content from the content definition 225 newContent = CmsXmlContentFactory.createDocument( 226 newCms, 227 locale, 228 OpenCms.getSystemInfo().getDefaultEncoding(), 229 contentDefinition); 230 } 231 // get the bytes from the created content 232 content = newContent.marshal(); 233 } 234 235 // now create the resource using the super class 236 CmsResource resource = super.createResource(cms, securityManager, resourcename, content, properties); 237 238 // a model file was used, call the content handler for post-processing 239 if (hasModelUri) { 240 CmsFile file = cms.readFile(resource); 241 newContent = CmsXmlContentFactory.unmarshal(cms, file); 242 newContent.setAutoCorrectionEnabled(true); 243 resource = newContent.getHandler().prepareForWrite(cms, newContent, file); 244 } 245 246 return resource; 247 } 248 249 /** 250 * @see org.opencms.file.types.A_CmsResourceType#deleteResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.file.CmsResource.CmsResourceDeleteMode) 251 */ 252 @Override 253 public void deleteResource( 254 CmsObject cms, 255 CmsSecurityManager securityManager, 256 CmsResource resource, 257 CmsResourceDeleteMode siblingMode) 258 throws CmsException { 259 260 List<CmsResource> detailOnlyPages = null; 261 if (isPossiblyDetailContent(resource)) { 262 detailOnlyPages = getDetailContainerResources(cms, resource); 263 } 264 super.deleteResource(cms, securityManager, resource, siblingMode); 265 if (detailOnlyPages != null) { 266 for (CmsResource page : detailOnlyPages) { 267 if (page.getState().isDeleted()) { 268 continue; 269 } 270 try { 271 CmsLockUtil.ensureLock(cms, page); 272 cms.deleteResource(page, CmsResource.DELETE_PRESERVE_SIBLINGS); 273 } catch (CmsException e) { 274 LOG.error(e.getLocalizedMessage(), e); 275 } 276 } 277 } 278 } 279 280 /** 281 * @see org.opencms.file.types.I_CmsResourceType#getCachePropertyDefault() 282 */ 283 @Override 284 public String getCachePropertyDefault() { 285 286 return "element;locale;"; 287 } 288 289 /** 290 * @see org.opencms.file.types.A_CmsResourceType#getConfiguration() 291 */ 292 @Override 293 public CmsParameterConfiguration getConfiguration() { 294 295 CmsParameterConfiguration result = new CmsParameterConfiguration(); 296 CmsParameterConfiguration additional = super.getConfiguration(); 297 if (additional != null) { 298 result.putAll(additional); 299 } 300 if (m_schema != null) { 301 result.put(CONFIGURATION_SCHEMA, m_schema); 302 } 303 return result; 304 } 305 306 /** 307 * Returns the edit handler if configured.<p> 308 * 309 * @param cms the cms context 310 * 311 * @return the edit handler 312 */ 313 public I_CmsEditHandler getEditHandler(CmsObject cms) { 314 315 String schema = getSchema(); 316 317 try { 318 CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema); 319 // get the content handler for the resource type to create 320 I_CmsXmlContentHandler handler = contentDefinition.getContentHandler(); 321 return handler.getEditHandler(); 322 323 } catch (CmsXmlException e) { 324 LOG.error(e.getMessage(), e); 325 } 326 return null; 327 } 328 329 /** 330 * @see org.opencms.file.types.A_CmsResourceType#getFormattersForResource(org.opencms.file.CmsObject, org.opencms.file.CmsResource) 331 */ 332 @Override 333 public CmsFormatterConfiguration getFormattersForResource(CmsObject cms, CmsResource resource) { 334 335 CmsFormatterConfiguration result = null; 336 CmsXmlContentDefinition cd = null; 337 try { 338 cd = CmsXmlContentDefinition.getContentDefinitionForResource(cms, resource); 339 result = cd.getContentHandler().getFormatterConfiguration(cms, resource); 340 } catch (CmsException e) { 341 // no content definition found, use the preview formatter 342 } 343 if (result == null) { 344 LOG.warn( 345 Messages.get().getBundle().key( 346 Messages.LOG_WARN_NO_FORMATTERS_DEFINED_1, 347 cd == null ? resource.getRootPath() : cd.getSchemaLocation())); 348 result = CmsFormatterConfiguration.EMPTY_CONFIGURATION; 349 } 350 return result; 351 } 352 353 /** 354 * @see org.opencms.file.types.A_CmsResourceType#getGalleryPreviewProvider() 355 */ 356 @Override 357 public String getGalleryPreviewProvider() { 358 359 if (m_galleryPreviewProvider == null) { 360 m_galleryPreviewProvider = getConfiguration().getString( 361 CONFIGURATION_GALLERY_PREVIEW_PROVIDER, 362 DEFAULT_GALLERY_PREVIEW_PROVIDER); 363 } 364 return m_galleryPreviewProvider; 365 } 366 367 /** 368 * @see org.opencms.file.types.I_CmsResourceType#getLoaderId() 369 */ 370 @Override 371 public int getLoaderId() { 372 373 return CmsXmlContentLoader.RESOURCE_LOADER_ID; 374 } 375 376 /** 377 * Returns the configured xsd schema uri.<p> 378 * 379 * @return the configured xsd schema uri, or <code>null</code> if not set 380 */ 381 public String getSchema() { 382 383 return m_schema; 384 } 385 386 /** 387 * @see org.opencms.file.types.A_CmsResourceType#initialize(org.opencms.file.CmsObject) 388 */ 389 @Override 390 public void initialize(CmsObject cms) { 391 392 super.initialize(cms); 393 if (m_schema != null) { 394 // unmarshal the XML schema, this is required to update the resource bundle cache 395 try { 396 if (cms.existsResource(m_schema)) { 397 CmsXmlContentDefinition.unmarshal(cms, m_schema); 398 } else { 399 LOG.debug( 400 Messages.get().getBundle().key( 401 Messages.LOG_WARN_SCHEMA_RESOURCE_DOES_NOT_EXIST_2, 402 m_schema, 403 getTypeName())); 404 } 405 } catch (Throwable e) { 406 // unable to unmarshal the XML schema configured 407 LOG.error(Messages.get().getBundle().key(Messages.ERR_BAD_XML_SCHEMA_2, m_schema, getTypeName()), e); 408 } 409 } 410 } 411 412 /** 413 * @see org.opencms.file.types.A_CmsResourceType#moveResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, java.lang.String) 414 */ 415 @Override 416 public void moveResource( 417 CmsObject cms, 418 CmsSecurityManager securityManager, 419 CmsResource resource, 420 String destination) 421 throws CmsException, CmsIllegalArgumentException { 422 423 super.moveResource(cms, securityManager, resource, destination); 424 if (isPossiblyDetailContent(resource)) { 425 String rootDest = cms.getRequestContext().addSiteRoot(destination); 426 CmsObject rootCms = OpenCms.initCmsObject(cms); 427 rootCms.getRequestContext().setSiteRoot(""); 428 String srcParent = CmsResource.getParentFolder(resource.getRootPath()); 429 String srcName = CmsResource.getName(resource.getRootPath()); 430 String destParent = CmsResource.getParentFolder(rootDest); 431 String destName = CmsResource.getName(rootDest); 432 if (srcParent.equals(destParent) && !srcName.equals(destName)) { 433 List<CmsResource> detailOnlyPages = getDetailContainerResources(cms, resource); 434 for (CmsResource page : detailOnlyPages) { 435 if (page.getState().isDeleted()) { 436 continue; 437 } 438 String newPath = CmsStringUtil.joinPaths(CmsResource.getParentFolder(page.getRootPath()), destName); 439 CmsLockActionRecord lockRecord = null; 440 try { 441 lockRecord = CmsLockUtil.ensureLock(cms, page); 442 rootCms.moveResource(page.getRootPath(), newPath); 443 } catch (Exception e) { 444 LOG.error(e.getLocalizedMessage(), e); 445 } finally { 446 if ((lockRecord != null) && (lockRecord.getChange() == LockChange.locked)) { 447 try { 448 CmsLockUtil.tryUnlock( 449 rootCms, 450 rootCms.readResource(page.getStructureId(), CmsResourceFilter.ALL)); 451 } catch (Exception e) { 452 LOG.error(e.getLocalizedMessage(), e); 453 } 454 } 455 } 456 } 457 } 458 } 459 } 460 461 /** 462 * @see org.opencms.relations.I_CmsLinkParseable#parseLinks(org.opencms.file.CmsObject, org.opencms.file.CmsFile) 463 */ 464 public List<CmsLink> parseLinks(CmsObject cms, CmsFile file) { 465 466 if (file.getLength() == 0) { 467 return Collections.emptyList(); 468 } 469 CmsXmlContent xmlContent; 470 long requestTime = cms.getRequestContext().getRequestTime(); 471 try { 472 // prevent the check rules to remove the broken links 473 cms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE); 474 xmlContent = CmsXmlContentFactory.unmarshal(cms, file); 475 } catch (CmsException e) { 476 if (LOG.isErrorEnabled()) { 477 LOG.error( 478 org.opencms.db.Messages.get().getBundle().key( 479 org.opencms.db.Messages.ERR_READ_RESOURCE_1, 480 cms.getSitePath(file)), 481 e); 482 } 483 return Collections.emptyList(); 484 } finally { 485 cms.getRequestContext().setRequestTime(requestTime); 486 } 487 // using linked set to keep the link order 488 Set<CmsLink> links = new LinkedHashSet<CmsLink>(); 489 490 // add XSD link 491 CmsLink xsdLink = getXsdLink(cms, xmlContent); 492 if (xsdLink != null) { 493 links.add(xsdLink); 494 } 495 496 // iterate over all languages 497 List<Locale> locales = xmlContent.getLocales(); 498 Iterator<Locale> i = locales.iterator(); 499 while (i.hasNext()) { 500 Locale locale = i.next(); 501 List<I_CmsXmlContentValue> values = xmlContent.getValues(locale); 502 503 // iterate over all body elements per language 504 Iterator<I_CmsXmlContentValue> j = values.iterator(); 505 while (j.hasNext()) { 506 I_CmsXmlContentValue value = j.next(); 507 if (value instanceof CmsXmlHtmlValue) { 508 CmsXmlHtmlValue htmlValue = (CmsXmlHtmlValue)value; 509 CmsLinkTable linkTable = htmlValue.getLinkTable(); 510 511 // iterate over all links inside a body element 512 Iterator<CmsLink> k = linkTable.iterator(); 513 while (k.hasNext()) { 514 CmsLink link = k.next(); 515 516 // external links are omitted 517 if (link.isInternal()) { 518 link.checkConsistency(cms); 519 links.add(link); 520 } 521 } 522 } else if (value instanceof CmsXmlVfsFileValue) { 523 CmsXmlVfsFileValue refValue = (CmsXmlVfsFileValue)value; 524 CmsLink link = refValue.getLink(cms); 525 if (link != null) { 526 links.add(link); 527 } 528 } else if (value instanceof CmsXmlVarLinkValue) { 529 CmsXmlVarLinkValue refValue = (CmsXmlVarLinkValue)value; 530 CmsLink link = refValue.getLink(cms); 531 if ((link != null) && link.isInternal()) { 532 links.add(link); 533 } 534 } 535 } 536 } 537 return new ArrayList<CmsLink>(links); 538 } 539 540 /** 541 * @see org.opencms.file.types.I_CmsResourceType#writeFile(org.opencms.file.CmsObject, CmsSecurityManager, CmsFile) 542 */ 543 @Override 544 public CmsFile writeFile(CmsObject cms, CmsSecurityManager securityManager, CmsFile resource) throws CmsException { 545 546 // check if the user has write access and if resource is locked 547 // done here so that all the XML operations are not performed if permissions not granted 548 securityManager.checkPermissions( 549 cms.getRequestContext(), 550 resource, 551 CmsPermissionSet.ACCESS_WRITE, 552 true, 553 CmsResourceFilter.ALL); 554 // read the XML content, use the encoding set in the property 555 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(cms, resource, true); 556 // call the content handler for post-processing 557 resource = xmlContent.getHandler().prepareForWrite(cms, xmlContent, resource); 558 559 // now write the file 560 return super.writeFile(cms, securityManager, resource); 561 } 562 563 /** 564 * Gets the locale which should be used for creating an empty content.<p> 565 * 566 * @param cms the current CMS context 567 * @param securityManager the security manager 568 * @param resourcename the name of the resource to create 569 * @param properties the properties for the resource to create 570 * 571 * @return the locale to use 572 */ 573 protected Locale getLocaleForNewContent( 574 CmsObject cms, 575 CmsSecurityManager securityManager, 576 String resourcename, 577 List<CmsProperty> properties) { 578 579 Locale locale = (Locale)(cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_NEW_RESOURCE_LOCALE)); 580 if (locale != null) { 581 return locale; 582 } 583 List<Locale> locales = OpenCms.getLocaleManager().getDefaultLocales( 584 cms, 585 CmsResource.getParentFolder(resourcename)); 586 return locales.get(0); 587 } 588 589 /** 590 * Creates a new link object for the schema definition.<p> 591 * 592 * @param cms the current CMS context 593 * @param xmlContent the xml content to crete the link for 594 * 595 * @return the generated link 596 */ 597 protected CmsLink getXsdLink(CmsObject cms, CmsXmlContent xmlContent) { 598 599 String schema = xmlContent.getContentDefinition().getSchemaLocation(); 600 if (schema.startsWith(CmsXmlEntityResolver.OPENCMS_SCHEME)) { 601 if (CmsXmlEntityResolver.isInternalId(schema)) { 602 return null; 603 } 604 schema = schema.substring(CmsXmlEntityResolver.OPENCMS_SCHEME.length() - 1); 605 } else if (CmsXmlEntityResolver.isCachedSystemId(schema)) { 606 // schema may not exist as a VFS file because it has just been cached (some test cases do this) 607 return null; 608 } 609 try { 610 CmsResource schemaRes = cms.readResource(cms.getRequestContext().removeSiteRoot(schema)); 611 CmsLink xsdLink = new CmsLink( 612 null, 613 CmsRelationType.XSD, 614 schemaRes.getStructureId(), 615 schemaRes.getRootPath(), 616 true); 617 return xsdLink; 618 } catch (CmsException e) { 619 LOG.error(e.getLocalizedMessage(), e); 620 } 621 return null; 622 } 623 624 /** 625 * Checks if the resource is possibly a detail content.<p> 626 * 627 * @param resource the resource to check 628 * @return true if the resource is possibly a detail content 629 */ 630 boolean isPossiblyDetailContent(CmsResource resource) { 631 632 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 633 return false; 634 } 635 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource); 636 if (type instanceof CmsResourceTypeXmlAdeConfiguration) { 637 return false; 638 } 639 return true; 640 } 641 642 /** 643 * Reads the detail container resources which are connected by relations to the given resource. 644 * 645 * @param cms the current CMS context 646 * @param res the detail content 647 * 648 * @return the list of detail only container resources 649 * 650 * @throws CmsException if something goes wrong 651 */ 652 private List<CmsResource> getDetailContainerResources(CmsObject cms, CmsResource res) throws CmsException { 653 654 CmsRelationFilter filter = CmsRelationFilter.relationsFromStructureId(res.getStructureId()).filterType( 655 CmsRelationType.DETAIL_ONLY); 656 List<CmsResource> result = Lists.newArrayList(); 657 List<CmsRelation> relations = cms.readRelations(filter); 658 for (CmsRelation relation : relations) { 659 try { 660 result.add(relation.getTarget(cms, CmsResourceFilter.ALL)); 661 } catch (Exception e) { 662 LOG.error(e.getLocalizedMessage(), e); 663 } 664 } 665 return result; 666 } 667}