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