Skip to content
Extraits de code Groupes Projets
FrontController.php 16,4 ko
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /**
     * Piwik - Open source web analytics
    
    robocoder's avatar
    robocoder a validé
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
    
     * @category Piwik
    
    namespace Piwik;
    use Exception;
    use Piwik\API\Request;
    use Piwik\API\ResponseBuilder;
    
    use Piwik\Session;
    
    /**
     * Front controller.
     * This is the class hit in the first place.
     * It dispatches the request to the right controller.
    
    JulienMoumne's avatar
    JulienMoumne a validé
     * For a detailed explanation, see the documentation on http://piwik.org/docs/plugins/framework-overview
    
        /**
         * Set to false and the Front Controller will not dispatch the request
         *
         * @var bool
         */
    
        public static $enableDispatch = true;
    
        private static $instance = null;
    
        protected function prepareDispatch($module, $action, $parameters)
    
        {
            if (is_null($module)) {
                $defaultModule = 'CoreHome';
    
                $module = Common::getRequestVar('module', $defaultModule, 'string');
    
                $action = Common::getRequestVar('action', false);
    
            if (!Session::isFileBasedSessions()
    
                && ($module !== 'API' || ($action && $action !== 'index'))
            ) {
    
            }
    
            if (is_null($parameters)) {
                $parameters = array();
            }
    
            if (!ctype_alnum($module)) {
                throw new Exception("Invalid module name '$module'");
            }
    
    
    sgiehl's avatar
    sgiehl a validé
            if (!PluginsManager::getInstance()->isPluginActivated($module)) {
    
                throw new PluginDeactivatedException($module);
    
            $controllerClassName = $this->getClassNameController($module);
    
    
            // FrontController's autoloader
            if (!class_exists($controllerClassName, false)) {
                $moduleController = PIWIK_INCLUDE_PATH . '/plugins/' . $module . '/Controller.php';
                if (!is_readable($moduleController)) {
                    throw new Exception("Module controller $moduleController not found!");
                }
                require_once $moduleController; // prefixed by PIWIK_INCLUDE_PATH
            }
    
    
            $class = $this->getClassNameController($module);
            /** @var $controller Controller */
            $controller = new $class;
    
            if ($action === false) {
                $action = $controller->getDefaultAction();
            }
    
            if (!is_callable(array($controller, $action))) {
                throw new Exception("Action '$action' not found in the controller '$controllerClassName'.");
            }
    
            return array($module, $action, $parameters, $controller);
        }
    
        /**
         * returns singleton
         *
         * @return \Piwik\FrontController
         */
        public static function getInstance()
        {
            if (self::$instance == null) {
                self::$instance = new self;
            }
            return self::$instance;
        }
    
        /**
         * Dispatches the request to the right plugin and executes the requested action on the plugin controller.
         *
         * @throws Exception|\Piwik\PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist, there is not enough permission, etc.
         *
         * @param string $module
         * @param string $action
         * @param array $parameters
         * @return void|mixed  The returned value of the calls, often nothing as the module print but don't return data
         * @see fetchDispatch()
         */
        public function dispatch($module = null, $action = null, $parameters = null)
        {
            if (self::$enableDispatch === false) {
                return;
            }
    
            // list($module, $action, $parameters, $controller)
            $params = $this->prepareDispatch($module, $action, $parameters);
    
            /**
             * Generic hook that plugins can use to modify any input to the function, or even change the plugin being
             * called. You could also use this to build an enhanced permission system. The event is triggered before any
             * controller is called.
             *
    
             * The `$params` array contains the following properties: `array($module, $action, $parameters, $controller)`
    
            Piwik_PostEvent('Request.dispatch', $params);
    
    
            /**
             * This event is similar to the `Request.dispatch` hook. It distinguishes the possibility to subscribe only to a
             * specific controller call instead of all controller calls. You can use it for example to modify any input
             * parameters or execute any other logic before the actual controller is called.
             */
    
            Piwik_PostEvent(sprintf('Controller.%s.%s', $module, $action), array($parameters));
    
                $result = call_user_func_array(array($params[3], $params[1]), $params[2]);
    
    
                /**
                 * This event is similar to the `Request.dispatch.end` hook. It distinguishes the possibility to subscribe
                 * only to the end of a specific controller call instead of all controller calls. You can use it for example
                 * to modify the response of a single controller.
                 */
    
                Piwik_PostEvent(sprintf('Controller.%s.%s.end', $module, $action), array(&$result, $parameters));
    
    
                /**
                 * Generic hook that plugins can use to modify any output of any controller. The event is triggered after
                 * any controller is executed but before the result is send to the user. The parameters originally
                 * passed to the controller are available as well.
                 */
    
                Piwik_PostEvent('Request.dispatch.end', array(&$result, $parameters));
    
                return $result;
    
    
            } catch (NoAccessException $exception) {
    
                /**
                 * This event is triggered in case the user wants to access anything in the Piwik UI but has not the
                 * required permissions to do this. You can subscribe to this event to display a custom error message or
                 * to display a custom login form in such a case.
                 */
                Piwik_PostEvent('User.isNotAuthorized', array($exception), $pending = true);
    
            } catch (Exception $e) {
                $debugTrace = $e->getTraceAsString();
    
                $message = Common::sanitizeInputValue($e->getMessage());
    
                Piwik_ExitWithMessage($message, $debugTrace, true, true);
    
        protected function getClassNameController($module)
        {
            return "\\Piwik\\Plugins\\$module\\Controller";
        }
    
    
        /**
         * Often plugins controller display stuff using echo/print.
         * Using this function instead of dispatch() returns the output string form the actions calls.
         *
         * @param string $controllerName
         * @param string $actionName
         * @param array $parameters
         * @return string
         */
    
        public function fetchDispatch($controllerName = null, $actionName = null, $parameters = null)
    
        {
            ob_start();
            $output = $this->dispatch($controllerName, $actionName, $parameters);
            // if nothing returned we try to load something that was printed on the screen
            if (empty($output)) {
                $output = ob_get_contents();
            }
            ob_end_clean();
            return $output;
        }
    
        /**
         * Called at the end of the page generation
         *
         */
    
        public function __destruct()
    
    Benaka Moorthi's avatar
    Benaka Moorthi a validé
                if (class_exists('Piwik\\Profiler')) {
                    Profiler::displayDbProfileReport();
                    Profiler::printQueryCount();
    
    Benaka Moorthi's avatar
    Benaka Moorthi a validé
                }
    
            } catch (Exception $e) {
            }
        }
    
        // Should we show exceptions messages directly rather than display an html error page?
        public static function shouldRethrowException()
        {
            // If we are in no dispatch mode, eg. a script reusing Piwik libs,
            // then we should return the exception directly, rather than trigger the event "bad config file"
            // which load the HTML page of the installer with the error.
            // This is at least required for misc/cron/archive.php and useful to all other scripts
            return (defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH)
    
    mattab's avatar
    mattab a validé
                || Common::isPhpCliMode()
    
                || SettingsServer::isArchivePhpTriggered();
    
        }
    
        /**
         * Loads the config file and assign to the global registry
    
    sgiehl's avatar
    sgiehl a validé
         * This is overridden in tests to ensure test config file is used
         *
         * @return Exception
    
        static public function createConfigObject()
    
                Config::getInstance();
    
            } catch (Exception $exception) {
    
                /**
                 * This event is triggered in case no configuration file is available. This usually means Piwik is not
                 * installed yet. The event can be used to start the installation process or to display a custom error
                 * message.
                 */
                Piwik_PostEvent('Config.NoConfigurationFile', array($exception), $pending = true);
                $exceptionToThrow = $exception;
    
            }
            return $exceptionToThrow;
        }
    
        /**
         * Must be called before dispatch()
         * - checks that directories are writable,
         * - loads the configuration file,
         * - loads the plugin,
         * - inits the DB connection,
         * - etc.
    
    sgiehl's avatar
    sgiehl a validé
         *
    
    sgiehl's avatar
    sgiehl a validé
         * @return void
    
        public function init()
    
        {
            static $initialized = false;
            if ($initialized) {
                return;
            }
            $initialized = true;
    
            try {
    
                Registry::set('timer', new Timer);
    
    
                $directoriesToCheck = array(
                    '/tmp/',
                    '/tmp/assets/',
    
                    '/tmp/cache/',
                    '/tmp/logs/',
                    '/tmp/tcpdf/',
                    '/tmp/templates_c/',
    
                Filechecks::dieIfDirectoriesNotWritable($directoriesToCheck);
    
                self::assignCliParametersToRequest();
    
                Translate::getInstance()->loadEnglishTranslation();
    
                $exceptionToThrow = self::createConfigObject();
    
                if (Session::isFileBasedSessions()) {
                    Session::start();
    
                }
    
                $this->handleMaintenanceMode();
                $this->handleSSLRedirection();
    
    
    sgiehl's avatar
    sgiehl a validé
                $pluginsManager = PluginsManager::getInstance();
    
                $pluginsToLoad = Config::getInstance()->Plugins['Plugins'];
    
                $pluginsManager->loadPlugins($pluginsToLoad);
    
                if ($exceptionToThrow) {
                    throw $exceptionToThrow;
                }
    
                try {
    
                    Db::createDatabaseObject();
    
                    Piwik_GetOption('TestingIfDatabaseConnectionWorked');
    
                } catch (Exception $exception) {
    
                    if (self::shouldRethrowException()) {
    
                        throw $exception;
    
    
                    /**
                     * This event is triggered in case a config file is not in the correct format or in case required values
                     * are missing. The event can be used to start the installation process or to display a custom error
                     * message.
                     */
                    Piwik_PostEvent('Config.badConfigurationFile', array($exception), $pending = true);
                    throw $exception;
    
                // Init the Access object, so that eg. core/Updates/* can enforce Super User and use some APIs
    
                Access::getInstance();
    
                /**
                 * This event is triggered after the platform is initialized and most plugins are loaded. The user is not
                 * authenticated at this point though. You can use this event for instance to initialize your own plugin.
                 * @matt
                 */
    
                Piwik_PostEvent('Request.dispatchCoreAndPluginUpdatesScreen');
    
    sgiehl's avatar
    sgiehl a validé
                PluginsManager::getInstance()->installLoadedPlugins();
    
                // ensure the current Piwik URL is known for later use
    
                if (method_exists('Piwik\SettingsPiwik', 'getPiwikUrl')) {
                    $host = SettingsPiwik::getPiwikUrl();
    
                /**
                 * This event is triggered shortly before the user is authenticated. Use it to create your own
                 * authentication object instead of the Piwik authentication. Make sure to implement the `Piwik\Auth`
                 * interface in case you want to define your own authentication.
                 */
    
                Piwik_PostEvent('Request.initAuthenticationObject');
    
                    $authAdapter = Registry::get('auth');
    
                } catch (Exception $e) {
                    throw new Exception("Authentication object cannot be found in the Registry. Maybe the Login plugin is not activated?
    
                                    <br />You can activate the plugin by adding:<br />
                                    <code>Plugins[] = Login</code><br />
                                    under the <code>[Plugins]</code> section in your config/config.ini.php");
    
                Access::getInstance()->reloadAccess($authAdapter);
    
    
                // Force the auth to use the token_auth if specified, so that embed dashboard
                // and all other non widgetized controller methods works fine
    
                if (($token_auth = Common::getRequestVar('token_auth', false, 'string')) !== false) {
    
                    Request::reloadAuthUsingTokenAuth();
    
                SettingsServer::raiseMemoryLimitIfNecessary();
    
                Translate::getInstance()->reloadLanguage();
    
                $pluginsManager->postLoadPlugins();
    
    
                /**
                 * This event is triggered to check for updates. It is triggered after the platform is initialized and after
                 * the user is authenticated but before the platform is going to dispatch the actual request. You can use
                 * it to check for any updates.
                 */
    
                Piwik_PostEvent('Updater.checkForUpdates');
    
            } catch (Exception $e) {
    
                if (self::shouldRethrowException()) {
                    throw $e;
                }
    
    
    Fabian Becker's avatar
    Fabian Becker a validé
                $debugTrace = $e->getTraceAsString();
    
                Piwik_ExitWithMessage($e->getMessage(), $debugTrace, true);
    
            }
        }
    
        protected function handleMaintenanceMode()
        {
    
            if (Config::getInstance()->General['maintenance_mode'] == 1
    
    mattab's avatar
    mattab a validé
                && !Common::isPhpCliMode()
    
                $format = Common::getRequestVar('format', '');
    
    
                $message = "Piwik is in scheduled maintenance. Please come back later."
                    . " The administrator can disable maintenance by editing the file piwik/config/config.ini.php and removing the following: "
                    . " maintenance_mode=1 ";
    
                if (Config::getInstance()->Tracker['record_statistics'] == 0) {
    
                    $message .= ' and record_statistics=0';
                }
    
                $exception = new Exception($message);
                // extend explain how to re-enable
                // show error message when record stats = 0
                if (empty($format)) {
                    throw $exception;
                }
    
                $response = new ResponseBuilder($format);
    
                echo $response->getResponseException($exception);
                exit;
            }
        }
    
        protected function handleSSLRedirection()
        {
    
    mattab's avatar
    mattab a validé
            if (!Common::isPhpCliMode()
    
                && Config::getInstance()->General['force_ssl'] == 1
    
                && !ProxyHttp::isHttps()
    
                // Specifically disable for the opt out iframe
    
                && !(Common::getRequestVar('module', '') == 'CoreAdminHome'
                    && Common::getRequestVar('action', '') == 'optOut')
    
                $url = str_replace("http://", "https://", $url);
    
    
        /**
         * Assign CLI parameters as if they were REQUEST or GET parameters.
         * You can trigger Piwik from the command line by
         * # /usr/bin/php5 /path/to/piwik/index.php -- "module=API&method=Actions.getActions&idSite=1&period=day&date=previous8&format=php"
         */
        public static function assignCliParametersToRequest()
        {
            if (isset($_SERVER['argc'])
                && $_SERVER['argc'] > 0
            ) {
                for ($i = 1; $i < $_SERVER['argc']; $i++) {
                    parse_str($_SERVER['argv'][$i], $tmp);
                    $_GET = array_merge($_GET, $tmp);
                }
            }
        }
    
    /**
     * Exception thrown when the requested plugin is not activated in the config file
     *
     * @package Piwik
    
    mattab's avatar
    mattab a validé
    class PluginDeactivatedException extends Exception
    
        public function __construct($module)
    
        {
            parent::__construct("The plugin $module is not enabled. You can activate the plugin on Settings > Plugins page in Piwik.");
        }