001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file; 029 030import org.opencms.file.types.A_CmsResourceTypeLinkParseable; 031import org.opencms.file.types.CmsResourceTypeJsp; 032import org.opencms.file.types.CmsResourceTypeXmlContent; 033import org.opencms.file.types.I_CmsResourceType; 034import org.opencms.i18n.CmsEncoder; 035import org.opencms.loader.CmsLoaderException; 036import org.opencms.lock.CmsLock; 037import org.opencms.main.CmsException; 038import org.opencms.main.CmsIllegalArgumentException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.relations.CmsRelation; 042import org.opencms.relations.CmsRelationFilter; 043import org.opencms.relations.CmsRelationType; 044import org.opencms.relations.I_CmsLinkParseable; 045import org.opencms.util.CmsFileUtil; 046import org.opencms.util.CmsPair; 047import org.opencms.util.CmsStringUtil; 048import org.opencms.util.CmsUUID; 049import org.opencms.util.I_CmsRegexSubstitution; 050import org.opencms.xml.CmsXmlEntityResolver; 051import org.opencms.xml.CmsXmlException; 052import org.opencms.xml.CmsXmlUtils; 053import org.opencms.xml.content.CmsXmlContent; 054import org.opencms.xml.content.CmsXmlContentFactory; 055import org.opencms.xml.content.Messages; 056 057import java.io.UnsupportedEncodingException; 058import java.util.ArrayList; 059import java.util.Collection; 060import java.util.HashMap; 061import java.util.HashSet; 062import java.util.List; 063import java.util.Map; 064import java.util.Set; 065import java.util.regex.Matcher; 066import java.util.regex.Pattern; 067 068import org.apache.commons.logging.Log; 069 070import org.dom4j.Document; 071 072import com.google.common.collect.ArrayListMultimap; 073import com.google.common.collect.Lists; 074import com.google.common.collect.Multimap; 075 076/** 077 * A class used to rewrite links and relations in one subtree such that relations from that subtree to another given subtree 078 * replaced with relations to the first subtree.<p> 079 */ 080public class CmsLinkRewriter { 081 082 /** The logger instance for this class. */ 083 private static final Log LOG = CmsLog.getLog(CmsLinkRewriter.class); 084 085 /** A map from source folder structure ids to corresponding target folder resources. */ 086 protected Map<CmsUUID, CmsResource> m_translationsById = new HashMap<CmsUUID, CmsResource>(); 087 088 /** A map from source folder root paths to the corresponding target folder resources. */ 089 protected Map<String, CmsResource> m_translationsByPath = new HashMap<String, CmsResource>(); 090 091 /** A map of resources which have been cached by structure id. */ 092 private Map<CmsUUID, CmsResource> m_cachedResources = new HashMap<CmsUUID, CmsResource>(); 093 094 /** The CMS object used for file operations. */ 095 private CmsObject m_cms; 096 097 /** If true, all XML contents will be rewritten instead of just those containing links to correct. */ 098 private boolean m_rewriteAllXmlContents = true; 099 100 /** The set of structure ids of resources whose content has been rewritten. */ 101 private Set<CmsUUID> m_rewrittenContent = new HashSet<CmsUUID>(); 102 103 /** A list of path pairs, each containing a source and a target of a copy operation. */ 104 private List<CmsPair<String, String>> m_sourceTargetPairs = new ArrayList<CmsPair<String, String>>(); 105 106 /** The target folder root path. */ 107 private String m_targetPath; 108 109 /** 110 * Creates a link rewriter for use after a multi-copy operation.<p> 111 * 112 * @param cms the current CMS context 113 * @param sources the list of source root paths 114 * @param target the target parent folder root path 115 */ 116 public CmsLinkRewriter(CmsObject cms, List<String> sources, String target) { 117 118 m_sourceTargetPairs = new ArrayList<CmsPair<String, String>>(); 119 for (String source : sources) { 120 checkNotSubPath(source, target); 121 String targetSub = CmsStringUtil.joinPaths(target, CmsResource.getName(source)); 122 m_sourceTargetPairs.add(CmsPair.create(source, targetSub)); 123 } 124 m_targetPath = target; 125 m_cms = cms; 126 } 127 128 /** 129 * Creates a new link rewriter for a list of sources and corresponding targets.<p> 130 * 131 * @param cms the current CMS context 132 * @param targetPath the target root path 133 * @param sourceTargetPairs the list of source-target pairs 134 */ 135 public CmsLinkRewriter(CmsObject cms, String targetPath, List<CmsPair<String, String>> sourceTargetPairs) { 136 137 m_cms = cms; 138 m_targetPath = targetPath; 139 m_sourceTargetPairs = sourceTargetPairs; 140 } 141 142 /** 143 * Creates a link rewriter for use after a single copy operation.<p> 144 * 145 * @param cms the current CMS context 146 * @param source the source folder root path 147 * @param target the target folder root path 148 */ 149 public CmsLinkRewriter(CmsObject cms, String source, String target) { 150 151 m_sourceTargetPairs = new ArrayList<CmsPair<String, String>>(); 152 checkNotSubPath(source, target); 153 154 m_sourceTargetPairs.add(CmsPair.create(source, target)); 155 m_targetPath = target; 156 m_cms = cms; 157 } 158 159 /** 160 * Checks whether a given resource is a folder and throws an exception otherwise.<p> 161 * 162 * @param resource the resource to check 163 * @throws CmsException if something goes wrong 164 */ 165 protected static void checkIsFolder(CmsResource resource) throws CmsException { 166 167 if (!isFolder(resource)) { 168 throw new CmsIllegalArgumentException( 169 Messages.get().container( 170 org.opencms.file.Messages.ERR_REWRITE_LINKS_ROOT_NOT_FOLDER_1, 171 resource.getRootPath())); 172 } 173 } 174 175 /** 176 * Helper method to check whether a given resource is a folder.<p> 177 * 178 * @param resource the resouce to check 179 * @return true if the resource is a folder 180 * 181 * @throws CmsLoaderException if the resource type couldn't be found 182 */ 183 protected static boolean isFolder(CmsResource resource) throws CmsLoaderException { 184 185 I_CmsResourceType resourceType = OpenCms.getResourceManager().getResourceType(resource.getTypeId()); 186 return resourceType.isFolder(); 187 } 188 189 /** 190 * Starts the link rewriting process.<p> 191 * 192 * @throws CmsException if something goes wrong 193 */ 194 public void rewriteLinks() throws CmsException { 195 196 init(); 197 List<CmsRelation> relationsToCorrect = findRelationsFromTargetToSource(); 198 // group relations by the structure id of their source 199 Multimap<CmsUUID, CmsRelation> relationsBySourceId = ArrayListMultimap.create(); 200 for (CmsRelation relation : relationsToCorrect) { 201 LOG.info( 202 "Found relation which needs to be corrected: " 203 + relation.getSourcePath() 204 + " -> " 205 + relation.getTargetPath() 206 + " [" 207 + relation.getType().getName() 208 + "]"); 209 relationsBySourceId.put(relation.getSourceId(), relation); 210 } 211 212 // make sure we have a lock on the target folder before doing any write operations 213 CmsLock lock = m_cms.getLock(m_targetPath); 214 if (lock.isUnlocked() || !lock.isOwnedBy(m_cms.getRequestContext().getCurrentUser())) { 215 // fail if locked by another user 216 m_cms.lockResource(m_targetPath); 217 } 218 219 for (CmsUUID structureId : relationsBySourceId.keySet()) { 220 221 Collection<CmsRelation> relationsForResource = relationsBySourceId.get(structureId); 222 CmsResource resource = null; 223 try { 224 resource = getResource(structureId); 225 rewriteLinks(resource, relationsForResource); 226 } catch (Exception e) { 227 LOG.error(e.getLocalizedMessage(), e); 228 } 229 } 230 if (!m_rewriteAllXmlContents) { 231 return; 232 } 233 for (Map.Entry<CmsUUID, CmsResource> entry : m_cachedResources.entrySet()) { 234 CmsUUID key = entry.getKey(); 235 CmsResource resource = entry.getValue(); 236 if (isInTargets(resource.getRootPath()) && !m_rewrittenContent.contains(key)) { 237 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(resource.getTypeId()); 238 // rewrite content for other files so 239 if (resType instanceof A_CmsResourceTypeLinkParseable) { 240 try { 241 CmsFile file = m_cms.readFile(resource); 242 if (resType instanceof CmsResourceTypeXmlContent) { 243 CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, file); 244 try { 245 content.validateXmlStructure(new CmsXmlEntityResolver(m_cms)); 246 } catch (CmsException e) { 247 LOG.info("XML content was corrected automatically for resource " + file.getRootPath()); 248 content.setAutoCorrectionEnabled(true); 249 content.correctXmlStructure(m_cms); 250 file.setContents(content.marshal()); 251 } 252 } 253 m_cms.writeFile(file); 254 } catch (CmsException e) { 255 LOG.error(e.getLocalizedMessage(), e); 256 } 257 } 258 } 259 } 260 copyLocaleRelations(); 261 } 262 263 /** 264 * Sets the 'rewriteAllContents' flag, which controls whether all XML contents will be rewritten 265 * or just those whose links need to be corrected.<p> 266 * 267 * @param rewriteAllContents if true, all contents will be rewritten 268 */ 269 public void setRewriteAllContents(boolean rewriteAllContents) { 270 271 m_rewriteAllXmlContents = rewriteAllContents; 272 } 273 274 /** 275 * Checks that the target path is not a subfolder of the source path.<p> 276 * 277 * @param source the source path 278 * @param target the target path 279 */ 280 protected void checkNotSubPath(String source, String target) { 281 282 source = CmsStringUtil.joinPaths("/", source, "/"); 283 target = CmsStringUtil.joinPaths("/", target, "/"); 284 if (target.startsWith(source)) { 285 throw new CmsIllegalArgumentException( 286 org.opencms.file.Messages.get().container( 287 org.opencms.file.Messages.ERR_REWRITE_LINKS_ROOTS_DEPENDENT_2, 288 source, 289 target)); 290 } 291 } 292 293 /** 294 * Separate method for copying locale relations..<p> 295 * 296 * This is necessary because the default copy mechanism does not copy locale relations. 297 * 298 * @throws CmsException if something goes wrong 299 */ 300 protected void copyLocaleRelations() throws CmsException { 301 302 long start = System.currentTimeMillis(); 303 List<CmsRelation> localeRelations = m_cms.readRelations( 304 CmsRelationFilter.ALL.filterType(CmsRelationType.LOCALE_VARIANT)); 305 for (CmsRelation rel : localeRelations) { 306 if (isInSources(rel.getSourcePath()) && isInSources(rel.getTargetPath())) { 307 CmsResource newRelationSource = m_translationsById.get(rel.getSourceId()); 308 CmsResource newRelationTarget = m_translationsById.get(rel.getTargetId()); 309 if ((newRelationSource != null) && (newRelationTarget != null)) { 310 try { 311 m_cms.addRelationToResource( 312 newRelationSource, 313 newRelationTarget, 314 CmsRelationType.LOCALE_VARIANT.getName()); 315 } catch (CmsException e) { 316 LOG.error("Could not transfer locale relation: " + e.getLocalizedMessage(), e); 317 } 318 } else { 319 LOG.warn("Could not transfer locale relation because source/target not found in copy: " + rel); 320 } 321 } 322 } 323 long end = System.currentTimeMillis(); 324 LOG.info("Copied locale relations, took " + (end - start) + "ms"); 325 } 326 327 /** 328 * Decodes a byte array into a string with a given encoding, or the default encoding if that fails.<p> 329 * 330 * @param bytes the byte array 331 * @param encoding the encoding to use 332 * 333 * @return the decoded string 334 */ 335 protected String decode(byte[] bytes, String encoding) { 336 337 try { 338 return new String(bytes, encoding); 339 } catch (UnsupportedEncodingException e) { 340 return new String(bytes); 341 } 342 } 343 344 /** 345 * Decodes a file's contents and return the content string and the encoding to use for writing the file 346 * back to the VFS.<p> 347 * 348 * @param file the file to decode 349 * @return a pair (content, encoding) 350 * @throws CmsException if something goes wrong 351 */ 352 protected CmsPair<String, String> decode(CmsFile file) throws CmsException { 353 354 String content = null; 355 String encoding = getConfiguredEncoding(m_cms, file); 356 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(file.getTypeId()); 357 if (resType instanceof CmsResourceTypeJsp) { 358 content = decode(file.getContents(), encoding); 359 } else { 360 try { 361 CmsXmlEntityResolver resolver = new CmsXmlEntityResolver(m_cms); 362 // parse the XML and serialize it back to a string with the configured encoding 363 Document doc = CmsXmlUtils.unmarshalHelper(file.getContents(), resolver); 364 content = CmsXmlUtils.marshal(doc, encoding); 365 } catch (Exception e) { 366 // invalid xml structure, just use the configured encoding 367 content = decode(file.getContents(), encoding); 368 } 369 } 370 return CmsPair.create(content, encoding); 371 } 372 373 /** 374 * Finds relations from the target root folder or its children to the source root folder or its children.<p> 375 * 376 * @return the list of relations from the target to the source 377 * 378 * @throws CmsException if something goes wrong 379 */ 380 protected List<CmsRelation> findRelationsFromTargetToSource() throws CmsException { 381 382 List<CmsRelation> relations = m_cms.readRelations( 383 CmsRelationFilter.SOURCES.filterPath(m_targetPath).filterIncludeChildren()); 384 List<CmsRelation> result = new ArrayList<CmsRelation>(); 385 for (CmsRelation rel : relations) { 386 if (isInTargets(rel.getSourcePath()) && isInSources(rel.getTargetPath())) { 387 result.add(rel); 388 } 389 } 390 return result; 391 } 392 393 /** 394 * Gets the encoding which is configured at the location of a given resource.<p> 395 * 396 * @param cms the current CMS context 397 * @param resource the resource for which the configured encoding should be retrieved 398 * @return the configured encoding for the resource 399 * 400 * @throws CmsException if something goes wrong 401 */ 402 protected String getConfiguredEncoding(CmsObject cms, CmsResource resource) throws CmsException { 403 404 String encoding = null; 405 try { 406 encoding = cms.readPropertyObject( 407 resource.getRootPath(), 408 CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, 409 true).getValue(); 410 } catch (CmsException e) { 411 // encoding will be null 412 } 413 if (encoding == null) { 414 encoding = OpenCms.getSystemInfo().getDefaultEncoding(); 415 } else { 416 encoding = CmsEncoder.lookupEncoding(encoding, null); 417 if (encoding == null) { 418 throw new CmsXmlException( 419 Messages.get().container(Messages.ERR_XMLCONTENT_INVALID_ENC_1, resource.getRootPath())); 420 } 421 } 422 return encoding; 423 } 424 425 /** 426 * Gets a list of resource pairs whose paths relative to the source/target roots passed match.<p> 427 * 428 * @param source the source root 429 * @param target the target root 430 * 431 * @return the list of matching resources 432 * 433 * @throws CmsException if something goes wrong 434 */ 435 protected List<CmsPair<CmsResource, CmsResource>> getMatchingResources(String source, String target) 436 throws CmsException { 437 438 List<CmsResource> sourceResources = readTree(source); 439 Map<String, CmsResource> sourceRelative = getResourcesByRelativePath(sourceResources, source); 440 441 List<CmsResource> targetResources = readTree(target); 442 Map<String, CmsResource> targetRelative = getResourcesByRelativePath(targetResources, target); 443 444 List<CmsPair<CmsResource, CmsResource>> result = new ArrayList<CmsPair<CmsResource, CmsResource>>(); 445 sourceRelative.keySet().retainAll(targetRelative.keySet()); 446 for (Map.Entry<String, CmsResource> entry : sourceRelative.entrySet()) { 447 String key = entry.getKey(); 448 CmsResource sourceRes = entry.getValue(); 449 CmsResource targetRes = targetRelative.get(key); 450 result.add(CmsPair.create(sourceRes, targetRes)); 451 } 452 return result; 453 } 454 455 /** 456 * Computes the relative path given an ancestor folder path.<p> 457 * 458 * @param ancestor the ancestor folder 459 * @param rootPath the path for which the relative path should be computed 460 * 461 * @return the relative path 462 */ 463 protected String getRelativePath(String ancestor, String rootPath) { 464 465 String result = rootPath.substring(ancestor.length()); 466 result = CmsStringUtil.joinPaths("/", result, "/"); 467 return result; 468 } 469 470 /** 471 * Accesses a resource by structure id.<p> 472 * 473 * @param structureId the structure id of the resource 474 * @return the resource with the given structure id 475 * 476 * @throws CmsException if the resource couldn't be read 477 */ 478 protected CmsResource getResource(CmsUUID structureId) throws CmsException { 479 480 if (m_cachedResources.containsKey(structureId)) { 481 return m_cachedResources.get(structureId); 482 } 483 return m_cms.readResource(structureId); 484 } 485 486 /** 487 * Collects a list of resources in a map where the key for each resource is the path relative to a given folder.<p> 488 * 489 * @param resources the resources to put in the map 490 * @param basePath the path relative to which the keys of the resulting map should be computed 491 * 492 * @return a map from relative paths to resources 493 */ 494 protected Map<String, CmsResource> getResourcesByRelativePath(List<CmsResource> resources, String basePath) { 495 496 Map<String, CmsResource> result = new HashMap<String, CmsResource>(); 497 for (CmsResource resource : resources) { 498 String relativeSubPath = CmsStringUtil.getRelativeSubPath(basePath, resource.getRootPath()); 499 if (relativeSubPath != null) { 500 result.put(relativeSubPath, resource); 501 } 502 } 503 return result; 504 } 505 506 /** 507 * Reads the data needed for rewriting the relations from the VFS.<p> 508 * 509 * @throws CmsException if something goes wrong 510 */ 511 protected void init() throws CmsException { 512 513 m_cms = OpenCms.initCmsObject(m_cms); 514 // we want to use autocorrection when writing XML contents back 515 //m_cms.getRequestContext().setAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE, Boolean.TRUE); 516 m_cms.getRequestContext().setSiteRoot(""); 517 List<CmsPair<CmsResource, CmsResource>> allMatchingResources = Lists.newArrayList(); 518 for (CmsPair<String, String> pair : m_sourceTargetPairs) { 519 List<CmsPair<CmsResource, CmsResource>> matchingResources = getMatchingResources( 520 pair.getFirst(), 521 pair.getSecond()); 522 allMatchingResources.addAll(matchingResources); 523 } 524 for (CmsPair<CmsResource, CmsResource> resPair : allMatchingResources) { 525 CmsResource source = resPair.getFirst(); 526 CmsResource target = resPair.getSecond(); 527 m_translationsById.put(source.getStructureId(), target); 528 m_translationsByPath.put(source.getRootPath(), target); 529 } 530 } 531 532 /** 533 * Checks if a path belongs to one of the sources.<p> 534 * 535 * @param path a root path 536 * 537 * @return true if the path belongs to the sources 538 */ 539 protected boolean isInSources(String path) { 540 541 for (CmsPair<String, String> sourceTargetPair : m_sourceTargetPairs) { 542 String source = sourceTargetPair.getFirst(); 543 if (CmsStringUtil.joinPaths(path, "/").startsWith(CmsStringUtil.joinPaths(source, "/"))) { 544 return true; 545 } 546 } 547 return false; 548 } 549 550 /** 551 * Checks if a path belongs to one of the targets.<p> 552 * 553 * @param path a root path 554 * 555 * @return true if the path belongs to the targets 556 */ 557 protected boolean isInTargets(String path) { 558 559 for (CmsPair<String, String> sourceTargetPair : m_sourceTargetPairs) { 560 String target = sourceTargetPair.getSecond(); 561 if (CmsStringUtil.joinPaths(path, "/").startsWith(CmsStringUtil.joinPaths(target, "/"))) { 562 return true; 563 } 564 } 565 return false; 566 } 567 568 /** 569 * Reads the resources in a subtree.<p> 570 * 571 * @param rootPath the root of the subtree 572 * 573 * @return the list of resources from the subtree 574 * 575 * @throws CmsException if something goes wrong 576 */ 577 protected List<CmsResource> readTree(String rootPath) throws CmsException { 578 579 rootPath = CmsFileUtil.removeTrailingSeparator(rootPath); 580 CmsResource base = m_cms.readResource(rootPath); 581 582 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(base); 583 List<CmsResource> result = new ArrayList<CmsResource>(); 584 if (resType.isFolder()) { 585 rootPath = CmsStringUtil.joinPaths(rootPath, "/"); 586 List<CmsResource> subResources = m_cms.readResources(rootPath, CmsResourceFilter.ALL, true); 587 result.add(base); 588 result.addAll(subResources); 589 } else { 590 result.add(base); 591 } 592 for (CmsResource resource : result) { 593 m_cachedResources.put(resource.getStructureId(), resource); 594 } 595 596 return result; 597 } 598 599 /** 600 * Rewrites the links included in the content itself.<p> 601 * 602 * @param file the file for which the links should be replaced 603 * @param relations the original relations 604 * 605 * @throws CmsException if something goes wrong 606 */ 607 protected void rewriteContent(CmsFile file, Collection<CmsRelation> relations) throws CmsException { 608 609 LOG.info("Rewriting in-content links for " + file.getRootPath()); 610 CmsPair<String, String> contentAndEncoding = decode(file); 611 612 String content = ""; 613 614 if (OpenCms.getResourceManager().getResourceType(file) instanceof CmsResourceTypeXmlContent) { 615 CmsXmlContent contentXml = CmsXmlContentFactory.unmarshal(m_cms, file); 616 try { 617 contentXml.validateXmlStructure(new CmsXmlEntityResolver(m_cms)); 618 } catch (CmsException e) { 619 LOG.info("XML content was corrected automatically for resource " + file.getRootPath()); 620 contentXml.setAutoCorrectionEnabled(true); 621 contentXml.correctXmlStructure(m_cms); 622 try { 623 content = new String(contentXml.marshal(), contentAndEncoding.getSecond()); 624 } catch (UnsupportedEncodingException e1) { 625 // 626 } 627 } 628 } 629 630 if (content.isEmpty()) { 631 content = contentAndEncoding.getFirst(); 632 } 633 String encodingForSave = contentAndEncoding.getSecond(); 634 String newContent = rewriteContentString(content); 635 byte[] newContentBytes; 636 try { 637 newContentBytes = newContent.getBytes(encodingForSave); 638 } catch (UnsupportedEncodingException e) { 639 newContentBytes = newContent.getBytes(); 640 } 641 file.setContents(newContentBytes); 642 m_cms.writeFile(file); 643 644 } 645 646 /** 647 * Replaces structure ids of resources in the source subtree with the structure ids of the corresponding 648 * resources in the target subtree inside a content string.<p> 649 * 650 * @param originalContent the original content 651 * 652 * @return the content with the new structure ids 653 */ 654 protected String rewriteContentString(String originalContent) { 655 656 Pattern uuidPattern = Pattern.compile(CmsUUID.UUID_REGEX); 657 I_CmsRegexSubstitution substitution = new I_CmsRegexSubstitution() { 658 659 public String substituteMatch(String text, Matcher matcher) { 660 661 String uuidString = text.substring(matcher.start(), matcher.end()); 662 CmsUUID uuid = new CmsUUID(uuidString); 663 String result = uuidString; 664 if (m_translationsById.containsKey(uuid)) { 665 result = m_translationsById.get(uuid).getStructureId().toString(); 666 } 667 return result; 668 } 669 }; 670 return CmsStringUtil.substitute(uuidPattern, originalContent, substitution); 671 } 672 673 /** 674 * Rewrites the links for a single resource.<p> 675 * 676 * @param resource the resource for which the links should be rewritten 677 * @param relations the relations to the source folder which have this resource as its source 678 * 679 * @throws CmsException if something goes wrong 680 */ 681 protected void rewriteLinks(CmsResource resource, Collection<CmsRelation> relations) throws CmsException { 682 683 LOG.info("Rewriting relations for resource " + resource.getRootPath()); 684 I_CmsResourceType resourceType = OpenCms.getResourceManager().getResourceType(resource.getTypeId()); 685 boolean hasContentLinks = false; 686 boolean hasOtherLinks = false; 687 688 for (CmsRelation relation : relations) { 689 if (relation.getType().isDefinedInContent()) { 690 hasContentLinks = true; 691 } else { 692 hasOtherLinks = true; 693 } 694 } 695 if (hasContentLinks) { 696 LOG.info("The resource " + resource.getRootPath() + " has links in the content."); 697 } 698 if (hasOtherLinks) { 699 LOG.info("The resource " + resource.getRootPath() + " has non-content links."); 700 } 701 702 if (hasContentLinks) { 703 if (resourceType instanceof I_CmsLinkParseable) { 704 CmsFile file = m_cms.readFile(resource); 705 rewriteContent(file, relations); 706 m_rewrittenContent.add(file.getStructureId()); 707 } 708 } 709 if (hasOtherLinks) { 710 rewriteOtherRelations(resource, relations); 711 } 712 } 713 714 /** 715 * Rewrites relations which are not derived from links in the content itself.<p> 716 * 717 * @param res the resource for which to rewrite the relations 718 * @param relations the original relations 719 * 720 * @throws CmsException if something goes wrong 721 */ 722 protected void rewriteOtherRelations(CmsResource res, Collection<CmsRelation> relations) throws CmsException { 723 724 LOG.info("Rewriting non-content links for " + res.getRootPath()); 725 for (CmsRelation rel : relations) { 726 CmsUUID targetId = rel.getTargetId(); 727 CmsResource newTargetResource = m_translationsById.get(targetId); 728 CmsRelationType relType = rel.getType(); 729 if (!relType.isDefinedInContent()) { 730 if (newTargetResource != null) { 731 m_cms.deleteRelationsFromResource( 732 rel.getSourcePath(), 733 CmsRelationFilter.TARGETS.filterStructureId(rel.getTargetId()).filterType(relType)); 734 m_cms.addRelationToResource( 735 rel.getSourcePath(), 736 newTargetResource.getRootPath(), 737 relType.getName()); 738 } 739 } 740 } 741 } 742}