1, 'week' => 2, 'month' => 3, 'year' => 4, 'range' => 5, ); const LABEL_ID_GOAL_IS_ECOMMERCE_CART = 'ecommerceAbandonedCart'; const LABEL_ID_GOAL_IS_ECOMMERCE_ORDER = 'ecommerceOrder'; /** * Uninstallation helper */ static public function uninstall() { Schema::getInstance()->dropTables(); } /** * Returns true if Piwik is installed * * @since 0.6.3 * * @return bool True if installed; false otherwise */ static public function isInstalled() { try { return Schema::getInstance()->hasTables(); } catch (Exception $e) { return false; } } /** * Logging and error handling * * @var bool|null */ public static $shouldLog = null; /** * Log a message * * @param string $message */ static public function log($message = '') { if (is_null(self::$shouldLog)) { self::$shouldLog = SettingsPiwik::shouldLoggerLog(); // It is possible that the logger is not setup: // - Tracker request, and debug disabled, // - and some scheduled tasks call code that tries and log something try { \Zend_Registry::get('logger_message'); } catch (Exception $e) { self::$shouldLog = false; } } if (self::$shouldLog) { \Zend_Registry::get('logger_message')->logEvent($message); } } /** * Trigger E_USER_ERROR with optional message * * @param string $message */ static public function error($message = '') { trigger_error($message, E_USER_ERROR); } /** * Display the message in a nice red font with a nice icon * ... and dies * * @param string $message */ static public function exitWithErrorMessage($message) { $output = "\n" . "
" . "

" . $message . "

"; print(ScreenFormatter::getFormattedString($output)); exit; } /* * Amounts, Percentages, Currency, Time, Math Operations, and Pretty Printing */ /** * Computes the division of i1 by i2. If either i1 or i2 are not number, or if i2 has a value of zero * we return 0 to avoid the division by zero. * * @param number $i1 * @param number $i2 * @return number The result of the division or zero */ static public function secureDiv($i1, $i2) { if (is_numeric($i1) && is_numeric($i2) && floatval($i2) != 0) { return $i1 / $i2; } return 0; } /** * Safely compute a percentage. Return 0 to avoid division by zero. * * @param number $dividend * @param number $divisor * @param int $precision * @return number */ static public function getPercentageSafe($dividend, $divisor, $precision = 0) { if ($divisor == 0) { return 0; } return round(100 * $dividend / $divisor, $precision); } /** * Returns the Javascript code to be inserted on every page to track * * @param int $idSite * @param string $piwikUrl http://path/to/piwik/directory/ * @return string */ static public function getJavascriptCode($idSite, $piwikUrl) { $jsCode = file_get_contents(PIWIK_INCLUDE_PATH . "/plugins/Zeitgeist/templates/javascriptCode.tpl"); $jsCode = htmlentities($jsCode); preg_match('~^(http|https)://(.*)$~D', $piwikUrl, $matches); $piwikUrl = @$matches[2]; $jsCode = str_replace('{$idSite}', $idSite, $jsCode); $jsCode = str_replace('{$piwikUrl}', Common::sanitizeInputValue($piwikUrl), $jsCode); return $jsCode; } /** * Generate a title for image tags * * @return string */ static public function getRandomTitle() { static $titles = array( 'Web analytics', 'Real Time Web Analytics', 'Analytics', 'Real Time Analytics', 'Analytics in Real time', 'Open Source Analytics', 'Open Source Web Analytics', 'Free Website Analytics', 'Free Web Analytics', 'Analytics Platform', ); $id = abs(intval(md5(Url::getCurrentHost()))); $title = $titles[$id % count($titles)]; return $title; } /* * Access */ /** * Get current user email address * * @return string */ static public function getCurrentUserEmail() { if (!Piwik::isUserIsSuperUser()) { $user = API::getInstance()->getUser(Piwik::getCurrentUserLogin()); return $user['email']; } return self::getSuperUserEmail(); } /** * Returns Super User login * * @return string */ static public function getSuperUserLogin() { return Access::getInstance()->getSuperUserLogin(); } /** * Returns Super User email * * @return string */ static public function getSuperUserEmail() { $superuser = Config::getInstance()->superuser; return $superuser['email']; } /** * Get current user login * * @return string login ID */ static public function getCurrentUserLogin() { return Access::getInstance()->getLogin(); } /** * Get current user's token auth * * @return string Token auth */ static public function getCurrentUserTokenAuth() { return Access::getInstance()->getTokenAuth(); } /** * Returns true if the current user is either the super user, or the user $theUser * Used when modifying user preference: this usually requires super user or being the user itself. * * @param string $theUser * @return bool */ static public function isUserIsSuperUserOrTheUser($theUser) { try { self::checkUserIsSuperUserOrTheUser($theUser); return true; } catch (Exception $e) { return false; } } /** * Check that current user is either the specified user or the superuser * * @param string $theUser * @throws NoAccessException if the user is neither the super user nor the user $theUser */ static public function checkUserIsSuperUserOrTheUser($theUser) { try { if (Piwik::getCurrentUserLogin() !== $theUser) { // or to the super user Piwik::checkUserIsSuperUser(); } } catch (NoAccessException $e) { throw new NoAccessException(Piwik_Translate('General_ExceptionCheckUserIsSuperUserOrTheUser', array($theUser))); } } /** * Returns true if the current user is the Super User * * @return bool */ static public function isUserIsSuperUser() { try { self::checkUserIsSuperUser(); return true; } catch (Exception $e) { return false; } } /** * Is user the anonymous user? * * @return bool True if anonymouse; false otherwise */ static public function isUserIsAnonymous() { return Piwik::getCurrentUserLogin() == 'anonymous'; } /** * Checks if user is not the anonymous user. * * @throws NoAccessException if user is anonymous. */ static public function checkUserIsNotAnonymous() { if (self::isUserIsAnonymous()) { throw new NoAccessException(Piwik_Translate('General_YouMustBeLoggedIn')); } } /** * Helper method user to set the current as Super User. * This should be used with great care as this gives the user all permissions. * * @param bool $bool true to set current user as super user */ static public function setUserIsSuperUser($bool = true) { Access::getInstance()->setSuperUser($bool); } /** * Check that user is the superuser * * @throws Exception if not the superuser */ static public function checkUserIsSuperUser() { Access::getInstance()->checkUserIsSuperUser(); } /** * Returns true if the user has admin access to the sites * * @param mixed $idSites * @return bool */ static public function isUserHasAdminAccess($idSites) { try { self::checkUserHasAdminAccess($idSites); return true; } catch (Exception $e) { return false; } } /** * Check user has admin access to the sites * * @param mixed $idSites * @throws Exception if user doesn't have admin access to the sites */ static public function checkUserHasAdminAccess($idSites) { Access::getInstance()->checkUserHasAdminAccess($idSites); } /** * Returns true if the user has admin access to any sites * * @return bool */ static public function isUserHasSomeAdminAccess() { try { self::checkUserHasSomeAdminAccess(); return true; } catch (Exception $e) { return false; } } /** * Check user has admin access to any sites * * @throws Exception if user doesn't have admin access to any sites */ static public function checkUserHasSomeAdminAccess() { Access::getInstance()->checkUserHasSomeAdminAccess(); } /** * Returns true if the user has view access to the sites * * @param mixed $idSites * @return bool */ static public function isUserHasViewAccess($idSites) { try { self::checkUserHasViewAccess($idSites); return true; } catch (Exception $e) { return false; } } /** * Check user has view access to the sites * * @param mixed $idSites * @throws Exception if user doesn't have view access to sites */ static public function checkUserHasViewAccess($idSites) { Access::getInstance()->checkUserHasViewAccess($idSites); } /** * Returns true if the user has view access to any sites * * @return bool */ static public function isUserHasSomeViewAccess() { try { self::checkUserHasSomeViewAccess(); return true; } catch (Exception $e) { return false; } } /** * Check user has view access to any sites * * @throws Exception if user doesn't have view access to any sites */ static public function checkUserHasSomeViewAccess() { Access::getInstance()->checkUserHasSomeViewAccess(); } /* * Current module, action, plugin */ /** * Returns the name of the Login plugin currently being used. * Must be used since it is not allowed to hardcode 'Login' in URLs * in case another Login plugin is being used. * * @return string */ static public function getLoginPluginName() { return \Zend_Registry::get('auth')->getName(); } /** * Returns the plugin currently being used to display the page * * @return Plugin */ static public function getCurrentPlugin() { return \Piwik\PluginsManager::getInstance()->getLoadedPlugin(Piwik::getModule()); } /** * Returns the current module read from the URL (eg. 'API', 'UserSettings', etc.) * * @return string */ static public function getModule() { return Common::getRequestVar('module', '', 'string'); } /** * Returns the current action read from the URL * * @return string */ static public function getAction() { return Common::getRequestVar('action', '', 'string'); } /** * Helper method used in API function to introduce array elements in API parameters. * Array elements can be passed by comma separated values, or using the notation * array[]=value1&array[]=value2 in the URL. * This function will handle both cases and return the array. * * @param array|string $columns * @return array */ static public function getArrayFromApiParameter($columns) { if (empty($columns)) { return array(); } if (is_array($columns)) { return $columns; } $array = explode(',', $columns); $array = array_unique($array); return $array; } /** * Redirect to module (and action) * * @param string $newModule Target module * @param string $newAction Target action * @param array $parameters Parameters to modify in the URL * @return bool false if the URL to redirect to is already this URL */ static public function redirectToModule($newModule, $newAction = '', $parameters = array()) { $newUrl = 'index.php' . Url::getCurrentQueryStringWithParametersModified( array('module' => $newModule, 'action' => $newAction) + $parameters ); Url::redirectToUrl($newUrl); } /* * Global database object */ /** * Create database object and connect to database * @param array|null $dbInfos */ static public function createDatabaseObject($dbInfos = null) { $config = Config::getInstance(); if (is_null($dbInfos)) { $dbInfos = $config->database; } Piwik_PostEvent('Reporting.getDatabaseConfig', array(&$dbInfos)); $dbInfos['profiler'] = $config->Debug['enable_sql_profiler']; $db = null; Piwik_PostEvent('Reporting.createDatabase', array(&$db)); if (is_null($db)) { $adapter = $dbInfos['adapter']; $db = @Adapter::factory($adapter, $dbInfos); } \Zend_Registry::set('db', $db); } /** * Disconnect from database */ static public function disconnectDatabase() { \Zend_Registry::get('db')->closeConnection(); } /** * Checks the database server version against the required minimum * version. * * @see config/global.ini.php * @since 0.4.4 * @throws Exception if server version is less than the required version */ static public function checkDatabaseVersion() { \Zend_Registry::get('db')->checkServerVersion(); } /** * Check database connection character set is utf8. * * @return bool True if it is (or doesn't matter); false otherwise */ static public function isDatabaseConnectionUTF8() { return \Zend_Registry::get('db')->isConnectionUTF8(); } /* * Global log object */ /* * User input validation */ /** * Returns true if the email is a valid email * * @param string $email * @return bool */ static public function isValidEmailString($email) { return (preg_match('/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,7}$/D', $email) > 0); } /** * Returns true if the login is valid. * Warning: does not check if the login already exists! You must use UsersManager_API->userExists as well. * * @param string $userLogin * @throws Exception * @return bool */ static public function checkValidLoginString($userLogin) { if (!SettingsPiwik::isUserCredentialsSanityCheckEnabled() && !empty($userLogin) ) { return; } $loginMinimumLength = 3; $loginMaximumLength = 100; $l = strlen($userLogin); if (!($l >= $loginMinimumLength && $l <= $loginMaximumLength && (preg_match('/^[A-Za-z0-9_.@+-]*$/D', $userLogin) > 0)) ) { throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidLoginFormat', array($loginMinimumLength, $loginMaximumLength))); } } /* * Database and table definition methods */ /** * Is the schema available? * * @return bool True if schema is available; false otherwise */ static public function isAvailable() { return Schema::getInstance()->isAvailable(); } /** * Get the SQL to create a specific Piwik table * * @param string $tableName * @return string SQL */ static public function getTableCreateSql($tableName) { return Schema::getInstance()->getTableCreateSql($tableName); } /** * Get the SQL to create Piwik tables * * @return array array of strings containing SQL */ static public function getTablesCreateSql() { return Schema::getInstance()->getTablesCreateSql(); } /** * Create database * * @param string|null $dbName */ static public function createDatabase($dbName = null) { Schema::getInstance()->createDatabase($dbName); } /** * Drop database */ static public function dropDatabase() { Schema::getInstance()->dropDatabase(); } /** * Create all tables */ static public function createTables() { Schema::getInstance()->createTables(); } /** * Creates an entry in the User table for the "anonymous" user. */ static public function createAnonymousUser() { Schema::getInstance()->createAnonymousUser(); } /** * Truncate all tables */ static public function truncateAllTables() { Schema::getInstance()->truncateAllTables(); } /** * Drop specific tables * * @param array $doNotDelete Names of tables to not delete */ static public function dropTables($doNotDelete = array()) { Schema::getInstance()->dropTables($doNotDelete); } /** * Names of all the prefixed tables in piwik * Doesn't use the DB * * @return array Table names */ static public function getTablesNames() { return Schema::getInstance()->getTablesNames(); } /** * Get list of tables installed * * @param bool $forceReload Invalidate cache * @return array Tables installed */ static public function getTablesInstalled($forceReload = true) { return Schema::getInstance()->getTablesInstalled($forceReload); } /** * Utility function that checks if an object type is in a set of types. * * @param mixed $o * @param array $types List of class names that $o is expected to be one of. * @throws Exception if $o is not an instance of the types contained in $types. */ static public function checkObjectTypeIs($o, $types) { foreach ($types as $type) { if ($o instanceof $type) { return; } } $oType = is_object($o) ? get_class($o) : gettype($o); throw new Exception("Invalid variable type '$oType', expected one of following: " . implode(', ', $types)); } /** * Returns true if an array is an associative array, false if otherwise. * * This method determines if an array is associative by checking that the * first element's key is 0, and that each successive element's key is * one greater than the last. * * @param array $array * @return bool */ static public function isAssociativeArray($array) { reset($array); if (!is_numeric(key($array)) || key($array) != 0 ) // first key must be 0 { return true; } // check that each key is == next key - 1 w/o actually indexing the array while (true) { $current = key($array); next($array); $next = key($array); if ($next === null) { break; } else if ($current + 1 != $next) { return true; } } return false; } /** * Returns the option name of the option that stores the time the archive.php * script was last run. * * @param string $period * @param string $idSite * @return string */ public static function getArchiveCronLastRunOptionName($period, $idSite) { return "lastRunArchive" . $period . "_" . $idSite; } /** * Returns the class name of an object without its namespace. * * @param mixed|string $object * @return string */ public static function getUnnamespacedClassName($object) { $className = is_string($object) ? $object : get_class($object); $parts = explode('\\', $className); return end($parts); } }