Commit b4f1b916 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

fix(routes): overwrite RouteCollection to include all routes + update js and php dependencies

parent ef04ce5c
Loading
Loading
Loading
Loading
+0 −40
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@

declare(strict_types=1);

use Config\Services;
use Config\View;
use ViewThemes\Theme;

@@ -49,42 +48,3 @@ if (! function_exists('view')) {
            ->render($name, $options, $saveData);
    }
}

if (! function_exists('lang')) {
    /**
     * A convenience method to translate a string or array of them and format the result with the intl extension's
     * MessageFormatter.
     *
     * Overwritten to include an escape parameter (escaped by default).
     *
     * @param array<int|string, string> $args
     *
     * TODO: remove, and escape args when necessary
     *
     * @return string|string[]
     */
    function lang(string $line, array $args = [], ?string $locale = null, bool $escape = true): string | array
    {
        $language = Services::language();

        // Get active locale
        $activeLocale = $language->getLocale();

        if ($locale && $locale !== $activeLocale) {
            $language->setLocale($locale);
        }

        $line = $language->getLine($line, $args);
        if (! $locale) {
            return $escape ? esc($line) : $line;
        }

        if ($locale === $activeLocale) {
            return $escape ? esc($line) : $line;
        }

        // Reset to active locale
        $language->setLocale($activeLocale);
        return $escape ? esc($line) : $line;
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ declare(strict_types=1);

namespace Config;

use App\Filters\AllowCorsFilter;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar;
@@ -25,6 +26,7 @@ class Filters extends BaseConfig
        'honeypot'      => Honeypot::class,
        'invalidchars'  => InvalidChars::class,
        'secureheaders' => SecureHeaders::class,
        'allow-cors'    => AllowCorsFilter::class,
    ];

    /**
+16 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ namespace Config;

use App\Libraries\Breadcrumb;
use App\Libraries\Negotiate;
use App\Libraries\RouteCollection;
use App\Libraries\Router;
use CodeIgniter\Config\BaseService;
use CodeIgniter\HTTP\Request;
@@ -43,6 +44,21 @@ class Services extends BaseService
        return new Router($routes, $request);
    }

    /**
     * The Routes service is a class that allows for easily building
     * a collection of routes.
     *
     * @return RouteCollection
     */
    public static function routes(bool $getShared = true)
    {
        if ($getShared) {
            return static::getSharedInstance('routes');
        }

        return new RouteCollection(self::locator(), config('Modules'));
    }

    /**
     * The Negotiate class provides the content negotiation features for working the request to determine correct
     * language, encoding, charset, and more.
+137 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

/**
 * This file extends the Router class from the CodeIgniter 4 framework.
 *
 * It introduces the alternate-content option for a route.
 *
 * @copyright  2023 Ad Aures
 * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
 * @link       https://castopod.org/
 */

namespace App\Libraries;

use CodeIgniter\Router\RouteCollection as CodeIgniterRouteCollection;

class RouteCollection extends CodeIgniterRouteCollection
{
    /**
     * Does the heavy lifting of creating an actual route. You must specify
     * the request method(s) that this route will work for. They can be separated
     * by a pipe character "|" if there is more than one.
     *
     * @param array|Closure|string $to
     */
    protected function create(string $verb, string $from, $to, ?array $options = null)
    {
        $overwrite = false;
        $prefix = $this->group === null ? '' : $this->group . '/';

        $from = esc(strip_tags($prefix . $from));

        // While we want to add a route within a group of '/',
        // it doesn't work with matching, so remove them...
        if ($from !== '/') {
            $from = trim($from, '/');
        }

        // When redirecting to named route, $to is an array like `['zombies' => '\Zombies::index']`.
        if (is_array($to) && count($to) === 2) {
            $to = $this->processArrayCallableSyntax($from, $to);
        }

        $options = array_merge($this->currentOptions ?? [], $options ?? []);

        // Route priority detect
        if (isset($options['priority'])) {
            $options['priority'] = abs((int) $options['priority']);

            if ($options['priority'] > 0) {
                $this->prioritizeDetected = true;
            }
        }

        // Hostname limiting?
        if (! empty($options['hostname'])) {
            // @todo determine if there's a way to whitelist hosts?
            if (! $this->checkHostname($options['hostname'])) {
                return;
            }

            $overwrite = true;
        }
        // Limiting to subdomains?
        elseif (! empty($options['subdomain'])) {
            // If we don't match the current subdomain, then
            // we don't need to add the route.
            if (! $this->checkSubdomains($options['subdomain'])) {
                return;
            }

            $overwrite = true;
        }

        // Are we offsetting the binds?
        // If so, take care of them here in one
        // fell swoop.
        if (isset($options['offset']) && is_string($to)) {
            // Get a constant string to work with.
            $to = preg_replace('/(\$\d+)/', '$X', $to);

            for ($i = (int) $options['offset'] + 1; $i < (int) $options['offset'] + 7; $i++) {
                $to = preg_replace_callback('/\$X/', static fn ($m) => '$' . $i, $to, 1);
            }
        }

        // Replace our regex pattern placeholders with the actual thing
        // so that the Router doesn't need to know about any of this.
        foreach ($this->placeholders as $tag => $pattern) {
            $from = str_ireplace(':' . $tag, $pattern, $from);
        }

        // If is redirect, No processing
        if (! isset($options['redirect']) && is_string($to)) {
            // If no namespace found, add the default namespace
            if (strpos($to, '\\') === false || strpos($to, '\\') > 0) {
                $namespace = $options['namespace'] ?? $this->defaultNamespace;
                $to = trim($namespace, '\\') . '\\' . $to;
            }
            // Always ensure that we escape our namespace so we're not pointing to
            // \CodeIgniter\Routes\Controller::method.
            $to = '\\' . ltrim($to, '\\');
        }

        $name = $options['as'] ?? $from;

        helper('array');

        // Don't overwrite any existing 'froms' so that auto-discovered routes
        // do not overwrite any app/Config/Routes settings. The app
        // routes should always be the "source of truth".
        // this works only because discovered routes are added just prior
        // to attempting to route the request.

        // TODO: see how to overwrite routes differently
        // restored change that broke Castopod routing with fediverse
        // in CI4 v4.2.8 https://github.com/codeigniter4/CodeIgniter4/pull/6644
        if (isset($this->routes[$verb][$name]) && ! $overwrite) {
            return;
        }

        $this->routes[$verb][$name] = [
            'route' => [
                $from => $to,
            ],
        ];

        $this->routesOptions[$verb][$from] = $options;

        // Is this a redirect?
        if (isset($options['redirect']) && is_numeric($options['redirect'])) {
            $this->routes['*'][$name]['redirect'] = $options['redirect'];
        }
    }
}
+2 −1
Original line number Diff line number Diff line
import {
  arrow,
  computePosition,
  Coords,
  flip,
  offset,
  Placement,
@@ -46,7 +47,7 @@ const Tooltip = (): void => {
        });

        // Accessing the data
        const { x: arrowX, y: arrowY } = middlewareData.arrow as any;
        const { x: arrowX, y: arrowY } = middlewareData.arrow as Coords;

        const staticSide = {
          top: "bottom",
Loading