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