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.site; 029 030import com.alkacon.simapi.CmykJpegReader.StringUtil; 031 032import org.opencms.configuration.CmsConfigurationException; 033import org.opencms.configuration.CmsSitesConfiguration; 034import org.opencms.db.CmsPublishedResource; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProject; 037import org.opencms.file.CmsPropertyDefinition; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.main.CmsContextInfo; 041import org.opencms.main.CmsEvent; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsLog; 044import org.opencms.main.CmsRuntimeException; 045import org.opencms.main.I_CmsEventListener; 046import org.opencms.main.OpenCms; 047import org.opencms.main.OpenCmsCore; 048import org.opencms.security.CmsOrganizationalUnit; 049import org.opencms.security.CmsPermissionSet; 050import org.opencms.security.CmsRole; 051import org.opencms.util.CmsFileUtil; 052import org.opencms.util.CmsPath; 053import org.opencms.util.CmsStringUtil; 054import org.opencms.util.CmsUUID; 055 056import java.net.URI; 057import java.net.URISyntaxException; 058import java.util.ArrayList; 059import java.util.Collection; 060import java.util.Collections; 061import java.util.Comparator; 062import java.util.HashMap; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.LinkedHashMap; 066import java.util.List; 067import java.util.Map; 068import java.util.Set; 069import java.util.SortedMap; 070 071import javax.servlet.http.HttpServletRequest; 072import javax.servlet.http.HttpSession; 073 074import org.apache.commons.logging.Log; 075 076import com.google.common.base.Optional; 077import com.google.common.collect.Lists; 078import com.google.common.collect.Maps; 079import com.google.common.collect.Sets; 080 081/** 082 * Manages all configured sites in OpenCms.<p> 083 * 084 * To obtain the configured site manager instance, use {@link OpenCms#getSiteManager()}.<p> 085 * 086 * @since 7.0.2 087 */ 088public final class CmsSiteManagerImpl implements I_CmsEventListener { 089 090 /** 091 * Holds data for the alternative site root mappings. 092 */ 093 private static class AlternativeSiteData { 094 095 /** Map from site roots as strings to the corresponding alternative site roots. */ 096 private Map<CmsPath, CmsSite> m_alternativeSites = new HashMap<>(); 097 098 /** Site roots for the alternative site data. */ 099 private Set<String> m_siteRoots = new HashSet<>(); 100 101 /** 102 * Creates a new instance from the alternative site root mappings of the given site. 103 * 104 * @param normalSites the normal sites 105 */ 106 public AlternativeSiteData(Collection<CmsSite> normalSites) { 107 108 for (CmsSite site : normalSites) { 109 if (site.getAlternativeSiteRootMapping().isPresent()) { 110 CmsSite extensionSite = site.createAlternativeSiteRootSite(); 111 CmsPath key = new CmsPath(extensionSite.getSiteRoot()); 112 m_alternativeSites.put(key, extensionSite); 113 m_siteRoots.add(key.asString()); 114 } 115 116 } 117 } 118 119 /** 120 * Gets the site for the given root path, or null if no site for that path is found. 121 * 122 * @param path a root path 123 * @return the site for the root path, or null 124 */ 125 public CmsSite getSiteForRootPath(String path) { 126 127 for (Map.Entry<CmsPath, CmsSite> entry : m_alternativeSites.entrySet()) { 128 CmsPath key = entry.getKey(); 129 if (key.isPrefixOfStr(path)) { 130 return entry.getValue(); 131 } 132 } 133 return null; 134 } 135 136 /** 137 * Gets the site for the given site root. 138 * 139 * @param path a site root 140 * @return the site for the site root 141 */ 142 public CmsSite getSiteForSiteRoot(String path) { 143 144 CmsPath key = new CmsPath(path); 145 CmsSite result = m_alternativeSites.get(key); 146 return result; 147 } 148 149 /** 150 * Gets the site roots for the alternative site root mappings. 151 * 152 * @return the site roots 153 */ 154 public Set<String> getSiteRoots() { 155 156 return Collections.unmodifiableSet(m_siteRoots); 157 } 158 159 } 160 161 /** The default shared folder name. */ 162 public static final String DEFAULT_SHARED_FOLDER = "shared"; 163 164 /** 165 * The VFS root path to the system shared folder, where shared content that belongs to modules, 166 * and that should not be edited by normal editors can be stored. 167 * The folder is searched in the gallery search when shared folders should be searched. 168 */ 169 public static final String PATH_SYSTEM_SHARED_FOLDER = "/system/shared/"; 170 171 /** A placeholder for the title of the shared folder. */ 172 public static final String SHARED_FOLDER_TITLE = "%SHARED_FOLDER%"; 173 174 /** Path to config template. */ 175 public static final String WEB_SERVER_CONFIG_CONFIGTEMPLATE = "configtemplate"; 176 177 /**prefix for files. */ 178 public static final String WEB_SERVER_CONFIG_FILENAMEPREFIX = "filenameprefix"; 179 180 /**Path to write logs to. */ 181 public static final String WEB_SERVER_CONFIG_LOGGINGDIR = "loggingdir"; 182 183 /** Path to secure template. */ 184 public static final String WEB_SERVER_CONFIG_SECURETEMPLATE = "securetemplate"; 185 186 /** Path to target. */ 187 public static final String WEB_SERVER_CONFIG_TARGETPATH = "targetpath"; 188 189 /** Path of webserver script.*/ 190 public static final String WEB_SERVER_CONFIG_WEBSERVERSCRIPT = "webserverscript"; 191 192 /** The static log object for this class. */ 193 private static final Log LOG = CmsLog.getLog(CmsSiteManagerImpl.class); 194 195 /** The path to the "/sites/" folder. */ 196 private static final String SITES_FOLDER = "/sites/"; 197 198 /** The length of the "/sites/" folder plus 1. */ 199 private static final int SITES_FOLDER_POS = SITES_FOLDER.length() + 1; 200 201 /** A list of additional site roots, that is site roots that are not below the "/sites/" folder. */ 202 private List<String> m_additionalSiteRoots; 203 204 /** Data for the alternative site root rules. */ 205 private volatile AlternativeSiteData m_alternativeSiteData = new AlternativeSiteData(new ArrayList<>()); 206 207 /**Map with webserver scripting parameter. */ 208 private Map<String, String> m_apacheConfig; 209 210 /**CmsObject.*/ 211 private CmsObject m_clone; 212 213 /** The default site root. */ 214 private CmsSite m_defaultSite; 215 216 /** The default URI. */ 217 private String m_defaultUri; 218 219 /** Indicates if the configuration is finalized (frozen). */ 220 private boolean m_frozen; 221 222 /**Is the publish listener already set? */ 223 private boolean m_isListenerSet; 224 225 /**Old style secure server allowed? */ 226 private boolean m_oldStyleSecureServer; 227 228 /**Site which are only available for offline project. */ 229 private List<CmsSite> m_onlyOfflineSites; 230 231 /** The shared folder name. */ 232 private String m_sharedFolder; 233 234 /** Contains all configured site matchers in a list for direct access. */ 235 private List<CmsSiteMatcher> m_siteMatchers; 236 237 /** Maps site matchers to sites. */ 238 private Map<CmsSiteMatcher, CmsSite> m_siteMatcherSites; 239 240 /** Maps site roots to sites. */ 241 private Map<String, CmsSite> m_siteRootSites; 242 243 /**Map from CmsUUID to CmsSite.*/ 244 private Map<CmsUUID, CmsSite> m_siteUUIDs; 245 246 /** The workplace site matchers. */ 247 private List<CmsSiteMatcher> m_workplaceMatchers; 248 249 /** The workplace servers. */ 250 private Map<String, CmsSSLMode> m_workplaceServers; 251 252 /** Sites nested under other sites. */ 253 private volatile Map<CmsSiteMatcher, CmsSite> m_nestedSites = new HashMap<>(); 254 255 /** 256 * Creates a new CmsSiteManager.<p> 257 * 258 */ 259 public CmsSiteManagerImpl() { 260 261 m_siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(); 262 m_siteRootSites = new HashMap<String, CmsSite>(); 263 m_additionalSiteRoots = new ArrayList<String>(); 264 m_workplaceServers = new LinkedHashMap<String, CmsSSLMode>(); 265 m_workplaceMatchers = new ArrayList<CmsSiteMatcher>(); 266 m_oldStyleSecureServer = true; 267 if (CmsLog.INIT.isInfoEnabled()) { 268 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_START_SITE_CONFIG_0)); 269 } 270 } 271 272 /** 273 * Creates a site matcher for an alias read from the configuration. 274 * 275 * @param alias the alias 276 * @param redirect redirection enabled (true/false) 277 * @param offset time offset or empty 278 * 279 * @return the alias site matcher 280 */ 281 public static CmsSiteMatcher createAliasSiteMatcher(String alias, String redirect, String offset) { 282 283 long timeOffset = 0; 284 try { 285 timeOffset = Long.parseLong(offset); 286 } catch (Throwable e) { 287 // ignore 288 } 289 CmsSiteMatcher siteMatcher = new CmsSiteMatcher(alias, timeOffset); 290 CmsSiteMatcher.RedirectMode redirectMode = CmsSiteMatcher.RedirectMode.parse(redirect); 291 siteMatcher.setRedirectMode(redirectMode); 292 return siteMatcher; 293 } 294 295 /** 296 * Parses the given string as an URI and returns its host component. 297 * 298 * @param uriStr the URI string 299 * @return the host component, or null if the URI can't be parsed 300 */ 301 private static String getHost(String uriStr) { 302 303 try { 304 URI uri = new URI(uriStr); 305 return uri.getHost(); 306 } catch (URISyntaxException e) { 307 return null; 308 } 309 } 310 311 /** 312 * Adds a site.<p> 313 * 314 * @param cms the CMS object 315 * @param site the site to add 316 * 317 * @throws CmsException if something goes wrong 318 */ 319 public void addSite(CmsObject cms, CmsSite site) throws CmsException { 320 321 // check permissions 322 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 323 // simple unit tests will have runlevel 1 and no CmsObject 324 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 325 } 326 327 validateSiteRoot(site.getSiteRoot()); 328 329 // un-freeze 330 m_frozen = false; 331 332 String secureUrl = null; 333 if (site.hasSecureServer()) { 334 secureUrl = site.getSecureUrl(); 335 } 336 337 // add the site 338 addSite( 339 site.getUrl(), 340 site.getSiteRoot(), 341 site.getTitle(), 342 Float.toString(site.getPosition()), 343 site.getErrorPage(), 344 Boolean.toString(site.isWebserver()), 345 site.getSSLMode().getXMLValue(), 346 secureUrl, 347 Boolean.toString(site.isExclusiveUrl()), 348 Boolean.toString(site.isExclusiveError()), 349 Boolean.toString(site.usesPermanentRedirects()), 350 Boolean.toString(site.isSubsiteSelectionEnabled()), 351 site.getParameters(), 352 site.getAliases(), 353 site.getAlternativeSiteRootMapping()); 354 355 // re-initialize, will freeze the state when finished 356 initialize(cms); 357 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 358 } 359 360 /** 361 * Adds a new CmsSite to the list of configured sites, 362 * this is only allowed during configuration.<p> 363 * 364 * If this method is called after the configuration is finished, 365 * a <code>RuntimeException</code> is thrown.<p> 366 * 367 * @param server the Server 368 * @param uri the VFS path 369 * @param title the display title for this site 370 * @param position the display order for this site 371 * @param errorPage the URI to use as error page for this site 372 * @param sslMode the SSLMode of the site 373 * @param webserver indicates whether to write the web server configuration for this site or not 374 * @param secureServer a secure server, can be <code>null</code> 375 * @param exclusive if set to <code>true</code>, secure resources will only be available using the configured secure url 376 * @param error if exclusive, and set to <code>true</code> will generate a 404 error, 377 * if set to <code>false</code> will redirect to secure URL 378 * @param usePermanentRedirects if set to "true", permanent redirects should be used when redirecting to the secure URL 379 * @param subsiteSelection true if subsite selection should be enabled 380 * @param params the site parameters 381 * @param aliases the aliases for the site 382 * @param alternativeSiteRootMapping an optional alternative site root mapping 383 * 384 * @throws CmsConfigurationException if the site contains a server name, that is already assigned 385 */ 386 public void addSite( 387 String server, 388 String uri, 389 String title, 390 String position, 391 String errorPage, 392 String webserver, 393 String sslMode, 394 String secureServer, 395 String exclusive, 396 String error, 397 String usePermanentRedirects, 398 String subsiteSelection, 399 SortedMap<String, String> params, 400 List<CmsSiteMatcher> aliases, 401 java.util.Optional<CmsAlternativeSiteRootMapping> alternativeSiteRootMapping) 402 throws CmsConfigurationException { 403 404 if (m_frozen) { 405 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 406 } 407 408 if (getSiteRoots().contains(uri)) { 409 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_SITE_ALREADY_CONFIGURED_1, uri)); 410 } 411 412 if (CmsStringUtil.isEmptyOrWhitespaceOnly(server)) { 413 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_EMPTY_SERVER_URL_0)); 414 } 415 416 validateSiteRoot(uri); 417 418 // create a new site object 419 CmsSiteMatcher matcher = new CmsSiteMatcher(server); 420 CmsSite site = new CmsSite(uri, matcher); 421 // set the title 422 site.setTitle(title); 423 // set the position 424 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(position)) { 425 float pos = Float.MAX_VALUE; 426 try { 427 pos = Float.parseFloat(position); 428 } catch (Throwable e) { 429 // m_position will have Float.MAX_VALUE, so this site will appear last 430 } 431 site.setPosition(pos); 432 } 433 // set the error page 434 site.setErrorPage(errorPage); 435 site.setWebserver(Boolean.valueOf(webserver).booleanValue()); 436 site.setSSLMode(CmsSSLMode.getModeFromXML(sslMode)); 437 if (CmsStringUtil.isNotEmpty(secureServer)) { 438 matcher = new CmsSiteMatcher(secureServer); 439 site.setSecureServer(matcher); 440 site.setExclusiveUrl(Boolean.valueOf(exclusive).booleanValue()); 441 site.setExclusiveError(Boolean.valueOf(error).booleanValue()); 442 site.setUsePermanentRedirects(Boolean.valueOf(usePermanentRedirects).booleanValue()); 443 } 444 site.setSubsiteSelectionEnabled(Boolean.parseBoolean(subsiteSelection)); 445 446 // note that Digester first calls the addAliasToConfigSite method. 447 // therefore, the aliases are already set 448 site.setAliases(aliases); 449 450 boolean valid = true; 451 List<CmsSiteMatcher> toAdd = new ArrayList<CmsSiteMatcher>(); 452 for (CmsSiteMatcher matcherToAdd : site.getAllMatchers()) { 453 valid = valid & isServerValid(matcherToAdd) & !toAdd.contains(matcherToAdd); 454 toAdd.add(matcherToAdd); 455 } 456 457 if (!valid) { 458 throw new CmsConfigurationException( 459 Messages.get().container(Messages.ERR_DUPLICATE_SERVER_NAME_1, matcher.getUrl())); 460 } 461 462 for (CmsSiteMatcher matcherToAdd : site.getAllMatchers()) { 463 addServer(matcherToAdd, site); 464 } 465 466 site.setParameters(params); 467 site.setAlternativeSiteRootMapping(alternativeSiteRootMapping); 468 m_siteRootSites = new HashMap<String, CmsSite>(m_siteRootSites); 469 m_siteRootSites.put(site.getSiteRoot(), site); 470 if (CmsLog.INIT.isInfoEnabled()) { 471 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SITE_ROOT_ADDED_1, site.toString())); 472 } 473 } 474 475 /** 476 * Adds a new CmsSite to the list of configured sites, 477 * this is only allowed during configuration.<p> 478 * 479 * If this method is called after the configuration is finished, 480 * a <code>RuntimeException</code> is thrown.<p> 481 * 482 * @param server the Server 483 * @param uri the VFS path 484 * @param title the display title for this site 485 * @param position the display order for this site 486 * @param errorPage the URI to use as error page for this site 487 * @param sslMode the SSLMode of the site 488 * @param webserver indicates whether to write the web server configuration for this site or not 489 * @param secureServer a secure server, can be <code>null</code> 490 * @param exclusive if set to <code>true</code>, secure resources will only be available using the configured secure url 491 * @param error if exclusive, and set to <code>true</code> will generate a 404 error, 492 * if set to <code>false</code> will redirect to secure URL 493 * @param usePermanentRedirects if set to "true", permanent redirects should be used when redirecting to the secure URL 494 * @param subsiteSelection true if subsite selection should be enabled for this site 495 * @param params the site parameters 496 * @param aliases the aliases 497 * @param alternativeSiteRoot an optional alternative site root mapping 498 * 499 * @throws CmsConfigurationException in case the site was not configured correctly 500 * 501 */ 502 public void addSiteInternally( 503 String server, 504 String uri, 505 String title, 506 String position, 507 String errorPage, 508 String webserver, 509 String sslMode, 510 String secureServer, 511 String exclusive, 512 String error, 513 String usePermanentRedirects, 514 String subsiteSelection, 515 SortedMap<String, String> params, 516 List<CmsSiteMatcher> aliases, 517 java.util.Optional<CmsAlternativeSiteRootMapping> alternativeSiteRoot) 518 throws CmsConfigurationException { 519 520 try { 521 addSite( 522 server, 523 uri, 524 title, 525 position, 526 errorPage, 527 webserver, 528 sslMode, 529 secureServer, 530 exclusive, 531 error, 532 usePermanentRedirects, 533 subsiteSelection, 534 params, 535 aliases, 536 alternativeSiteRoot); 537 538 } catch (CmsConfigurationException e) { 539 LOG.error("Error reading definitions. Trying to read without aliases.", e); 540 541 //If this fails, the webserver was defined before ->throw exception 542 543 addSite( 544 server, 545 uri, 546 title, 547 position, 548 errorPage, 549 webserver, 550 sslMode, 551 secureServer, 552 exclusive, 553 error, 554 usePermanentRedirects, 555 subsiteSelection, 556 params, 557 new ArrayList<>(), 558 alternativeSiteRoot); //If the aliases are making problems, just remove thems 559 560 } 561 } 562 563 /** 564 * Adds a workplace server, this is only allowed during configuration.<p> 565 * 566 * @param workplaceServer the workplace server 567 * @param sslmode CmsSSLMode of workplace server 568 */ 569 public void addWorkplaceServer(String workplaceServer, String sslmode) { 570 571 if (m_frozen) { 572 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 573 } 574 if (!m_workplaceServers.containsKey(workplaceServer)) { 575 m_workplaceServers.put(workplaceServer, CmsSSLMode.getModeFromXML(sslmode)); 576 } 577 } 578 579 /** 580 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 581 */ 582 public void cmsEvent(CmsEvent event) { 583 584 try { 585 CmsProject project = getOfflineProject(); 586 m_clone.getRequestContext().setCurrentProject(project); 587 List<CmsPublishedResource> res = null; 588 589 List<CmsPublishedResource> foundSites = new ArrayList<CmsPublishedResource>(); 590 591 res = m_clone.readPublishedResources( 592 new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID))); 593 594 if (res != null) { 595 for (CmsPublishedResource r : res) { 596 if (!foundSites.contains(r)) { 597 if (m_siteUUIDs.containsKey(r.getStructureId())) { 598 foundSites.add(r); 599 } 600 } 601 } 602 } 603 project = m_clone.readProject(CmsProject.ONLINE_PROJECT_ID); 604 m_clone.getRequestContext().setCurrentProject(project); 605 Map<CmsSite, CmsSite> updateMap = new HashMap<CmsSite, CmsSite>(); 606 607 for (CmsPublishedResource r : foundSites) { 608 if (m_clone.existsResource(r.getStructureId())) { 609 //Resource was not deleted 610 CmsResource siteRoot = m_clone.readResource(r.getStructureId()); 611 if (!m_siteRootSites.containsKey(CmsFileUtil.removeTrailingSeparator(siteRoot.getRootPath())) 612 | m_onlyOfflineSites.contains(m_siteUUIDs.get(r.getStructureId()))) { 613 //Site was moved or site root was renamed.. or site was published the first time 614 CmsSite oldSite = m_siteUUIDs.get(siteRoot.getStructureId()); 615 CmsSite newSite = oldSite.clone(); 616 newSite.setSiteRoot(siteRoot.getRootPath()); 617 updateMap.put(oldSite, newSite); 618 } 619 } 620 } 621 622 for (CmsSite site : updateMap.keySet()) { 623 updateSite(m_clone, site, updateMap.get(site)); 624 } 625 } catch (CmsException e) { 626 LOG.error("Unable to handle publish event", e); 627 } 628 629 } 630 631 /** 632 * Returns all wrong configured sites.<p> 633 * 634 * @param cms CmsObject 635 * @param workplaceMode workplace mode 636 * @return List of CmsSite 637 */ 638 public List<CmsSite> getAvailableCorruptedSites(CmsObject cms, boolean workplaceMode) { 639 640 List<CmsSite> res = new ArrayList<CmsSite>(); 641 List<CmsSite> visSites = getAvailableSites(cms, workplaceMode); 642 Map<CmsSiteMatcher, CmsSite> allsites = getSites(); 643 for (CmsSiteMatcher matcher : allsites.keySet()) { 644 CmsSite site = allsites.get(matcher); 645 if (!visSites.contains(site) & !res.contains(site)) { 646 res.add(site); 647 } 648 } 649 return res; 650 } 651 652 /** 653 * Returns a list of all sites available (visible) for the current user.<p> 654 * 655 * @param cms the current OpenCms user context 656 * @param workplaceMode if true, the root and current site is included for the admin user 657 * and the view permission is required to see the site root 658 * 659 * @return a list of all sites available for the current user 660 */ 661 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode) { 662 663 return getAvailableSites(cms, workplaceMode, cms.getRequestContext().getOuFqn()); 664 } 665 666 /** 667 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 668 * 669 * @param cms the current OpenCms user context 670 * @param workplaceMode if true, the root and current site is included for the admin user 671 * and the view permission is required to see the site root 672 * @param showShared if the shared folder should be shown 673 * @param ouFqn the organizational unit 674 * 675 * @return a list of all site available for the current user 676 */ 677 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, boolean showShared, String ouFqn) { 678 679 return getAvailableSites(cms, workplaceMode, showShared, ouFqn, null); 680 } 681 682 /** 683 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 684 * 685 * @param cms the current OpenCms user context 686 * @param workplaceMode if true, the root and current site is included for the admin user 687 * and the view permission is required to see the site root 688 * @param showShared if the shared folder should be shown 689 * @param ouFqn the organizational unit 690 * @param filterMode The CmsSLLMode to filter, null if no filter 691 * 692 * @return a list of all site available for the current user 693 */ 694 public List<CmsSite> getAvailableSites( 695 CmsObject cms, 696 boolean workplaceMode, 697 boolean showShared, 698 String ouFqn, 699 CmsSSLMode filterMode) { 700 701 List<String> siteroots = new ArrayList<String>(m_siteMatcherSites.size() + 1); 702 Map<String, CmsSiteMatcher> siteServers = new HashMap<String, CmsSiteMatcher>(m_siteMatcherSites.size() + 1); 703 List<CmsSite> result = new ArrayList<CmsSite>(m_siteMatcherSites.size() + 1); 704 705 for (CmsSite mainSite : m_siteMatcherSites.values()) { 706 List<CmsSite> sitesToProcess = new ArrayList<>(); 707 sitesToProcess.add(mainSite); 708 CmsSite extensionFolderSite = mainSite.createAlternativeSiteRootSite(); 709 if (extensionFolderSite != null) { 710 sitesToProcess.add(extensionFolderSite); 711 } 712 for (CmsSite site : sitesToProcess) { 713 String folder = CmsFileUtil.addTrailingSeparator(site.getSiteRoot()); 714 if (!siteroots.contains(folder)) { 715 siteroots.add(folder); 716 siteServers.put(folder, site.getSiteMatcher()); 717 } 718 } 719 } 720 // add default site 721 if (workplaceMode && (m_defaultSite != null)) { 722 String folder = CmsFileUtil.addTrailingSeparator(m_defaultSite.getSiteRoot()); 723 if (!siteroots.contains(folder)) { 724 siteroots.add(folder); 725 } 726 } 727 728 String storedSiteRoot = cms.getRequestContext().getSiteRoot(); 729 try { 730 // for all operations here we need no context 731 cms.getRequestContext().setSiteRoot("/"); 732 if (workplaceMode && OpenCms.getRoleManager().hasRole(cms, CmsRole.VFS_MANAGER)) { 733 if (!siteroots.contains("/")) { 734 // add the root site if the user is in the workplace and has the required role 735 siteroots.add("/"); 736 } 737 if (!siteroots.contains(CmsFileUtil.addTrailingSeparator(storedSiteRoot))) { 738 siteroots.add(CmsFileUtil.addTrailingSeparator(storedSiteRoot)); 739 } 740 } 741 // add the shared site 742 String shared = OpenCms.getSiteManager().getSharedFolder(); 743 if (showShared && (shared != null) && !siteroots.contains(shared)) { 744 siteroots.add(shared); 745 } 746 // all sites are compatible for root admins in the root OU, skip unnecessary tests 747 boolean allCompatible = OpenCms.getRoleManager().hasRole(cms, CmsRole.ROOT_ADMIN) 748 && (ouFqn.isEmpty() || ouFqn.equals(CmsOrganizationalUnit.SEPARATOR)); 749 List<CmsResource> resources = Collections.emptyList(); 750 if (!allCompatible) { 751 try { 752 resources = OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit(cms, ouFqn); 753 } catch (CmsException e) { 754 return Collections.emptyList(); 755 } 756 } 757 Collections.sort(siteroots); // sort by resource name 758 Iterator<String> roots = siteroots.iterator(); 759 while (roots.hasNext()) { 760 String folder = roots.next(); 761 boolean compatible = allCompatible; 762 if (!compatible) { 763 Iterator<CmsResource> itResources = resources.iterator(); 764 while (itResources.hasNext()) { 765 CmsResource resource = itResources.next(); 766 if (resource.getRootPath().startsWith(folder) || folder.startsWith(resource.getRootPath())) { 767 compatible = true; 768 break; 769 } 770 } 771 } 772 // select only sites compatibles to the given organizational unit 773 if (compatible) { 774 try { 775 CmsResource res = cms.readResource(folder); 776 if (!workplaceMode 777 || cms.hasPermissions( 778 res, 779 CmsPermissionSet.ACCESS_VIEW, 780 false, 781 CmsResourceFilter.ONLY_VISIBLE)) { 782 783 // get the title and the position from the system configuration first 784 CmsSite configuredSite = getSiteForSiteRoot(CmsFileUtil.removeTrailingSeparator(folder)); 785 // CmsSite configuredSite = m_siteRootSites.get(CmsFileUtil.removeTrailingSeparator(folder)); 786 787 // get the title 788 String title = null; 789 if ((configuredSite != null) 790 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(configuredSite.getTitle())) { 791 title = configuredSite.getTitle(); 792 } 793 if (title == null) { 794 title = getSiteTitle(cms, res); 795 } 796 797 // get the position 798 String position = null; 799 if ((configuredSite != null) && (configuredSite.getPosition() != Float.MAX_VALUE)) { 800 position = Float.toString(configuredSite.getPosition()); 801 } 802 if (position == null) { 803 // not found, use the 'NavPos' property 804 position = cms.readPropertyObject( 805 res, 806 CmsPropertyDefinition.PROPERTY_NAVPOS, 807 false).getValue(); 808 } 809 if (configuredSite != null) { 810 float pos = Float.MAX_VALUE; 811 try { 812 pos = Float.parseFloat(position); 813 } catch (Throwable e) { 814 // m_position will have Float.MAX_VALUE, so this site will appear last 815 } 816 CmsSite clone = configuredSite.clone(); 817 clone.setPosition(pos); 818 clone.setTitle(title); 819 if (filterMode == null) { 820 result.add(clone); 821 } else { 822 if (filterMode.equals(clone.getSSLMode())) { 823 result.add(clone); 824 } 825 } 826 } else { 827 // add the site to the result 828 829 result.add( 830 new CmsSite( 831 folder, 832 res.getStructureId(), 833 title, 834 siteServers.get(folder), 835 position)); 836 } 837 } 838 } catch (CmsException e) { 839 // user probably has no read access to the folder, ignore and continue iterating 840 } 841 } 842 } 843 844 // sort and ensure that the shared folder is the last element in the list 845 Collections.sort(result, new Comparator<CmsSite>() { 846 847 public int compare(CmsSite o1, CmsSite o2) { 848 849 if (isSharedFolder(o1.getSiteRoot())) { 850 return +1; 851 } 852 if (isSharedFolder(o2.getSiteRoot())) { 853 return -1; 854 } 855 return o1.compareTo(o2); 856 } 857 }); 858 } catch (Throwable t) { 859 LOG.error(Messages.get().getBundle().key(Messages.LOG_READ_SITE_PROP_FAILED_0), t); 860 } finally { 861 // restore the user's current context 862 cms.getRequestContext().setSiteRoot(storedSiteRoot); 863 } 864 return result; 865 866 } 867 868 /** 869 * Returns a list of all sites available (visible) for the current user.<p> 870 * 871 * @param cms the current OpenCms user context 872 * @param workplaceMode if true, the root and current site is included for the admin user 873 * and the view permission is required to see the site root 874 * @param filterMode The CmsSLLMode to filter, null if no filter 875 * 876 * @return a list of all sites available for the current user 877 */ 878 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, CmsSSLMode filterMode) { 879 880 return getAvailableSites(cms, workplaceMode, workplaceMode, cms.getRequestContext().getOuFqn(), filterMode); 881 } 882 883 /** 884 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 885 * 886 * @param cms the current OpenCms user context 887 * @param workplaceMode if true, the root and current site is included for the admin user 888 * and the view permission is required to see the site root 889 * @param ouFqn the organizational unit 890 * 891 * @return a list of all site available for the current user 892 */ 893 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, String ouFqn) { 894 895 return getAvailableSites(cms, workplaceMode, workplaceMode, ouFqn); 896 } 897 898 /** 899 * Returns the current site for the provided OpenCms user context object.<p> 900 * 901 * In the unlikely case that no site matches with the provided OpenCms user context, 902 * the default site is returned.<p> 903 * 904 * @param cms the OpenCms user context object to check for the site 905 * 906 * @return the current site for the provided OpenCms user context object 907 */ 908 public CmsSite getCurrentSite(CmsObject cms) { 909 910 CmsSite site = getSiteForSiteRoot(cms.getRequestContext().getSiteRoot()); 911 return (site == null) ? m_defaultSite : site; 912 } 913 914 /** 915 * Returns the default site.<p> 916 * 917 * @return the default site 918 */ 919 public CmsSite getDefaultSite() { 920 921 return m_defaultSite; 922 } 923 924 /** 925 * Returns the defaultUri.<p> 926 * 927 * @return the defaultUri 928 */ 929 public String getDefaultUri() { 930 931 return m_defaultUri; 932 } 933 934 /** 935 * Returns the shared folder path.<p> 936 * 937 * @return the shared folder path 938 */ 939 public String getSharedFolder() { 940 941 return m_sharedFolder; 942 } 943 944 /** 945 * Returns the site for the given resource path, using the fall back site root 946 * in case the resource path is no root path.<p> 947 * 948 * In case neither the given resource path, nor the given fall back site root 949 * matches any configured site, the default site is returned.<p> 950 * 951 * Usually the fall back site root should be taken from {@link org.opencms.file.CmsRequestContext#getSiteRoot()}, 952 * in which case a site for the site root should always exist.<p> 953 * 954 * This is the same as first calling {@link #getSiteForRootPath(String)} with the 955 * <code>resourcePath</code> parameter, and if this fails calling 956 * {@link #getSiteForSiteRoot(String)} with the <code>fallbackSiteRoot</code> parameter, 957 * and if this fails calling {@link #getDefaultSite()}.<p> 958 * 959 * @param rootPath the resource root path to get the site for 960 * @param fallbackSiteRoot site root to use in case the resource path is no root path 961 * 962 * @return the site for the given resource path, using the fall back site root 963 * in case the resource path is no root path 964 * 965 * @see #getSiteForRootPath(String) 966 */ 967 public CmsSite getSite(String rootPath, String fallbackSiteRoot) { 968 969 CmsSite result = getSiteForRootPath(rootPath); 970 if (result == null) { 971 result = getSiteForSiteRoot(fallbackSiteRoot); 972 if (result == null) { 973 result = getDefaultSite(); 974 } 975 } 976 return result; 977 } 978 979 /** 980 * Gets the site which is mapped to the default uri, or the 'absent' value of no such site exists.<p> 981 * 982 * @return the optional site mapped to the default uri 983 */ 984 public Optional<CmsSite> getSiteForDefaultUri() { 985 986 String defaultUri = getDefaultUri(); 987 CmsSite candidate = m_siteRootSites.get(CmsFileUtil.removeTrailingSeparator(defaultUri)); 988 return Optional.fromNullable(candidate); 989 } 990 991 /** 992 * Returns the site for the given resources root path, 993 * or <code>null</code> if the resources root path does not match any site.<p> 994 * 995 * @param rootPath the root path of a resource 996 * 997 * @return the site for the given resources root path, 998 * or <code>null</code> if the resources root path does not match any site 999 * 1000 * @see #getSiteForSiteRoot(String) 1001 * @see #getSiteRoot(String) 1002 */ 1003 public CmsSite getSiteForRootPath(String rootPath) { 1004 1005 if ((rootPath.length() > 0) && !rootPath.endsWith("/")) { 1006 rootPath = rootPath + "/"; 1007 } 1008 for (CmsSite site : m_nestedSites.values()) { 1009 if (CmsStringUtil.isPrefixPath(site.getSiteRoot(), rootPath)) { 1010 return site; 1011 } 1012 } 1013 1014 // most sites will be below the "/sites/" folder, 1015 CmsSite result = lookupSitesFolder(rootPath); 1016 if (result != null) { 1017 return result; 1018 } 1019 // look through all folders that are not below "/sites/" 1020 String siteRoot = lookupAdditionalSite(rootPath); 1021 if (siteRoot != null) { 1022 return getSiteForSiteRoot(siteRoot); 1023 } 1024 return m_alternativeSiteData.getSiteForRootPath(rootPath); 1025 1026 } 1027 1028 /** 1029 * Returns the site with has the provided site root, 1030 * or <code>null</code> if no configured site has that site root.<p> 1031 * 1032 * The site root must have the form: 1033 * <code>/sites/default</code>.<br> 1034 * That means there must be a leading, but no trailing slash.<p> 1035 * 1036 * @param siteRoot the site root to look up the site for 1037 * 1038 * @return the site with has the provided site root, 1039 * or <code>null</code> if no configured site has that site root 1040 * 1041 * @see #getSiteForRootPath(String) 1042 */ 1043 public CmsSite getSiteForSiteRoot(String siteRoot) { 1044 1045 if (siteRoot == null) { 1046 return null; 1047 } 1048 for (CmsSite site : m_nestedSites.values()) { 1049 if (siteRoot.equals(site.getSiteRoot())) { 1050 return site; 1051 } 1052 } 1053 CmsSite result = m_siteRootSites.get(siteRoot); 1054 if (result != null) { 1055 return result; 1056 } else { 1057 return m_alternativeSiteData.getSiteForSiteRoot(siteRoot); 1058 } 1059 } 1060 1061 /** 1062 * Returns the site root part for the given resources root path, 1063 * or <code>null</code> if the given resources root path does not match any site root.<p> 1064 * 1065 * The site root returned will have the form: 1066 * <code>/sites/default</code>.<br> 1067 * That means there will a leading, but no trailing slash.<p> 1068 * 1069 * @param rootPath the root path of a resource 1070 * 1071 * @return the site root part of the resources root path, 1072 * or <code>null</code> if the path does not match any site root 1073 * 1074 * @see #getSiteForRootPath(String) 1075 */ 1076 public String getSiteRoot(String rootPath) { 1077 1078 // add a trailing slash, because the path may be the path of a site root itself 1079 if (!rootPath.endsWith("/")) { 1080 rootPath = rootPath + "/"; 1081 } 1082 1083 for (CmsSite site : m_nestedSites.values()) { 1084 if (CmsStringUtil.isPrefixPath(site.getSiteRoot(), rootPath)) { 1085 return site.getSiteRoot(); 1086 } 1087 } 1088 1089 // most sites will be below the "/sites/" folder, 1090 CmsSite site = lookupSitesFolder(rootPath); 1091 if (site != null) { 1092 return site.getSiteRoot(); 1093 } 1094 // look through all folders that are not below "/sites/" 1095 String result = lookupAdditionalSite(rootPath); 1096 if (result != null) { 1097 return result; 1098 } 1099 CmsSite extSite = m_alternativeSiteData.getSiteForRootPath(rootPath); 1100 if (extSite != null) { 1101 result = extSite.getSiteRoot(); 1102 } 1103 return result; 1104 1105 } 1106 1107 /** 1108 * Returns an unmodifiable set of all configured site roots (Strings).<p> 1109 * 1110 * @return an unmodifiable set of all configured site roots (Strings) 1111 */ 1112 public Set<String> getSiteRoots() { 1113 1114 return Sets.union(m_siteRootSites.keySet(), m_alternativeSiteData.getSiteRoots()); 1115 1116 } 1117 1118 /** 1119 * Returns the map of configured sites, using 1120 * {@link CmsSiteMatcher} objects as keys and {@link CmsSite} objects as values.<p> 1121 * 1122 * @return the map of configured sites, using {@link CmsSiteMatcher} 1123 * objects as keys and {@link CmsSite} objects as values 1124 */ 1125 public Map<CmsSiteMatcher, CmsSite> getSites() { 1126 1127 return m_siteMatcherSites; 1128 } 1129 1130 /** 1131 * Returns the site title.<p> 1132 * 1133 * @param cms the cms context 1134 * @param resource the site root resource 1135 * 1136 * @return the title 1137 * 1138 * @throws CmsException in case reading the title property fails 1139 */ 1140 public String getSiteTitle(CmsObject cms, CmsResource resource) throws CmsException { 1141 1142 String title = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue(); 1143 if (title == null) { 1144 title = resource.getRootPath(); 1145 } 1146 if (resource.getRootPath().equals(getSharedFolder())) { 1147 title = SHARED_FOLDER_TITLE; 1148 } 1149 return title; 1150 } 1151 1152 /** 1153 * Gets the SSLMode for given workplace server.<p> 1154 * 1155 * @param server to obtain ssl mode for 1156 * @return CmsSSLMode 1157 */ 1158 public CmsSSLMode getSSLModeForWorkplaceServer(String server) { 1159 1160 if (server == null) { 1161 return CmsSSLMode.NO; 1162 } 1163 if (!m_workplaceServers.containsKey(server)) { 1164 return CmsSSLMode.NO; 1165 } 1166 1167 return m_workplaceServers.get(server); 1168 } 1169 1170 /** 1171 * Get web server scripting configurations.<p> 1172 * 1173 * @return Map with configuration data 1174 */ 1175 public Map<String, String> getWebServerConfig() { 1176 1177 return m_apacheConfig; 1178 } 1179 1180 /** 1181 * Returns the workplace server.<p> 1182 * 1183 * @return the workplace server 1184 */ 1185 public String getWorkplaceServer() { 1186 1187 return m_workplaceServers.keySet().isEmpty() ? null : m_workplaceServers.keySet().iterator().next(); 1188 } 1189 1190 /** 1191 * Gets the first configured workplace server that matches the host from the current CmsRequestContext, or 1192 * the first configured workplace server if there is no match. 1193 * 1194 * <p>If there are no workplace configured at all, null is returned. 1195 * 1196 * @param cms the CmsObject used to check the host 1197 * @return the workplace server 1198 */ 1199 public String getWorkplaceServer(CmsObject cms) { 1200 1201 if (m_workplaceServers.keySet().isEmpty()) { 1202 return null; 1203 } 1204 CmsSiteMatcher requestMatcher = cms.getRequestContext().getRequestMatcher(); 1205 if (requestMatcher != null) { 1206 String reqHost = getHost(requestMatcher.toString()); 1207 if (reqHost != null) { 1208 for (String wpServer : m_workplaceServers.keySet()) { 1209 String wpHost = getHost(wpServer); 1210 if (reqHost.equals(wpHost)) { 1211 return wpServer; 1212 } 1213 } 1214 } 1215 } 1216 return m_workplaceServers.keySet().iterator().next(); 1217 } 1218 1219 /** 1220 * Returns the configured worklace servers.<p> 1221 * 1222 * @return the workplace servers 1223 */ 1224 public List<String> getWorkplaceServers() { 1225 1226 return Collections.unmodifiableList(new ArrayList<String>(m_workplaceServers.keySet())); 1227 } 1228 1229 /** 1230 * Returns the configured worklace servers.<p> 1231 * 1232 * @param filterMode CmsSSLMode to filter results for. 1233 * @return the workplace servers 1234 */ 1235 public List<String> getWorkplaceServers(CmsSSLMode filterMode) { 1236 1237 if (filterMode == null) { 1238 return getWorkplaceServers(); 1239 } 1240 List<String> ret = new ArrayList<String>(); 1241 for (String server : m_workplaceServers.keySet()) { 1242 if (m_workplaceServers.get(server).equals(filterMode)) { 1243 ret.add(server); 1244 } 1245 } 1246 return ret; 1247 } 1248 1249 /** 1250 * Returns the configured worklace servers.<p> 1251 * 1252 * @return the workplace servers 1253 */ 1254 public Map<String, CmsSSLMode> getWorkplaceServersMap() { 1255 1256 return Collections.unmodifiableMap(m_workplaceServers); 1257 } 1258 1259 /** 1260 * Returns the site matcher that matches the workplace site.<p> 1261 * 1262 * @return the site matcher that matches the workplace site 1263 */ 1264 public CmsSiteMatcher getWorkplaceSiteMatcher() { 1265 1266 return m_workplaceMatchers.isEmpty() ? null : m_workplaceMatchers.get(0); 1267 } 1268 1269 /** 1270 * Initializes the site manager with the OpenCms system configuration.<p> 1271 * 1272 * @param cms an OpenCms context object that must have been initialized with "Admin" permissions 1273 */ 1274 public void initialize(CmsObject cms) { 1275 1276 if (CmsLog.INIT.isInfoEnabled()) { 1277 CmsLog.INIT.info( 1278 Messages.get().getBundle().key( 1279 Messages.INIT_NUM_SITE_ROOTS_CONFIGURED_1, 1280 Integer.valueOf((m_siteMatcherSites.size() + ((m_defaultUri != null) ? 1 : 0))))); 1281 } 1282 1283 try { 1284 1285 m_clone = OpenCms.initCmsObject(cms); 1286 m_clone.getRequestContext().setSiteRoot(""); 1287 m_clone.getRequestContext().setCurrentProject(m_clone.readProject(CmsProject.ONLINE_PROJECT_NAME)); 1288 1289 CmsObject cms_offline = OpenCms.initCmsObject(m_clone); 1290 CmsProject tempProject = null; 1291 try { 1292 tempProject = cms_offline.createProject( 1293 "tempProjectSites", 1294 "", 1295 "/Users", 1296 "/Users", 1297 CmsProject.PROJECT_TYPE_TEMPORARY); 1298 cms_offline.getRequestContext().setCurrentProject(tempProject); 1299 1300 } catch (Exception e) { 1301 LOG.warn(e.getLocalizedMessage(), e); 1302 } 1303 1304 m_siteUUIDs = new HashMap<CmsUUID, CmsSite>(); 1305 // check the presence of sites in VFS 1306 1307 m_onlyOfflineSites = new ArrayList<CmsSite>(); 1308 1309 for (CmsSite site : m_siteMatcherSites.values()) { 1310 checkUUIDOfSiteRoot(site, m_clone, tempProject != null ? cms_offline : null); 1311 try { 1312 CmsResource siteRes = m_clone.readResource(site.getSiteRoot()); 1313 site.setSiteRootUUID(siteRes.getStructureId()); 1314 1315 m_siteUUIDs.put(siteRes.getStructureId(), site); 1316 // during server startup the digester can not access properties, so set the title afterwards 1317 if (CmsStringUtil.isEmptyOrWhitespaceOnly(site.getTitle())) { 1318 String title = m_clone.readPropertyObject( 1319 siteRes, 1320 CmsPropertyDefinition.PROPERTY_TITLE, 1321 false).getValue(); 1322 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(title)) { 1323 site.setTitle(title); 1324 } 1325 } 1326 } catch (Throwable t) { 1327 if (CmsLog.INIT.isWarnEnabled()) { 1328 CmsLog.INIT.warn(Messages.get().getBundle().key(Messages.INIT_NO_ROOT_FOLDER_1, site)); 1329 } 1330 } 1331 } 1332 if (tempProject != null) { 1333 cms_offline.deleteProject(tempProject.getUuid()); 1334 } 1335 1336 // check the presence of the default site in VFS 1337 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_defaultUri)) { 1338 m_defaultSite = null; 1339 } else { 1340 m_defaultSite = new CmsSite(m_defaultUri, CmsSiteMatcher.DEFAULT_MATCHER); 1341 try { 1342 m_clone.readResource(m_defaultSite.getSiteRoot()); 1343 } catch (Throwable t) { 1344 if (CmsLog.INIT.isWarnEnabled()) { 1345 CmsLog.INIT.warn( 1346 Messages.get().getBundle().key(Messages.INIT_NO_ROOT_FOLDER_DEFAULT_SITE_1, m_defaultSite)); 1347 } 1348 } 1349 } 1350 if (m_defaultSite == null) { 1351 m_defaultSite = new CmsSite("/", CmsSiteMatcher.DEFAULT_MATCHER); 1352 } 1353 if (CmsLog.INIT.isInfoEnabled()) { 1354 if (m_defaultSite != null) { 1355 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DEFAULT_SITE_ROOT_1, m_defaultSite)); 1356 } else { 1357 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DEFAULT_SITE_ROOT_0)); 1358 } 1359 } 1360 initWorkplaceMatchers(); 1361 1362 // set site lists to unmodifiable 1363 setSiteMatcherSites(m_siteMatcherSites); 1364 1365 // store additional site roots to optimize lookups later 1366 for (String root : m_siteRootSites.keySet()) { 1367 if (!root.startsWith(SITES_FOLDER) || (root.split("/").length >= 4)) { 1368 m_additionalSiteRoots.add(root); 1369 } 1370 } 1371 1372 initExtensionSites(); 1373 1374 if (m_sharedFolder == null) { 1375 m_sharedFolder = DEFAULT_SHARED_FOLDER; 1376 } 1377 1378 // initialization is done, set the frozen flag to true 1379 m_frozen = true; 1380 } catch (CmsException e) { 1381 LOG.warn(e.getLocalizedMessage(), e); 1382 } 1383 if (!m_isListenerSet) { 1384 OpenCms.addCmsEventListener(this, new int[] {I_CmsEventListener.EVENT_PUBLISH_PROJECT}); 1385 m_isListenerSet = true; 1386 } 1387 } 1388 1389 /** 1390 * Checks if web server scripting is enabled.<p> 1391 * 1392 * @return true if web server scripting is set to available 1393 */ 1394 public boolean isConfigurableWebServer() { 1395 1396 return m_apacheConfig != null; 1397 } 1398 1399 /** 1400 * Returns <code>true</code> if the given site matcher matches any configured site, 1401 * which includes the workplace site.<p> 1402 * 1403 * @param matcher the site matcher to match the site with 1404 * 1405 * @return <code>true</code> if the matcher matches a site 1406 */ 1407 public boolean isMatching(CmsSiteMatcher matcher) { 1408 1409 boolean result = m_siteMatcherSites.get(matcher) != null; 1410 if (!result) { 1411 // try to match the workplace site 1412 result = isWorkplaceRequest(matcher); 1413 } 1414 return result; 1415 } 1416 1417 /** 1418 * Returns <code>true</code> if the given site matcher matches the current site.<p> 1419 * 1420 * @param cms the current OpenCms user context 1421 * @param matcher the site matcher to match the site with 1422 * 1423 * @return <code>true</code> if the matcher matches the current site 1424 */ 1425 public boolean isMatchingCurrentSite(CmsObject cms, CmsSiteMatcher matcher) { 1426 1427 return m_siteMatcherSites.get(matcher) == getCurrentSite(cms); 1428 } 1429 1430 /** 1431 * Checks if the given site root is the site root of a nested site. 1432 * 1433 * @param siteRoot the site root 1434 */ 1435 public boolean isNestedSite(String siteRoot) { 1436 1437 if (CmsStringUtil.isEmpty(siteRoot)) { 1438 return false; 1439 } 1440 return m_nestedSites.values().stream().anyMatch(site -> siteRoot.equals(site.getSiteRoot())); 1441 } 1442 1443 /** 1444 * Checks if old style secure server is allowed.<p> 1445 * 1446 * @return boolean 1447 */ 1448 public boolean isOldStyleSecureServerAllowed() { 1449 1450 return m_oldStyleSecureServer; 1451 } 1452 1453 /** 1454 * Indicates if given site is only available for offline repository.<p> 1455 * 1456 * @param site to be looked up 1457 * @return true if only offline exists, false otherwise 1458 */ 1459 public boolean isOnlyOfflineSite(CmsSite site) { 1460 1461 return m_onlyOfflineSites.contains(site); 1462 } 1463 1464 /** 1465 * Checks if the given path is that of a shared folder.<p> 1466 * 1467 * @param name a path prefix 1468 * 1469 * @return true if the given prefix represents a shared folder 1470 */ 1471 public boolean isSharedFolder(String name) { 1472 1473 return (m_sharedFolder != null) && m_sharedFolder.equals(CmsStringUtil.joinPaths("/", name, "/")); 1474 } 1475 1476 /** 1477 * Checks whether a given root path is a site root.<p> 1478 * 1479 * @param rootPath a root path 1480 * 1481 * @return true if the given path is the path of a site root 1482 */ 1483 public boolean isSiteRoot(String rootPath) { 1484 1485 String siteRoot = getSiteRoot(rootPath); 1486 rootPath = CmsStringUtil.joinPaths(rootPath, "/"); 1487 return rootPath.equals(siteRoot); 1488 1489 } 1490 1491 /** 1492 * Returns <code>true</code> if the given site matcher matches the configured OpenCms workplace.<p> 1493 * 1494 * @param matcher the site matcher to match the site with 1495 * 1496 * @return <code>true</code> if the given site matcher matches the configured OpenCms workplace 1497 */ 1498 public boolean isWorkplaceRequest(CmsSiteMatcher matcher) { 1499 1500 return m_workplaceMatchers.contains(matcher); 1501 } 1502 1503 /** 1504 * Returns <code>true</code> if the given request is against the configured OpenCms workplace.<p> 1505 * 1506 * @param req the request to match 1507 * 1508 * @return <code>true</code> if the given request is against the configured OpenCms workplace 1509 */ 1510 public boolean isWorkplaceRequest(HttpServletRequest req) { 1511 1512 if (req == null) { 1513 // this may be true inside a static export test case scenario 1514 return false; 1515 } 1516 return isWorkplaceRequest(getRequestMatcher(req)); 1517 } 1518 1519 /** 1520 * Matches the given request against all configures sites and returns 1521 * the matching site, or the default site if no sites matches.<p> 1522 * 1523 * @param req the request to match 1524 * 1525 * @return the matching site, or the default site if no sites matches 1526 */ 1527 public CmsSite matchRequest(HttpServletRequest req) { 1528 1529 CmsSiteMatcher matcher = getRequestMatcher(req); 1530 if (matcher.getTimeOffset() != 0) { 1531 HttpSession session = req.getSession(); 1532 if (session != null) { 1533 session.setAttribute( 1534 CmsContextInfo.ATTRIBUTE_REQUEST_TIME, 1535 Long.valueOf(System.currentTimeMillis() + matcher.getTimeOffset())); 1536 } 1537 } 1538 CmsSite site = matchSite(matcher); 1539 if (site.matchAlternativeSiteRoot(OpenCmsCore.getPathInfo(req))) { 1540 CmsSite alternativeSite = site.createAlternativeSiteRootSite(); 1541 if (alternativeSite != null) { 1542 LOG.debug( 1543 req.getRequestURL().toString() 1544 + ": " 1545 + "Matched extension folder rule, changing site root from " 1546 + site.getSiteRoot() 1547 + " to " 1548 + alternativeSite.getSiteRoot()); 1549 site = alternativeSite; 1550 } 1551 } 1552 1553 if (LOG.isDebugEnabled()) { 1554 String requestServer = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort(); 1555 LOG.debug( 1556 Messages.get().getBundle().key( 1557 Messages.LOG_MATCHING_REQUEST_TO_SITE_2, 1558 requestServer, 1559 site.toString())); 1560 } 1561 return site; 1562 } 1563 1564 /** 1565 * Return the configured site that matches the given site matcher, 1566 * or the default site if no sites matches.<p> 1567 * 1568 * Does NOT match auto-generated sites from alternative site root mappings, since the site matcher does not contain path information. 1569 * 1570 * @param matcher the site matcher to match the site with 1571 * @return the matching site, or the default site if no sites matches 1572 */ 1573 public CmsSite matchSite(CmsSiteMatcher matcher) { 1574 1575 CmsSite site = m_siteMatcherSites.get(matcher); 1576 if (site == null) { 1577 // return the default site (might be null as well) 1578 site = m_defaultSite; 1579 } 1580 return site; 1581 } 1582 1583 /** 1584 * Removes a site from the list of configured sites.<p> 1585 * 1586 * @param cms the cms object 1587 * @param site the site to remove 1588 * 1589 * @throws CmsException if something goes wrong 1590 */ 1591 public void removeSite(CmsObject cms, CmsSite site) throws CmsException { 1592 1593 // check permissions 1594 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 1595 // simple unit tests will have runlevel 1 and no CmsObject 1596 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 1597 } 1598 1599 // un-freeze 1600 m_frozen = false; 1601 1602 // create a new map containing all existing sites without the one to remove 1603 Map<CmsSiteMatcher, CmsSite> siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(); 1604 List<CmsSiteMatcher> matchersForSite = site.getAllMatchers(); 1605 for (Map.Entry<CmsSiteMatcher, CmsSite> entry : m_siteMatcherSites.entrySet()) { 1606 if (!(matchersForSite.contains(entry.getKey()))) { 1607 // entry not the site itself nor an alias of the site nor the secure URL of the site, so add it 1608 siteMatcherSites.put(entry.getKey(), entry.getValue()); 1609 } 1610 } 1611 setSiteMatcherSites(siteMatcherSites); 1612 1613 // remove the site from the map holding the site roots as keys and the sites as values 1614 Map<String, CmsSite> siteRootSites = new HashMap<String, CmsSite>(m_siteRootSites); 1615 siteRootSites.remove(site.getSiteRoot()); 1616 m_siteRootSites = Collections.unmodifiableMap(siteRootSites); 1617 1618 // re-initialize, will freeze the state when finished 1619 initialize(cms); 1620 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 1621 } 1622 1623 /** 1624 * Sets the default URI, this is only allowed during configuration.<p> 1625 * 1626 * If this method is called after the configuration is finished, 1627 * a <code>RuntimeException</code> is thrown.<p> 1628 * 1629 * @param defaultUri the defaultUri to set 1630 */ 1631 public void setDefaultUri(String defaultUri) { 1632 1633 if (m_frozen) { 1634 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 1635 } 1636 m_defaultUri = defaultUri; 1637 } 1638 1639 /** 1640 * Sets the old style secure server boolean.<p> 1641 * 1642 * @param value value 1643 */ 1644 public void setOldStyleSecureServerAllowed(String value) { 1645 1646 m_oldStyleSecureServer = Boolean.parseBoolean(StringUtil.toLowerCase(value)); 1647 } 1648 1649 /** 1650 * Sets the shared folder path.<p> 1651 * 1652 * @param sharedFolder the shared folder path 1653 */ 1654 public void setSharedFolder(String sharedFolder) { 1655 1656 if (m_frozen) { 1657 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 1658 } 1659 m_sharedFolder = CmsStringUtil.joinPaths("/", sharedFolder, "/"); 1660 } 1661 1662 /** 1663 * Set webserver script configuration.<p> 1664 * 1665 * 1666 * @param webserverscript path 1667 * @param targetpath path 1668 * @param configtemplate path 1669 * @param securetemplate path 1670 * @param filenameprefix to add to files 1671 * @param loggingdir path 1672 */ 1673 public void setWebServerScripting( 1674 String webserverscript, 1675 String targetpath, 1676 String configtemplate, 1677 String securetemplate, 1678 String filenameprefix, 1679 String loggingdir) { 1680 1681 m_apacheConfig = new HashMap<String, String>(); 1682 m_apacheConfig.put(WEB_SERVER_CONFIG_WEBSERVERSCRIPT, webserverscript); 1683 m_apacheConfig.put(WEB_SERVER_CONFIG_TARGETPATH, targetpath); 1684 m_apacheConfig.put(WEB_SERVER_CONFIG_CONFIGTEMPLATE, configtemplate); 1685 m_apacheConfig.put(WEB_SERVER_CONFIG_SECURETEMPLATE, securetemplate); 1686 m_apacheConfig.put(WEB_SERVER_CONFIG_FILENAMEPREFIX, filenameprefix); 1687 m_apacheConfig.put(WEB_SERVER_CONFIG_LOGGINGDIR, loggingdir); 1688 } 1689 1690 /** 1691 * Returns true if the path starts with the shared folder path.<p> 1692 * 1693 * @param path the path to check 1694 * 1695 * @return true if the path starts with the shared folder path 1696 */ 1697 public boolean startsWithShared(String path) { 1698 1699 return (m_sharedFolder != null) && CmsFileUtil.addTrailingSeparator(path).startsWith(m_sharedFolder); 1700 } 1701 1702 /** 1703 * Method for backward compability reasons. Not sure if really needed //TODO check! 1704 * CmsSSLMode are set to No as default.<p> 1705 * 1706 * @param cms the cms to use 1707 * @param defaultUri the default URI 1708 * @param workplaceServersList the workplace server URLs 1709 * @param sharedFolder the shared folder URI 1710 * 1711 * @throws CmsException if something goes wrong 1712 */ 1713 public void updateGeneralSettings( 1714 CmsObject cms, 1715 String defaultUri, 1716 List<String> workplaceServersList, 1717 String sharedFolder) 1718 throws CmsException { 1719 1720 Map<String, CmsSSLMode> workplaceServers = new LinkedHashMap<String, CmsSSLMode>(); 1721 for (String server : workplaceServersList) { 1722 if (m_workplaceServers.containsKey(server)) { 1723 workplaceServers.put(server, m_workplaceServers.get(server)); 1724 } else { 1725 workplaceServers.put(server, CmsSSLMode.NO); 1726 } 1727 } 1728 updateGeneralSettings(cms, defaultUri, workplaceServers, sharedFolder); 1729 } 1730 1731 /** 1732 * Updates the general settings.<p> 1733 * 1734 * @param cms the cms to use 1735 * @param defaulrUri the default URI 1736 * @param workplaceServers the workplace server URLs 1737 * @param sharedFolder the shared folder URI 1738 * 1739 * @throws CmsException if something goes wrong 1740 */ 1741 public void updateGeneralSettings( 1742 CmsObject cms, 1743 String defaulrUri, 1744 Map<String, CmsSSLMode> workplaceServers, 1745 String sharedFolder) 1746 throws CmsException { 1747 1748 CmsObject clone = OpenCms.initCmsObject(cms); 1749 clone.getRequestContext().setSiteRoot(""); 1750 1751 // set the shared folder 1752 if ((sharedFolder == null) 1753 || sharedFolder.equals("") 1754 || sharedFolder.equals("/") 1755 || !sharedFolder.startsWith("/") 1756 || !sharedFolder.endsWith("/") 1757 || sharedFolder.startsWith("/sites/")) { 1758 throw new CmsException( 1759 Messages.get().container(Messages.ERR_INVALID_PATH_FOR_SHARED_FOLDER_1, sharedFolder)); 1760 } 1761 1762 m_frozen = false; 1763 setDefaultUri(clone.readResource(defaulrUri).getRootPath()); 1764 setSharedFolder(clone.readResource(sharedFolder).getRootPath()); 1765 m_workplaceServers = workplaceServers; 1766 initialize(cms); 1767 m_frozen = true; 1768 } 1769 1770 /** 1771 * Updates or creates a site.<p> 1772 * 1773 * @param cms the CMS object 1774 * @param oldSite the site to remove if not <code>null</code> 1775 * @param newSite the site to add if not <code>null</code> 1776 * 1777 * @throws CmsException if something goes wrong 1778 */ 1779 public void updateSite(CmsObject cms, CmsSite oldSite, CmsSite newSite) throws CmsException { 1780 1781 if (oldSite != null) { 1782 // remove the old site 1783 removeSite(cms, oldSite); 1784 } 1785 1786 if (newSite != null) { 1787 // add the new site 1788 addSite(cms, newSite); 1789 } 1790 } 1791 1792 /** 1793 * Returns true if this request goes to a secure site.<p> 1794 * 1795 * @param req the request to check 1796 * 1797 * @return true if the request goes to a secure site 1798 */ 1799 public boolean usesSecureSite(HttpServletRequest req) { 1800 1801 CmsSite site = matchRequest(req); 1802 if (site == null) { 1803 return false; 1804 } 1805 CmsSiteMatcher secureMatcher = site.getSecureServerMatcher(); 1806 boolean result = false; 1807 if (secureMatcher != null) { 1808 result = secureMatcher.equals(getRequestMatcher(req)); 1809 } 1810 return result; 1811 } 1812 1813 /** 1814 * Validates the site root, throwing an exception if the validation fails. 1815 * 1816 * @param siteRoot the site root to check 1817 */ 1818 public void validateSiteRoot(String siteRoot) { 1819 1820 if (!isValidSiteRoot(siteRoot)) { 1821 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_INVALID_SITE_ROOT_1, siteRoot)); 1822 } 1823 } 1824 1825 /** 1826 * Adds a new Site matcher object to the map of server names. 1827 * 1828 * @param matcher the SiteMatcher of the server 1829 * @param site the site to add 1830 */ 1831 private void addServer(CmsSiteMatcher matcher, CmsSite site) { 1832 1833 Map<CmsSiteMatcher, CmsSite> siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(m_siteMatcherSites); 1834 siteMatcherSites.put(matcher, site); 1835 setSiteMatcherSites(siteMatcherSites); 1836 } 1837 1838 /** 1839 * Fetches UUID for given site root from online and offline repository.<p> 1840 * 1841 * @param site to read and set UUID for 1842 * @param clone online CmsObject 1843 * @param cms_offline offline CmsObject 1844 */ 1845 private void checkUUIDOfSiteRoot(CmsSite site, CmsObject clone, CmsObject cms_offline) { 1846 1847 CmsUUID id = null; 1848 try { 1849 id = clone.readResource(site.getSiteRoot()).getStructureId(); 1850 } catch (CmsException e) { 1851 //Ok, site root not available for online repository. 1852 } 1853 1854 if ((id == null) && (cms_offline != null)) { 1855 try { 1856 id = cms_offline.readResource(site.getSiteRoot()).getStructureId(); 1857 m_onlyOfflineSites.add(site); 1858 } catch (CmsException e) { 1859 //Siteroot not valid for on- and offline repository. 1860 } 1861 } 1862 if (id != null) { 1863 site.setSiteRootUUID(id); 1864 LOG.debug("Initializing site id: " + site + " => " + id); 1865 m_siteUUIDs.put(id, site); 1866 } 1867 } 1868 1869 /** 1870 * Gets an offline project to read offline resources from.<p> 1871 * 1872 * @return CmsProject 1873 */ 1874 private CmsProject getOfflineProject() { 1875 1876 try { 1877 return m_clone.readProject("Offline"); 1878 } catch (CmsException e) { 1879 try { 1880 for (CmsProject p : OpenCms.getOrgUnitManager().getAllAccessibleProjects(m_clone, "/", true)) { 1881 if (!p.isOnlineProject()) { 1882 return p; 1883 } 1884 } 1885 } catch (CmsException e1) { 1886 LOG.error("Unable to get ptoject", e); 1887 } 1888 } 1889 return null; 1890 } 1891 1892 /** 1893 * Returns the site matcher for the given request.<p> 1894 * 1895 * @param req the request to get the site matcher for 1896 * 1897 * @return the site matcher for the given request 1898 */ 1899 private CmsSiteMatcher getRequestMatcher(HttpServletRequest req) { 1900 1901 CmsSiteMatcher matcher = new CmsSiteMatcher(req.getScheme(), req.getServerName(), req.getServerPort()); 1902 // this is required to get the right configured time offset 1903 int index = m_siteMatchers.indexOf(matcher); 1904 if (index < 0) { 1905 return matcher; 1906 } 1907 return m_siteMatchers.get(index); 1908 } 1909 1910 /** 1911 * Finds the configured extension folders for all normal sites and stores them in a separate list. 1912 */ 1913 private void initExtensionSites() { 1914 1915 m_alternativeSiteData = new AlternativeSiteData(m_siteMatcherSites.values()); 1916 Set<CmsPath> siteRoots = new HashSet<CmsPath>(); 1917 for (CmsSite site : m_siteMatcherSites.values()) { 1918 if (site.getSiteRoot() != null) { 1919 siteRoots.add(new CmsPath(site.getSiteRoot())); 1920 } 1921 } 1922 for (String siteRoot : m_alternativeSiteData.getSiteRoots()) { 1923 siteRoots.add(new CmsPath(siteRoot)); 1924 } 1925 m_nestedSites = new HashMap<>(); 1926 for (CmsSite site : m_siteMatcherSites.values()) { 1927 if (Boolean.parseBoolean(site.getParameters().get("isNestedSite"))) { 1928 String parent = CmsResource.getParentFolder(site.getSiteRoot()); 1929 if (parent != null) { 1930 if (siteRoots.contains(new CmsPath(parent))) { 1931 m_nestedSites.put(site.getSiteMatcher(), site); 1932 } 1933 } 1934 } 1935 } 1936 LOG.info("Nested sites: " + m_nestedSites); 1937 } 1938 1939 /** 1940 * Initializes the workplace matchers.<p> 1941 */ 1942 private void initWorkplaceMatchers() { 1943 1944 List<CmsSiteMatcher> matchers = new ArrayList<CmsSiteMatcher>(); 1945 if (!m_workplaceServers.isEmpty()) { 1946 Map<String, CmsSiteMatcher> matchersByUrl = Maps.newHashMap(); 1947 for (String server : m_workplaceServers.keySet()) { 1948 CmsSSLMode mode = m_workplaceServers.get(server); 1949 CmsSiteMatcher matcher = new CmsSiteMatcher(server); 1950 if ((mode == CmsSSLMode.LETS_ENCRYPT) || (mode == CmsSSLMode.MANUAL_EP_TERMINATION)) { 1951 CmsSiteMatcher httpMatcher = matcher.forDifferentScheme("http"); 1952 CmsSiteMatcher httpsMatcher = matcher.forDifferentScheme("https"); 1953 for (CmsSiteMatcher current : new CmsSiteMatcher[] {httpMatcher, httpsMatcher}) { 1954 matchersByUrl.put(current.getUrl(), current); 1955 if (CmsLog.INIT.isInfoEnabled()) { 1956 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_1, matcher)); 1957 } 1958 } 1959 } else { 1960 matchersByUrl.put(matcher.getUrl(), matcher); 1961 if (CmsLog.INIT.isInfoEnabled()) { 1962 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_1, matcher)); 1963 } 1964 1965 } 1966 } 1967 matchers = Lists.newArrayList(matchersByUrl.values()); 1968 } else if (CmsLog.INIT.isInfoEnabled()) { 1969 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_0)); 1970 } 1971 m_workplaceMatchers = matchers; 1972 } 1973 1974 /** 1975 * Checks whether the given matcher is included in the currently configured and valid matchers.<p> 1976 * 1977 * @param matcher the matcher to check 1978 * 1979 * @return <code>true</code> in case the given matcher is included in the currently configured and valid matchers 1980 */ 1981 private boolean isServerValid(CmsSiteMatcher matcher) { 1982 1983 return !m_siteMatcherSites.containsKey(matcher); 1984 1985 } 1986 1987 /** 1988 * Validates the site root. 1989 * 1990 * @param uri the site root to validate 1991 * @return true if the site root is valid 1992 */ 1993 private boolean isValidSiteRoot(String uri) { 1994 1995 if ("".equals(uri) 1996 || "/".equals(uri) 1997 || CmsSiteManagerImpl.SITES_FOLDER.equals(uri) 1998 || CmsSiteManagerImpl.SITES_FOLDER.equals(uri + "/")) { 1999 return false; 2000 } 2001 return true; 2002 } 2003 2004 /** 2005 * Returns <code>true</code> if the given root path matches any of the stored additional sites.<p> 2006 * 2007 * @param rootPath the root path to check 2008 * 2009 * @return <code>true</code> if the given root path matches any of the stored additional sites 2010 */ 2011 private String lookupAdditionalSite(String rootPath) { 2012 2013 for (int i = 0, size = m_additionalSiteRoots.size(); i < size; i++) { 2014 String siteRoot = m_additionalSiteRoots.get(i); 2015 if (rootPath.startsWith(siteRoot + "/")) { 2016 return siteRoot; 2017 } 2018 } 2019 return null; 2020 } 2021 2022 /** 2023 * Returns the configured site if the given root path matches site in the "/sites/" folder, 2024 * or <code>null</code> otherwise.<p> 2025 * 2026 * @param rootPath the root path to check 2027 * 2028 * @return the configured site if the given root path matches site in the "/sites/" folder, 2029 * or <code>null</code> otherwise 2030 */ 2031 private CmsSite lookupSitesFolder(String rootPath) { 2032 2033 int pos = rootPath.indexOf('/', SITES_FOLDER_POS); 2034 if (pos > 0) { 2035 // this assumes that the root path may likely start with something like "/sites/default/" 2036 // just cut the first 2 directories from the root path and do a direct lookup in the internal map 2037 return m_siteRootSites.get(rootPath.substring(0, pos)); 2038 } 2039 return null; 2040 } 2041 2042 /** 2043 * Sets the class member variables {@link #m_siteMatcherSites} and {@link #m_siteMatchers} 2044 * from the provided map of configured site matchers.<p> 2045 * 2046 * @param siteMatcherSites the site matches to set 2047 */ 2048 private void setSiteMatcherSites(Map<CmsSiteMatcher, CmsSite> siteMatcherSites) { 2049 2050 m_siteMatcherSites = Collections.unmodifiableMap(siteMatcherSites); 2051 m_siteMatchers = Collections.unmodifiableList(new ArrayList<CmsSiteMatcher>(m_siteMatcherSites.keySet())); 2052 } 2053}