'Multisite', 'version' => 11, 'summary' => 'Allows multiple sites with different domains run from single PW-site and database.', 'href' => 'https://github.com/somatonic/Multisite', 'singular' => true, 'autoload' => true ); } public function __construct() { // read config settings if(!wire("config")->MultisiteDomains) { $this->domains = array(); } else { $this->domains = wire("config")->MultisiteDomains; } $this->site = ""; } public function init() { // domains from the config if(!count($this->domains)) return; $this->it = (isset($_GET['it'])) ? ltrim($_GET['it'], '/') : ''; $this->isAdmin = false; $this->isAssets = false; $this->isFormBuilder = false; $this->domain = ''; $httpHost = strtolower(wire('config')->httpHost); $rootUrl = rtrim(wire('config')->urls->root, '/'); // if in admin, we store that for later usage if(strpos($rootUrl . $this->it, trim(wire('config')->urls->admin, '/')) === 0) { $this->isAdmin = true; } // if it's an assets file request and not a page, we don't need to modify "it" if(strpos($rootUrl . $this->it, ltrim(wire('config')->urls->assets, '/')) === 0){ $this->isAssets = true; } // if it's form-builder if(strpos($rootUrl . $this->it, "/form-builder/") !== false){ $this->isFormBuilder = true; } // exclude calls from PW front-end editing if($this->wire("input")->post->action == "PageFrontEditSave") return; // check for root domain name in url and redirect to domain if found $this->_checkDomainInUrl(); if(array_key_exists($httpHost, $this->domains)) { // set the current domain, so it can be picked up in templates or other modules $this->domain = $httpHost; $this->site = $this->domains[$httpHost]; $this->set404Page($this->domain); $domainPageName = $this->domains[$httpHost]['root']; // if the requested URI is an admin url we don't need to go further // we just need to make sure the $this->domain is set, as it's may be // used later in other modules to modify the url, or localUrl() if($this->isAdmin) return; // if requested url is a file we don't change if($this->isAssets) return; // if requested url is a file we don't change if($this->isFormBuilder) return; // language page names support if installed // strip off the lang segment, to later prepend it again $this->langSegment = ''; if($this->modules->isInstalled("LanguageSupportPageNames")) { foreach($this->languages as $lang) { $langID = $lang->id; if($lang->isDefault()) $langID = ''; $rootPage = $this->pages->get(1); $rootName = $rootPage->get("name$langID"); if(strlen($rootName)) { if(strpos($this->it, $rootName . "/") === 0){ $this->langSegment = $rootName; // $new_it = substr($this->it,strlen($this->langSegment)+1); $this->it = substr($this->it,strlen($this->langSegment)+1); } } } } $langSegment = $this->langSegment ? $this->langSegment . "/" : ""; // we modify the GET it to tell PW what page to load $_GET['it'] = (isset($_GET['it'])) ? "$langSegment{$domainPageName}/" . $this->it : "$langSegment{$domainPageName}/"; } if(!$this->isAdmin) { $this->addHookAfter('Page::render', $this, 'hookPageRender'); } } /** * Hook page render function and parse the output for modifying urls * @param HookEvent $event Event object from the hook */ public function hookPageRender(HookEvent $event){ $out = $event->return; $out = $this->parseLinks($out); $event->return = $out; } /** * Parse hrefs on the rendered output of a page to replace physical page urls with * the correct ones. If a URL found of another domain, the full domain URL will * be inserted. * * @param string $out The output from page render * @return string The parsed output */ public function ___parseLinks($out){ if(!strlen($out)) return $out; $protocol = $this->wire('config')->https ? 'https' : 'http'; foreach($this->domains as $key => $domain){ if($this->modules->isInstalled("LanguageSupportPageNames")) { foreach($this->languages as $lang) { $langID = $lang->id; if($lang->isDefault()) $langID = ''; $rootPage = $this->pages->get(1); $rootName = $rootPage->get("name$langID"); if($rootName == "home") $rootName = ""; if(strlen($rootName)) $rootName = "/" . $rootName; if($key == $this->domain){ // internal relative urls $replace = array( "\"$rootName/{$domain['root']}/", "'$rootName/{$domain['root']}/", ); $with = array( "\"$rootName/", "'$rootName/", ); // echo (" - " . $replace . " / " . $with); } else { // external urls $replace = array( "\"$rootName/{$domain['root']}/", "'$rootName/{$domain['root']}/", "\"$protocol://{$key}$rootName/{$domain['root']}/", "'$protocol://{$key}$rootName/{$domain['root']}/", ); $with = array( "\"$protocol://{$key}$rootName/", "'$protocol://{$key}$rootName/", "\"$protocol://{$key}$rootName/", "\'$protocol://{$key}$rootName/", ); // echo (" - " . $replace . " / " . $with); } $out = str_replace($replace, $with, $out); } } else { if($key == $this->domain){ // local urls $replace = array( "\"/" . $domain['root'] . "/", "'/" . $domain['root'] . "/", "/" . $domain['root'] . "/", ); $with = array( "\"/", "'/", "/", ); } else { // external urls $replace = array( "\"/" . $domain['root'] . "/", "'/" . $domain['root'] . "/", "\"$protocol://$key/" . $domain['root'] . "/", "'$protocol://$key/" . $domain['root'] . "/", ); $with = array( "\"$protocol://" . $key . "/", "'$protocol://" . $key . "/", "\"$protocol://" . $key . "/", "'$protocol://" . $key . "/", ); } $out = str_replace($replace, $with, $out); } } return $out; } /** * In case we have a request that has the physical page URL we check * for the domain root names, replace it if found and redirect to the * correct URL * */ public function _checkDomainInUrl() { // in case we find a multisite root domain in request path, we redirect to the correct domain/url $cleanIt = rtrim($this->it, "/"); $cleanIt = $cleanIt . "/"; $dfound = false; foreach($this->domains as $domain => $domainInfo) { $rootName = $domainInfo['root']; // is root name found in url, NOTE: "domain" would match with "domainB", // so we need additional checks if(strpos($cleanIt, $rootName) !== false) { if($cleanIt == $rootName) { // we found a 1:1 match $dfound = true; } else if(strpos($cleanIt, "/") !== false) { // we need to search further down, since there's a "/" we can search for a match // this works also in case of having language segments or in case a domain root name // is a substring of another domain root name $search = "#{$rootName}\/#i"; if(preg_match($search, $cleanIt, $matches)){ $dfound = true; } // echo "
" . $search . " - " . $cleanIt . " " . preg_match($search, $cleanIt, $matches); } if($dfound) { if(strpos($this->it, $rootName . "/") !== false) { $url = str_replace("{$rootName}/", '', $this->it); } else { $url = str_replace("{$rootName}", '', $this->it); } $protocol = $this->wire('config')->https ? 'https' : 'http'; $qsArray = $_GET; unset($qsArray['it']); $qs = http_build_query($qsArray); $url = $protocol . "://" . $domain . "/" . $url; if($qs) $qs = "?" . $qs; $this->session->redirect($url . $qs); } } } } /** * Set the 404 page id * @param string $domain the domain * @return [type] [description] */ public function ___set404Page($domain){ if($this->domains[$domain]){ wire("config")->http404PageID = (int) $this->domains[$domain]['http404']; } } }