X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FApp%2FModule.php;h=5b7c3d15007bb11c995ae001f43027d87c1de708;hb=0e2e488521fbcf2d52dc8037ee6e9dd577fbf14c;hp=726c8c00a0f4862377e2f19679b37f00b53e70c8;hpb=b1ae58cdc578203dedf9377af7c8c6ba225857bc;p=friendica.git diff --git a/src/App/Module.php b/src/App/Module.php index 726c8c00a0..5b7c3d1500 100644 --- a/src/App/Module.php +++ b/src/App/Module.php @@ -1,13 +1,38 @@ . + * + */ namespace Friendica\App; use Friendica\App; -use Friendica\BaseObject; +use Friendica\BaseModule; use Friendica\Core; +use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\LegacyModule; use Friendica\Module\Home; -use Friendica\Module\PageNotFound; +use Friendica\Module\HTTPException\MethodNotAllowed; +use Friendica\Module\HTTPException\PageNotFound; +use Friendica\Network\HTTPException\MethodNotAllowedException; +use Friendica\Network\HTTPException\NoContentException; +use Friendica\Network\HTTPException\NotFoundException; +use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; /** @@ -40,7 +65,6 @@ class Module 'outbox', 'poco', 'post', - 'proxy', 'pubsub', 'pubsubhubbub', 'receive', @@ -56,10 +80,15 @@ class Module private $module; /** - * @var BaseObject The module class + * @var BaseModule The module class */ private $module_class; + /** + * @var array The module parameters + */ + private $module_parameters; + /** * @var bool true, if the module is a backend module */ @@ -86,6 +115,14 @@ class Module return $this->module_class; } + /** + * @return array The module parameters extracted from the route + */ + public function getParameters() + { + return $this->module_parameters; + } + /** * @return bool True, if the current module is a backend module * @see Module::BACKEND_MODULES for a list @@ -95,10 +132,11 @@ class Module return $this->isBackend; } - public function __construct(string $module = self::DEFAULT, string $moduleClass = self::DEFAULT_CLASS, bool $isBackend = false, bool $printNotAllowedAddon = false) + public function __construct(string $module = self::DEFAULT, string $moduleClass = self::DEFAULT_CLASS, array $moduleParameters = [], bool $isBackend = false, bool $printNotAllowedAddon = false) { $this->module = $module; $this->module_class = $moduleClass; + $this->module_parameters = $moduleParameters; $this->isBackend = $isBackend; $this->printNotAllowedAddon = $printNotAllowedAddon; } @@ -126,83 +164,82 @@ class Module $isBackend = in_array($module, Module::BACKEND_MODULES);; - return new Module($module, $this->module_class, $isBackend, $this->printNotAllowedAddon); + return new Module($module, $this->module_class, [], $isBackend, $this->printNotAllowedAddon); } /** * Determine the class of the current module * - * @param Arguments $args The Friendica execution arguments - * @param Router $router The Friendica routing instance - * @param Core\Config\Configuration $config The Friendica Configuration + * @param Arguments $args The Friendica execution arguments + * @param Router $router The Friendica routing instance + * @param IManageConfigValues $config The Friendica Configuration * * @return Module The determined module of this call * - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \Exception */ - public function determineClass(Arguments $args, Router $router, Core\Config\Configuration $config) + public function determineClass(Arguments $args, Router $router, IManageConfigValues $config) { $printNotAllowedAddon = false; + $module_class = null; + $module_parameters = []; /** * ROUTING * * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the * post() and/or content() static methods can be respectively called to produce a data change or an output. **/ - - // First we try explicit routes defined in App\Router - $router->collectRoutes(); - - $data = $router->getRouteCollector(); - Core\Hook::callAll('route_collection', $data); - - $module_class = $router->getModuleClass($args->getCommand()); - - // Then we try addon-provided modules that we wrap in the LegacyModule class - if (!$module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { - //Check if module is an app and if public access to apps is allowed or not - $privateapps = $config->get('config', 'private_addons', false); - if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { - $printNotAllowedAddon = true; - } else { - include_once "addon/{$this->module}/{$this->module}.php"; - if (function_exists($this->module . '_module')) { - LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); - $module_class = LegacyModule::class; + try { + $module_class = $router->getModuleClass($args->getCommand()); + $module_parameters = $router->getModuleParameters(); + } catch (MethodNotAllowedException $e) { + $module_class = MethodNotAllowed::class; + } catch (NotFoundException $e) { + // Then we try addon-provided modules that we wrap in the LegacyModule class + if (Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { + //Check if module is an app and if public access to apps is allowed or not + $privateapps = $config->get('config', 'private_addons', false); + if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { + $printNotAllowedAddon = true; + } else { + include_once "addon/{$this->module}/{$this->module}.php"; + if (function_exists($this->module . '_module')) { + LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); + $module_class = LegacyModule::class; + } } } - } - /* Finally, we look for a 'standard' program module in the 'mod' directory - * We emulate a Module class through the LegacyModule class - */ - if (!$module_class && file_exists("mod/{$this->module}.php")) { - LegacyModule::setModuleFile("mod/{$this->module}.php"); - $module_class = LegacyModule::class; - } + /* Finally, we look for a 'standard' program module in the 'mod' directory + * We emulate a Module class through the LegacyModule class + */ + if (!$module_class && file_exists("mod/{$this->module}.php")) { + LegacyModule::setModuleFile("mod/{$this->module}.php"); + $module_class = LegacyModule::class; + } - $module_class = !isset($module_class) ? PageNotFound::class : $module_class; + $module_class = $module_class ?: PageNotFound::class; + } - return new Module($this->module, $module_class, $this->isBackend, $printNotAllowedAddon); + return new Module($this->module, $module_class, $module_parameters, $this->isBackend, $printNotAllowedAddon); } /** * Run the determined module class and calls all hooks applied to * - * @param Core\L10n\L10n $l10n The L10n instance - * @param App $app The whole Friendica app (for method arguments) - * @param LoggerInterface $logger The Friendica logger - * @param string $currentTheme The chosen theme - * @param array $server The $_SERVER variable - * @param array $post The $_POST variables + * @param \Friendica\Core\L10n $l10n The L10n instance + * @param App\BaseURL $baseUrl The Friendica Base URL + * @param LoggerInterface $logger The Friendica logger + * @param array $server The $_SERVER variable + * @param array $post The $_POST variables * * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function run(Core\L10n\L10n $l10n, App $app, LoggerInterface $logger, string $currentTheme, array $server, array $post) + public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, Profiler $profiler, array $server, array $post) { if ($this->printNotAllowedAddon) { - info($l10n->t("You must be logged in to use addons. ")); + notice($l10n->t("You must be logged in to use addons. ")); } /* The URL provided does not resolve to a valid module. @@ -223,39 +260,76 @@ class Module if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) { $logger->info('index.php: dreamhost_error_hack invoked.', ['Original URI' => $server['REQUEST_URI']]); - $app->internalRedirect($server['REQUEST_URI']); + $baseUrl->redirect($server['REQUEST_URI']); } $logger->debug('index.php: page not found.', ['request_uri' => $server['REQUEST_URI'], 'address' => $server['REMOTE_ADDR'], 'query' => $server['QUERY_STRING']]); } + // @see https://github.com/tootsuite/mastodon/blob/c3aef491d66aec743a3a53e934a494f653745b61/config/initializers/cors.rb + if (substr($_REQUEST['pagename'] ?? '', 0, 12) == '.well-known/') { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Headers: *'); + header('Access-Control-Allow-Methods: ' . Router::GET); + header('Access-Control-Allow-Credentials: false'); + } elseif (substr($_REQUEST['pagename'] ?? '', 0, 8) == 'profile/') { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Headers: *'); + header('Access-Control-Allow-Methods: ' . Router::GET); + header('Access-Control-Allow-Credentials: false'); + } elseif (substr($_REQUEST['pagename'] ?? '', 0, 4) == 'api/') { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Headers: *'); + header('Access-Control-Allow-Methods: ' . implode(',', Router::ALLOWED_METHODS)); + header('Access-Control-Allow-Credentials: false'); + header('Access-Control-Expose-Headers: Link'); + } elseif (substr($_REQUEST['pagename'] ?? '', 0, 11) == 'oauth/token') { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Headers: *'); + header('Access-Control-Allow-Methods: ' . Router::POST); + header('Access-Control-Allow-Credentials: false'); + } + + // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS + // @todo Check allowed methods per requested path + if ($server['REQUEST_METHOD'] === Router::OPTIONS) { + header('Allow: ' . implode(',', Router::ALLOWED_METHODS)); + throw new NoContentException(); + } + $placeholder = ''; + $profiler->set(microtime(true), 'ready'); + $timestamp = microtime(true); + Core\Hook::callAll($this->module . '_mod_init', $placeholder); - call_user_func([$this->module_class, 'init']); + call_user_func([$this->module_class, 'init'], $this->module_parameters); - // "rawContent" is especially meant for technical endpoints. - // This endpoint doesn't need any theme initialization or other comparable stuff. - call_user_func([$this->module_class, 'rawContent']); + $profiler->set(microtime(true) - $timestamp, 'init'); - // Load current theme info after module has been initialized as theme could have been set in module - $theme_info_file = 'view/theme/' . $currentTheme . '/theme.php'; - if (file_exists($theme_info_file)) { - require_once $theme_info_file; + if ($server['REQUEST_METHOD'] === Router::DELETE) { + call_user_func([$this->module_class, 'delete'], $this->module_parameters); } - if (function_exists(str_replace('-', '_', $currentTheme) . '_init')) { - $func = str_replace('-', '_', $currentTheme) . '_init'; - $func($app); + if ($server['REQUEST_METHOD'] === Router::PATCH) { + call_user_func([$this->module_class, 'patch'], $this->module_parameters); } - if ($server['REQUEST_METHOD'] === 'POST') { + if ($server['REQUEST_METHOD'] === Router::POST) { Core\Hook::callAll($this->module . '_mod_post', $post); - call_user_func([$this->module_class, 'post']); + call_user_func([$this->module_class, 'post'], $this->module_parameters); + } + + if ($server['REQUEST_METHOD'] === Router::PUT) { + call_user_func([$this->module_class, 'put'], $this->module_parameters); } Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder); - call_user_func([$this->module_class, 'afterpost']); + call_user_func([$this->module_class, 'afterpost'], $this->module_parameters); + + // "rawContent" is especially meant for technical endpoints. + // This endpoint doesn't need any theme initialization or other comparable stuff. + call_user_func([$this->module_class, 'rawContent'], $this->module_parameters); } }