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.staticexport; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.CmsPermalinkResourceHandler; 037import org.opencms.main.OpenCms; 038import org.opencms.relations.CmsExternalLinksValidationResult; 039import org.opencms.security.CmsRole; 040import org.opencms.security.CmsRoleViolationException; 041import org.opencms.site.CmsSite; 042import org.opencms.util.CmsFileUtil; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.util.CmsUUID; 045 046import java.net.MalformedURLException; 047import java.net.URI; 048import java.net.URISyntaxException; 049import java.net.URL; 050 051import org.apache.commons.logging.Log; 052 053import com.google.common.base.Optional; 054 055/** 056 * Does the link replacement for the ≶link> tags.<p> 057 * 058 * Since this functionality is closely related to the static export, 059 * this class resides in the static export package.<p> 060 * 061 * @since 6.0.0 062 */ 063public class CmsLinkManager { 064 065 /** The log object for this class. */ 066 private static final Log LOG = CmsLog.getLog(CmsLinkManager.class); 067 068 /** Base URL to calculate absolute links. */ 069 private static URL m_baseUrl; 070 071 /** The configured link substitution handler. */ 072 private I_CmsLinkSubstitutionHandler m_linkSubstitutionHandler; 073 074 /** Stores the results of a external link validation. */ 075 private CmsExternalLinksValidationResult m_pointerLinkValidationResult; 076 077 /** 078 * Public constructor.<p> 079 * 080 * @param linkSubstitutionHandler the link substitution handler to use 081 */ 082 public CmsLinkManager(I_CmsLinkSubstitutionHandler linkSubstitutionHandler) { 083 084 m_linkSubstitutionHandler = linkSubstitutionHandler; 085 if (m_linkSubstitutionHandler == null) { 086 // just make very sure that this is not null 087 m_linkSubstitutionHandler = new CmsDefaultLinkSubstitutionHandler(); 088 } 089 } 090 091 /** 092 * Static initializer for the base URL.<p> 093 */ 094 static { 095 m_baseUrl = null; 096 try { 097 m_baseUrl = new URL("http://127.0.0.1"); 098 } catch (MalformedURLException e) { 099 // this won't happen 100 LOG.error(e.getLocalizedMessage(), e); 101 } 102 } 103 104 /** 105 * Calculates the absolute URI for the "relativeUri" with the given absolute "baseUri" as start. <p> 106 * 107 * If "relativeUri" is already absolute, it is returned unchanged. 108 * This method also returns "relativeUri" unchanged if it is not well-formed.<p> 109 * 110 * @param relativeUri the relative URI to calculate an absolute URI for 111 * @param baseUri the base URI, this must be an absolute URI 112 * 113 * @return an absolute URI calculated from "relativeUri" and "baseUri" 114 */ 115 public static String getAbsoluteUri(String relativeUri, String baseUri) { 116 117 if (isAbsoluteUri(relativeUri)) { 118 // URI is null or already absolute 119 return relativeUri; 120 } 121 try { 122 URL url = new URL(new URL(m_baseUrl, baseUri), relativeUri); 123 StringBuffer result = new StringBuffer(100); 124 result.append(url.getPath()); 125 if (url.getQuery() != null) { 126 result.append('?'); 127 result.append(url.getQuery()); 128 } 129 if (url.getRef() != null) { 130 result.append('#'); 131 result.append(url.getRef()); 132 } 133 return result.toString(); 134 } catch (MalformedURLException e) { 135 LOG.debug(e.getLocalizedMessage(), e); 136 return relativeUri; 137 } 138 } 139 140 141 /** 142 * Gets the absolute path for the subsite a link links to. 143 * 144 * <p>For detail links, the subsite of the detail page is returned, not the subsite of the detail content 145 * <p>If the link is not internal, null will be returned. 146 * 147 * @param cms a CMS context 148 * @param link the link to check 149 * @return the subsite path for the link target, or null if not applicable 150 */ 151 public static String getLinkSubsite(CmsObject cms, String link) { 152 153 try { 154 155 URI uri = new URI(link); 156 String path = uri.getPath(); 157 String name = CmsResource.getName(path); 158 name = CmsFileUtil.removeTrailingSeparator(name); 159 String rootPath = OpenCms.getLinkManager().getRootPath(cms, link); 160 if (rootPath == null) { 161 return null; 162 } 163 String parentRootPath = null; 164 try { 165 CmsUUID detailId = cms.readIdForUrlName(name); 166 if (detailId != null) { 167 CmsResource detailRes = cms.readResource(detailId, CmsResourceFilter.IGNORE_EXPIRATION); 168 // When the last part of the path, interpreted as a detail name, resolves to the same root path returned by CmsLinkManager.getRootPath(), it is a detail page URL 169 if (detailRes.getRootPath().equals(rootPath)) { 170 URI parentUri = new URI( 171 uri.getScheme(), 172 uri.getAuthority(), 173 CmsResource.getParentFolder(uri.getPath()), 174 null, 175 null); 176 parentRootPath = OpenCms.getLinkManager().getRootPath(cms, parentUri.toASCIIString()); 177 } 178 } 179 } catch (CmsException e) { 180 LOG.info(e.getLocalizedMessage(), e); 181 } 182 if (parentRootPath != null) { 183 return OpenCms.getADEManager().getSubSiteRoot(cms, parentRootPath); 184 } else { 185 return OpenCms.getADEManager().getSubSiteRoot(cms, rootPath); 186 } 187 } catch (URISyntaxException e) { 188 LOG.warn(e.getLocalizedMessage(), e); 189 return null; 190 } 191 192 } 193 194 /** 195 * Calculates a relative URI from "fromUri" to "toUri", 196 * both URI must be absolute.<p> 197 * 198 * @param fromUri the URI to start 199 * @param toUri the URI to calculate a relative path to 200 * @return a relative URI from "fromUri" to "toUri" 201 */ 202 public static String getRelativeUri(String fromUri, String toUri) { 203 204 StringBuffer result = new StringBuffer(); 205 int pos = 0; 206 207 while (true) { 208 int i = fromUri.indexOf('/', pos); 209 int j = toUri.indexOf('/', pos); 210 if ((i == -1) || (i != j) || !fromUri.regionMatches(pos, toUri, pos, i - pos)) { 211 break; 212 } 213 pos = i + 1; 214 } 215 216 // count hops up from here to the common ancestor 217 for (int i = fromUri.indexOf('/', pos); i > 0; i = fromUri.indexOf('/', i + 1)) { 218 result.append("../"); 219 } 220 221 // append path down from common ancestor to there 222 result.append(toUri.substring(pos)); 223 224 if (result.length() == 0) { 225 // special case: relative link to the parent folder from a file in that folder 226 result.append("./"); 227 } 228 229 return result.toString(); 230 } 231 232 /** 233 * Returns the resource root path for the given target URI in the OpenCms VFS, or <code>null</code> in 234 * case the target URI points to an external site.<p> 235 * 236 * @param cms the current users OpenCms context 237 * @param basePath path to use as base site for the target URI (can be <code>null</code>) 238 * @param targetUri the target URI 239 * 240 * @return the resource root path for the given target URI in the OpenCms VFS, or <code>null</code> in 241 * case the target URI points to an external site 242 * 243 * @deprecated use {@link #getRootPath(CmsObject, String, String)} instead, obtain the link manager 244 * with {@link OpenCms#getLinkManager()} 245 */ 246 @Deprecated 247 public static String getSitePath(CmsObject cms, String basePath, String targetUri) { 248 249 return OpenCms.getLinkManager().getRootPath(cms, targetUri, basePath); 250 } 251 252 /** 253 * Tests if the given URI starts with a scheme component.<p> 254 * 255 * The scheme component is something like <code>http:</code> or <code>ftp:</code>.<p> 256 * 257 * @param uri the URI to test 258 * 259 * @return <code>true</code> if the given URI starts with a scheme component 260 */ 261 public static boolean hasScheme(String uri) { 262 263 int pos = uri.indexOf(':'); 264 // don't want to be misguided by a potential ':' in the query section of the URI (is this possible / allowed?) 265 // so consider only a ':' in the first 10 chars as a scheme 266 return (pos > -1) && (pos < 10); 267 } 268 269 /** 270 * Returns <code>true</code> in case the given URI is absolute.<p> 271 * 272 * An URI is considered absolute if one of the following is true:<ul> 273 * <li>The URI starts with a <code>'/'</code> char. 274 * <li>The URI contains a <code>':'</code> in the first 10 chars. 275 * <li>The URI is <code>null</code> 276 * </ul> 277 * 278 * @param uri the URI to test 279 * 280 * @return <code>true</code> in case the given URI is absolute 281 */ 282 public static boolean isAbsoluteUri(String uri) { 283 284 return (uri == null) || ((uri.length() >= 1) && ((uri.charAt(0) == '/') || hasScheme(uri))); 285 } 286 287 /** 288 * Returns if the given link points to the OpenCms workplace UI.<p> 289 * 290 * @param link the link to test 291 * 292 * @return <code>true</code> in case the given URI points to the OpenCms workplace UI 293 */ 294 public static boolean isWorkplaceLink(String link) { 295 296 boolean result = false; 297 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(link)) { 298 result = link.startsWith(OpenCms.getSystemInfo().getWorkplaceContext()); 299 if (!result) { 300 try { 301 URI uri = new URI(link); 302 result = isWorkplaceUri(uri); 303 } catch (URISyntaxException e) { 304 LOG.debug(e.getLocalizedMessage(), e); 305 } 306 } 307 } 308 309 return result; 310 } 311 312 /** 313 * Returns if the given URI is pointing to the OpenCms workplace UI.<p> 314 * 315 * @param uri the URI 316 * 317 * @return <code>true</code> if the given URI is pointing to the OpenCms workplace UI 318 */ 319 public static boolean isWorkplaceUri(URI uri) { 320 321 return (uri != null) && uri.getPath().startsWith(OpenCms.getSystemInfo().getWorkplaceContext()); 322 } 323 324 325 /** 326 * Given a path to a VFS resource, the method removes the OpenCms context, 327 * in case the path is prefixed by that context. 328 * @param path the path where the OpenCms context should be removed 329 * @return the adjusted path 330 */ 331 public static String removeOpenCmsContext(final String path) { 332 333 String context = OpenCms.getSystemInfo().getOpenCmsContext(); 334 if (path.startsWith(context + "/")) { 335 return path.substring(context.length()); 336 } 337 String renderPrefix = OpenCms.getStaticExportManager().getVfsPrefix(); 338 if (path.startsWith(renderPrefix + "/")) { 339 return path.substring(renderPrefix.length()); 340 } 341 return path; 342 } 343 344 /** 345 * Returns the online link for the given resource, with full server prefix.<p> 346 * 347 * Like <code>http://site.enterprise.com:8080/index.html</code>.<p> 348 * 349 * In case the resource name is a full root path, the site from the root path will be used. 350 * Otherwise the resource is assumed to be in the current site set be the OpenCms user context.<p> 351 * 352 * Please note that this method will always return the link as it will appear in the "Online" 353 * project, that is after the resource has been published. In case you need a method that 354 * just returns the link with the full server prefix, use {@link #getServerLink(CmsObject, String)}.<p> 355 * 356 * @param cms the current OpenCms user context 357 * @param resourceName the resource to generate the online link for 358 * 359 * @return the online link for the given resource, with full server prefix 360 * 361 * @see #getServerLink(CmsObject, String) 362 */ 363 public String getOnlineLink(CmsObject cms, String resourceName) { 364 365 return getOnlineLink(cms, resourceName, false); 366 } 367 368 /** 369 * Returns the online link for the given resource, with full server prefix.<p> 370 * 371 * Like <code>http://site.enterprise.com:8080/index.html</code>.<p> 372 * 373 * In case the resource name is a full root path, the site from the root path will be used. 374 * Otherwise the resource is assumed to be in the current site set be the OpenCms user context.<p> 375 * 376 * Please note that this method will always return the link as it will appear in the "Online" 377 * project, that is after the resource has been published. In case you need a method that 378 * just returns the link with the full server prefix, use {@link #getServerLink(CmsObject, String)}.<p> 379 * 380 * @param cms the current OpenCms user context 381 * @param resourceName the resource to generate the online link for 382 * @param forceSecure forces the secure server prefix if the target is secure 383 * 384 * @return the online link for the given resource, with full server prefix 385 * 386 * @see #getServerLink(CmsObject, String) 387 */ 388 public String getOnlineLink(CmsObject cms, String resourceName, boolean forceSecure) { 389 390 String result = ""; 391 try { 392 CmsProject currentProject = cms.getRequestContext().getCurrentProject(); 393 try { 394 cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID)); 395 result = substituteLinkForUnknownTarget(cms, resourceName, forceSecure); 396 result = appendServerPrefix(cms, result, resourceName, false); 397 } finally { 398 cms.getRequestContext().setCurrentProject(currentProject); 399 } 400 } catch (CmsException e) { 401 // should never happen 402 result = e.getLocalizedMessage(); 403 if (LOG.isErrorEnabled()) { 404 LOG.error(e.getLocalizedMessage(), e); 405 } 406 } 407 return result; 408 } 409 410 /** 411 * Returns the online link for the given resource, with full server prefix.<p> 412 * 413 * Like <code>http://site.enterprise.com:8080/index.html</code>.<p> 414 * 415 * In case the resource name is a full root path, the site from the root path will be used. 416 * Otherwise the resource is assumed to be in the current site set be the OpenCms user context.<p> 417 * 418 * Please note that this method will always return the link as it will appear in the "Online" 419 * project, that is after the resource has been published. In case you need a method that 420 * just returns the link with the full server prefix, use {@link #getServerLink(CmsObject, String)}.<p> 421 * 422 * @param cms the current OpenCms user context 423 * @param resourceName the resource to generate the online link for 424 * @param targetDetailPage the target detail page, in case of linking to a specific detail page 425 * @param forceSecure forces the secure server prefix if the target is secure 426 * 427 * @return the online link for the given resource, with full server prefix 428 * 429 * @see #getServerLink(CmsObject, String) 430 */ 431 public String getOnlineLink(CmsObject cms, String resourceName, String targetDetailPage, boolean forceSecure) { 432 433 String result = ""; 434 try { 435 CmsProject currentProject = cms.getRequestContext().getCurrentProject(); 436 try { 437 cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID)); 438 result = substituteLinkForUnknownTarget(cms, resourceName, targetDetailPage, forceSecure); 439 result = appendServerPrefix(cms, result, resourceName, false); 440 } finally { 441 cms.getRequestContext().setCurrentProject(currentProject); 442 } 443 } catch (CmsException e) { 444 // should never happen 445 result = e.getLocalizedMessage(); 446 if (LOG.isErrorEnabled()) { 447 LOG.error(e.getLocalizedMessage(), e); 448 } 449 } 450 return result; 451 } 452 453 /** 454 * Returns the perma link for the given resource.<p> 455 * 456 * Like 457 * <code>http://site.enterprise.com:8080/permalink/4b65369f-1266-11db-8360-bf0f6fbae1f8.html</code>.<p> 458 * 459 * @param cms the cms context 460 * @param resourceName the resource to generate the perma link for 461 * 462 * @return the perma link 463 */ 464 public String getPermalink(CmsObject cms, String resourceName) { 465 466 return getPermalink(cms, resourceName, null); 467 } 468 469 /** 470 * Returns the perma link for the given resource and optional detail content.<p< 471 * 472 * @param cms the CMS context to use 473 * @param resourceName the page to generate the perma link for 474 * @param detailContentId the structure id of the detail content (may be null) 475 * 476 * @return the perma link 477 */ 478 public String getPermalink(CmsObject cms, String resourceName, CmsUUID detailContentId) { 479 480 String permalink = ""; 481 try { 482 permalink = substituteLink(cms, CmsPermalinkResourceHandler.PERMALINK_HANDLER); 483 String id = cms.readResource(resourceName, CmsResourceFilter.ALL).getStructureId().toString(); 484 permalink += id; 485 if (detailContentId != null) { 486 permalink += ":" + detailContentId; 487 } 488 String ext = CmsFileUtil.getExtension(resourceName); 489 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(ext)) { 490 permalink += ext; 491 } 492 CmsSite currentSite = OpenCms.getSiteManager().getCurrentSite(cms); 493 String serverPrefix = null; 494 if (currentSite == OpenCms.getSiteManager().getDefaultSite()) { 495 Optional<CmsSite> siteForDefaultUri = OpenCms.getSiteManager().getSiteForDefaultUri(); 496 if (siteForDefaultUri.isPresent()) { 497 serverPrefix = siteForDefaultUri.get().getServerPrefix(cms, resourceName); 498 } else { 499 serverPrefix = OpenCms.getSiteManager().getWorkplaceServer(); 500 } 501 } else { 502 serverPrefix = currentSite.getServerPrefix(cms, resourceName); 503 } 504 505 if (!permalink.startsWith(serverPrefix)) { 506 permalink = serverPrefix + permalink; 507 } 508 } catch (CmsException e) { 509 // if something wrong 510 permalink = e.getLocalizedMessage(); 511 if (LOG.isErrorEnabled()) { 512 LOG.error(e.getLocalizedMessage(), e); 513 } 514 } 515 return permalink; 516 } 517 518 /** 519 * Returns the perma link for the current page based on the URI and detail content id stored in the CmsObject passed as a parameter.<p< 520 * 521 * @param cms the CMS context to use to generate the permalink 522 * 523 * @return the permalink 524 */ 525 public String getPermalinkForCurrentPage(CmsObject cms) { 526 527 return getPermalink(cms, cms.getRequestContext().getUri(), cms.getRequestContext().getDetailContentId()); 528 } 529 530 /** 531 * Returns the result of the last extern link validation.<p> 532 * 533 * @return the result of the last extern link validation 534 */ 535 public CmsExternalLinksValidationResult getPointerLinkValidationResult() { 536 537 return m_pointerLinkValidationResult; 538 } 539 540 /** 541 * Returns the resource root path in the OpenCms VFS for the given target URI link, or <code>null</code> in 542 * case the link points to an external site.<p> 543 * 544 * This methods does not support relative target URI links, so the given URI must be an absolute link.<p> 545 * 546 * See {@link #getRootPath(CmsObject, String)} for a full explanation of this method.<p> 547 * 548 * @param cms the current users OpenCms context 549 * @param targetUri the target URI link 550 * 551 * @return the resource root path in the OpenCms VFS for the given target URI link, or <code>null</code> in 552 * case the link points to an external site 553 * 554 * @see #getRootPath(CmsObject, String, String) 555 * 556 * @since 7.0.2 557 */ 558 public String getRootPath(CmsObject cms, String targetUri) { 559 560 return getRootPath(cms, targetUri, null); 561 } 562 563 /** 564 * Returns the resource root path in the OpenCms VFS for the given target URI link, or <code>null</code> in 565 * case the link points to an external site.<p> 566 * 567 * The default implementation applies the following transformations to the link:<ul> 568 * <li>In case the link starts with a VFS prefix (for example <code>/opencms/opencms</code>, 569 * this prefix is removed from the result 570 * <li>In case the link is not a root path, the current site root is appended to the result.<p> 571 * <li>In case the link is relative, it will be made absolute using the given absolute <code>basePath</code> 572 * as starting point.<p> 573 * <li>In case the link contains a server schema (for example <code>http://www.mysite.de/</code>), 574 * which points to a configured site in OpenCms, the server schema is replaced with 575 * the root path of the site.<p> 576 * <li>In case the link points to an external site, or in case it is not a valid URI, 577 * then <code>null</code> is returned.<p> 578 * </ul> 579 * 580 * Please note the above text describes the default behavior as implemented by 581 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using 582 * the {@link I_CmsLinkSubstitutionHandler} interface.<p> 583 * 584 * @param cms the current users OpenCms context 585 * @param targetUri the target URI link 586 * @param basePath path to use as base in case the target URI is relative (can be <code>null</code>) 587 * 588 * @return the resource root path in the OpenCms VFS for the given target URI link, or <code>null</code> in 589 * case the link points to an external site 590 * 591 * @see I_CmsLinkSubstitutionHandler for the interface that can be used to fully customize the link substitution 592 * @see CmsDefaultLinkSubstitutionHandler for the default link substitution handler 593 * 594 * @since 7.0.2 595 */ 596 public String getRootPath(CmsObject cms, String targetUri, String basePath) { 597 598 return m_linkSubstitutionHandler.getRootPath(cms, targetUri, basePath); 599 } 600 601 /** 602 * Returns the link for the given resource in the current project, with full server prefix.<p> 603 * 604 * Like <code>http://site.enterprise.com:8080/index.html</code>.<p> 605 * 606 * In case the resource name is a full root path, the site from the root path will be used. 607 * Otherwise the resource is assumed to be in the current site set be the OpenCms user context.<p> 608 * 609 * @param cms the current OpenCms user context 610 * @param resourceName the resource to generate the online link for 611 * 612 * @return the link for the given resource in the current project, with full server prefix 613 * 614 * @see #getOnlineLink(CmsObject, String) 615 */ 616 public String getServerLink(CmsObject cms, String resourceName) { 617 618 return getServerLink(cms, resourceName, false); 619 } 620 621 /** 622 * Returns the link for the given resource in the current project, with full server prefix.<p> 623 * 624 * Like <code>http://site.enterprise.com:8080/index.html</code>.<p> 625 * 626 * In case the resource name is a full root path, the site from the root path will be used. 627 * Otherwise the resource is assumed to be in the current site set be the OpenCms user context.<p> 628 * 629 * @param cms the current OpenCms user context 630 * @param resourceName the resource to generate the online link for 631 * @param forceSecure forces the secure server prefix 632 * 633 * @return the link for the given resource in the current project, with full server prefix 634 * 635 * @see #getOnlineLink(CmsObject, String) 636 */ 637 public String getServerLink(CmsObject cms, String resourceName, boolean forceSecure) { 638 639 String result = substituteLinkForUnknownTarget(cms, resourceName, forceSecure); 640 return appendServerPrefix(cms, result, resourceName, false); 641 } 642 643 /** 644 * Returns the link for the given workplace resource. 645 * 646 * This should only be used for resources under /system or /shared.<p< 647 * 648 * @param cms the current OpenCms user context 649 * @param resourceName the resource to generate the online link for 650 * @param forceSecure forces the secure server prefix 651 * 652 * @return the link for the given resource 653 */ 654 public String getWorkplaceLink(CmsObject cms, String resourceName, boolean forceSecure) { 655 656 String result = substituteLinkForUnknownTarget(cms, resourceName, forceSecure); 657 return appendServerPrefix(cms, result, resourceName, true); 658 659 } 660 661 /** 662 * Sets the internal link substitution handler.<p> 663 * 664 * @param cms an OpenCms user context that must have the permissions for role {@link CmsRole#ROOT_ADMIN}.<p> 665 * @param linkSubstitutionHandler the handler to set 666 * 667 * @throws CmsRoleViolationException in case the provided OpenCms user context does not have the required permissions 668 */ 669 public void setLinkSubstitutionHandler(CmsObject cms, I_CmsLinkSubstitutionHandler linkSubstitutionHandler) 670 throws CmsRoleViolationException { 671 672 OpenCms.getRoleManager().checkRole(cms, CmsRole.ROOT_ADMIN); 673 m_linkSubstitutionHandler = linkSubstitutionHandler; 674 } 675 676 /** 677 * Sets the result of an external link validation.<p> 678 * 679 * @param externLinkValidationResult the result an external link validation 680 */ 681 public void setPointerLinkValidationResult(CmsExternalLinksValidationResult externLinkValidationResult) { 682 683 m_pointerLinkValidationResult = externLinkValidationResult; 684 } 685 686 /** 687 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 688 * <i>to</i> the given VFS resource, for use on web pages.<p> 689 * 690 * The result will contain the configured context path and 691 * servlet name, and in the case of the "online" project it will also be rewritten according to 692 * to the configured static export settings.<p> 693 * 694 * Should the current site of the given OpenCms user context <code>cms</code> be different from the 695 * site root of the given resource, the result will contain the full server URL to the target resource.<p> 696 * 697 * Please note the above text describes the default behavior as implemented by 698 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using the 699 * {@link I_CmsLinkSubstitutionHandler} interface.<p> 700 * 701 * @param cms the current OpenCms user context 702 * @param resource the VFS resource the link should point to 703 * 704 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 705 * <i>to</i> the given VFS resource, for use on web pages 706 */ 707 public String substituteLink(CmsObject cms, CmsResource resource) { 708 709 return substituteLinkForRootPath(cms, resource.getRootPath()); 710 } 711 712 /** 713 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 714 * <i>to</i> the VFS resource indicated by the given <code>link</code> in the current site, 715 * for use on web pages.<p> 716 * 717 * The provided <code>link</code> is assumed to be the contained in the site currently 718 * set in the provided OpenCms user context <code>cms</code>.<p> 719 * 720 * The result will be an absolute link that contains the configured context path and 721 * servlet name, and in the case of the "online" project it will also be rewritten according to 722 * to the configured static export settings.<p> 723 * 724 * In case <code>link</code> is a relative URI, the current URI contained in the provided 725 * OpenCms user context <code>cms</code> is used to make the relative <code>link</code> absolute.<p> 726 * 727 * Please note the above text describes the default behavior as implemented by 728 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using the 729 * {@link I_CmsLinkSubstitutionHandler} interface.<p> 730 * 731 * @param cms the current OpenCms user context 732 * @param link the link to process which is assumed to point to a VFS resource, with optional parameters 733 734 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 735 * <i>to</i> the VFS resource indicated by the given <code>link</code> in the current site 736 */ 737 public String substituteLink(CmsObject cms, String link) { 738 739 return substituteLink(cms, link, null, false); 740 } 741 742 /** 743 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 744 * <i>to</i> the VFS resource indicated by the given <code>link</code> and <code>siteRoot</code>, 745 * for use on web pages.<p> 746 * 747 * The result will be an absolute link that contains the configured context path and 748 * servlet name, and in the case of the "online" project it will also be rewritten according to 749 * to the configured static export settings.<p> 750 * 751 * In case <code>link</code> is a relative URI, the current URI contained in the provided 752 * OpenCms user context <code>cms</code> is used to make the relative <code>link</code> absolute.<p> 753 * 754 * The provided <code>siteRoot</code> is assumed to be the "home" of the link. 755 * In case the current site of the given OpenCms user context <code>cms</code> is different from the 756 * provided <code>siteRoot</code>, the full server prefix is appended to the result link.<p> 757 * 758 * Please note the above text describes the default behavior as implemented by 759 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using the 760 * {@link I_CmsLinkSubstitutionHandler} interface.<p> 761 * 762 * @param cms the current OpenCms user context 763 * @param link the link to process which is assumed to point to a VFS resource, with optional parameters 764 * @param siteRoot the site root of the <code>link</code> 765 * 766 * @return the substituted link 767 */ 768 public String substituteLink(CmsObject cms, String link, String siteRoot) { 769 770 return substituteLink(cms, link, siteRoot, false); 771 } 772 773 /** 774 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 775 * <i>to</i> the VFS resource indicated by the given <code>link</code> and <code>siteRoot</code>, 776 * for use on web pages, using the configured link substitution handler.<p> 777 * 778 * The result will be an absolute link that contains the configured context path and 779 * servlet name, and in the case of the "online" project it will also be rewritten according to 780 * to the configured static export settings.<p> 781 * 782 * In case <code>link</code> is a relative URI, the current URI contained in the provided 783 * OpenCms user context <code>cms</code> is used to make the relative <code>link</code> absolute.<p> 784 * 785 * The provided <code>siteRoot</code> is assumed to be the "home" of the link. 786 * In case the current site of the given OpenCms user context <code>cms</code> is different from the 787 * provided <code>siteRoot</code>, the full server prefix is appended to the result link.<p> 788 * 789 * A server prefix is also added if 790 * <ul> 791 * <li>the link is contained in a normal document and the link references a secure document</li> 792 * <li>the link is contained in a secure document and the link references a normal document</li> 793 * </ul> 794 * 795 * Please note the above text describes the default behavior as implemented by 796 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using the 797 * {@link I_CmsLinkSubstitutionHandler} interface.<p> 798 * 799 * @param cms the current OpenCms user context 800 * @param link the link to process which is assumed to point to a VFS resource, with optional parameters 801 * @param siteRoot the site root of the <code>link</code> 802 * @param forceSecure if <code>true</code> generates always an absolute URL (with protocol and server name) for secure links 803 * 804 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 805 * <i>to</i> the VFS resource indicated by the given <code>link</code> and <code>siteRoot</code> 806 * 807 * @see I_CmsLinkSubstitutionHandler for the interface that can be used to fully customize the link substitution 808 * @see CmsDefaultLinkSubstitutionHandler for the default link substitution handler 809 */ 810 public String substituteLink(CmsObject cms, String link, String siteRoot, boolean forceSecure) { 811 812 return substituteLink(cms, link, siteRoot, null, forceSecure); 813 } 814 815 /** 816 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 817 * <i>to</i> the VFS resource indicated by the given <code>link</code> and <code>siteRoot</code>, 818 * for use on web pages, using the configured link substitution handler.<p> 819 * 820 * The result will be an absolute link that contains the configured context path and 821 * servlet name, and in the case of the "online" project it will also be rewritten according to 822 * to the configured static export settings.<p> 823 * 824 * In case <code>link</code> is a relative URI, the current URI contained in the provided 825 * OpenCms user context <code>cms</code> is used to make the relative <code>link</code> absolute.<p> 826 * 827 * The provided <code>siteRoot</code> is assumed to be the "home" of the link. 828 * In case the current site of the given OpenCms user context <code>cms</code> is different from the 829 * provided <code>siteRoot</code>, the full server prefix is appended to the result link.<p> 830 * 831 * A server prefix is also added if 832 * <ul> 833 * <li>the link is contained in a normal document and the link references a secure document</li> 834 * <li>the link is contained in a secure document and the link references a normal document</li> 835 * </ul> 836 * 837 * Please note the above text describes the default behavior as implemented by 838 * {@link CmsDefaultLinkSubstitutionHandler}, which can be fully customized using the 839 * {@link I_CmsLinkSubstitutionHandler} interface.<p> 840 * 841 * @param cms the current OpenCms user context 842 * @param link the link to process which is assumed to point to a VFS resource, with optional parameters 843 * @param siteRoot the site root of the <code>link</code> 844 * @param targetDetailPage the target detail page, in case of linking to a specific detail page 845 * @param forceSecure if <code>true</code> generates always an absolute URL (with protocol and server name) for secure links 846 * 847 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 848 * <i>to</i> the VFS resource indicated by the given <code>link</code> and <code>siteRoot</code> 849 * 850 * @see I_CmsLinkSubstitutionHandler for the interface that can be used to fully customize the link substitution 851 * @see CmsDefaultLinkSubstitutionHandler for the default link substitution handler 852 */ 853 public String substituteLink( 854 CmsObject cms, 855 String link, 856 String siteRoot, 857 String targetDetailPage, 858 boolean forceSecure) { 859 860 if (targetDetailPage != null) { 861 return m_linkSubstitutionHandler.getLink(cms, link, siteRoot, targetDetailPage, forceSecure); 862 } else { 863 return m_linkSubstitutionHandler.getLink(cms, link, siteRoot, forceSecure); 864 } 865 866 } 867 868 /** 869 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 870 * <i>to</i> the VFS resource indicated by the given root path, for use on web pages.<p> 871 * 872 * The result will contain the configured context path and 873 * servlet name, and in the case of the "online" project it will also be rewritten according to 874 * to the configured static export settings.<p> 875 * 876 * Should the current site of the given OpenCms user context <code>cms</code> be different from the 877 * site root of the given resource root path, the result will contain the full server URL to the target resource.<p> 878 * 879 * @param cms the current OpenCms user context 880 * @param rootPath the VFS resource root path the link should point to 881 * 882 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 883 * <i>to</i> the VFS resource indicated by the given root path 884 */ 885 public String substituteLinkForRootPath(CmsObject cms, String rootPath) { 886 887 String siteRoot = OpenCms.getSiteManager().getSiteRoot(rootPath); 888 if (siteRoot == null) { 889 // use current site root in case no valid site root is available 890 // this will also be the case if a "/system" link is used 891 siteRoot = cms.getRequestContext().getSiteRoot(); 892 } 893 String sitePath; 894 if (rootPath.startsWith(siteRoot)) { 895 // only cut the site root if the root part really has this prefix 896 sitePath = rootPath.substring(siteRoot.length()); 897 } else { 898 sitePath = rootPath; 899 } 900 return substituteLink(cms, sitePath, siteRoot, false); 901 } 902 903 /** 904 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 905 * <i>to</i> the given <code>link</code>, for use on web pages.<p> 906 * 907 * A number of tests are performed with the <code>link</code> in order to find out how to create the link:<ul> 908 * <li>If <code>link</code> is empty, an empty String is returned. 909 * <li>If <code>link</code> starts with an URI scheme component, for example <code>http://</code>, 910 * and does not point to an internal OpenCms site, it is returned unchanged. 911 * <li>If <code>link</code> is an absolute URI that starts with a configured site root, 912 * the site root is cut from the link and 913 * the same result as {@link #substituteLink(CmsObject, String, String)} is returned. 914 * <li>Otherwise the same result as {@link #substituteLink(CmsObject, String)} is returned. 915 * </ul> 916 * 917 * @param cms the current OpenCms user context 918 * @param link the link to process 919 * 920 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 921 * <i>to</i> the given <code>link</code> 922 */ 923 public String substituteLinkForUnknownTarget(CmsObject cms, String link) { 924 925 return substituteLinkForUnknownTarget(cms, link, false); 926 927 } 928 929 /** 930 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 931 * <i>to</i> the given <code>link</code>, for use on web pages.<p> 932 * 933 * A number of tests are performed with the <code>link</code> in order to find out how to create the link:<ul> 934 * <li>If <code>link</code> is empty, an empty String is returned. 935 * <li>If <code>link</code> starts with an URI scheme component, for example <code>http://</code>, 936 * and does not point to an internal OpenCms site, it is returned unchanged. 937 * <li>If <code>link</code> is an absolute URI that starts with a configured site root, 938 * the site root is cut from the link and 939 * the same result as {@link #substituteLink(CmsObject, String, String)} is returned. 940 * <li>Otherwise the same result as {@link #substituteLink(CmsObject, String)} is returned. 941 * </ul> 942 * 943 * @param cms the current OpenCms user context 944 * @param link the link to process 945 * @param forceSecure forces the secure server prefix if the link target is secure 946 * 947 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 948 * <i>to</i> the given <code>link</code> 949 */ 950 public String substituteLinkForUnknownTarget(CmsObject cms, String link, boolean forceSecure) { 951 952 return substituteLinkForUnknownTarget(cms, link, null, forceSecure); 953 } 954 955 /** 956 * Returns a link <i>from</i> the URI stored in the provided OpenCms user context 957 * <i>to</i> the given <code>link</code>, for use on web pages.<p> 958 * 959 * A number of tests are performed with the <code>link</code> in order to find out how to create the link:<ul> 960 * <li>If <code>link</code> is empty, an empty String is returned. 961 * <li>If <code>link</code> starts with an URI scheme component, for example <code>http://</code>, 962 * and does not point to an internal OpenCms site, it is returned unchanged. 963 * <li>If <code>link</code> is an absolute URI that starts with a configured site root, 964 * the site root is cut from the link and 965 * the same result as {@link #substituteLink(CmsObject, String, String)} is returned. 966 * <li>Otherwise the same result as {@link #substituteLink(CmsObject, String)} is returned. 967 * </ul> 968 * 969 * @param cms the current OpenCms user context 970 * @param link the link to process 971 * @param targetDetailPage the target detail page, in case of linking to a specific detail page 972 * @param forceSecure forces the secure server prefix if the link target is secure 973 * 974 * @return a link <i>from</i> the URI stored in the provided OpenCms user context 975 * <i>to</i> the given <code>link</code> 976 */ 977 public String substituteLinkForUnknownTarget( 978 CmsObject cms, 979 String link, 980 String targetDetailPage, 981 boolean forceSecure) { 982 983 if (CmsStringUtil.isEmpty(link)) { 984 return ""; 985 } 986 String sitePath = link; 987 String siteRoot = null; 988 if (hasScheme(link)) { 989 // the link has a scheme, that is starts with something like "http://" 990 // usually this should be a link to an external resource, but check anyway 991 sitePath = getRootPath(cms, link); 992 if (sitePath == null) { 993 // probably an external link, don't touch this 994 return link; 995 } 996 } 997 // check if we can find a site from the link 998 siteRoot = OpenCms.getSiteManager().getSiteRoot(sitePath); 999 if (siteRoot == null) { 1000 // use current site root in case no valid site root is available 1001 // this will also be the case if a "/system" link is used 1002 siteRoot = cms.getRequestContext().getSiteRoot(); 1003 } else { 1004 // we found a site root, cut this from the resource path 1005 sitePath = sitePath.substring(siteRoot.length()); 1006 } 1007 return substituteLink(cms, sitePath, siteRoot, targetDetailPage, forceSecure); 1008 } 1009 1010 /** 1011 * Returns the link for the given resource in the current project, with full server prefix.<p> 1012 * 1013 * The input link must already have been processed according to the link substitution rules. 1014 * This method does just append the server prefix in case this is requires.<p> 1015 * 1016 * @param cms the current OpenCms user context 1017 * @param link the resource to generate the online link for 1018 * @param pathWithOptionalParameters the resource name 1019 * @param workplaceLink if this is set, use the workplace server prefix even if we are in the Online project 1020 * 1021 * @return the link for the given resource in the current project, with full server prefix 1022 */ 1023 private String appendServerPrefix( 1024 CmsObject cms, 1025 String link, 1026 String pathWithOptionalParameters, 1027 boolean workplaceLink) { 1028 1029 int paramPos = pathWithOptionalParameters.indexOf("?"); 1030 String resourceName = paramPos > -1 1031 ? pathWithOptionalParameters.substring(0, paramPos) 1032 : pathWithOptionalParameters; 1033 1034 if (isAbsoluteUri(link) && !hasScheme(link)) { 1035 // URI is absolute and contains no schema 1036 // this indicates source and target link are in the same site 1037 String serverPrefix; 1038 if (cms.getRequestContext().getCurrentProject().isOnlineProject() && !workplaceLink) { 1039 String overrideSiteRoot = (String)(cms.getRequestContext().getAttribute( 1040 CmsDefaultLinkSubstitutionHandler.OVERRIDE_SITEROOT_PREFIX + link)); 1041 // on online project, get the real site name from the site manager 1042 CmsSite currentSite = OpenCms.getSiteManager().getSite( 1043 overrideSiteRoot != null ? overrideSiteRoot : resourceName, 1044 cms.getRequestContext().getSiteRoot()); 1045 serverPrefix = currentSite.getServerPrefix(cms, resourceName); 1046 } else { 1047 // in offline mode, source must be the workplace 1048 // so append the workplace server so links can still be clicked 1049 serverPrefix = OpenCms.getSiteManager().getWorkplaceServer(cms); 1050 } 1051 link = serverPrefix + link; 1052 } 1053 return link; 1054 } 1055}