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