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, 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.ade.detailpage.I_CmsDetailPageFinder; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.file.CmsVfsException; 035import org.opencms.file.CmsVfsResourceNotFoundException; 036import org.opencms.file.types.CmsResourceTypeImage; 037import org.opencms.loader.CmsLoaderException; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.CmsStaticResourceHandler; 041import org.opencms.main.OpenCms; 042import org.opencms.site.CmsSite; 043import org.opencms.site.CmsSiteMatcher; 044import org.opencms.util.CmsFileUtil; 045import org.opencms.util.CmsPair; 046import org.opencms.util.CmsStringUtil; 047import org.opencms.util.CmsUUID; 048import org.opencms.workplace.CmsWorkplace; 049 050import java.net.URI; 051import java.util.List; 052import java.util.Locale; 053 054import org.apache.commons.logging.Log; 055 056/** 057 * Default link substitution behavior.<p> 058 * 059 * @since 7.0.2 060 * 061 * @see CmsLinkManager#substituteLink(org.opencms.file.CmsObject, String, String, boolean) 062 * for the method where this handler is used. 063 */ 064public class CmsDefaultLinkSubstitutionHandler implements I_CmsLinkSubstitutionHandler { 065 066 /** 067 * Request context attribute name to make the link substitution handler treat the link like an image link.<p> 068 */ 069 public static final String ATTR_IS_IMAGE_LINK = "IS_IMAGE_LINK"; 070 071 /** Key for a request context attribute to control whether the getRootPath method uses the current site root for workplace requests. 072 * The getRootPath method clears this attribute when called. 073 */ 074 public static final String DONT_USE_CURRENT_SITE_FOR_WORKPLACE_REQUESTS = "DONT_USE_CURRENT_SITE_FOR_WORKPLACE_REQUESTS"; 075 076 /** The log object for this class. */ 077 private static final Log LOG = CmsLog.getLog(CmsDefaultLinkSubstitutionHandler.class); 078 079 /** Prefix used for request context attributes to control whether a different site root should be used in appendServerPrefix. */ 080 public static final String OVERRIDE_SITEROOT_PREFIX = "OVERRIDE_SITEROOT:"; 081 082 /** 083 * Returns the resource root path in the OpenCms VFS for the given link, or <code>null</code> in 084 * case the link points to an external site.<p> 085 * 086 * If the target URI contains no site information, but starts with the opencms context, the context is removed:<pre> 087 * /opencms/opencms/system/further_path -> /system/further_path</pre> 088 * 089 * If the target URI contains no site information, the path will be prefixed with the current site 090 * from the provided OpenCms user context:<pre> 091 * /folder/page.html -> /sites/mysite/folder/page.html</pre> 092 * 093 * If the path of the target URI is relative, i.e. does not start with "/", 094 * the path will be prefixed with the current site and the given relative path, 095 * then normalized. 096 * If no relative path is given, <code>null</code> is returned. 097 * If the normalized path is outsite a site, null is returned.<pre> 098 * page.html -> /sites/mysite/page.html 099 * ../page.html -> /sites/mysite/page.html 100 * ../../page.html -> null</pre> 101 * 102 * If the target URI contains a scheme/server name that denotes an opencms site, 103 * it is replaced by the appropriate site path:<pre> 104 * http://www.mysite.de/folder/page.html -> /sites/mysite/folder/page.html</pre><p> 105 * 106 * If the target URI contains a scheme/server name that does not match with any site, 107 * or if the URI is opaque or invalid, 108 * <code>null</code> is returned:<pre> 109 * http://www.elsewhere.com/page.html -> null 110 * mailto:someone@elsewhere.com -> null</pre> 111 * 112 * @see org.opencms.staticexport.I_CmsLinkSubstitutionHandler#getLink(org.opencms.file.CmsObject, java.lang.String, java.lang.String, boolean) 113 */ 114 public String getLink(CmsObject cms, String link, String siteRoot, boolean forceSecure) { 115 116 return getLink(cms, link, siteRoot, null, forceSecure); 117 } 118 119 /** 120 * @see org.opencms.staticexport.I_CmsLinkSubstitutionHandler#getLink(org.opencms.file.CmsObject, java.lang.String, java.lang.String, java.lang.String, boolean) 121 */ 122 public String getLink(CmsObject cms, String link, String siteRoot, String targetDetailPage, boolean forceSecure) { 123 124 if (CmsStringUtil.isEmpty(link)) { 125 // not a valid link parameter, return an empty String 126 return ""; 127 } 128 129 if (CmsStaticResourceHandler.isStaticResourceUri(link)) { 130 return CmsWorkplace.getStaticResourceUri(link); 131 } 132 133 // make sure we have an absolute link 134 String absoluteLink = CmsLinkManager.getAbsoluteUri(link, cms.getRequestContext().getUri()); 135 String overrideSiteRoot = null; 136 137 String vfsName; 138 String parameters; 139 // check if the link has parameters, if so cut them 140 int pos = absoluteLink.indexOf('?'); 141 if (pos >= 0) { 142 vfsName = absoluteLink.substring(0, pos); 143 parameters = absoluteLink.substring(pos); 144 } else { 145 vfsName = absoluteLink; 146 parameters = null; 147 } 148 149 // check for anchor 150 String anchor = null; 151 pos = vfsName.indexOf('#'); 152 if (pos >= 0) { 153 anchor = vfsName.substring(pos); 154 vfsName = vfsName.substring(0, pos); 155 } 156 157 String resultLink = null; 158 String uriBaseName = null; 159 boolean useRelativeLinks = false; 160 161 // determine the target site of the link 162 CmsSite currentSite = OpenCms.getSiteManager().getCurrentSite(cms); 163 CmsSite targetSite = null; 164 if (CmsStringUtil.isNotEmpty(siteRoot)) { 165 targetSite = OpenCms.getSiteManager().getSiteForSiteRoot(siteRoot); 166 } 167 if (targetSite == null) { 168 targetSite = currentSite; 169 } 170 171 String targetSiteRoot = targetSite.getSiteRoot(); 172 String originalVfsName = vfsName; 173 String detailPage = null; 174 CmsResource detailContent = null; 175 try { 176 String rootVfsName; 177 if (!vfsName.startsWith(targetSiteRoot) 178 && !vfsName.startsWith(CmsResource.VFS_FOLDER_SYSTEM + "/") 179 && !OpenCms.getSiteManager().startsWithShared(vfsName)) { 180 rootVfsName = CmsStringUtil.joinPaths(targetSiteRoot, vfsName); 181 } else { 182 rootVfsName = vfsName; 183 } 184 if (!rootVfsName.startsWith(CmsWorkplace.VFS_PATH_WORKPLACE)) { 185 // never use the ADE manager for workplace links, to be sure the workplace stays usable in case of configuration errors 186 I_CmsDetailPageFinder finder = OpenCms.getADEManager().getDetailPageFinder(); 187 detailPage = finder.getDetailPage(cms, rootVfsName, cms.getRequestContext().getUri(), targetDetailPage); 188 } 189 if (detailPage != null) { 190 CmsSite detailPageSite = OpenCms.getSiteManager().getSiteForRootPath(detailPage); 191 if (detailPageSite != null) { 192 targetSite = detailPageSite; 193 overrideSiteRoot = targetSiteRoot = targetSite.getSiteRoot(); 194 detailPage = detailPage.substring(targetSiteRoot.length()); 195 if (!detailPage.startsWith("/")) { 196 detailPage = "/" + detailPage; 197 } 198 } 199 String originalSiteRoot = cms.getRequestContext().getSiteRoot(); 200 try { 201 cms.getRequestContext().setSiteRoot(""); 202 CmsResource element = cms.readResource(rootVfsName, CmsResourceFilter.IGNORE_EXPIRATION); 203 detailContent = element; 204 Locale locale = cms.getRequestContext().getLocale(); 205 List<Locale> defaultLocales = OpenCms.getLocaleManager().getDefaultLocales(); 206 vfsName = CmsStringUtil.joinPaths( 207 detailPage, 208 cms.getDetailName(element, locale, defaultLocales), 209 "/"); 210 211 } catch (CmsVfsException e) { 212 if (LOG.isWarnEnabled()) { 213 LOG.warn(e.getLocalizedMessage(), e); 214 } 215 } finally { 216 cms.getRequestContext().setSiteRoot(originalSiteRoot); 217 218 } 219 } 220 } catch (CmsVfsResourceNotFoundException e) { 221 LOG.info(e.getLocalizedMessage(), e); 222 } catch (CmsException e) { 223 LOG.error(e.getLocalizedMessage(), e); 224 } 225 226 // if the link points to another site, there needs to be a server prefix 227 String serverPrefix; 228 if (targetSite != currentSite) { 229 serverPrefix = targetSite.getUrl(); 230 } else { 231 serverPrefix = ""; 232 } 233 234 // in the online project, check static export and secure settings 235 if (cms.getRequestContext().getCurrentProject().isOnlineProject()) { 236 // first check if this link needs static export 237 CmsStaticExportManager exportManager = OpenCms.getStaticExportManager(); 238 String oriUri = cms.getRequestContext().getUri(); 239 // check if we need relative links in the exported pages 240 if (exportManager.relativeLinksInExport(cms.getRequestContext().getSiteRoot() + oriUri)) { 241 // try to get base URI from cache 242 String cacheKey = exportManager.getCacheKey(targetSiteRoot, oriUri); 243 uriBaseName = exportManager.getCachedOnlineLink(cacheKey); 244 if (uriBaseName == null) { 245 // base not cached, check if we must export it 246 if (exportManager.isExportLink(cms, oriUri)) { 247 // base URI must also be exported 248 uriBaseName = exportManager.getRfsName(cms, oriUri); 249 } else { 250 // base URI dosn't need to be exported 251 CmsPair<String, String> uriParamPair = addVfsPrefix(cms, oriUri, targetSite, parameters); 252 uriBaseName = uriParamPair.getFirst(); 253 parameters = uriParamPair.getSecond(); 254 } 255 // cache export base URI 256 exportManager.cacheOnlineLink(cacheKey, uriBaseName); 257 } 258 // use relative links only on pages that get exported 259 useRelativeLinks = uriBaseName.startsWith( 260 exportManager.getRfsPrefix(cms.getRequestContext().getSiteRoot() + oriUri)); 261 } 262 263 String detailPagePart = detailPage == null ? "" : detailPage + ":"; 264 // check if we have the absolute VFS name for the link target cached 265 // (We really need the target site root in the cache key, because different resources with the same site paths 266 // but in different sites may have different export settings. It seems we don't really need the site root 267 // from the request context as part of the key, but we'll leave it in to make sure we don't break anything.) 268 String cacheKey = generateCacheKey(cms, targetSiteRoot, detailPagePart, absoluteLink); 269 resultLink = exportManager.getCachedOnlineLink(cacheKey); 270 if (resultLink == null) { 271 String storedSiteRoot = cms.getRequestContext().getSiteRoot(); 272 try { 273 cms.getRequestContext().setSiteRoot(targetSite.getSiteRoot()); 274 // didn't find the link in the cache 275 if (exportManager.isExportLink(cms, vfsName)) { 276 parameters = prepareExportParameters(cms, vfsName, parameters); 277 // export required, get export name for target link 278 resultLink = exportManager.getRfsName(cms, vfsName, parameters, targetDetailPage); 279 // now set the parameters to null, we do not need them anymore 280 parameters = null; 281 } else { 282 // no export required for the target link 283 CmsPair<String, String> uriParamPair = addVfsPrefix(cms, vfsName, targetSite, parameters); 284 resultLink = uriParamPair.getFirst(); 285 parameters = uriParamPair.getSecond(); 286 // add cut off parameters if required 287 if (parameters != null) { 288 resultLink = resultLink.concat(parameters); 289 } 290 } 291 } finally { 292 cms.getRequestContext().setSiteRoot(storedSiteRoot); 293 } 294 // cache the result 295 exportManager.cacheOnlineLink(cacheKey, resultLink); 296 } 297 298 // now check for the secure settings 299 300 // check if either the current site or the target site does have a secure server configured 301 if (targetSite.hasSecureServer() || currentSite.hasSecureServer()) { 302 303 if (!vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) { 304 // don't make a secure connection to the "/system" folder (why ?) 305 int linkType = -1; 306 try { 307 // read the linked resource 308 linkType = cms.readResource(originalVfsName).getTypeId(); 309 } catch (CmsException e) { 310 // the resource could not be read 311 if (LOG.isInfoEnabled()) { 312 String message = Messages.get().getBundle().key( 313 Messages.LOG_RESOURCE_ACESS_ERROR_3, 314 vfsName, 315 cms.getRequestContext().getCurrentUser().getName(), 316 cms.getRequestContext().getSiteRoot()); 317 if (LOG.isDebugEnabled()) { 318 LOG.debug(message, e); 319 } else { 320 LOG.info(message); 321 } 322 } 323 } 324 325 // images are always referenced without a server prefix 326 int imageId; 327 try { 328 imageId = OpenCms.getResourceManager().getResourceType( 329 CmsResourceTypeImage.getStaticTypeName()).getTypeId(); 330 } catch (CmsLoaderException e1) { 331 // should really never happen 332 LOG.warn(e1.getLocalizedMessage(), e1); 333 imageId = CmsResourceTypeImage.getStaticTypeId(); 334 } 335 boolean hasIsImageLinkAttr = Boolean.parseBoolean( 336 "" + cms.getRequestContext().getAttribute(ATTR_IS_IMAGE_LINK)); 337 if ((linkType != imageId) && !hasIsImageLinkAttr) { 338 // check the secure property of the link 339 boolean secureRequest = cms.getRequestContext().isSecureRequest() 340 || exportManager.isSecureLink(cms, oriUri); 341 342 boolean secureLink; 343 if (detailContent == null) { 344 secureLink = isSecureLink(cms, vfsName, targetSite, secureRequest); 345 } else { 346 secureLink = isDetailPageLinkSecure( 347 cms, 348 detailPage, 349 detailContent, 350 targetSite, 351 secureRequest); 352 353 } 354 // if we are on a normal server, and the requested resource is secure, 355 // the server name has to be prepended 356 if (secureLink && (forceSecure || !secureRequest)) { 357 serverPrefix = targetSite.getSecureUrl(); 358 } else if (!secureLink && secureRequest) { 359 serverPrefix = targetSite.getUrl(); 360 } 361 } 362 } 363 } 364 // make absolute link relative, if relative links in export are required 365 // and if the link does not point to another server 366 if (useRelativeLinks && CmsStringUtil.isEmpty(serverPrefix)) { 367 // in case the current page is a detailpage, append another path level 368 if (cms.getRequestContext().getDetailContentId() != null) { 369 uriBaseName = CmsStringUtil.joinPaths( 370 CmsResource.getFolderPath(uriBaseName), 371 cms.getRequestContext().getDetailContentId().toString() + "/index.html"); 372 } 373 resultLink = CmsLinkManager.getRelativeUri(uriBaseName, resultLink); 374 } 375 376 } else { 377 // offline project, no export or secure handling required 378 if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 379 // in unit test this code would fail otherwise 380 CmsPair<String, String> uriParamPair = addVfsPrefix(cms, vfsName, targetSite, parameters); 381 resultLink = uriParamPair.getFirst(); 382 parameters = uriParamPair.getSecond(); 383 } 384 385 // add cut off parameters and return the result 386 if ((parameters != null) && (resultLink != null)) { 387 resultLink = resultLink.concat(parameters); 388 } 389 } 390 391 if ((anchor != null) && (resultLink != null)) { 392 resultLink = resultLink.concat(anchor); 393 } 394 if (overrideSiteRoot != null) { 395 cms.getRequestContext().setAttribute(OVERRIDE_SITEROOT_PREFIX + resultLink, overrideSiteRoot); 396 } 397 398 return serverPrefix.concat(resultLink); 399 } 400 401 /** 402 * @see org.opencms.staticexport.I_CmsLinkSubstitutionHandler#getRootPath(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 403 */ 404 public String getRootPath(CmsObject cms, String targetUri, String basePath) { 405 406 String result = getSimpleRootPath(cms, targetUri, basePath); 407 String detailRootPath = getDetailRootPath(cms, result); 408 if (detailRootPath != null) { 409 result = detailRootPath; 410 } 411 return result; 412 413 } 414 415 /** 416 * Adds the VFS prefix to the VFS name and potentially adjusts request parameters<p> 417 * This method is required as a hook used in {@link CmsLocalePrefixLinkSubstitutionHandler}.<p> 418 * 419 * @param cms the cms context 420 * @param vfsName the VFS name 421 * @param targetSite the target site 422 * @param parameters the request parameters 423 * 424 * @return the path and the (adjusted) request parameters. 425 */ 426 protected CmsPair<String, String> addVfsPrefix( 427 CmsObject cms, 428 String vfsName, 429 CmsSite targetSite, 430 String parameters) { 431 432 return new CmsPair<String, String>(OpenCms.getStaticExportManager().getVfsPrefix().concat(vfsName), parameters); 433 } 434 435 /** 436 * Generates the cache key for Online links. 437 * @param cms the current CmsObject 438 * @param targetSiteRoot the target site root 439 * @param detailPagePart the detail page part 440 * @param absoluteLink the absolute (site-relative) link to the resource 441 * @return the cache key 442 */ 443 protected String generateCacheKey( 444 CmsObject cms, 445 String targetSiteRoot, 446 String detailPagePart, 447 String absoluteLink) { 448 449 return cms.getRequestContext().getSiteRoot() + ":" + targetSiteRoot + ":" + detailPagePart + absoluteLink; 450 } 451 452 /** 453 * Returns the root path for given site.<p> 454 * This method is required as a hook used in {@link CmsLocalePrefixLinkSubstitutionHandler}.<p> 455 * @param cms the cms context 456 * @param path the path 457 * @param siteRoot the site root, will be null in case of the root site 458 * @param isRootPath in case the path is already a root path 459 * 460 * @return the root path 461 */ 462 protected String getRootPathForSite(CmsObject cms, String path, String siteRoot, boolean isRootPath) { 463 464 if (isRootPath || (siteRoot == null)) { 465 return CmsStringUtil.joinPaths("/", path); 466 } else { 467 return cms.getRequestContext().addSiteRoot(siteRoot, path); 468 } 469 } 470 471 /** 472 * Gets the root path without taking into account detail page links.<p> 473 * 474 * @param cms - see the getRootPath() method 475 * @param targetUri - see the getRootPath() method 476 * @param basePath - see the getRootPath() method 477 * @return - see the getRootPath() method 478 */ 479 protected String getSimpleRootPath(CmsObject cms, String targetUri, String basePath) { 480 481 if (cms == null) { 482 // required by unit test cases 483 return targetUri; 484 } 485 486 URI uri; 487 String path; 488 String suffix = ""; 489 490 // malformed uri 491 try { 492 uri = new URI(targetUri); 493 path = uri.getPath(); 494 suffix = getSuffix(uri); 495 } catch (Exception e) { 496 if (LOG.isWarnEnabled()) { 497 LOG.warn(Messages.get().getBundle().key(Messages.LOG_MALFORMED_URI_1, targetUri), e); 498 } 499 return null; 500 } 501 // opaque URI 502 if (uri.isOpaque()) { 503 return null; 504 } 505 506 // in case the target is the workplace UI 507 if (CmsLinkManager.isWorkplaceUri(uri)) { 508 return null; 509 } 510 511 // in case the target is a static resource served from the class path 512 if (CmsStaticResourceHandler.isStaticResourceUri(uri)) { 513 return CmsStringUtil.joinPaths( 514 CmsStaticResourceHandler.STATIC_RESOURCE_PREFIX, 515 CmsStaticResourceHandler.removeStaticResourcePrefix(path)); 516 } 517 518 CmsStaticExportManager exportManager = OpenCms.getStaticExportManager(); 519 if (exportManager.isValidRfsName(path)) { 520 String originalSiteRoot = cms.getRequestContext().getSiteRoot(); 521 String vfsName = null; 522 try { 523 cms.getRequestContext().setSiteRoot(""); 524 vfsName = exportManager.getVfsName(cms, path); 525 if (vfsName != null) { 526 return vfsName; 527 } 528 } finally { 529 cms.getRequestContext().setSiteRoot(originalSiteRoot); 530 } 531 } 532 533 // absolute URI (i.e. URI has a scheme component like http:// ...) 534 if (uri.isAbsolute()) { 535 CmsSiteMatcher targetMatcher = new CmsSiteMatcher(targetUri); 536 if (OpenCms.getSiteManager().isMatching(targetMatcher) 537 || targetMatcher.equals(cms.getRequestContext().getRequestMatcher())) { 538 539 path = CmsLinkManager.removeOpenCmsContext(path); 540 boolean isWorkplaceServer = OpenCms.getSiteManager().isWorkplaceRequest(targetMatcher) 541 || targetMatcher.equals(cms.getRequestContext().getRequestMatcher()); 542 if (isWorkplaceServer) { 543 String selectedPath; 544 String targetSiteRoot = OpenCms.getSiteManager().getSiteRoot(path); 545 if (targetSiteRoot != null) { 546 selectedPath = getRootPathForSite(cms, path, targetSiteRoot, true); 547 } else { 548 // set selectedPath with the path for the current site 549 selectedPath = getRootPathForSite(cms, path, cms.getRequestContext().getSiteRoot(), false); 550 String pathForMatchedSite = getRootPathForSite( 551 cms, 552 path, 553 OpenCms.getSiteManager().matchSite(targetMatcher).getSiteRoot(), 554 false); 555 String originalSiteRoot = cms.getRequestContext().getSiteRoot(); 556 try { 557 cms.getRequestContext().setSiteRoot(""); 558 // the path for the current site normally is preferred, but if it doesn't exist and the path for the matched site 559 // does exist, then use the path for the matched site 560 if (!cms.existsResource(selectedPath, CmsResourceFilter.ALL) 561 && cms.existsResource(pathForMatchedSite, CmsResourceFilter.ALL)) { 562 selectedPath = pathForMatchedSite; 563 } 564 } finally { 565 cms.getRequestContext().setSiteRoot(originalSiteRoot); 566 } 567 } 568 return selectedPath + suffix; 569 } else { 570 // add the site root of the matching site 571 return getRootPathForSite( 572 cms, 573 path + suffix, 574 OpenCms.getSiteManager().matchSite(targetMatcher).getSiteRoot(), 575 false); 576 } 577 } else { 578 return null; 579 } 580 } 581 582 // relative URI (i.e. no scheme component, but filename can still start with "/") 583 String context = OpenCms.getSystemInfo().getOpenCmsContext(); 584 String vfsPrefix = OpenCms.getStaticExportManager().getVfsPrefix(); 585 if ((context != null) && (path.startsWith(context + "/") || (path.startsWith(vfsPrefix + "/")))) { 586 // URI is starting with opencms context 587 588 // cut context from path 589 path = CmsLinkManager.removeOpenCmsContext(path); 590 591 String targetSiteRoot = getTargetSiteRoot(cms, path, basePath); 592 593 return getRootPathForSite( 594 cms, 595 path + suffix, 596 targetSiteRoot, 597 (targetSiteRoot != null) && path.startsWith(targetSiteRoot)); 598 } 599 600 // URI with relative path is relative to the given relativePath if available and in a site, 601 // otherwise invalid 602 if (CmsStringUtil.isNotEmpty(path) && (path.charAt(0) != '/')) { 603 if (basePath != null) { 604 String absolutePath; 605 int pos = path.indexOf("../../galleries/pics/"); 606 if (pos >= 0) { 607 // HACK: mixed up editor path to system gallery image folder 608 return CmsWorkplace.VFS_PATH_SYSTEM + path.substring(pos + 6) + suffix; 609 } 610 absolutePath = CmsLinkManager.getAbsoluteUri(path, cms.getRequestContext().addSiteRoot(basePath)); 611 if (OpenCms.getSiteManager().getSiteRoot(absolutePath) != null) { 612 return absolutePath + suffix; 613 } 614 // HACK: some editor components (e.g. HtmlArea) mix up the editor URL with the current request URL 615 absolutePath = CmsLinkManager.getAbsoluteUri( 616 path, 617 cms.getRequestContext().getSiteRoot() + CmsWorkplace.VFS_PATH_EDITORS); 618 if (OpenCms.getSiteManager().getSiteRoot(absolutePath) != null) { 619 return absolutePath + suffix; 620 } 621 // HACK: same as above, but XmlContent editor has one path element more 622 absolutePath = CmsLinkManager.getAbsoluteUri( 623 path, 624 cms.getRequestContext().getSiteRoot() + CmsWorkplace.VFS_PATH_EDITORS + "xmlcontent/"); 625 if (OpenCms.getSiteManager().getSiteRoot(absolutePath) != null) { 626 return absolutePath + suffix; 627 } 628 } 629 630 return null; 631 } 632 633 if (CmsStringUtil.isNotEmpty(path)) { 634 String targetSiteRoot = getTargetSiteRoot(cms, path, basePath); 635 636 return getRootPathForSite( 637 cms, 638 path + suffix, 639 targetSiteRoot, 640 (targetSiteRoot != null) && path.startsWith(targetSiteRoot)); 641 } 642 643 // URI without path (typically local link) 644 return suffix; 645 } 646 647 /** 648 * Checks whether a link to a detail page should be secure.<p> 649 * 650 * @param cms the current CMS context 651 * @param detailPage the detail page path 652 * @param detailContent the detail content resource 653 * @param targetSite the target site containing the detail page 654 * @param secureRequest true if the currently running request is secure 655 * 656 * @return true if the link should be a secure link 657 */ 658 protected boolean isDetailPageLinkSecure( 659 CmsObject cms, 660 String detailPage, 661 CmsResource detailContent, 662 CmsSite targetSite, 663 boolean secureRequest) { 664 665 boolean result = false; 666 CmsStaticExportManager exportManager = OpenCms.getStaticExportManager(); 667 try { 668 cms = OpenCms.initCmsObject(cms); 669 if (targetSite.getSiteRoot() != null) { 670 cms.getRequestContext().setSiteRoot(targetSite.getSiteRoot()); 671 } 672 CmsResource defaultFile = cms.readDefaultFile(detailPage); 673 if (defaultFile != null) { 674 result = exportManager.isSecureLink(cms, defaultFile.getRootPath(), "", secureRequest); 675 } 676 } catch (Exception e) { 677 LOG.error("Error while checking whether detail page link should be secure: " + e.getLocalizedMessage(), e); 678 } 679 return result; 680 } 681 682 /** 683 * Checks if the link target is a secure link.<p 684 * 685 * @param cms the current CMS context 686 * @param vfsName the path of the link target 687 * @param targetSite the target site containing the detail page 688 * @param secureRequest true if the currently running request is secure 689 * 690 * @return true if the link should be a secure link 691 */ 692 protected boolean isSecureLink(CmsObject cms, String vfsName, CmsSite targetSite, boolean secureRequest) { 693 694 return OpenCms.getStaticExportManager().isSecureLink(cms, vfsName, targetSite.getSiteRoot(), secureRequest); 695 } 696 697 /** 698 * Prepares the request parameters for the given resource.<p> 699 * This method is required as a hook used in {@link CmsLocalePrefixLinkSubstitutionHandler}.<p> 700 * 701 * @param cms the cms context 702 * @param vfsName the vfs name 703 * @param parameters the parameters to prepare 704 * 705 * @return the root path 706 */ 707 protected String prepareExportParameters(CmsObject cms, String vfsName, String parameters) { 708 709 return parameters; 710 } 711 712 /** 713 * Gets the suffix (query + fragment) of the URI.<p> 714 * 715 * @param uri the URI 716 * @return the suffix of the URI 717 */ 718 String getSuffix(URI uri) { 719 720 String fragment = uri.getFragment(); 721 if (fragment != null) { 722 fragment = "#" + fragment; 723 } else { 724 fragment = ""; 725 } 726 727 String query = uri.getRawQuery(); 728 if (query != null) { 729 query = "?" + query; 730 } else { 731 query = ""; 732 } 733 return query.concat(fragment); 734 } 735 736 /** 737 * Tries to interpret the given URI as a detail page URI and returns the detail content's root path if possible.<p> 738 * 739 * If the given URI is not a detail URI, null will be returned.<p> 740 * 741 * @param cms the CMS context to use 742 * @param result the detail root path, or null if the given uri is not a detail page URI 743 * 744 * @return the detail content root path 745 */ 746 private String getDetailRootPath(CmsObject cms, String result) { 747 748 if (result == null) { 749 return null; 750 } 751 try { 752 URI uri = new URI(result); 753 String path = uri.getPath(); 754 if (CmsStringUtil.isEmptyOrWhitespaceOnly(path) || !OpenCms.getADEManager().isInitialized()) { 755 return null; 756 } 757 String name = CmsFileUtil.removeTrailingSeparator(CmsResource.getName(path)); 758 CmsUUID detailId = OpenCms.getADEManager().getDetailIdCache( 759 cms.getRequestContext().getCurrentProject().isOnlineProject()).getDetailId(name); 760 if (detailId == null) { 761 return null; 762 } 763 String origSiteRoot = cms.getRequestContext().getSiteRoot(); 764 try { 765 cms.getRequestContext().setSiteRoot(""); 766 // real root paths have priority over detail contents 767 if (cms.existsResource(path)) { 768 return null; 769 } 770 } finally { 771 cms.getRequestContext().setSiteRoot(origSiteRoot); 772 } 773 CmsResource detailResource = cms.readResource(detailId, CmsResourceFilter.ALL); 774 return detailResource.getRootPath() + getSuffix(uri); 775 } catch (Exception e) { 776 LOG.error(e.getLocalizedMessage(), e); 777 return null; 778 } 779 } 780 781 /** 782 * Returns the target site for the given path.<p> 783 * 784 * @param cms the cms context 785 * @param path the path 786 * @param basePath the base path 787 * 788 * @return the target site 789 */ 790 private String getTargetSiteRoot(CmsObject cms, String path, String basePath) { 791 792 if (OpenCms.getSiteManager().startsWithShared(path) || path.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) { 793 return null; 794 } 795 String targetSiteRoot = OpenCms.getSiteManager().getSiteRoot(path); 796 if ((targetSiteRoot == null) && (basePath != null)) { 797 targetSiteRoot = OpenCms.getSiteManager().getSiteRoot(basePath); 798 } 799 if (targetSiteRoot == null) { 800 targetSiteRoot = cms.getRequestContext().getSiteRoot(); 801 } 802 return targetSiteRoot; 803 } 804 805}