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