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 (Access_NoAccessException $e) {
Piwik_PostEvent('FrontController.NoAccessException', array($e), $pending = true);
} catch (Exception $e) {
$debugTrace = $e->getTraceAsString();
$message = 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
*/
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()
{
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)
|| Common::isPhpCliMode()
|| Common::isArchivePhpTriggered();
}
/**
* Loads the config file and assign to the global registry
* This is overridden in tests to ensure test config file is used
*
* @return Exception
*/
protected function createConfigObject()
{
$exceptionToThrow = false;
try {
Config::getInstance();
} catch (Exception $e) {
Piwik_PostEvent('FrontController.NoConfigurationFile', array($e), $pending = true);
$exceptionToThrow = $e;
}
return $exceptionToThrow;
}
/**
* Must be called before dispatch()
* - checks that directories are writable,
* - loads the configuration file,
* - loads the plugin,
* - inits the DB connection,
* - etc.
*
* @throws Exception
* @return void
*/
public 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::dieIfDirectoriesNotWritable($directoriesToCheck);
Common::assignCliParametersToRequest();
Piwik_Translate::getInstance()->loadEnglishTranslation();
$exceptionToThrow = $this->createConfigObject();
if (Piwik_Session::isFileBasedSessions()) {
Piwik_Session::start();
}
$this->handleMaintenanceMode();
$this->handleSSLRedirection();
$pluginsManager = PluginsManager::getInstance();
$pluginsToLoad = 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', array($e), $pending = true);
throw $e;
}
Piwik::createLogObject();
// Init the Access object, so that eg. core/Updates/* can enforce Super User and use some APIs
Access::getInstance();
Piwik_PostEvent('FrontController.dispatchCoreAndPluginUpdatesScreen');
PluginsManager::getInstance()->installLoadedPlugins();
// ensure the current Piwik URL is known for later use
if (method_exists('Piwik\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?
You can activate the plugin by adding:
Plugins[] = Login
under the [Plugins]
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) {
Piwik_API_Request::reloadAuthUsingTokenAuth();
}
Piwik::raiseMemoryLimitIfNecessary();
Piwik_Translate::getInstance()->reloadLanguage();
$pluginsManager->postLoadPlugins();
Piwik_PostEvent('FrontController.checkForUpdates');
} catch (Exception $e) {
if (self::shouldRethrowException()) {
throw $e;
}
$trace = $e->getTraceAsString();
Piwik_ExitWithMessage($e->getMessage(), false /* $debugTrace */, true);
}
}
protected function handleMaintenanceMode()
{
if (Config::getInstance()->General['maintenance_mode'] == 1
&& !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 Piwik_API_ResponseBuilder($format);
echo $response->getResponseException($exception);
exit;
}
}
protected function handleSSLRedirection()
{
if (!Common::isPhpCliMode()
&& Config::getInstance()->General['force_ssl'] == 1
&& !Piwik::isHttps()
// Specifically disable for the opt out iframe
&& !(Common::getRequestVar('module', '') == 'CoreAdminHome'
&& Common::getRequestVar('action', '') == 'optOut')
) {
$url = Piwik_Url::getCurrentUrl();
$url = str_replace("http://", "https://", $url);
Piwik_Url::redirectToUrl($url);
}
}
}
/**
* Exception thrown when the requested plugin is not activated in the config file
*
* @package Piwik
* @subpackage Piwik_FrontController
*/
class Piwik_FrontController_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.");
}
}