<commit_before><?php
/**
 * @package    Grav.Common.Page
 *
 * @copyright  Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
 * @license    MIT License; see LICENSE file for details.
 */

namespace Grav\Common\Page;

use Grav\Common\Cache;
use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Blueprints;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Taxonomy;
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;

class Pages
{
    /**
     * @var Grav
     */
    protected $grav;

    /**
     * @var array|Page[]
     */
    protected $instances;

    /**
     * @var array|string[]
     */
    protected $children;

    /**
     * @var string
     */
    protected $base;

    /**
     * @var array|string[]
     */
    protected $routes = [];

    /**
     * @var array
     */
    protected $sort;

    /**
     * @var Blueprints
     */
    protected $blueprints;

    /**
     * @var int
     */
    protected $last_modified;

    /**
     * @var array|string[]
     */
    protected $ignore_files;

    /**
     * @var array|string[]
     */
    protected $ignore_folders;

    /**
     * @var bool
     */
    protected $ignore_hidden;

    /**
     * @var Types
     */
    static protected $types;

    /**
     * @var string
     */
    static protected $home_route;

    protected $pages_cache_id;

    /**
     * Constructor
     *
     * @param Grav $c
     */
    public function __construct(Grav $c)
    {
        $this->grav = $c;
        $this->base = '';
    }

    /**
     * Get or set base path for the pages.
     *
     * @param  string $path
     *
     * @return string
     */
    public function base($path = null)
    {
        if ($path !== null) {
            $path = trim($path, '/');
            $this->base = $path ? '/' . $path : null;
        }

        return $this->base;
    }

    /**
     * Class initialization. Must be called before using this class.
     */
    public function init()
    {
        $config = $this->grav['config'];
        $this->ignore_files = $config->get('system.pages.ignore_files');
        $this->ignore_folders = $config->get('system.pages.ignore_folders');
        $this->ignore_hidden = $config->get('system.pages.ignore_hidden');

        $this->instances = [];
        $this->children = [];
        $this->routes = [];

        $this->buildPages();
    }

    /**
     * Get or set last modification time.
     *
     * @param int $modified
     *
     * @return int|null
     */
    public function lastModified($modified = null)
    {
        if ($modified && $modified > $this->last_modified) {
            $this->last_modified = $modified;
        }

        return $this->last_modified;
    }

    /**
     * Returns a list of all pages.
     *
     * @return array|Page[]
     */
    public function instances()
    {
        return $this->instances;
    }

    /**
     * Returns a list of all routes.
     *
     * @return array
     */
    public function routes()
    {
        return $this->routes;
    }

    /**
     * Adds a page and assigns a route to it.
     *
     * @param Page   $page  Page to be added.
     * @param string $route Optional route (uses route from the object if not set).
     */
    public function addPage(Page $page, $route = null)
    {
        if (!isset($this->instances[$page->path()])) {
            $this->instances[$page->path()] = $page;
        }
        $route = $page->route($route);
        if ($page->parent()) {
            $this->children[$page->parent()->path()][$page->path()] = ['slug' => $page->slug()];
        }
        $this->routes[$route] = $page->path();
    }

    /**
     * Sort sub-pages in a page.
     *
     * @param Page   $page
     * @param string $order_by
     * @param string $order_dir
     *
     * @return array
     */
    public function sort(Page $page, $order_by = null, $order_dir = null, $sort_flags = null)
    {
        if ($order_by === null) {
            $order_by = $page->orderBy();
        }
        if ($order_dir === null) {
            $order_dir = $page->orderDir();
        }

        $path = $page->path();
        $children = isset($this->children[$path]) ? $this->children[$path] : [];

        if (!$children) {
            return $children;
        }

        if (!isset($this->sort[$path][$order_by])) {
            $this->buildSort($path, $children, $order_by, $page->orderManual(), $sort_flags);
        }

        $sort = $this->sort[$path][$order_by];

        if ($order_dir != 'asc') {
            $sort = array_reverse($sort);
        }

        return $sort;
    }

    /**
     * @param Collection $collection
     * @param            $orderBy
     * @param string     $orderDir
     * @param null       $orderManual
     *
     * @return array
     * @internal
     */
    public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null, $sort_flags = null)
    {
        $items = $collection->toArray();
        if (!$items) {
            return [];
        }

        $lookup = md5(json_encode($items) . json_encode($orderManual) . $orderBy . $orderDir);
        if (!isset($this->sort[$lookup][$orderBy])) {
            $this->buildSort($lookup, $items, $orderBy, $orderManual, $sort_flags);
        }

        $sort = $this->sort[$lookup][$orderBy];

        if ($orderDir != 'asc') {
            $sort = array_reverse($sort);
        }

        return $sort;

    }

    /**
     * Get a page instance.
     *
     * @param  string $path The filesystem full path of the page
     *
     * @return Page
     * @throws \Exception
     */
    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;
    }

    /**
     * Get children of the path.
     *
     * @param string $path
     *
     * @return Collection
     */
    public function children($path)
    {
        $children = isset($this->children[(string)$path]) ? $this->children[(string)$path] : [];

        return new Collection($children, [], $this);
    }

    /**
     * alias method to return find a page.
     *
     * @param string $url The relative URL of the page
     * @param bool   $all
     *
     * @return Page|null
     */
    public function find($url, $all = false)
    {
        return $this->dispatch($url, $all, false);
    }

    /**
     * Dispatch URI to a page.
     *
     * @param string $url The relative URL of the page
     * @param bool $all
     *
     * @param bool $redirect
     * @return Page|null
     * @throws \Exception
     */
    public function dispatch($url, $all = false, $redirect = true)
    {
        // Fetch page if there's a defined route to it.
        $page = isset($this->routes[$url]) ? $this->get($this->routes[$url]) : null;
        // Try without trailing slash
        if (!$page && Utils::endsWith($url, '/')) {
            $page = isset($this->routes[rtrim($url, '/')]) ? $this->get($this->routes[rtrim($url, '/')]) : null;
        }

        // Are we in the admin? this is important!
        $not_admin = !isset($this->grav['admin']);

        // If the page cannot be reached, look into site wide redirects, routes + wildcards
        if (!$all && $not_admin) {

            // If the page is a simple redirect, just do it.
            if ($redirect && $page && $page->redirect()) {
                $this->grav->redirectLangSafe($page->redirect());
            }

            // fall back and check site based redirects
            if (!$page || ($page && !$page->routable())) {
                /** @var Config $config */
                $config = $this->grav['config'];

                // See if route matches one in the site configuration
                $route = $config->get("site.routes.{$url}");
                if ($route) {
                    $page = $this->dispatch($route, $all);
                } else {
                    // Try Regex style redirects
                    $source_url = $url;
                    $extension = $this->grav['uri']->extension();
                    if (isset($extension)) {
                        $source_url.= '.' . $extension;
                    }

                    $site_redirects = $config->get("site.redirects");
                    if (is_array($site_redirects)) {
                        foreach ((array)$site_redirects as $pattern => $replace) {
                            $pattern = '#' . $pattern . '#';
                            try {
                                $found = preg_replace($pattern, $replace, $source_url);
                                if ($found != $source_url) {
                                    $this->grav->redirectLangSafe($found);
                                }
                            } catch (ErrorException $e) {<commit_msg>Fix for double extension during some redirects #1307<commit_after><?php
/**
 * @package    Grav.Common.Page
 *
 * @copyright  Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
 * @license    MIT License; see LICENSE file for details.
 */

namespace Grav\Common\Page;

use Grav\Common\Cache;
use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Blueprints;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Taxonomy;
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;

class Pages
{
    /**
     * @var Grav
     */
    protected $grav;

    /**
     * @var array|Page[]
     */
    protected $instances;

    /**
     * @var array|string[]
     */
    protected $children;

    /**
     * @var string
     */
    protected $base;

    /**
     * @var array|string[]
     */
    protected $routes = [];

    /**
     * @var array
     */
    protected $sort;

    /**
     * @var Blueprints
     */
    protected $blueprints;

    /**
     * @var int
     */
    protected $last_modified;

    /**
     * @var array|string[]
     */
    protected $ignore_files;

    /**
     * @var array|string[]
     */
    protected $ignore_folders;

    /**
     * @var bool
     */
    protected $ignore_hidden;

    /**
     * @var Types
     */
    static protected $types;

    /**
     * @var string
     */
    static protected $home_route;

    protected $pages_cache_id;

    /**
     * Constructor
     *
     * @param Grav $c
     */
    public function __construct(Grav $c)
    {
        $this->grav = $c;
        $this->base = '';
    }

    /**
     * Get or set base path for the pages.
     *
     * @param  string $path
     *
     * @return string
     */
    public function base($path = null)
    {
        if ($path !== null) {
            $path = trim($path, '/');
            $this->base = $path ? '/' . $path : null;
        }

        return $this->base;
    }

    /**
     * Class initialization. Must be called before using this class.
     */
    public function init()
    {
        $config = $this->grav['config'];
        $this->ignore_files = $config->get('system.pages.ignore_files');
        $this->ignore_folders = $config->get('system.pages.ignore_folders');
        $this->ignore_hidden = $config->get('system.pages.ignore_hidden');

        $this->instances = [];
        $this->children = [];
        $this->routes = [];

        $this->buildPages();
    }

    /**
     * Get or set last modification time.
     *
     * @param int $modified
     *
     * @return int|null
     */
    public function lastModified($modified = null)
    {
        if ($modified && $modified > $this->last_modified) {
            $this->last_modified = $modified;
        }

        return $this->last_modified;
    }

    /**
     * Returns a list of all pages.
     *
     * @return array|Page[]
     */
    public function instances()
    {
        return $this->instances;
    }

    /**
     * Returns a list of all routes.
     *
     * @return array
     */
    public function routes()
    {
        return $this->routes;
    }

    /**
     * Adds a page and assigns a route to it.
     *
     * @param Page   $page  Page to be added.
     * @param string $route Optional route (uses route from the object if not set).
     */
    public function addPage(Page $page, $route = null)
    {
        if (!isset($this->instances[$page->path()])) {
            $this->instances[$page->path()] = $page;
        }
        $route = $page->route($route);
        if ($page->parent()) {
            $this->children[$page->parent()->path()][$page->path()] = ['slug' => $page->slug()];
        }
        $this->routes[$route] = $page->path();
    }

    /**
     * Sort sub-pages in a page.
     *
     * @param Page   $page
     * @param string $order_by
     * @param string $order_dir
     *
     * @return array
     */
    public function sort(Page $page, $order_by = null, $order_dir = null, $sort_flags = null)
    {
        if ($order_by === null) {
            $order_by = $page->orderBy();
        }
        if ($order_dir === null) {
            $order_dir = $page->orderDir();
        }

        $path = $page->path();
        $children = isset($this->children[$path]) ? $this->children[$path] : [];

        if (!$children) {
            return $children;
        }

        if (!isset($this->sort[$path][$order_by])) {
            $this->buildSort($path, $children, $order_by, $page->orderManual(), $sort_flags);
        }

        $sort = $this->sort[$path][$order_by];

        if ($order_dir != 'asc') {
            $sort = array_reverse($sort);
        }

        return $sort;
    }

    /**
     * @param Collection $collection
     * @param            $orderBy
     * @param string     $orderDir
     * @param null       $orderManual
     *
     * @return array
     * @internal
     */
    public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null, $sort_flags = null)
    {
        $items = $collection->toArray();
        if (!$items) {
            return [];
        }

        $lookup = md5(json_encode($items) . json_encode($orderManual) . $orderBy . $orderDir);
        if (!isset($this->sort[$lookup][$orderBy])) {
            $this->buildSort($lookup, $items, $orderBy, $orderManual, $sort_flags);
        }

        $sort = $this->sort[$lookup][$orderBy];

        if ($orderDir != 'asc') {
            $sort = array_reverse($sort);
        }

        return $sort;

    }

    /**
     * Get a page instance.
     *
     * @param  string $path The filesystem full path of the page
     *
     * @return Page
     * @throws \Exception
     */
    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;
    }

    /**
     * Get children of the path.
     *
     * @param string $path
     *
     * @return Collection
     */
    public function children($path)
    {
        $children = isset($this->children[(string)$path]) ? $this->children[(string)$path] : [];

        return new Collection($children, [], $this);
    }

    /**
     * alias method to return find a page.
     *
     * @param string $url The relative URL of the page
     * @param bool   $all
     *
     * @return Page|null
     */
    public function find($url, $all = false)
    {
        return $this->dispatch($url, $all, false);
    }

    /**
     * Dispatch URI to a page.
     *
     * @param string $url The relative URL of the page
     * @param bool $all
     *
     * @param bool $redirect
     * @return Page|null
     * @throws \Exception
     */
    public function dispatch($url, $all = false, $redirect = true)
    {
        // Fetch page if there's a defined route to it.
        $page = isset($this->routes[$url]) ? $this->get($this->routes[$url]) : null;
        // Try without trailing slash
        if (!$page && Utils::endsWith($url, '/')) {
            $page = isset($this->routes[rtrim($url, '/')]) ? $this->get($this->routes[rtrim($url, '/')]) : null;
        }

        // Are we in the admin? this is important!
        $not_admin = !isset($this->grav['admin']);

        // If the page cannot be reached, look into site wide redirects, routes + wildcards
        if (!$all && $not_admin) {

            // If the page is a simple redirect, just do it.
            if ($redirect && $page && $page->redirect()) {
                $this->grav->redirectLangSafe($page->redirect());
            }

            // fall back and check site based redirects
            if (!$page || ($page && !$page->routable())) {
                /** @var Config $config */
                $config = $this->grav['config'];

                // See if route matches one in the site configuration
                $route = $config->get("site.routes.{$url}");
                if ($route) {
                    $page = $this->dispatch($route, $all);
                } else {
                    // Try Regex style redirects
                    $uri = $this->grav['uri'];
                    $source_url = $url;
                    $extension = $uri->extension();
                    if (isset($extension) && !Utils::endsWith($uri->url(), $extension)) {
                        $source_url.= '.' . $extension;
                    }

                    $site_redirects = $config->get("site.redirects");
                    if (is_array($site_redirects)) {
                        foreach ((array)$site_redirects as $pattern => $replace) {
                            $pattern = '#' . $pattern . '#';
                            try {
                                $found = preg_replace($pattern, $replace, $source_url);
                                if ($found != $source_url) {
                                    $this->grav->redirectLangSafe($found);
                                }
                            } catch (ErrorException $e) {
{'fraction_non_alphanumeric': 0.15369109234540998, 'fraction_numerical': 0.0011985834922364478, 'mean_word_length': 1.7093726937269373, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 152, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6799710', 'n_tokens_mistral': 5493, 'n_tokens_neox': 5335, 'n_words': 1269}