/**
  * Will try to include a GET parameter for an existing URL, preserving existing parameters and
  * fragments. If no URL is given, falls back to $_SERVER['REQUEST_URI']. Uses parse_url() to
  * dissect the URL, and http_build_query() to reconstruct it with the additional parameter.
  * Converts any '&' (ampersand) URL parameter separators to the more XHTML compliant '&'.
  *
  * CAUTION: If the URL is determined to be relative, it is prepended with Director::absoluteBaseURL().
  * This method will always return an absolute URL because Director::makeRelative() can lead to
  * inconsistent results.
  *
  * @param string $varname
  * @param string $varvalue
  * @param string $currentURL Relative or absolute URL.
  * @param string $separator Separator for http_build_query().
  *
  * @return string
  */
 public static function setGetVar($varname, $varvalue, $currentURL = null, $separator = '&')
 {
     $uri = $currentURL ? $currentURL : Director::makeRelative($_SERVER['REQUEST_URI']);
     $isRelative = false;
     // We need absolute URLs for parse_url()
     if (Director::is_relative_url($uri)) {
         $uri = Director::absoluteBaseURL() . $uri;
         $isRelative = true;
     }
     // try to parse uri
     $parts = parse_url($uri);
     if (!$parts) {
         throw new InvalidArgumentException("Can't parse URL: " . $uri);
     }
     // Parse params and add new variable
     $params = array();
     if (isset($parts['query'])) {
         parse_str($parts['query'], $params);
     }
     $params[$varname] = $varvalue;
     // Generate URI segments and formatting
     $scheme = isset($parts['scheme']) ? $parts['scheme'] : 'http';
     $user = isset($parts['user']) && $parts['user'] != '' ? $parts['user'] : '';
     if ($user != '') {
         // format in either user:pass@host.com or user@host.com
         $user .= isset($parts['pass']) && $parts['pass'] != '' ? ':' . $parts['pass'] . '@' : '@';
     }
     $host = isset($parts['host']) ? $parts['host'] : '';
     $port = isset($parts['port']) && $parts['port'] != '' ? ':' . $parts['port'] : '';
     $path = isset($parts['path']) && $parts['path'] != '' ? $parts['path'] : '';
     // handle URL params which are existing / new
     $params = $params ? '?' . http_build_query($params, null, $separator) : '';
     // keep fragments (anchors) intact.
     $fragment = isset($parts['fragment']) && $parts['fragment'] != '' ? '#' . $parts['fragment'] : '';
     // Recompile URI segments
     $newUri = $scheme . '://' . $user . $host . $port . $path . $params . $fragment;
     if ($isRelative) {
         return Director::makeRelative($newUri);
     }
     return $newUri;
 }
 /**
  * The process() method handles the "meat" of the template processing.
  *
  * It takes care of caching the output (via {@link Cache}), as well as
  * replacing the special "$Content" and "$Layout" placeholders with their
  * respective subtemplates.
  *
  * The method injects extra HTML in the header via {@link Requirements::includeInHTML()}.
  *
  * Note: You can call this method indirectly by {@link ViewableData->renderWith()}.
  *
  * @param ViewableData $item
  * @param array|null $arguments Arguments to an included template
  * @param ViewableData $inheritedScope The current scope of a parent template including a sub-template
  * @return DBHTMLText Parsed template output.
  */
 public function process($item, $arguments = null, $inheritedScope = null)
 {
     SSViewer::$topLevel[] = $item;
     $template = $this->chosen;
     $cacheFile = TEMP_FOLDER . "/.cache" . str_replace(array('\\', '/', ':'), '.', Director::makeRelative(realpath($template)));
     $lastEdited = filemtime($template);
     if (!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
         $content = file_get_contents($template);
         $content = $this->parseTemplateContent($content, $template);
         $fh = fopen($cacheFile, 'w');
         fwrite($fh, $content);
         fclose($fh);
     }
     $underlay = array('I18NNamespace' => basename($template));
     // Makes the rendered sub-templates available on the parent item,
     // through $Content and $Layout placeholders.
     foreach (array('Content', 'Layout') as $subtemplate) {
         $sub = null;
         if (isset($this->subTemplates[$subtemplate])) {
             $sub = $this->subTemplates[$subtemplate];
         } elseif (!is_array($this->templates)) {
             $sub = ['type' => $subtemplate, $this->templates];
         } elseif (!array_key_exists('type', $this->templates) || !$this->templates['type']) {
             $sub = array_merge($this->templates, ['type' => $subtemplate]);
         }
         if ($sub) {
             $subtemplateViewer = clone $this;
             // Disable requirements - this will be handled by the parent template
             $subtemplateViewer->includeRequirements(false);
             // Select the right template
             $subtemplateViewer->setTemplate($sub);
             if ($subtemplateViewer->exists()) {
                 $underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
             }
         }
     }
     $output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay, $inheritedScope);
     if ($this->includeRequirements) {
         $output = Requirements::includeInHTML($output);
     }
     array_pop(SSViewer::$topLevel);
     // If we have our crazy base tag, then fix # links referencing the current page.
     $rewrite = SSViewer::config()->get('rewrite_hash_links');
     if ($this->rewriteHashlinks && $rewrite) {
         if (strpos($output, '<base') !== false) {
             if ($rewrite === 'php') {
                 $thisURLRelativeToBase = "<?php echo \\SilverStripe\\Core\\Convert::raw2att(preg_replace(\"/^(\\\\/)+/\", \"/\", \$_SERVER['REQUEST_URI'])); ?>";
             } else {
                 $thisURLRelativeToBase = Convert::raw2att(preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI']));
             }
             $output = preg_replace('/(<a[^>]+href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output);
         }
     }
     return DBField::create_field('HTMLFragment', $output);
 }
 /**
  * Given a PHP class name, finds the module where it's located.
  *
  * @param  string $name
  * @return string
  */
 public static function get_owner_module($name)
 {
     $manifest = ClassLoader::instance()->getManifest();
     $path = $manifest->getItemPath($name);
     if (!$path) {
         return false;
     }
     $path = Director::makeRelative($path);
     $path = str_replace('\\', '/', $path);
     $parts = explode('/', trim($path, '/'));
     return array_shift($parts);
 }
 /**
  * If the last request was a 3xx response, then follow the redirection
  *
  * @return HTTPResponse The response given, or null if no redirect occurred
  */
 public function followRedirection()
 {
     if ($this->lastResponse->getHeader('Location')) {
         $url = Director::makeRelative($this->lastResponse->getHeader('Location'));
         $url = strtok($url, '#');
         return $this->get($url);
     }
 }
 public function testMakeRelative()
 {
     $siteUrl = Director::absoluteBaseURL();
     $siteUrlNoProtocol = preg_replace('/https?:\\/\\//', '', $siteUrl);
     $this->assertEquals(Director::makeRelative("{$siteUrl}"), '');
     $this->assertEquals(Director::makeRelative("https://{$siteUrlNoProtocol}"), '');
     $this->assertEquals(Director::makeRelative("http://{$siteUrlNoProtocol}"), '');
     $this->assertEquals(Director::makeRelative("   {$siteUrl}/testpage   "), 'testpage');
     $this->assertEquals(Director::makeRelative("{$siteUrlNoProtocol}/testpage"), 'testpage');
     $this->assertEquals(Director::makeRelative('ftp://test.com'), 'ftp://test.com');
     $this->assertEquals(Director::makeRelative('http://test.com'), 'http://test.com');
     $this->assertEquals(Director::makeRelative('relative'), 'relative');
     $this->assertEquals(Director::makeRelative("{$siteUrl}/?url=http://test.com"), '?url=http://test.com');
     $this->assertEquals("test", Director::makeRelative("https://" . $siteUrlNoProtocol . "/test"));
     $this->assertEquals("test", Director::makeRelative("http://" . $siteUrlNoProtocol . "/test"));
 }