001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file; 029 030import org.opencms.gwt.shared.CmsGwtConstants; 031import org.opencms.main.CmsIllegalArgumentException; 032import org.opencms.main.OpenCms; 033import org.opencms.security.CmsOrganizationalUnit; 034import org.opencms.site.CmsSiteMatcher; 035import org.opencms.util.CmsResourceTranslator; 036import org.opencms.util.CmsUUID; 037import org.opencms.workplace.CmsWorkplace; 038 039import java.util.Hashtable; 040import java.util.Locale; 041import java.util.Map; 042 043/** 044 * Stores the information about the current users OpenCms context, 045 * for example the requested URI, the current project, the selected site and more.<p> 046 * 047 * @since 6.0.0 048 */ 049public final class CmsRequestContext { 050 051 /** Request context attribute for the ADE context path (should be a root path). */ 052 public static final String ATTRIBUTE_ADE_CONTEXT_PATH = CmsRequestContext.class.getName() + ".ADE_CONTEXT_PATH"; 053 054 /** Request context attribute for the optional client label a session created from this context is marked with. */ 055 public static final String ATTRIBUTE_CLIENT_LABEL = CmsRequestContext.class.getName() + ".CLIENT_LABEL"; 056 057 /** Request context attribute for indicating that an editor is currently open. */ 058 public static final String ATTRIBUTE_EDITOR = CmsRequestContext.class.getName() + ".ATTRIBUTE_EDITOR"; 059 060 /** Request context attribute for indicating we want full links generated for HTML fields. */ 061 public static final String ATTRIBUTE_FULLLINKS = CmsRequestContext.class.getName() + ".ATTRIBUTE_FULLLINKS"; 062 063 /** Request context attribute for indicating the model file for a create resource operation. */ 064 public static final String ATTRIBUTE_MODEL = CmsRequestContext.class.getName() + ".ATTRIBUTE_MODEL"; 065 066 /** Request context attribute for indicating content locale for a create resource operation. */ 067 public static final String ATTRIBUTE_NEW_RESOURCE_LOCALE = CmsRequestContext.class.getName() 068 + ".NEW_RESOURCE_LOCALE"; 069 070 /** A map for storing (optional) request context attributes. */ 071 private Map<String, Object> m_attributeMap; 072 073 /** The current project. */ 074 private CmsProject m_currentProject; 075 076 /** The detail content resource (possibly null). */ 077 private CmsResource m_detailResource; 078 079 /** Directory name translator. */ 080 private CmsResourceTranslator m_directoryTranslator; 081 082 /** Current encoding. */ 083 private String m_encoding; 084 085 /** File name translator. */ 086 private CmsResourceTranslator m_fileTranslator; 087 088 /** Flag to control whether links should be absolute even if we're linking to the current site. */ 089 private boolean m_forceAbsoluteLinks; 090 091 /** The secure request flag. */ 092 private boolean m_isSecureRequest; 093 094 /** The locale used by this request context. */ 095 private Locale m_locale; 096 097 /** The fully qualified name of the organizational unit for this request. */ 098 private String m_ouFqn; 099 100 /** The remote ip address. */ 101 private String m_remoteAddr; 102 103 /** the matcher for the current request, that is the host part of the URI from the original http request. */ 104 private CmsSiteMatcher m_requestMatcher; 105 106 /** The current request time. */ 107 private long m_requestTime; 108 109 /** The name of the root, e.g. /site_a/vfs. */ 110 private String m_siteRoot; 111 112 /** Flag to indicate that this context should not update the user session. */ 113 private boolean m_updateSession; 114 115 /** The URI for getUri() in case it is "overwritten". */ 116 private String m_uri; 117 118 /** The current user. */ 119 private CmsUser m_user; 120 121 /** 122 * Constructs a new request context.<p> 123 * 124 * @param user the current user 125 * @param project the current project 126 * @param requestedUri the requested OpenCms VFS URI 127 * @param requestMatcher the matcher for the current request, that is the host part of the URI from the original http request 128 * @param siteRoot the users current site root 129 * @param isSecureRequest true if this is a secure request 130 * @param locale the users current locale 131 * @param encoding the encoding to use for this request 132 * @param remoteAddr the remote IP address of the user 133 * @param requestTime the time of the request (used for resource publication / expiration date) 134 * @param directoryTranslator the directory translator 135 * @param fileTranslator the file translator 136 * @param ouFqn the fully qualified name of the organizational unit 137 * @param forceAbsoluteLinks if true, links should be generated with a server prefix even if we're linking to the current site 138 */ 139 public CmsRequestContext( 140 CmsUser user, 141 CmsProject project, 142 String requestedUri, 143 CmsSiteMatcher requestMatcher, 144 String siteRoot, 145 boolean isSecureRequest, 146 Locale locale, 147 String encoding, 148 String remoteAddr, 149 long requestTime, 150 CmsResourceTranslator directoryTranslator, 151 CmsResourceTranslator fileTranslator, 152 String ouFqn, 153 boolean forceAbsoluteLinks) { 154 155 m_updateSession = true; 156 m_user = user; 157 m_currentProject = project; 158 m_uri = requestedUri; 159 m_requestMatcher = requestMatcher; 160 m_isSecureRequest = isSecureRequest; 161 setSiteRoot(siteRoot); 162 m_locale = locale; 163 m_encoding = encoding; 164 m_remoteAddr = remoteAddr; 165 m_requestTime = requestTime; 166 m_directoryTranslator = directoryTranslator; 167 m_fileTranslator = fileTranslator; 168 setOuFqn(ouFqn); 169 m_forceAbsoluteLinks = forceAbsoluteLinks; 170 } 171 172 /** 173 * Returns the adjusted site root for a resource using the provided site root as a base.<p> 174 * 175 * Usually, this would be the site root for the current site. 176 * However, if a resource from the <code>/system/</code> folder is requested, 177 * this will be the empty String.<p> 178 * 179 * @param siteRoot the site root of the current site 180 * @param resourcename the resource name to get the adjusted site root for 181 * 182 * @return the adjusted site root for the resource 183 */ 184 public static String getAdjustedSiteRoot(String siteRoot, String resourcename) { 185 186 if (resourcename.startsWith(CmsWorkplace.VFS_PATH_SYSTEM) 187 || OpenCms.getSiteManager().startsWithShared(resourcename) 188 || (resourcename.startsWith(CmsWorkplace.VFS_PATH_SITES) && !resourcename.startsWith(siteRoot))) { 189 return ""; 190 } else { 191 return siteRoot; 192 } 193 } 194 195 /** 196 * Adds the current site root of this context to the given resource name, 197 * and also translates the resource name with the configured the directory translator.<p> 198 * 199 * @param resourcename the resource name 200 * @return the translated resource name including site root 201 * @see #addSiteRoot(String, String) 202 */ 203 public String addSiteRoot(String resourcename) { 204 205 return addSiteRoot(m_siteRoot, resourcename); 206 } 207 208 /** 209 * Adds the given site root of this context to the given resource name, 210 * taking into account special folders like "/system" where no site root must be added, 211 * and also translates the resource name with the configured the directory translator.<p> 212 * 213 * @param siteRoot the site root to add 214 * @param resourcename the resource name 215 * @return the translated resource name including site root 216 */ 217 public String addSiteRoot(String siteRoot, String resourcename) { 218 219 if ((resourcename == null) || (siteRoot == null)) { 220 return null; 221 } 222 siteRoot = getAdjustedSiteRoot(siteRoot, resourcename); 223 StringBuffer result = new StringBuffer(128); 224 result.append(siteRoot); 225 if (((siteRoot.length() == 0) || (siteRoot.charAt(siteRoot.length() - 1) != '/')) 226 && ((resourcename.length() == 0) || (resourcename.charAt(0) != '/'))) { 227 // add slash between site root and resource if required 228 result.append('/'); 229 } 230 result.append(resourcename); 231 return m_directoryTranslator.translateResource(result.toString()); 232 } 233 234 /** 235 * Returns the current project of the current user. 236 * 237 * @return the current project of the current user 238 * 239 * @deprecated use {@link #getCurrentProject()} instead 240 */ 241 @Deprecated 242 public CmsProject currentProject() { 243 244 return getCurrentProject(); 245 } 246 247 /** 248 * Returns the current user object.<p> 249 * 250 * @return the current user object 251 * 252 * @deprecated use {@link #getCurrentUser()} instead 253 */ 254 @Deprecated 255 public CmsUser currentUser() { 256 257 return getCurrentUser(); 258 } 259 260 /** 261 * Returns the adjusted site root for a resource this context current site root.<p> 262 * 263 * @param resourcename the resource name to get the adjusted site root for 264 * 265 * @return the adjusted site root for the resource 266 * 267 * @see #getAdjustedSiteRoot(String, String) 268 */ 269 public String getAdjustedSiteRoot(String resourcename) { 270 271 return getAdjustedSiteRoot(m_siteRoot, resourcename); 272 } 273 274 /** 275 * Gets the value of an attribute from the OpenCms request context attribute list.<p> 276 * 277 * @param attributeName the attribute name 278 * @return Object the attribute value, or <code>null</code> if the attribute was not found 279 */ 280 public Object getAttribute(String attributeName) { 281 282 if (m_attributeMap == null) { 283 return null; 284 } 285 return m_attributeMap.get(attributeName); 286 } 287 288 /** 289 * Returns the current project of the current user. 290 * 291 * @return the current project of the current user 292 */ 293 public CmsProject getCurrentProject() { 294 295 return m_currentProject; 296 } 297 298 /** 299 * Returns the current user object.<p> 300 * 301 * @return the current user object 302 */ 303 public CmsUser getCurrentUser() { 304 305 return m_user; 306 } 307 308 /** 309 * Gets the detail content structure id (or null if no detail content has been loaded).<p> 310 * 311 * @return the detail content id 312 */ 313 public CmsUUID getDetailContentId() { 314 315 if (m_detailResource == null) { 316 return null; 317 } 318 return m_detailResource.getStructureId(); 319 } 320 321 /** 322 * Gets the detail content resource (or null if no detail content has been loaded).<p> 323 * 324 * @return the detail content resource 325 */ 326 public CmsResource getDetailResource() { 327 328 return m_detailResource; 329 } 330 331 /** 332 * Returns the directory name translator this context was initialized with.<p> 333 * 334 * The directory translator is used to translate old VFS path information 335 * to a new location. Example: <code>/bodys/index.html --> /system/bodies/</code>.<p> 336 * 337 * @return the directory name translator this context was initialized with 338 */ 339 public CmsResourceTranslator getDirectoryTranslator() { 340 341 return m_directoryTranslator; 342 } 343 344 /** 345 * Returns the current content encoding to be used in HTTP response.<p> 346 * 347 * @return the encoding 348 */ 349 public String getEncoding() { 350 351 return m_encoding; 352 } 353 354 /** 355 * Returns the file name translator this context was initialized with.<p> 356 * 357 * The file name translator is used to translate filenames from uploaded files 358 * to valid OpenCms filenames. Example: <code>Wüste Wörter.doc --> Wueste_Woerter.doc</code>.<p> 359 * 360 * @return the file name translator this context was initialized with 361 */ 362 public CmsResourceTranslator getFileTranslator() { 363 364 return m_fileTranslator; 365 } 366 367 /** 368 * Gets the name of the parent folder of the requested file.<p> 369 * 370 * @return the name of the parent folder of the requested file 371 */ 372 public String getFolderUri() { 373 374 return CmsResource.getFolderPath(m_uri); 375 } 376 377 /** 378 * Returns the locale used by this request context.<p> 379 * 380 * In normal operation, the request context locale is initialized using 381 * {@link org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(jakarta.servlet.http.HttpServletRequest, CmsUser, CmsProject, String)} 382 * depending on the requested resource URI.<p> 383 * 384 * @return the locale used by this request context 385 * 386 * @see org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(jakarta.servlet.http.HttpServletRequest, CmsUser, CmsProject, String) 387 * @see org.opencms.i18n.CmsLocaleManager#getDefaultLocale(CmsObject, String) 388 */ 389 public Locale getLocale() { 390 391 return m_locale; 392 } 393 394 /** 395 * Returns the fully qualified name of the organizational unit.<p> 396 * 397 * @return the fully qualified name of the organizational unit 398 */ 399 public String getOuFqn() { 400 401 return m_ouFqn; 402 } 403 404 /** 405 * Returns the remote ip address.<p> 406 * 407 * @return the remote ip address as string 408 */ 409 public String getRemoteAddress() { 410 411 return m_remoteAddr; 412 } 413 414 /** 415 * Returns the matcher for the current request, that is the host part of the URI from the original http request.<p> 416 * 417 * @return the matcher for the current request, that is the host part of the URI from the original http request 418 */ 419 public CmsSiteMatcher getRequestMatcher() { 420 421 return m_requestMatcher; 422 } 423 424 /** 425 * Returns the current request time.<p> 426 * 427 * @return the current request time 428 */ 429 public long getRequestTime() { 430 431 return m_requestTime; 432 } 433 434 /** 435 * Returns this request contexts uri extended with the current site root path.<p> 436 * 437 * @return this request contexts uri extended with the current site root path 438 * 439 * @see #getUri() 440 * @see #addSiteRoot(String) 441 */ 442 public String getRootUri() { 443 444 return addSiteRoot(m_siteRoot, m_uri); 445 } 446 447 /** 448 * Adjusts the absolute resource root path for the current site.<p> 449 * 450 * The full root path of a resource is always available using 451 * <code>{@link CmsResource#getRootPath()}</code>. From this name this method cuts 452 * of the current site root using 453 * <code>{@link CmsRequestContext#removeSiteRoot(String)}</code>.<p> 454 * 455 * If the resource root path does not start with the current site root, 456 * it is left untouched.<p> 457 * 458 * @param resource the resource to get the adjusted site root path for 459 * 460 * @return the absolute resource path adjusted for the current site 461 * 462 * @see #removeSiteRoot(String) 463 * @see CmsResource#getRootPath() 464 * @see CmsObject#getSitePath(CmsResource) 465 */ 466 public String getSitePath(CmsResource resource) { 467 468 return removeSiteRoot(resource.getRootPath()); 469 } 470 471 /** 472 * Returns the current root directory in the virtual file system.<p> 473 * 474 * @return the current root directory in the virtual file system 475 */ 476 public String getSiteRoot() { 477 478 return m_siteRoot; 479 } 480 481 /** 482 * Returns the OpenCms VFS URI of the requested resource.<p> 483 * 484 * @return the OpenCms VFS URI of the requested resource 485 */ 486 public String getUri() { 487 488 return m_uri; 489 } 490 491 /** 492 * Returns true if links to the current site should be generated as absolute links, i.e. with a server prefix. 493 * 494 * @return true if links to the current site should be absolute 495 */ 496 public boolean isForceAbsoluteLinks() { 497 498 return m_forceAbsoluteLinks; 499 } 500 501 /** 502 * Checks if we are currently either in the Online project or the 'direct edit disabled' mode. 503 * 504 * @return true if we are online or in 'direct edit enabled' mode 505 */ 506 public boolean isOnlineOrEditDisabled() { 507 508 if (getCurrentProject().isOnlineProject()) { 509 return true; 510 } 511 Object directEdit = getAttribute(CmsGwtConstants.PARAM_DISABLE_DIRECT_EDIT); 512 if (Boolean.TRUE.equals(directEdit)) { 513 return true; 514 } 515 return false; 516 } 517 518 /** 519 * Returns true if this is a secure request.<p> 520 * 521 * @return true if this is secure 522 */ 523 public boolean isSecureRequest() { 524 525 return m_isSecureRequest; 526 } 527 528 /** 529 * Check if this request context will update the session.<p> 530 * 531 * This is used mainly for CmsReports that continue to use the 532 * users context, even after the http request is already finished.<p> 533 * 534 * @return true if this request context will update the session, false otherwise 535 */ 536 public boolean isUpdateSessionEnabled() { 537 538 return m_updateSession; 539 } 540 541 /** 542 * Removes an attribute from the request context.<p> 543 * 544 * @param key the name of the attribute to remove 545 * 546 * @return the removed attribute, or <code>null</code> if no attribute was set with this name 547 */ 548 public Object removeAttribute(String key) { 549 550 if (m_attributeMap != null) { 551 return m_attributeMap.remove(key); 552 } 553 return null; 554 } 555 556 /** 557 * Removes the current site root prefix from the absolute path in the resource name, 558 * that is adjusts the resource name for the current site root.<p> 559 * 560 * If the resource name does not start with the current site root, 561 * it is left untouched.<p> 562 * 563 * @param resourcename the resource name 564 * 565 * @return the resource name adjusted for the current site root 566 * 567 * @see #getSitePath(CmsResource) 568 */ 569 public String removeSiteRoot(String resourcename) { 570 571 String siteRoot = getAdjustedSiteRoot(m_siteRoot, resourcename); 572 if ((siteRoot == m_siteRoot) 573 && resourcename.startsWith(siteRoot) 574 && ((resourcename.length() == siteRoot.length()) || (resourcename.charAt(siteRoot.length()) == '/'))) { 575 resourcename = resourcename.substring(siteRoot.length()); 576 } 577 if (resourcename.length() == 0) { 578 // input was a site root folder without trailing slash 579 resourcename = "/"; 580 } 581 return resourcename; 582 } 583 584 /** 585 * Sets an attribute in the request context.<p> 586 * 587 * @param key the attribute name 588 * @param value the attribute value 589 */ 590 public void setAttribute(String key, Object value) { 591 592 if (m_attributeMap == null) { 593 // hash table is still the most efficient form of a synchronized Map 594 m_attributeMap = new Hashtable<String, Object>(); 595 } 596 m_attributeMap.put(key, value); 597 } 598 599 /** 600 * Sets the current project for the user.<p> 601 * 602 * @param project the project to be set as current project 603 * 604 * @return the CmsProject instance 605 */ 606 public CmsProject setCurrentProject(CmsProject project) { 607 608 if (project != null) { 609 m_currentProject = project; 610 } 611 return m_currentProject; 612 } 613 614 /** 615 * Sets the detail content resource.<p> 616 * 617 * @param detailResource the detail content resource 618 */ 619 public void setDetailResource(CmsResource detailResource) { 620 621 m_detailResource = detailResource; 622 } 623 624 /** 625 * Sets the current content encoding to be used in HTTP response.<p> 626 * 627 * @param encoding the encoding 628 */ 629 public void setEncoding(String encoding) { 630 631 m_encoding = encoding; 632 } 633 634 /** 635 * Enables/disables link generation with full server prefix for the current site. 636 * 637 * @param forceAbsoluteLinks true if links to the current site should be generated with server prefix 638 */ 639 public void setForceAbsoluteLinks(boolean forceAbsoluteLinks) { 640 641 m_forceAbsoluteLinks = forceAbsoluteLinks; 642 } 643 644 /** 645 * Sets the locale used by this request context.<p> 646 * 647 * @param locale the locale to set 648 * 649 * @see #getLocale() for more information about how the locale is set in normal operation 650 */ 651 public void setLocale(Locale locale) { 652 653 m_locale = locale; 654 } 655 656 /** 657 * Sets the organizational unit fully qualified name.<p> 658 * 659 * @param ouFqn the organizational unit fully qualified name 660 */ 661 public void setOuFqn(String ouFqn) { 662 663 String userOu = CmsOrganizationalUnit.getParentFqn(m_user.getName()); 664 if (ouFqn != null) { 665 if (ouFqn.startsWith(userOu) 666 || (ouFqn.startsWith(CmsOrganizationalUnit.SEPARATOR) && ouFqn.substring(1).startsWith(userOu))) { 667 m_ouFqn = ouFqn; 668 } else { 669 throw new CmsIllegalArgumentException( 670 Messages.get().container(Messages.ERR_BAD_ORGUNIT_2, ouFqn, userOu)); 671 } 672 } else { 673 m_ouFqn = userOu; 674 } 675 m_ouFqn = CmsOrganizationalUnit.removeLeadingSeparator(m_ouFqn); 676 } 677 678 /** 679 * Sets the current request time.<p> 680 * 681 * @param time the request time 682 */ 683 public void setRequestTime(long time) { 684 685 m_requestTime = time; 686 } 687 688 /** 689 * Sets the 'secure request' status.<p> 690 * 691 * @param secureRequest the new value 692 */ 693 public void setSecureRequest(boolean secureRequest) { 694 695 m_isSecureRequest = secureRequest; 696 } 697 698 /** 699 * Sets the current root directory in the virtual file system.<p> 700 * 701 * @param root the name of the new root directory 702 */ 703 public void setSiteRoot(String root) { 704 705 // site roots must never end with a "/" 706 if (root.endsWith("/")) { 707 m_siteRoot = root.substring(0, root.length() - 1); 708 } else { 709 m_siteRoot = root; 710 } 711 } 712 713 /** 714 * Mark this request context to update the session or not.<p> 715 * 716 * @param value true if this request context will update the session, false otherwise 717 */ 718 public void setUpdateSessionEnabled(boolean value) { 719 720 m_updateSession = value; 721 } 722 723 /** 724 * Set the requested resource OpenCms VFS URI, that is the value returned by {@link #getUri()}.<p> 725 * 726 * Use this with caution! Many things (caches etc.) depend on this value. 727 * If you change this value, better make sure that you change it only temporarily 728 * and reset it in a <code>try { // do something // } finally { // reset URI // }</code> statement.<p> 729 * 730 * @param value the value to set the Uri to, must be a complete OpenCms path name like /system/workplace/style.css 731 */ 732 public void setUri(String value) { 733 734 m_uri = value; 735 } 736 737 /** 738 * Switches the user in the context, required after a login.<p> 739 * 740 * @param user the new user to use 741 * @param project the new users current project 742 * @param ouFqn the organizational unit 743 */ 744 protected void switchUser(CmsUser user, CmsProject project, String ouFqn) { 745 746 m_user = user; 747 m_currentProject = project; 748 setOuFqn(ouFqn); 749 } 750}