PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.1.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.1.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / FrontController.php
matomo / app / core Last commit date
API 6 years ago Access 6 years ago Application 6 years ago Archive 6 years ago ArchiveProcessor 6 years ago Archiver 6 years ago AssetManager 6 years ago Auth 6 years ago Category 6 years ago CliMulti 6 years ago Columns 6 years ago Composer 6 years ago Concurrency 6 years ago Config 6 years ago Container 6 years ago CronArchive 6 years ago DataAccess 6 years ago DataFiles 6 years ago DataTable 6 years ago Db 6 years ago DeviceDetector 6 years ago Email 6 years ago Exception 6 years ago Http 6 years ago Intl 6 years ago Mail 6 years ago Measurable 6 years ago Menu 6 years ago Metrics 6 years ago Notification 6 years ago Period 6 years ago Plugin 6 years ago ProfessionalServices 6 years ago Report 6 years ago ReportRenderer 6 years ago Scheduler 6 years ago Segment 6 years ago Session 6 years ago Settings 6 years ago Tracker 6 years ago Translation 6 years ago UpdateCheck 6 years ago Updater 6 years ago Updates 6 years ago Validators 6 years ago View 6 years ago ViewDataTable 6 years ago Visualization 6 years ago Widget 6 years ago .htaccess 6 years ago Access.php 6 years ago Archive.php 6 years ago ArchiveProcessor.php 6 years ago AssetManager.php 6 years ago Auth.php 6 years ago BaseFactory.php 6 years ago Cache.php 6 years ago CacheId.php 6 years ago CliMulti.php 6 years ago Common.php 6 years ago Config.php 6 years ago Console.php 6 years ago Context.php 6 years ago Cookie.php 6 years ago CronArchive.php 6 years ago DataArray.php 6 years ago DataTable.php 6 years ago Date.php 6 years ago Db.php 6 years ago DbHelper.php 6 years ago Development.php 6 years ago DeviceDetectorFactory.php 6 years ago ErrorHandler.php 6 years ago EventDispatcher.php 6 years ago ExceptionHandler.php 6 years ago FileIntegrity.php 6 years ago Filechecks.php 6 years ago Filesystem.php 6 years ago FrontController.php 6 years ago Http.php 6 years ago IP.php 6 years ago Log.php 6 years ago LogDeleter.php 6 years ago Mail.php 6 years ago Metrics.php 6 years ago MetricsFormatter.php 6 years ago Nonce.php 6 years ago Notification.php 6 years ago NumberFormatter.php 6 years ago Option.php 6 years ago Period.php 6 years ago Piwik.php 6 years ago Plugin.php 6 years ago Profiler.php 6 years ago ProxyHeaders.php 6 years ago ProxyHttp.php 6 years ago QuickForm2.php 6 years ago RankingQuery.php 6 years ago Registry.php 6 years ago ReportRenderer.php 6 years ago ScheduledTask.php 6 years ago Segment.php 6 years ago Sequence.php 6 years ago Session.php 6 years ago SettingsPiwik.php 6 years ago SettingsServer.php 6 years ago Singleton.php 6 years ago Site.php 6 years ago TCPDF.php 6 years ago TaskScheduler.php 6 years ago Theme.php 6 years ago Timer.php 6 years ago Tracker.php 6 years ago Translate.php 6 years ago Twig.php 6 years ago Unzip.php 6 years ago UpdateCheck.php 6 years ago Updater.php 6 years ago Updates.php 6 years ago Url.php 6 years ago UrlHelper.php 6 years ago Version.php 6 years ago View.php 6 years ago bootstrap.php 6 years ago dispatch.php 6 years ago testMinimumPhpVersion.php 6 years ago
FrontController.php
732 lines
1 <?php
2 /**
3 * Piwik - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9
10 namespace Piwik;
11
12 use Exception;
13 use Piwik\API\Request;
14 use Piwik\Container\StaticContainer;
15 use Piwik\Exception\AuthenticationFailedException;
16 use Piwik\Exception\DatabaseSchemaIsNewerThanCodebaseException;
17 use Piwik\Exception\PluginDeactivatedException;
18 use Piwik\Exception\PluginRequiresInternetException;
19 use Piwik\Exception\StylesheetLessCompileException;
20 use Piwik\Http\ControllerResolver;
21 use Piwik\Http\Router;
22 use Piwik\Plugins\CoreAdminHome\CustomLogo;
23 use Piwik\Session\SessionAuth;
24 use Psr\Log\LoggerInterface;
25
26 /**
27 * This singleton dispatches requests to the appropriate plugin Controller.
28 *
29 * Piwik uses this class for all requests that go through **index.php**. Plugins can
30 * use it to call controller actions of other plugins.
31 *
32 * ### Examples
33 *
34 * **Forwarding controller requests**
35 *
36 * public function myConfiguredRealtimeMap()
37 * {
38 * $_GET['changeVisitAlpha'] = false;
39 * $_GET['removeOldVisits'] = false;
40 * $_GET['showFooterMessage'] = false;
41 * return FrontController::getInstance()->dispatch('UserCountryMap', 'realtimeMap');
42 * }
43 *
44 * **Using other plugin controller actions**
45 *
46 * public function myPopupWithRealtimeMap()
47 * {
48 * $_GET['changeVisitAlpha'] = false;
49 * $_GET['removeOldVisits'] = false;
50 * $_GET['showFooterMessage'] = false;
51 * $realtimeMap = FrontController::getInstance()->dispatch('UserCountryMap', 'realtimeMap');
52 *
53 * $view = new View('@MyPlugin/myPopupWithRealtimeMap.twig');
54 * $view->realtimeMap = $realtimeMap;
55 * return $realtimeMap->render();
56 * }
57 *
58 * For a detailed explanation, see the documentation [here](https://developer.piwik.org/guides/how-piwik-works).
59 *
60 * @method static \Piwik\FrontController getInstance()
61 */
62 class FrontController extends Singleton
63 {
64 const DEFAULT_MODULE = 'CoreHome';
65 const DEFAULT_LOGIN = 'anonymous';
66 const DEFAULT_TOKEN_AUTH = 'anonymous';
67
68 // public for tests
69 public static $requestId = null;
70
71 /**
72 * Set to false and the Front Controller will not dispatch the request
73 *
74 * @var bool
75 */
76 public static $enableDispatch = true;
77
78 /**
79 * @var bool
80 */
81 private $initialized = false;
82
83 /**
84 * @param $lastError
85 * @return string
86 * @throws AuthenticationFailedException
87 * @throws Exception
88 */
89 private static function generateSafeModeOutputFromError($lastError)
90 {
91 Common::sendResponseCode(500);
92
93 $controller = FrontController::getInstance();
94 try {
95 $controller->init();
96 $message = $controller->dispatch('CorePluginsAdmin', 'safemode', array($lastError));
97 } catch(Exception $e) {
98 // may fail in safe mode (eg. global.ini.php not found)
99 $message = sprintf("Matomo encountered an error: %s (which lead to: %s)", $lastError['message'], $e->getMessage());
100 }
101
102 return $message;
103 }
104
105 /**
106 * @param Exception $e
107 * @return string
108 */
109 public static function generateSafeModeOutputFromException($e)
110 {
111 StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', [
112 'exception' => $e,
113 'ignoreInScreenWriter' => true,
114 ]);
115
116 $error = array(
117 'message' => $e->getMessage(),
118 'file' => $e->getFile(),
119 'line' => $e->getLine(),
120 );
121
122 if (isset(self::$requestId)) {
123 $error['request_id'] = self::$requestId;
124 }
125
126 $error['backtrace'] = ' on ' . $error['file'] . '(' . $error['line'] . ")\n";
127 $error['backtrace'] .= $e->getTraceAsString();
128
129 $exception = $e;
130 while ($exception = $exception->getPrevious()) {
131 $error['backtrace'] .= "\ncaused by: " . $exception->getMessage();
132 $error['backtrace'] .= ' on ' . $exception->getFile() . '(' . $exception->getLine() . ")\n";
133 $error['backtrace'] .= $exception->getTraceAsString();
134 }
135
136 return self::generateSafeModeOutputFromError($error);
137 }
138
139 /**
140 * Executes the requested plugin controller method.
141 *
142 * @throws Exception|\Piwik\Exception\PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist,
143 * there is not enough permission, etc.
144 *
145 * @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
146 * @param string $action The controller method name, eg, `'realtimeMap'`.
147 * @param array $parameters Array of parameters to pass to the controller method.
148 * @return void|mixed The returned value of the call. This is the output of the controller method.
149 * @api
150 */
151 public function dispatch($module = null, $action = null, $parameters = null)
152 {
153 if (self::$enableDispatch === false) {
154 return;
155 }
156
157 $filter = new Router();
158 $redirection = $filter->filterUrl(Url::getCurrentUrl());
159 if ($redirection !== null) {
160 Url::redirectToUrl($redirection);
161 return;
162 }
163
164 try {
165 $result = $this->doDispatch($module, $action, $parameters);
166 return $result;
167 } catch (NoAccessException $exception) {
168 Log::debug($exception);
169
170 /**
171 * Triggered when a user with insufficient access permissions tries to view some resource.
172 *
173 * This event can be used to customize the error that occurs when a user is denied access
174 * (for example, displaying an error message, redirecting to a page other than login, etc.).
175 *
176 * @param \Piwik\NoAccessException $exception The exception that was caught.
177 */
178 Piwik::postEvent('User.isNotAuthorized', array($exception), $pending = true);
179 } catch (\Twig_Error_Runtime $e) {
180 echo $this->generateSafeModeOutputFromException($e);
181 exit;
182 } catch(StylesheetLessCompileException $e) {
183 echo $this->generateSafeModeOutputFromException($e);
184 exit;
185 } catch(\Error $e) {
186 echo $this->generateSafeModeOutputFromException($e);
187 exit;
188 }
189 }
190
191 /**
192 * Executes the requested plugin controller method and returns the data, capturing anything the
193 * method `echo`s.
194 *
195 * _Note: If the plugin controller returns something, the return value is returned instead
196 * of whatever is in the output buffer._
197 *
198 * @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
199 * @param string $actionName The controller action name, eg, `'realtimeMap'`.
200 * @param array $parameters Array of parameters to pass to the controller action method.
201 * @return string The `echo`'d data or the return value of the controller action.
202 * @deprecated
203 */
204 public function fetchDispatch($module = null, $actionName = null, $parameters = null)
205 {
206 ob_start();
207 $output = $this->dispatch($module, $actionName, $parameters);
208 // if nothing returned we try to load something that was printed on the screen
209 if (empty($output)) {
210 $output = ob_get_contents();
211 } else {
212 // if something was returned, flush output buffer as it is meant to be written to the screen
213 ob_flush();
214 }
215 ob_end_clean();
216 return $output;
217 }
218
219 /**
220 * Called at the end of the page generation
221 */
222 public function __destruct()
223 {
224 try {
225 if (class_exists('Piwik\\Profiler')
226 && !SettingsServer::isTrackerApiRequest()
227 ) {
228 // in tracker mode Piwik\Tracker\Db\Pdo\Mysql does currently not implement profiling
229 Profiler::displayDbProfileReport();
230 Profiler::printQueryCount();
231 }
232 } catch (Exception $e) {
233 Log::debug($e);
234 }
235 }
236
237 // Should we show exceptions messages directly rather than display an html error page?
238 public static function shouldRethrowException()
239 {
240 // If we are in no dispatch mode, eg. a script reusing Piwik libs,
241 // then we should return the exception directly, rather than trigger the event "bad config file"
242 // which load the HTML page of the installer with the error.
243 return (defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH)
244 || Common::isPhpCliMode()
245 || SettingsServer::isArchivePhpTriggered();
246 }
247
248 public static function setUpSafeMode()
249 {
250 register_shutdown_function(array('\\Piwik\\FrontController', 'triggerSafeModeWhenError'));
251 }
252
253 public static function triggerSafeModeWhenError()
254 {
255 $lastError = error_get_last();
256
257 if (!empty($lastError) && isset(self::$requestId)) {
258 $lastError['request_id'] = self::$requestId;
259 }
260
261 if (!empty($lastError) && $lastError['type'] == E_ERROR) {
262 $lastError['backtrace'] = ' on ' . $lastError['file'] . '(' . $lastError['line'] . ")\n"
263 . ErrorHandler::getFatalErrorPartialBacktrace();
264
265 StaticContainer::get(LoggerInterface::class)->error('Fatal error encountered: {exception}', [
266 'exception' => $lastError,
267 'ignoreInScreenWriter' => true,
268 ]);
269
270 $message = self::generateSafeModeOutputFromError($lastError);
271 echo $message;
272 }
273 }
274
275 /**
276 * Must be called before dispatch()
277 * - checks that directories are writable,
278 * - loads the configuration file,
279 * - loads the plugin,
280 * - inits the DB connection,
281 * - etc.
282 *
283 * @throws Exception
284 * @return void
285 */
286 public function init()
287 {
288 if ($this->initialized) {
289 return;
290 }
291
292 self::setRequestIdHeader();
293
294 $this->initialized = true;
295
296 $tmpPath = StaticContainer::get('path.tmp');
297
298 $directoriesToCheck = array(
299 $tmpPath,
300 $tmpPath . '/assets/',
301 $tmpPath . '/cache/',
302 $tmpPath . '/logs/',
303 $tmpPath . '/tcpdf/',
304 $tmpPath . '/templates_c/',
305 );
306
307 Filechecks::dieIfDirectoriesNotWritable($directoriesToCheck);
308
309 $this->handleMaintenanceMode();
310 $this->handleProfiler();
311 $this->handleSSLRedirection();
312
313 Plugin\Manager::getInstance()->loadPluginTranslations();
314 Plugin\Manager::getInstance()->loadActivatedPlugins();
315
316 // try to connect to the database
317 try {
318 Db::createDatabaseObject();
319 Db::fetchAll("SELECT DATABASE()");
320 } catch (Exception $exception) {
321 if (self::shouldRethrowException()) {
322 throw $exception;
323 }
324
325 Log::debug($exception);
326
327 /**
328 * Triggered when Piwik cannot connect to the database.
329 *
330 * This event can be used to start the installation process or to display a custom error
331 * message.
332 *
333 * @param Exception $exception The exception thrown from creating and testing the database
334 * connection.
335 */
336 Piwik::postEvent('Db.cannotConnectToDb', array($exception), $pending = true);
337
338 throw $exception;
339 }
340
341 // try to get an option (to check if data can be queried)
342 try {
343 Option::get('TestingIfDatabaseConnectionWorked');
344 } catch (Exception $exception) {
345 if (self::shouldRethrowException()) {
346 throw $exception;
347 }
348
349 Log::debug($exception);
350
351 /**
352 * Triggered when Piwik cannot access database data.
353 *
354 * This event can be used to start the installation process or to display a custom error
355 * message.
356 *
357 * @param Exception $exception The exception thrown from trying to get an option value.
358 */
359 Piwik::postEvent('Config.badConfigurationFile', array($exception), $pending = true);
360
361 throw $exception;
362 }
363
364 // Init the Access object, so that eg. core/Updates/* can enforce Super User and use some APIs
365 Access::getInstance();
366
367 /**
368 * Triggered just after the platform is initialized and plugins are loaded.
369 *
370 * This event can be used to do early initialization.
371 *
372 * _Note: At this point the user is not authenticated yet._
373 */
374 Piwik::postEvent('Request.dispatchCoreAndPluginUpdatesScreen');
375
376 $this->throwIfPiwikVersionIsOlderThanDBSchema();
377
378 $module = Piwik::getModule();
379 $action = Piwik::getAction();
380
381 if (empty($module)
382 || empty($action)
383 || $module !== 'Installation'
384 || !in_array($action, array('getInstallationCss', 'getInstallationJs'))) {
385 \Piwik\Plugin\Manager::getInstance()->installLoadedPlugins();
386 }
387
388 // ensure the current Piwik URL is known for later use
389 if (method_exists('Piwik\SettingsPiwik', 'getPiwikUrl')) {
390 SettingsPiwik::getPiwikUrl();
391 }
392
393 $loggedIn = false;
394
395 // don't use sessionauth in cli mode
396 // try authenticating w/ session first...
397 $sessionAuth = $this->makeSessionAuthenticator();
398 if ($sessionAuth) {
399 $loggedIn = Access::getInstance()->reloadAccess($sessionAuth);
400 }
401
402 // ... if session auth fails try normal auth (which will login the anonymous user)
403 if (!$loggedIn) {
404 $authAdapter = $this->makeAuthenticator();
405 Access::getInstance()->reloadAccess($authAdapter);
406 } else {
407 $this->makeAuthenticator($sessionAuth); // Piwik\Auth must be set to the correct Login plugin
408 }
409
410 // Force the auth to use the token_auth if specified, so that embed dashboard
411 // and all other non widgetized controller methods works fine
412 if (Common::getRequestVar('token_auth', false, 'string') !== false) {
413 Request::reloadAuthUsingTokenAuth();
414 }
415 SettingsServer::raiseMemoryLimitIfNecessary();
416
417 \Piwik\Plugin\Manager::getInstance()->postLoadPlugins();
418
419 /**
420 * Triggered after the platform is initialized and after the user has been authenticated, but
421 * before the platform has handled the request.
422 *
423 * Piwik uses this event to check for updates to Piwik.
424 */
425 Piwik::postEvent('Platform.initialized');
426 }
427
428 protected function prepareDispatch($module, $action, $parameters)
429 {
430 if (is_null($module)) {
431 $module = Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
432 }
433
434 if (is_null($action)) {
435 $action = Common::getRequestVar('action', false);
436 }
437
438 if (Session::isSessionStarted()) {
439 $this->closeSessionEarlyForFasterUI();
440 }
441
442 if (is_null($parameters)) {
443 $parameters = array();
444 }
445
446 if (!ctype_alnum($module)) {
447 throw new Exception("Invalid module name '$module'");
448 }
449
450 list($module, $action) = Request::getRenamedModuleAndAction($module, $action);
451
452 if (!SettingsPiwik::isInternetEnabled() && \Piwik\Plugin\Manager::getInstance()->doesPluginRequireInternetConnection($module)) {
453 throw new PluginRequiresInternetException($module);
454 }
455
456 if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated($module)) {
457 throw new PluginDeactivatedException($module);
458 }
459
460 return array($module, $action, $parameters);
461 }
462
463 protected function handleMaintenanceMode()
464 {
465 if ((Config::getInstance()->General['maintenance_mode'] != 1) || Common::isPhpCliMode()) {
466 return;
467 }
468 Common::sendResponseCode(503);
469
470 $logoUrl = 'plugins/Morpheus/images/logo.svg';
471 $faviconUrl = 'plugins/CoreHome/images/favicon.png';
472 try {
473 $logo = new CustomLogo();
474 if ($logo->hasSVGLogo()) {
475 $logoUrl = $logo->getSVGLogoUrl();
476 } else {
477 $logoUrl = $logo->getHeaderLogoUrl();
478 }
479 $faviconUrl = $logo->getPathUserFavicon();
480 } catch (Exception $ex) {
481 }
482
483 $recordStatistics = Config::getInstance()->Tracker['record_statistics'];
484 $trackMessage = '';
485
486 if ($recordStatistics) {
487 $trackMessage = 'Your analytics data will continue to be tracked as normal.';
488 } else {
489 $trackMessage = 'While the maintenance mode is active, data tracking is disabled.';
490 }
491
492 $page = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/maintenance.tpl');
493 $page = str_replace('%logoUrl%', $logoUrl, $page);
494 $page = str_replace('%faviconUrl%', $faviconUrl, $page);
495 $page = str_replace('%piwikTitle%', Piwik::getRandomTitle(), $page);
496
497 $page = str_replace('%trackMessage%', $trackMessage, $page);
498
499 echo $page;
500 exit;
501 }
502
503 protected function handleSSLRedirection()
504 {
505 // Specifically disable for the opt out iframe
506 if (Piwik::getModule() == 'CoreAdminHome' && Piwik::getAction() == 'optOut') {
507 return;
508 }
509 // Disable Https for VisitorGenerator
510 if (Piwik::getModule() == 'VisitorGenerator') {
511 return;
512 }
513 if (Common::isPhpCliMode()) {
514 return;
515 }
516 // proceed only when force_ssl = 1
517 if (!SettingsPiwik::isHttpsForced()) {
518 return;
519 }
520 Url::redirectToHttps();
521 }
522
523 private function closeSessionEarlyForFasterUI()
524 {
525 $isDashboardReferrer = !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'module=CoreHome&action=index') !== false;
526 $isAllWebsitesReferrer = !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'module=MultiSites&action=index') !== false;
527
528 if ($isDashboardReferrer
529 && !empty($_POST['token_auth'])
530 && Common::getRequestVar('widget', 0, 'int') === 1
531 ) {
532 Session::close();
533 }
534
535 if (($isDashboardReferrer || $isAllWebsitesReferrer)
536 && Common::getRequestVar('viewDataTable', '', 'string') === 'sparkline'
537 ) {
538 Session::close();
539 }
540 }
541
542 private function handleProfiler()
543 {
544 if (!empty($_GET['xhprof'])) {
545 $mainRun = $_GET['xhprof'] == 1; // core:archive command sets xhprof=2
546 Profiler::setupProfilerXHProf($mainRun);
547 }
548 }
549
550 /**
551 * @param $module
552 * @param $action
553 * @param $parameters
554 * @return mixed
555 */
556 private function doDispatch($module, $action, $parameters)
557 {
558 list($module, $action, $parameters) = $this->prepareDispatch($module, $action, $parameters);
559
560 /**
561 * Triggered directly before controller actions are dispatched.
562 *
563 * This event can be used to modify the parameters passed to one or more controller actions
564 * and can be used to change the controller action being dispatched to.
565 *
566 * @param string &$module The name of the plugin being dispatched to.
567 * @param string &$action The name of the controller method being dispatched to.
568 * @param array &$parameters The arguments passed to the controller action.
569 */
570 Piwik::postEvent('Request.dispatch', array(&$module, &$action, &$parameters));
571
572 /** @var ControllerResolver $controllerResolver */
573 $controllerResolver = StaticContainer::get('Piwik\Http\ControllerResolver');
574
575 $controller = $controllerResolver->getController($module, $action, $parameters);
576
577 /**
578 * Triggered directly before controller actions are dispatched.
579 *
580 * This event exists for convenience and is triggered directly after the {@hook Request.dispatch}
581 * event is triggered.
582 *
583 * It can be used to do the same things as the {@hook Request.dispatch} event, but for one controller
584 * action only. Using this event will result in a little less code than {@hook Request.dispatch}.
585 *
586 * @param array &$parameters The arguments passed to the controller action.
587 */
588 Piwik::postEvent(sprintf('Controller.%s.%s', $module, $action), array(&$parameters));
589
590 $result = call_user_func_array($controller, $parameters);
591
592 /**
593 * Triggered after a controller action is successfully called.
594 *
595 * This event exists for convenience and is triggered immediately before the {@hook Request.dispatch.end}
596 * event is triggered.
597 *
598 * It can be used to do the same things as the {@hook Request.dispatch.end} event, but for one
599 * controller action only. Using this event will result in a little less code than
600 * {@hook Request.dispatch.end}.
601 *
602 * @param mixed &$result The result of the controller action.
603 * @param array $parameters The arguments passed to the controller action.
604 */
605 Piwik::postEvent(sprintf('Controller.%s.%s.end', $module, $action), array(&$result, $parameters));
606
607 /**
608 * Triggered after a controller action is successfully called.
609 *
610 * This event can be used to modify controller action output (if any) before the output is returned.
611 *
612 * @param mixed &$result The controller action result.
613 * @param array $parameters The arguments passed to the controller action.
614 */
615 Piwik::postEvent('Request.dispatch.end', array(&$result, $module, $action, $parameters));
616
617 return $result;
618 }
619
620 /**
621 * This method ensures that Piwik Platform cannot be running when using a NEWER database.
622 */
623 private function throwIfPiwikVersionIsOlderThanDBSchema()
624 {
625 // When developing this situation happens often when switching branches
626 if (Development::isEnabled()) {
627 return;
628 }
629
630 if (!StaticContainer::get('EnableDbVersionCheck')) {
631 return;
632 }
633
634 $updater = new Updater();
635
636 $dbSchemaVersion = $updater->getCurrentComponentVersion('core');
637 $current = Version::VERSION;
638 if (-1 === version_compare($current, $dbSchemaVersion)) {
639 $messages = array(
640 Piwik::translate('General_ExceptionDatabaseVersionNewerThanCodebase', array($current, $dbSchemaVersion)),
641 Piwik::translate('General_ExceptionDatabaseVersionNewerThanCodebaseWait'),
642 // we cannot fill in the Super User emails as we are failing before Authentication was ready
643 Piwik::translate('General_ExceptionContactSupportGeneric', array('', ''))
644 );
645 throw new DatabaseSchemaIsNewerThanCodebaseException(implode(" ", $messages));
646 }
647 }
648
649 private function makeSessionAuthenticator()
650 {
651 if (Common::isPhpClimode()
652 && !defined('PIWIK_TEST_MODE')
653 ) { // don't use the session auth during CLI requests
654 return null;
655 }
656
657 $module = Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
658 $action = Common::getRequestVar('action', false);
659
660 // the session must be started before using the session authenticator,
661 // so we do it here, if this is not an API request.
662 if (SettingsPiwik::isPiwikInstalled()
663 && ($module !== 'API' || ($action && $action !== 'index'))
664 ) {
665 /**
666 * @ignore
667 */
668 Piwik::postEvent('Session.beforeSessionStart');
669
670 Session::start();
671 return StaticContainer::get(SessionAuth::class);
672 }
673
674 return null;
675 }
676
677 private function makeAuthenticator(SessionAuth $auth = null)
678 {
679 /**
680 * Triggered before the user is authenticated, when the global authentication object
681 * should be created.
682 *
683 * Plugins that provide their own authentication implementation should use this event
684 * to set the global authentication object (which must derive from {@link Piwik\Auth}).
685 *
686 * **Example**
687 *
688 * Piwik::addAction('Request.initAuthenticationObject', function() {
689 * StaticContainer::getContainer()->set('Piwik\Auth', new MyAuthImplementation());
690 * });
691 */
692 Piwik::postEvent('Request.initAuthenticationObject');
693 try {
694 $authAdapter = StaticContainer::get('Piwik\Auth');
695 } catch (Exception $e) {
696 $message = "Authentication object cannot be found in the container. Maybe the Login plugin is not activated?
697 <br />You can activate the plugin by adding:<br />
698 <code>Plugins[] = Login</code><br />
699 under the <code>[Plugins]</code> section in your config/config.ini.php";
700
701 $ex = new AuthenticationFailedException($message);
702 $ex->setIsHtmlMessage();
703
704 throw $ex;
705 }
706
707 if ($auth) {
708 $authAdapter->setLogin($auth->getLogin());
709 $authAdapter->setTokenAuth($auth->getTokenAuth());
710 } else {
711 $authAdapter->setLogin(self::DEFAULT_LOGIN);
712 $authAdapter->setTokenAuth(self::DEFAULT_TOKEN_AUTH);
713 }
714
715 return $authAdapter;
716 }
717
718 public static function getUniqueRequestId()
719 {
720 if (self::$requestId === null) {
721 self::$requestId = substr(Common::generateUniqId(), 0, 5);
722 }
723 return self::$requestId;
724 }
725
726 private static function setRequestIdHeader()
727 {
728 $requestId = self::getUniqueRequestId();
729 Common::sendHeader("X-Matomo-Request-Id: $requestId");
730 }
731 }
732