3 namespace Friendica\App;
6 use Friendica\BaseObject;
8 use Friendica\LegacyModule;
9 use Friendica\Module\Home;
10 use Friendica\Module\HTTPException\MethodNotAllowed;
11 use Friendica\Module\HTTPException\PageNotFound;
12 use Friendica\Network\HTTPException\MethodNotAllowedException;
13 use Friendica\Network\HTTPException\NotFoundException;
14 use Psr\Log\LoggerInterface;
17 * Holds the common context of the current, loaded module
21 const DEFAULT = 'home';
22 const DEFAULT_CLASS = Home::class;
24 * A list of modules, which are backend methods
28 const BACKEND_MODULES = [
57 * @var string The module name
62 * @var BaseObject The module class
64 private $module_class;
67 * @var array The module parameters
69 private $module_parameters;
72 * @var bool true, if the module is a backend module
77 * @var bool true, if the loaded addon is private, so we have to print out not allowed
79 private $printNotAllowedAddon;
84 public function getName()
90 * @return string The base class name
92 public function getClassName()
94 return $this->module_class;
98 * @return bool True, if the current module is a backend module
99 * @see Module::BACKEND_MODULES for a list
101 public function isBackend()
103 return $this->isBackend;
106 public function __construct(string $module = self::DEFAULT, string $moduleClass = self::DEFAULT_CLASS, array $moduleParameters = [], bool $isBackend = false, bool $printNotAllowedAddon = false)
108 $this->module = $module;
109 $this->module_class = $moduleClass;
110 $this->module_parameters = $moduleParameters;
111 $this->isBackend = $isBackend;
112 $this->printNotAllowedAddon = $printNotAllowedAddon;
116 * Determines the current module based on the App arguments and the server variable
118 * @param Arguments $args The Friendica arguments
120 * @return Module The module with the determined module
122 public function determineModule(Arguments $args)
124 if ($args->getArgc() > 0) {
125 $module = str_replace('.', '_', $args->get(0));
126 $module = str_replace('-', '_', $module);
128 $module = self::DEFAULT;
131 // Compatibility with the Firefox App
132 if (($module == "users") && ($args->getCommand() == "users/sign_in")) {
136 $isBackend = in_array($module, Module::BACKEND_MODULES);;
138 return new Module($module, $this->module_class, [], $isBackend, $this->printNotAllowedAddon);
142 * Determine the class of the current module
144 * @param Arguments $args The Friendica execution arguments
145 * @param Router $router The Friendica routing instance
146 * @param Core\Config\Configuration $config The Friendica Configuration
148 * @return Module The determined module of this call
152 public function determineClass(Arguments $args, Router $router, Core\Config\Configuration $config)
154 $printNotAllowedAddon = false;
156 $module_class = null;
157 $module_parameters = [];
161 * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the
162 * post() and/or content() static methods can be respectively called to produce a data change or an output.
165 $module_class = $router->getModuleClass($args->getCommand());
166 $module_parameters = $router->getModuleParameters();
167 } catch (MethodNotAllowedException $e) {
168 $module_class = MethodNotAllowed::class;
169 } catch (NotFoundException $e) {
170 // Then we try addon-provided modules that we wrap in the LegacyModule class
171 if (Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) {
172 //Check if module is an app and if public access to apps is allowed or not
173 $privateapps = $config->get('config', 'private_addons', false);
174 if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) {
175 $printNotAllowedAddon = true;
177 include_once "addon/{$this->module}/{$this->module}.php";
178 if (function_exists($this->module . '_module')) {
179 LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php");
180 $module_class = LegacyModule::class;
185 /* Finally, we look for a 'standard' program module in the 'mod' directory
186 * We emulate a Module class through the LegacyModule class
188 if (!$module_class && file_exists("mod/{$this->module}.php")) {
189 LegacyModule::setModuleFile("mod/{$this->module}.php");
190 $module_class = LegacyModule::class;
193 $module_class = $module_class ?: PageNotFound::class;
196 return new Module($this->module, $module_class, $module_parameters, $this->isBackend, $printNotAllowedAddon);
200 * Run the determined module class and calls all hooks applied to
202 * @param Core\L10n\L10n $l10n The L10n instance
203 * @param App $app The whole Friendica app (for method arguments)
204 * @param LoggerInterface $logger The Friendica logger
205 * @param array $server The $_SERVER variable
206 * @param array $post The $_POST variables
208 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
210 public function run(Core\L10n\L10n $l10n, App $app, LoggerInterface $logger, array $server, array $post)
212 if ($this->printNotAllowedAddon) {
213 info($l10n->t("You must be logged in to use addons. "));
216 /* The URL provided does not resolve to a valid module.
218 * On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'.
219 * We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic -
220 * we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page
221 * this will often succeed and eventually do the right thing.
223 * Otherwise we are going to emit a 404 not found.
225 if ($this->module_class === PageNotFound::class) {
226 $queryString = $server['QUERY_STRING'];
227 // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
228 if (!empty($queryString) && preg_match('/{[0-9]}/', $queryString) !== 0) {
232 if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
233 $logger->info('index.php: dreamhost_error_hack invoked.', ['Original URI' => $server['REQUEST_URI']]);
234 $app->internalRedirect($server['REQUEST_URI']);
237 $logger->debug('index.php: page not found.', ['request_uri' => $server['REQUEST_URI'], 'address' => $server['REMOTE_ADDR'], 'query' => $server['QUERY_STRING']]);
242 Core\Hook::callAll($this->module . '_mod_init', $placeholder);
244 call_user_func([$this->module_class, 'init'], $this->module_parameters);
246 // "rawContent" is especially meant for technical endpoints.
247 // This endpoint doesn't need any theme initialization or other comparable stuff.
248 call_user_func([$this->module_class, 'rawContent'], $this->module_parameters);
250 if ($server['REQUEST_METHOD'] === 'POST') {
251 Core\Hook::callAll($this->module . '_mod_post', $post);
252 call_user_func([$this->module_class, 'post'], $this->module_parameters);
255 Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
256 call_user_func([$this->module_class, 'afterpost'], $this->module_parameters);