diff options
Diffstat (limited to 'core/FrontController.php')
-rw-r--r-- | core/FrontController.php | 672 |
1 files changed, 325 insertions, 347 deletions
diff --git a/core/FrontController.php b/core/FrontController.php index 203a911f8a..8f136acd65 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -1,10 +1,10 @@ <?php /** * Piwik - Open source web analytics - * + * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * + * * @category Piwik * @package Piwik */ @@ -22,356 +22,334 @@ require_once PIWIK_INCLUDE_PATH . '/core/Option.php'; * Front controller. * This is the class hit in the first place. * It dispatches the request to the right controller. - * + * * For a detailed explanation, see the documentation on http://piwik.org/docs/plugins/framework-overview - * + * * @package Piwik * @subpackage Piwik_FrontController */ class Piwik_FrontController { - /** - * Set to false and the Front Controller will not dispatch the request - * - * @var bool - */ - static public $enableDispatch = true; - - static private $instance = null; - - /** - * returns singleton - * - * @return Piwik_FrontController - */ - static public 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_FrontController_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 mixed The returned value of the calls, often nothing as the module print but don't return data - * @see fetchDispatch() - */ - function dispatch( $module = null, $action = null, $parameters = null) - { - if( self::$enableDispatch === false) - { - return; - } - - if(is_null($module)) - { - $defaultModule = 'CoreHome'; - $module = Piwik_Common::getRequestVar('module', $defaultModule, 'string'); - } - - if(is_null($action)) - { - $action = Piwik_Common::getRequestVar('action', false); - } - - if(!Piwik_Session::isFileBasedSessions() - && ($module !== 'API' || ($action && $action !== 'index'))) - { - Piwik_Session::start(); - } - - if(is_null($parameters)) - { - $parameters = array(); - } - - if(!ctype_alnum($module)) - { - throw new Exception("Invalid module name '$module'"); - } - - if( ! Piwik_PluginsManager::getInstance()->isPluginActivated( $module )) - { - throw new Piwik_FrontController_PluginDeactivatedException($module); - } - - $controllerClassName = 'Piwik_'.$module.'_Controller'; - - // 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 - } - - $controller = new $controllerClassName(); - if($action === false) - { - $action = $controller->getDefaultAction(); - } - - if( !is_callable(array($controller, $action))) - { - throw new Exception("Action '$action' not found in the controller '$controllerClassName'."); - } - - // Generic hook that plugins can use to modify any input to the function, - // or even change the plugin being called - $params = array($controller, $action, $parameters); - Piwik_PostEvent('FrontController.dispatch', $params); - - try { - return call_user_func_array( array($params[0], $params[1] ), $params[2]); - } catch(Piwik_Access_NoAccessException $e) { - Piwik_PostEvent('FrontController.NoAccessException', $e); - } catch(Exception $e) { - $debugTrace = $e->getTraceAsString(); - $message = Piwik_Common::sanitizeInputValue($e->getMessage()); - Piwik_ExitWithMessage($message, '' /* $debugTrace */, true); - } - } - - /** - * 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 - */ - 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 - * - */ - function __destruct() - { - try { - Piwik::printSqlProfilingReportZend(); - Piwik::printQueryCount(); - Piwik::printTimer(); - } 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) - || Piwik_Common::isPhpCliMode() - || Piwik_Common::isArchivePhpTriggered() - ; - } - - /** - * Loads the config file and assign to the global registry - * This is overriden in tests to ensure test config file is used - */ - protected function createConfigObject() - { - $exceptionToThrow = false; - try { - Piwik::createConfigObject(); - } catch(Exception $e) { - Piwik_PostEvent('FrontController.NoConfigurationFile', $e, $info = array(), $pending = true); - $exceptionToThrow = $e; - } - return $exceptionToThrow; - } - - protected function createAccessObject() - { - Piwik::createAccessObject(); - } - - /** - * Must be called before dispatch() - * - checks that directories are writable, - * - loads the configuration file, - * - loads the plugin, - * - inits the DB connection, - * - etc. - * @throws Exception - * @throws Exception - * @throws bool|Exception - * @return - */ - function init() - { - static $initialized = false; - if($initialized) - { - return; - } - $initialized = true; - - - try { - Zend_Registry::set('timer', new Piwik_Timer); - - $directoriesToCheck = array( - '/tmp/', - '/tmp/templates_c/', - '/tmp/cache/', - '/tmp/assets/', - '/tmp/tcpdf/' - ); - - Piwik::checkDirectoriesWritableOrDie($directoriesToCheck); - Piwik_Common::assignCliParametersToRequest(); - - Piwik_Translate::getInstance()->loadEnglishTranslation(); - - $exceptionToThrow = $this->createConfigObject(); - - if(Piwik_Session::isFileBasedSessions()) - { - Piwik_Session::start(); - } - - $this->handleMaintenanceMode(); - $this->handleSSLRedirection(); - - $pluginsManager = Piwik_PluginsManager::getInstance(); - $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins']; - $pluginsManager->loadPlugins( $pluginsToLoad ); - - if($exceptionToThrow) - { - throw $exceptionToThrow; - } - - try { - Piwik::createDatabaseObject(); - } catch(Exception $e) { - if(self::shouldRethrowException()) - { - throw $e; - } - Piwik_PostEvent('FrontController.badConfigurationFile', $e, $info = array(), $pending = true); - throw $e; - } - - Piwik::createLogObject(); - - // creating the access object, so that core/Updates/* can enforce Super User and use some APIs - $this->createAccessObject(); - Piwik_PostEvent('FrontController.dispatchCoreAndPluginUpdatesScreen'); - - Piwik_PluginsManager::getInstance()->installLoadedPlugins(); - Piwik::install(); - - // ensure the current Piwik URL is known for later use - if(method_exists('Piwik', 'getPiwikUrl')) - { - $host = Piwik::getPiwikUrl(); - } - - Piwik_PostEvent('FrontController.initAuthenticationObject'); - try { - $authAdapter = Zend_Registry::get('auth'); - } catch(Exception $e){ - throw new Exception("Authentication object cannot be found in the Registry. Maybe the Login plugin is not activated? + /** + * Set to false and the Front Controller will not dispatch the request + * + * @var bool + */ + static public $enableDispatch = true; + + static private $instance = null; + + /** + * returns singleton + * + * @return Piwik_FrontController + */ + static public 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_FrontController_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 mixed The returned value of the calls, often nothing as the module print but don't return data + * @see fetchDispatch() + */ + function dispatch($module = null, $action = null, $parameters = null) + { + if (self::$enableDispatch === false) { + return; + } + + if (is_null($module)) { + $defaultModule = 'CoreHome'; + $module = Piwik_Common::getRequestVar('module', $defaultModule, 'string'); + } + + if (is_null($action)) { + $action = Piwik_Common::getRequestVar('action', false); + } + + if (!Piwik_Session::isFileBasedSessions() + && ($module !== 'API' || ($action && $action !== 'index')) + ) { + Piwik_Session::start(); + } + + if (is_null($parameters)) { + $parameters = array(); + } + + if (!ctype_alnum($module)) { + throw new Exception("Invalid module name '$module'"); + } + + if (!Piwik_PluginsManager::getInstance()->isPluginActivated($module)) { + throw new Piwik_FrontController_PluginDeactivatedException($module); + } + + $controllerClassName = 'Piwik_' . $module . '_Controller'; + + // 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 + } + + $controller = new $controllerClassName(); + if ($action === false) { + $action = $controller->getDefaultAction(); + } + + if (!is_callable(array($controller, $action))) { + throw new Exception("Action '$action' not found in the controller '$controllerClassName'."); + } + + // Generic hook that plugins can use to modify any input to the function, + // or even change the plugin being called + $params = array($controller, $action, $parameters); + Piwik_PostEvent('FrontController.dispatch', $params); + + try { + return call_user_func_array(array($params[0], $params[1]), $params[2]); + } catch (Piwik_Access_NoAccessException $e) { + Piwik_PostEvent('FrontController.NoAccessException', $e); + } catch (Exception $e) { + $debugTrace = $e->getTraceAsString(); + $message = Piwik_Common::sanitizeInputValue($e->getMessage()); + Piwik_ExitWithMessage($message, '' /* $debugTrace */, true); + } + } + + /** + * 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 + */ + 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 + * + */ + function __destruct() + { + try { + Piwik::printSqlProfilingReportZend(); + Piwik::printQueryCount(); + Piwik::printTimer(); + } 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) + || Piwik_Common::isPhpCliMode() + || Piwik_Common::isArchivePhpTriggered(); + } + + /** + * Loads the config file and assign to the global registry + * This is overriden in tests to ensure test config file is used + */ + protected function createConfigObject() + { + $exceptionToThrow = false; + try { + Piwik::createConfigObject(); + } catch (Exception $e) { + Piwik_PostEvent('FrontController.NoConfigurationFile', $e, $info = array(), $pending = true); + $exceptionToThrow = $e; + } + return $exceptionToThrow; + } + + protected function createAccessObject() + { + Piwik::createAccessObject(); + } + + /** + * Must be called before dispatch() + * - checks that directories are writable, + * - loads the configuration file, + * - loads the plugin, + * - inits the DB connection, + * - etc. + * @throws Exception + * @throws Exception + * @throws bool|Exception + * @return + */ + function init() + { + static $initialized = false; + if ($initialized) { + return; + } + $initialized = true; + + + try { + Zend_Registry::set('timer', new Piwik_Timer); + + $directoriesToCheck = array( + '/tmp/', + '/tmp/templates_c/', + '/tmp/cache/', + '/tmp/assets/', + '/tmp/tcpdf/' + ); + + Piwik::checkDirectoriesWritableOrDie($directoriesToCheck); + Piwik_Common::assignCliParametersToRequest(); + + Piwik_Translate::getInstance()->loadEnglishTranslation(); + + $exceptionToThrow = $this->createConfigObject(); + + if (Piwik_Session::isFileBasedSessions()) { + Piwik_Session::start(); + } + + $this->handleMaintenanceMode(); + $this->handleSSLRedirection(); + + $pluginsManager = Piwik_PluginsManager::getInstance(); + $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins']; + $pluginsManager->loadPlugins($pluginsToLoad); + + if ($exceptionToThrow) { + throw $exceptionToThrow; + } + + try { + Piwik::createDatabaseObject(); + } catch (Exception $e) { + if (self::shouldRethrowException()) { + throw $e; + } + Piwik_PostEvent('FrontController.badConfigurationFile', $e, $info = array(), $pending = true); + throw $e; + } + + Piwik::createLogObject(); + + // creating the access object, so that core/Updates/* can enforce Super User and use some APIs + $this->createAccessObject(); + Piwik_PostEvent('FrontController.dispatchCoreAndPluginUpdatesScreen'); + + Piwik_PluginsManager::getInstance()->installLoadedPlugins(); + Piwik::install(); + + // ensure the current Piwik URL is known for later use + if (method_exists('Piwik', 'getPiwikUrl')) { + $host = Piwik::getPiwikUrl(); + } + + Piwik_PostEvent('FrontController.initAuthenticationObject'); + try { + $authAdapter = Zend_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"); - } - Zend_Registry::get('access')->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 = Piwik_Common::getRequestVar('token_auth', false, 'string')) !== false) - { - Piwik_API_Request::reloadAuthUsingTokenAuth(); - } - Piwik::raiseMemoryLimitIfNecessary(); - - Piwik_Translate::getInstance()->reloadLanguage(); - $pluginsManager->postLoadPlugins(); - - Piwik_PostEvent('FrontController.checkForUpdates'); - } catch(Exception $e) { - - if(self::shouldRethrowException()) - { - throw $e; - } - - Piwik_ExitWithMessage($e->getMessage(), false, true); - } - } - - protected function handleMaintenanceMode() - { - if(Piwik_Config::getInstance()->General['maintenance_mode'] == 1 - && !Piwik_Common::isPhpCliMode()) - { - $format = Piwik_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(Piwik_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 Piwik_API_ResponseBuilder( $format ); - echo $response->getResponseException( $exception ); - exit; - } - } - - protected function handleSSLRedirection() - { - if(!Piwik_Common::isPhpCliMode() - && Piwik_Config::getInstance()->General['force_ssl'] == 1 - && !Piwik::isHttps() - // Specifically disable for the opt out iframe - && !(Piwik_Common::getRequestVar('module', '') == 'CoreAdminHome' - && Piwik_Common::getRequestVar('action', '') == 'optOut') - ) - { - $url = Piwik_Url::getCurrentUrl(); - $url = str_replace("http://", "https://", $url); - Piwik_Url::redirectToUrl($url); - } - } + } + Zend_Registry::get('access')->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 = Piwik_Common::getRequestVar('token_auth', false, 'string')) !== false) { + Piwik_API_Request::reloadAuthUsingTokenAuth(); + } + Piwik::raiseMemoryLimitIfNecessary(); + + Piwik_Translate::getInstance()->reloadLanguage(); + $pluginsManager->postLoadPlugins(); + + Piwik_PostEvent('FrontController.checkForUpdates'); + } catch (Exception $e) { + + if (self::shouldRethrowException()) { + throw $e; + } + + Piwik_ExitWithMessage($e->getMessage(), false, true); + } + } + + protected function handleMaintenanceMode() + { + if (Piwik_Config::getInstance()->General['maintenance_mode'] == 1 + && !Piwik_Common::isPhpCliMode() + ) { + $format = Piwik_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 (Piwik_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 Piwik_API_ResponseBuilder($format); + echo $response->getResponseException($exception); + exit; + } + } + + protected function handleSSLRedirection() + { + if (!Piwik_Common::isPhpCliMode() + && Piwik_Config::getInstance()->General['force_ssl'] == 1 + && !Piwik::isHttps() + // Specifically disable for the opt out iframe + && !(Piwik_Common::getRequestVar('module', '') == 'CoreAdminHome' + && Piwik_Common::getRequestVar('action', '') == 'optOut') + ) { + $url = Piwik_Url::getCurrentUrl(); + $url = str_replace("http://", "https://", $url); + Piwik_Url::redirectToUrl($url); + } + } } /** @@ -382,9 +360,9 @@ class Piwik_FrontController */ class Piwik_FrontController_PluginDeactivatedException extends Exception { - function __construct($module) - { - parent::__construct("The plugin $module is not enabled. You can activate the plugin on Settings > Plugins page in Piwik."); - } + function __construct($module) + { + parent::__construct("The plugin $module is not enabled. You can activate the plugin on Settings > Plugins page in Piwik."); + } } |