diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6182ad7..61715e9bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ 1. [](#improved) * Make it possible to include debug bar also into non-HTML responses +# v1.3.7 +## mm/dd/2017 + +1. [](#bugfix) + * Regressionin Uri: `base_url_absolute` always has the port number (#1690) + * Uri: Prefer using REQUEST_SCHEME instead of HTTPS (#1698) + * Fixed routing paths with urlencoded spaces and non-latin letters (#1688) + # v1.3.6 ## 10/12/2017 diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 96411f7d9..781bccb72 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -135,7 +135,7 @@ class Page $this->metadata(); $this->url(); $this->visible(); - $this->modularTwig($this->slug[0] == '_'); + $this->modularTwig($this->slug[0] === '_'); $this->setPublishState(); $this->published(); $this->urlExtension(); @@ -517,17 +517,18 @@ class Page // Return entire page content on wrong/ unknown format if (!in_array($format, ['short', 'long'])) { return $content; - } elseif (($format === 'short') && isset($summary_size)) { + } + if (($format === 'short') && isset($summary_size)) { // Use mb_strimwidth to slice the string if (mb_strwidth($content, 'utf8') > $summary_size) { return mb_substr($content, 0, $summary_size); - } else { - return $content; } + + return $content; } // Get summary size from site config's file - if (is_null($size)) { + if ($size === null) { $size = $config['size']; } @@ -535,7 +536,8 @@ class Page if ($size === 0) { return $content; // Return calculated summary based on defaults - } elseif (!is_numeric($size) || ($size < 0)) { + } + if (!is_numeric($size) || ($size < 0)) { $size = 300; } @@ -726,10 +728,9 @@ class Page if ($name) { if (isset($this->content_meta[$name])) { return $this->content_meta[$name]; - } else { - return null; } + return null; } return $this->content_meta; @@ -825,27 +826,27 @@ class Page */ public function value($name, $default = null) { - if ($name == 'content') { + if ($name === 'content') { return $this->raw_content; } - if ($name == 'route') { + if ($name === 'route') { return $this->parent()->rawRoute(); } - if ($name == 'order') { + if ($name === 'order') { $order = $this->order(); return $order ? (int)$this->order() : ''; } - if ($name == 'ordering') { + if ($name === 'ordering') { return (bool)$this->order(); } - if ($name == 'folder') { + if ($name === 'folder') { return preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder); } - if ($name == 'slug') { + if ($name === 'slug') { return $this->slug(); } - if ($name == 'name') { + if ($name === 'name') { $language = $this->language() ? '.' . $this->language() : ''; $name_val = str_replace($language . '.md', '', $this->name()); if ($this->modular()) { @@ -854,30 +855,30 @@ class Page return $name_val; } - if ($name == 'media') { + if ($name === 'media') { return $this->media()->all(); } - if ($name == 'media.file') { + if ($name === 'media.file') { return $this->media()->files(); } - if ($name == 'media.video') { + if ($name === 'media.video') { return $this->media()->videos(); } - if ($name == 'media.image') { + if ($name === 'media.image') { return $this->media()->images(); } - if ($name == 'media.audio') { + if ($name === 'media.audio') { return $this->media()->audios(); } $path = explode('.', $name); $scope = array_shift($path); - if ($name == 'frontmatter') { + if ($name === 'frontmatter') { return $this->frontmatter; } - if ($scope == 'header') { + if ($scope === 'header') { $current = $this->header(); foreach ($path as $field) { if (is_object($current) && isset($current->{$field})) { @@ -969,7 +970,7 @@ class Page $this->_action = 'move'; - if ($this->route() == $parent->route()) { + if ($this->route() === $parent->route()) { throw new Exception('Failed: Cannot set page parent to self'); } if (Utils::startsWith($parent->rawRoute(), $this->rawRoute())) { @@ -1029,12 +1030,12 @@ class Page $edit_mode = isset($grav['admin']) ? $grav['config']->get('plugins.admin.edit_mode') : null; // override if you only want 'normal' mode - if (empty($fields) && ($edit_mode == 'auto' || $edit_mode == 'normal')) { + if (empty($fields) && ($edit_mode === 'auto' || $edit_mode === 'normal')) { $blueprint = $pages->blueprints('default'); } // override if you only want 'expert' mode - if (!empty($fields) && $edit_mode == 'expert') { + if (!empty($fields) && $edit_mode === 'expert') { $blueprint = $pages->blueprints(''); } @@ -1459,9 +1460,9 @@ class Page { if (isset($this->debugger) && $this->debugger === false) { return false; - } else { - return true; } + + return true; } /** @@ -1502,7 +1503,7 @@ class Page // Backward compatibility for nested arrays in metas if (is_array($value)) { foreach ($value as $property => $prop_value) { - $prop_key = $key . ":" . $property; + $prop_key = $key . ':' . $property; $this->metadata[$prop_key] = [ 'name' => $prop_key, 'property' => $prop_key, @@ -1517,7 +1518,7 @@ class Page 'http_equiv' => $key, 'content' => htmlspecialchars($value, ENT_QUOTES, 'UTF-8') ]; - } elseif ($key == 'charset') { + } elseif ($key === 'charset') { $this->metadata[$key] = ['charset' => htmlspecialchars($value, ENT_QUOTES, 'UTF-8')]; } else { // if it's a social metadata with separator, render as property @@ -1552,7 +1553,7 @@ class Page */ public function slug($var = null) { - if ($var !== null && $var !== "") { + if ($var !== null && $var !== '') { $this->slug = $var; } @@ -1679,7 +1680,7 @@ class Page $url = rtrim($url, '/'); } - return $url; + return Uri::filterPath($url); } /** @@ -1702,7 +1703,7 @@ class Page // calculate route based on parent slugs $parent = $this->parent(); if (isset($parent)) { - if ($this->hide_home_route && $parent->route() == $this->home_route) { + if ($this->hide_home_route && $parent->route() === $this->home_route) { $baseRoute = ''; } else { $baseRoute = (string)$parent->route(); @@ -1770,9 +1771,9 @@ class Page if (!empty($this->routes) && isset($this->routes['aliases'])) { return $this->routes['aliases']; - } else { - return []; } + + return []; } /** @@ -2298,7 +2299,7 @@ class Page $routes = Grav::instance()['pages']->routes(); if (isset($routes[$uri_path])) { - if ($routes[$uri_path] == $this->path()) { + if ($routes[$uri_path] === $this->path()) { return true; } @@ -2325,7 +2326,7 @@ class Page $child_page = $pages->dispatch($uri->route())->parent(); if ($child_page) { while (!$child_page->root()) { - if ($this->path() == $child_page->path()) { + if ($this->path() === $child_page->path()) { return true; } $child_page = $child_page->parent(); @@ -2344,7 +2345,7 @@ class Page public function home() { $home = Grav::instance()['config']->get('system.home.alias'); - $is_home = ($this->route() == $home || $this->rawRoute() == $home); + $is_home = ($this->route() === $home || $this->rawRoute() === $home); return $is_home; } @@ -2358,9 +2359,9 @@ class Page { if (!$this->parent && !$this->name && !$this->visible) { return true; - } else { - return false; } + + return false; } /** @@ -2587,7 +2588,7 @@ class Page } /** - * @param string $value + * @param string|array $value * * @return mixed * @internal @@ -2605,7 +2606,7 @@ class Page $params = (array)current($value); } else { $result = []; - foreach ($value as $key => $val) { + foreach ((array)$value as $key => $val) { if (is_int($key)) { $result = $result + $this->evaluate($val)->toArray(); } else { @@ -2711,7 +2712,7 @@ class Page case 'root@': case '@root': - if (!empty($parts) && $parts[0] == 'descendants') { + if (!empty($parts) && $parts[0] === 'descendants') { $results = $pages->all($pages->root())->nonModular()->published(); } else { $results = $pages->root()->children()->nonModular()->published(); @@ -2824,11 +2825,11 @@ class Page // Reorder all moved pages. foreach ($siblings as $slug => $page) { - $order = intval(trim($page->order(), '.')); + $order = (int)trim($page->order(), '.'); $counter++; if ($order) { - if ($page->path() == $this->path() && $this->folderExists()) { + if ($page->path() === $this->path() && $this->folderExists()) { // Handle current page; we do want to change ordering number, but nothing else. $this->order($counter); $this->save(false); @@ -2859,14 +2860,14 @@ class Page } if (is_dir($this->_original->path())) { - if ($this->_action == 'move') { + if ($this->_action === 'move') { Folder::move($this->_original->path(), $this->path()); - } elseif ($this->_action == 'copy') { + } elseif ($this->_action === 'copy') { Folder::copy($this->_original->path(), $this->path()); } } - if ($this->name() != $this->_original->name()) { + if ($this->name() !== $this->_original->name()) { $path = $this->path(); if (is_file($path . '/' . $this->_original->name())) { rename($path . '/' . $this->_original->name(), $path . '/' . $this->name()); @@ -2889,7 +2890,7 @@ class Page } } // publish if required, if not clear cache right before page is published - if ($this->publishDate() && $this->publishDate() && $this->publishDate() > time()) { + if ($this->publishDate() && $this->publishDate() > time()) { $this->published(false); Grav::instance()['cache']->setLifeTime($this->publishDate()); } diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index a59cee97d..f9f6a5240 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -16,12 +16,13 @@ use Grav\Common\Filesystem\Folder; use Grav\Common\Grav; use Grav\Common\Language\Language; use Grav\Common\Taxonomy; +use Grav\Common\Uri; use Grav\Common\Utils; use Grav\Plugin\Admin; use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use Whoops\Exception\ErrorException; -use Collator as Collator; +use Collator; class Pages { @@ -152,7 +153,7 @@ class Pages } $path_append = rtrim($this->grav['pages']->base(), '/'); - if ($language->getDefault() != $lang || $config->get('system.languages.include_default_lang') === true) { + if ($language->getDefault() !== $lang || $config->get('system.languages.include_default_lang') === true) { $path_append .= $lang ? '/' . $lang : ''; } @@ -192,7 +193,7 @@ class Pages return $this->homeUrl($lang, $absolute); } - return $this->baseUrl($lang, $absolute) . $route; + return $this->baseUrl($lang, $absolute) . Uri::filterPath($route); } /** @@ -297,7 +298,7 @@ class Pages $sort = $this->sort[$path][$order_by]; - if ($order_dir != 'asc') { + if ($order_dir !== 'asc') { $sort = array_reverse($sort); } @@ -327,7 +328,7 @@ class Pages $sort = $this->sort[$lookup][$orderBy]; - if ($orderDir != 'asc') { + if ($orderDir !== 'asc') { $sort = array_reverse($sort); } @@ -345,10 +346,6 @@ class Pages */ public function get($path) { - if (!is_null($path) && !is_string($path)) { - throw new \Exception(); - } - return isset($this->instances[(string)$path]) ? $this->instances[(string)$path] : null; } @@ -376,13 +373,13 @@ class Pages */ public function ancestor($route, $path = null) { - if (!is_null($path)) { - + if ($path !== null) { $page = $this->dispatch($route, true); - if ($page->path() == $path) { + if ($page && $page->path() === $path) { return $page; - } elseif (!$page->parent()->root()) { + } + if ($page && !$page->parent()->root()) { return $this->ancestor($page->parent()->route(), $path); } } @@ -400,15 +397,14 @@ class Pages */ public function inherited($route, $field = null) { - if (!is_null($field)) { + if ($field !== null) { $page = $this->dispatch($route, true); - $ancestorField = $page->parent()->value('header.' . $field); - - if ($ancestorField !== null) { + if ($page && $page->parent()->value('header.' . $field) !== null) { return $page->parent(); - } elseif (!$page->parent()->root()) { + } + if ($page && !$page->parent()->root()) { return $this->inherited($page->parent()->route(), $field); } } @@ -441,6 +437,8 @@ class Pages */ public function dispatch($route, $all = false, $redirect = true) { + $route = urldecode($route); + // Fetch page if there's a defined route to it. $page = isset($this->routes[$route]) ? $this->get($this->routes[$route]) : null; // Try without trailing slash @@ -499,7 +497,7 @@ class Pages $pattern = '#^' . str_replace('/', '\/', ltrim($pattern, '^')) . '#'; try { $found = preg_replace($pattern, $replace, $source_url); - if ($found != $source_url) { + if ($found !== $source_url) { $page = $this->dispatch($found, $all); } } catch (ErrorException $e) { @@ -535,7 +533,7 @@ class Pages */ public function blueprints($type) { - if (!isset($this->blueprints)) { + if ($this->blueprints === null) { $this->blueprints = new Blueprints(self::getTypes()); } @@ -970,7 +968,7 @@ class Pages * @throws \RuntimeException * @internal */ - protected function recurse($directory, Page &$parent = null) + protected function recurse($directory, Page $parent = null) { $directory = rtrim($directory, DS); $page = new Page; @@ -1023,7 +1021,7 @@ class Pages if ($found->isDir()) { continue; } - $regex = '/^[^\.]*' . preg_quote($extension) . '$/'; + $regex = '/^[^\.]*' . preg_quote($extension, '/') . '$/'; if (preg_match($regex, $found->getFilename())) { $page_found = $found; $page_extension = $extension; @@ -1053,10 +1051,8 @@ class Pages $name = $file->getFilename(); // Ignore all hidden files if set. - if ($this->ignore_hidden) { - if ($name && $name[0] == '.') { - continue; - } + if ($this->ignore_hidden && $name && $name[0] === '.') { + continue; } if ($file->isFile()) { @@ -1096,7 +1092,7 @@ class Pages } // Override the modified time if modular - if ($page->template() == 'modular') { + if ($page->template() === 'modular') { foreach ($page->collection() as $child) { $modified = $child->modified(); @@ -1174,6 +1170,7 @@ class Pages * @param array $pages * @param string $order_by * @param array $manual + * @param int $sort_flags * * @throws \RuntimeException * @internal @@ -1249,7 +1246,7 @@ class Pages } // handle special case when order_by is random - if ($order_by == 'random') { + if ($order_by === 'random') { $list = $this->arrayShuffle($list); } else { // else just sort the list according to specified key diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 06cf8bc87..cb903fdc5 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -58,8 +58,12 @@ class Uri protected function createFromEnvironment(array $env) { // Build scheme. - $https = isset($env['HTTPS']) ? $env['HTTPS'] : ''; - $this->scheme = (empty($https) || strtolower($https) === 'off') ? 'http' : 'https'; + if (isset($env['REQUEST_SCHEME'])) { + $this->scheme = $env['REQUEST_SCHEME']; + } else { + $https = isset($env['HTTPS']) ? $env['HTTPS'] : ''; + $this->scheme = (empty($https) || strtolower($https) === 'off') ? 'http' : 'https'; + } // Build user and password. $this->user = isset($env['PHP_AUTH_USER']) ? $env['PHP_AUTH_USER'] : null; @@ -79,6 +83,9 @@ class Uri // Build port. $this->port = isset($env['SERVER_PORT']) ? (int)$env['SERVER_PORT'] : null; + if ($this->hasStandardPort()) { + $this->port = null; + } // Build path. $request_uri = isset($env['REQUEST_URI']) ? $env['REQUEST_URI'] : ''; @@ -107,6 +114,16 @@ class Uri $this->reset(); } + /** + * Does this Uri use a standard port? + * + * @return bool + */ + protected function hasStandardPort() + { + return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); + } + /** * @param string $url */ diff --git a/system/src/Grav/Console/Cli/InstallCommand.php b/system/src/Grav/Console/Cli/InstallCommand.php index 461763643..53869fcc7 100644 --- a/system/src/Grav/Console/Cli/InstallCommand.php +++ b/system/src/Grav/Console/Cli/InstallCommand.php @@ -117,7 +117,7 @@ class InstallCommand extends ConsoleCommand $this->destination = rtrim($this->destination, DS); $path = $this->destination . DS . $data['path']; if (!file_exists($path)) { - exec('cd "' . $this->destination . '" && git clone -b ' . $data['branch'] . ' ' . $data['url'] . ' ' . $data['path'], $output, $return); + exec('cd "' . $this->destination . '" && git clone -b ' . $data['branch'] . ' --depth 1 ' . $data['url'] . ' ' . $data['path'], $output, $return); if (!$return) { $this->output->writeln('SUCCESS cloned ' . $data['url'] . ' -> ' . $path . ''); diff --git a/tests/unit/Grav/Common/UriTest.php b/tests/unit/Grav/Common/UriTest.php index 398d12d2f..8ddb03f0b 100644 --- a/tests/unit/Grav/Common/UriTest.php +++ b/tests/unit/Grav/Common/UriTest.php @@ -371,6 +371,52 @@ class UriTest extends \Codeception\TestCase\Test 'extension' => null, 'addNonce' => 'http://localhost:8080/a/b/c/d/e/f/a/b/c/d/e/f/a/b/c/d/e/f/nonce:{{nonce}}', ], + 'http://localhost/this is the path/my page' => [ + 'scheme' => 'http://', + 'user' => null, + 'password' => null, + 'host' => 'localhost', + 'port' => 80, + 'path' => '/this%20is%20the%20path/my%20page', + 'query' => '', + 'fragment' => null, + + 'route' => '/this%20is%20the%20path/my%20page', + 'paths' => ['this%20is%20the%20path', 'my%20page'], + 'params' => null, + 'url' => '/this%20is%20the%20path/my%20page', + 'environment' => 'localhost', + 'basename' => 'my%20page', + 'base' => 'http://localhost', + 'currentPage' => 1, + 'rootUrl' => 'http://localhost', + 'extension' => null, + 'addNonce' => 'http://localhost/this%20is%20the%20path/my%20page/nonce:{{nonce}}', + '__toString' => 'http://localhost/this%20is%20the%20path/my%20page' + ], + 'http://localhost/pölöpölö/päläpälä' => [ + 'scheme' => 'http://', + 'user' => null, + 'password' => null, + 'host' => 'localhost', + 'port' => 80, + 'path' => '/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4', + 'query' => '', + 'fragment' => null, + + 'route' => '/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4', + 'paths' => ['p%C3%B6l%C3%B6p%C3%B6l%C3%B6', 'p%C3%A4l%C3%A4p%C3%A4l%C3%A4'], + 'params' => null, + 'url' => '/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4', + 'environment' => 'localhost', + 'basename' => 'p%C3%A4l%C3%A4p%C3%A4l%C3%A4', + 'base' => 'http://localhost', + 'currentPage' => 1, + 'rootUrl' => 'http://localhost', + 'extension' => null, + 'addNonce' => 'http://localhost/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4/nonce:{{nonce}}', + '__toString' => 'http://localhost/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4' + ], // Query params tests. 'http://localhost:8080/grav/it/ueper?test=x&test2=y' => [ 'scheme' => 'http://',