Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattpiwik <matthieu.aubry@gmail.com>2007-08-07 20:17:50 +0400
committermattpiwik <matthieu.aubry@gmail.com>2007-08-07 20:17:50 +0400
commit9716c35f6d3a68e410a9f05b60ab4b4436aeb706 (patch)
tree2187d570ae0aec3a5443e1c09715f81c0843e253
parentd94998d4b6ea972c4dd4f4ecf95972f89662a886 (diff)
- reorganized LogStats modules into separate files
- edited config.php to be able to change to path (simpletest was annoying and wouldn't want to behave properly with my include_path) - implemented a simple class for Table partitionning by date (month or day). It generates a table name depeding on a given date. Useful for the archives and maybe later for the logging engine. + wrote tests for this class git-svn-id: http://dev.piwik.org/svn/trunk@30 59fd770c-687e-43c8-a1e3-f5a4ff64c105
-rw-r--r--TODO2
-rwxr-xr-xconfig/config.ini.php7
-rwxr-xr-xindex.php10
-rw-r--r--misc/generateVisits.php15
-rwxr-xr-xmodules/Config.php9
-rwxr-xr-xmodules/Log.php3
-rw-r--r--modules/LogStats.php1280
-rw-r--r--modules/LogStats/Action.php198
-rw-r--r--modules/LogStats/Config.php41
-rw-r--r--modules/LogStats/Cookie.php228
-rw-r--r--modules/LogStats/Db.php74
-rw-r--r--modules/LogStats/Visit.php749
-rwxr-xr-xmodules/Piwik.php29
-rw-r--r--modules/TablePartitioning.php100
-rw-r--r--piwik.php11
-rwxr-xr-xtests/config_test.php37
-rwxr-xr-xtests/modules/Database.test.php8
-rwxr-xr-xtests/modules/TablePartitioning.test.php138
-rwxr-xr-xtests/modules/blank.test.php9
19 files changed, 1621 insertions, 1327 deletions
diff --git a/TODO b/TODO
index 73a41da10f..0f32bdd17a 100644
--- a/TODO
+++ b/TODO
@@ -5,7 +5,7 @@ FEATURES
- in the piwik.php process, we could do without all the information in the cookie except for the idvisitor
we could select the information last_action_time, last_id_action, etc. assuming we have the idvisitor in the cookie
this would allow to save the logs later by big bulk
-
+
BUGS
====
- the token md5 generation doesn't check that the md5 generated is unique,
diff --git a/config/config.ini.php b/config/config.ini.php
index cb2556f9c2..c410579b28 100755
--- a/config/config.ini.php
+++ b/config/config.ini.php
@@ -83,6 +83,13 @@ logger_query_profile[] = screen
;logger_query_profile[] = database
;logger_query_profile[] = file
+[log_tests]
+logger_message[] = screen
+logger_api_call[] = screen
+logger_error[] = screen
+logger_exception[] = screen
+logger_query_profile[] = screen
+
[path]
log = logs/
diff --git a/index.php b/index.php
index cc22cd8eb4..927969db53 100755
--- a/index.php
+++ b/index.php
@@ -9,8 +9,7 @@ define('PIWIK_INCLUDE_PATH', '.');
set_include_path(PIWIK_INCLUDE_PATH
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/'
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/'
- . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/modules'
- . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/models'
+ . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/modules/'
. PATH_SEPARATOR . get_include_path());
assert_options(ASSERT_ACTIVE, 1);
@@ -52,16 +51,19 @@ Zend_Loader::loadClass('Piwik');
//move into a init() method
Piwik::createConfigObject();
+
+// database object
Piwik::createDatabaseObject();
+// Create the log objects
+Piwik::createLogObject();
+
//TODO move all DB related methods in a DB static class
Piwik::createDatabase();
Piwik::createDatabaseObject();
Piwik::dropTables();
Piwik::createTables();
-// Create the log objects
-Piwik::createLogObject();
// Create auth object
$auth = Zend_Auth::getInstance();
diff --git a/misc/generateVisits.php b/misc/generateVisits.php
index 74d491f9c5..39ac9618d7 100644
--- a/misc/generateVisits.php
+++ b/misc/generateVisits.php
@@ -16,10 +16,17 @@ set_include_path(PIWIK_INCLUDE_PATH
require_once "Event/Dispatcher.php";
require_once "Common.php";
-require_once "LogStats.php";
require_once "PluginManager.php";
require_once "LogStats/Plugins.php";
+require_once "LogStats.php";
+require_once "LogStats/Plugins.php";
+require_once "LogStats/Config.php";
+require_once "LogStats/Action.php";
+require_once "LogStats/Cookie.php";
+require_once "LogStats/Db.php";
+require_once "LogStats/Visit.php";
+
$GLOBALS['DEBUGPIWIK'] = false;
@@ -326,13 +333,13 @@ class Piwik_LogStats_Generator
private function saveVisit()
{
$this->setFakeRequest();
- $process = new Piwik_LogStats_Generator_Controller;
+ $process = new Piwik_LogStats_Generator_Main;
$process->main('Piwik_LogStats_Generator_Visit');
}
}
-class Piwik_LogStats_Generator_Controller extends Piwik_LogStats_Controller
+class Piwik_LogStats_Generator_Main extends Piwik_LogStats
{
protected function sendHeader($header)
{
@@ -376,7 +383,7 @@ $generator = new Piwik_LogStats_Generator;
$generator->init();
$t = new Piwik_Timer;
-$nbActionsTotal = $generator->generate(1000,5);
+$nbActionsTotal = $generator->generate(10000,5);
echo "<br>Request per sec: ". round($nbActionsTotal / $t->getTime(),0);
echo "<br>".$t;
diff --git a/modules/Config.php b/modules/Config.php
index 541f6ac178..d39b688280 100755
--- a/modules/Config.php
+++ b/modules/Config.php
@@ -1,10 +1,12 @@
<?php
class Piwik_Config extends Zend_Config_Ini
{
- function __construct()
+ function __construct($pathIniFile = null)
{
- $pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
-
+ if(is_null($pathIniFile))
+ {
+ $pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
+ }
parent::__construct($pathIniFile, null, true);
Zend_Registry::set('config', $this);
@@ -15,6 +17,7 @@ class Piwik_Config extends Zend_Config_Ini
public function setTestEnvironment()
{
$this->database = $this->database_tests;
+ $this->log = $this->log_tests;
$this->setPrefixTables();
}
diff --git a/modules/Log.php b/modules/Log.php
index cb732bd94f..5dac91bb1b 100755
--- a/modules/Log.php
+++ b/modules/Log.php
@@ -1,6 +1,5 @@
<?php
Zend_Loader::loadClass('Zend_Log');
-Zend_Loader::loadClass('Zend_Log');
Zend_Loader::loadClass('Zend_Log_Formatter_Interface');
Zend_Loader::loadClass('Zend_Log_Writer_Stream');
Zend_Loader::loadClass('Zend_Log_Writer_Db');
@@ -22,7 +21,6 @@ class Piwik_Log extends Zend_Log
{
parent::__construct();
- Piwik::mkdir(Zend_Registry::get('config')->path->log);
$this->logToFileFilename = Zend_Registry::get('config')->path->log . $logToFileFilename;
$this->fileFormatter = $fileFormatter;
@@ -39,6 +37,7 @@ class Piwik_Log extends Zend_Log
function addWriteToFile()
{
$writerFile = new Zend_Log_Writer_Stream($this->logToFileFilename);
+ Piwik::mkdir(Zend_Registry::get('config')->path->log);
$writerFile->setFormatter( $this->fileFormatter );
$this->addWriter($writerFile);
}
diff --git a/modules/LogStats.php b/modules/LogStats.php
index 711e780375..6d7827fb0b 100644
--- a/modules/LogStats.php
+++ b/modules/LogStats.php
@@ -1,116 +1,4 @@
<?php
-
-/**
- * Simple database PDO wrapper
- *
- */
-class Piwik_LogStats_Db
-{
- private $connection;
- private $username;
- private $password;
-
- public function __construct( $host, $username, $password, $dbname)
- {
- $this->dsn = "mysql:dbname=$dbname;host=$host";
- $this->username = $username;
- $this->password = $password;
- }
-
- public function connect()
- {
- try {
- $pdoConnect = new PDO($this->dsn, $this->username, $this->password);
- $pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- $this->connection = $pdoConnect;
- } catch (PDOException $e) {
- throw new Exception("Error connecting database: ".$e->getMessage());
- }
- }
-
- public function prefixTable( $suffix )
- {
- $prefix = Piwik_LogStats_Config::getInstance()->database['tables_prefix'];
-
- return $prefix . $suffix;
- }
-
- public function fetchAll( $query, $parameters )
- {
- try {
- $sth = $this->query( $query, $parameters );
- return $sth->fetchAll(PDO::FETCH_ASSOC);
- } catch (PDOException $e) {
- throw new Exception("Error query: ".$e->getMessage());
- }
- }
-
- public function fetch( $query, $parameters )
- {
- try {
- $sth = $this->query( $query, $parameters );
- return $sth->fetch(PDO::FETCH_ASSOC);
- } catch (PDOException $e) {
- throw new Exception("Error query: ".$e->getMessage());
- }
- }
-
- public function query($query, $parameters = array())
- {
- try {
- $sth = $this->connection->prepare($query);
- $sth->execute( $parameters );
- return $sth;
- } catch (PDOException $e) {
- throw new Exception("Error query: ".$e->getMessage());
- }
- }
-
- public function lastInsertId()
- {
- return $this->connection->lastInsertId();
- }
-}
-
-/**
- * Simple class to access the configuration file
- */
-class Piwik_LogStats_Config
-{
- static private $instance = null;
-
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- $c = __CLASS__;
- self::$instance = new $c();
- }
- return self::$instance;
- }
-
- public $config = array();
-
- private function __construct()
- {
- $pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
- $this->config = parse_ini_file($pathIniFile, true);
- }
-
- public function __get( $name )
- {
- if(isset($this->config[$name]))
- {
- return $this->config[$name];
- }
- else
- {
- throw new Exception("The config element $name is not available in the configuration (check the configuration file).");
- }
- }
-}
-
-
/**
* To maximise the performance of the logging module, we use different techniques.
*
@@ -147,1174 +35,8 @@ class Piwik_LogStats_Config
* - use_cookie ; defines if we try to get/set a cookie to help recognize a unique visitor
*/
-/**
- * Simple class to handle the cookies.
- * Its features are:
- *
- * - read a cookie values
- * - edit an existing cookie and save it
- * - create a new cookie, set values, expiration date, etc. and save it
- *
- * The cookie content is saved in an optimized way.
- */
-class Piwik_LogStats_Cookie
-{
- /**
- * The name of the cookie
- */
- protected $name = null;
-
- /**
- * The expire time for the cookie (expressed in UNIX Timestamp)
- */
- protected $expire = null;
-
- /**
- * The content of the cookie
- */
- protected $value = array();
-
- const VALUE_SEPARATOR = ':';
-
- public function __construct( $cookieName, $expire = null)
- {
- $this->name = $cookieName;
-
- if(is_null($expire)
- || !is_numeric($expire)
- || $expire <= 0)
- {
- $this->expire = $this->getDefaultExpire();
- }
-
- if($this->isCookieFound())
- {
- $this->loadContentFromCookie();
- }
- }
-
- public function isCookieFound()
- {
- return isset($_COOKIE[$this->name]);
- }
-
- protected function getDefaultExpire()
- {
- return time() + 86400*365*10;
- }
-
- /**
- * taken from http://usphp.com/manual/en/function.setcookie.php
- * fix expires bug for IE users (should i say expected to fix the bug in 2.3 b2)
- * TODO setCookie: use the other parameters of the function
- */
- protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
- {
- if (!empty($Domain))
- {
- // Fix the domain to accept domains with and without 'www.'.
- if (strtolower(substr($Domain, 0, 4)) == 'www.') $Domain = substr($Domain, 4);
-
- $Domain = '.' . $Domain;
-
- // Remove port information.
- $Port = strpos($Domain, ':');
- if ($Port !== false) $Domain = substr($Domain, 0, $Port);
- }
-
- $header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
- . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
- . (empty($Path) ? '' : '; path=' . $Path)
- . (empty($Domain) ? '' : '; domain=' . $Domain)
- . (!$Secure ? '' : '; secure')
- . (!$HTTPOnly ? '' : '; HttpOnly');
-
- header($header, false);
- }
-
- protected function setP3PHeader()
- {
- header("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
- }
-
- public function deleteCookie()
- {
- $this->setP3PHeader();
- setcookie($this->name, false, time() - 86400);
- }
-
- public function save()
- {
- $this->setP3PHeader();
- $this->setCookie( $this->name, $this->generateContentString(), $this->expire);
- }
-
- /**
- * Load the cookie content into a php array
- */
- protected function loadContentFromCookie()
- {
- $cookieStr = $_COOKIE[$this->name];
-
- $values = explode( self::VALUE_SEPARATOR, $cookieStr);
- foreach($values as $nameValue)
- {
- $equalPos = strpos($nameValue, '=');
- $varName = substr($nameValue,0,$equalPos);
- $varValue = substr($nameValue,$equalPos+1);
-
- // no numeric value are base64 encoded so we need to decode them
- if(!is_numeric($varValue))
- {
- $varValue = base64_decode($varValue);
-
- // some of the values may be serialized array so we try to unserialize it
- if( ($arrayValue = @unserialize($varValue)) !== false
- // we set the unserialized version only for arrays as you can have set a serialized string on purpose
- && is_array($arrayValue)
- )
- {
- $varValue = $arrayValue;
- }
- }
-
- $this->set($varName, $varValue);
- }
- }
-
- /**
- * Returns the string to save in the cookie frpm the $this->value array of values
- *
- */
- public function generateContentString()
- {
- $cookieStr = '';
- foreach($this->value as $name=>$value)
- {
- if(is_array($value))
- {
- $value = base64_encode(serialize($value));
- }
- elseif(is_string($value))
- {
- $value = base64_encode($value);
- }
-
- $cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
- }
- $cookieStr = substr($cookieStr, 0, strlen($cookieStr)-1);
- return $cookieStr;
- }
-
- /**
- * Registers a new name => value association in the cookie.
- *
- * Registering new values is optimal if the value is a numeric value.
- * If the value is a string, it will be saved as a base64 encoded string.
- * If the value is an array, it will be saved as a serialized and base64 encoded
- * string which is not very good in terms of bytes usage.
- * You should save arrays only when you are sure about their maximum data size.
- *
- * @param string Name of the value to save; the name will be used to retrieve this value
- * @param string|array|numeric Value to save
- *
- */
- public function set( $name, $value )
- {
- $name = self::escapeValue($name);
- $this->value[$name] = $value;
- }
-
- /**
- * Returns the value defined by $name from the cookie.
- *
- * @param string|integer Index name of the value to return
- * @return mixed The value if found, false if the value is not found
- */
- public function get( $name )
- {
- $name = self::escapeValue($name);
- return isset($this->value[$name]) ? self::escapeValue($this->value[$name]) : false;
- }
-
- public function __toString()
- {
- $str = "<-- Content of the cookie '{$this->name}' <br>\n";
- foreach($this->value as $name => $value )
- {
- $str .= $name . " = " . var_export($this->get($name), true) . "<br>\n";
- }
- $str .= "--> <br>\n";
- return $str;
- }
-
- static protected function escapeValue( $value )
- {
- return Piwik_Common::sanitizeInputValues($value);
- }
-}
-
-//
-//$c = new Piwik_LogStats_Cookie( 'piwik_logstats', 86400);
-//echo $c;
-//$c->set(1,1);
-//$c->set('test',1);
-//$c->set('test2','test=432:gea785');
-//$c->set('test3',array('test=432:gea785'));
-//$c->set('test4',array(array(0=>1),1=>'test'));
-//echo $c;
-//echo "<br>";
-//echo $c->generateContentString();
-//echo "<br>";
-//$v=$c->get('more!');
-//if(empty($v)) $c->set('more!',1);
-//$c->set('more!', array($c->get('more!')));
-//$c->save();
-//$c->deleteCookie();
-
-class Piwik_LogStats_Action
-{
-
- /*
- * Specifications
- *
- * - External file tracking
- *
- * * MANUAL Download tracking
- * download = http://piwik.org/hellokity.zip
- * (name = dir1/file alias name)
- *
- * * AUTOMATIC Download tracking for a known list of file extensions.
- * Make a hit to the piwik.php with the parameter:
- * download = http://piwik.org/hellokity.zip
- *
- * When 'name' is not specified,
- * if AUTOMATIC and if anchor not empty => name = link title anchor
- * else name = path+query of the URL
- * Ex: myfiles/beta.zip
- *
- * - External link tracking
- *
- * * MANUAL External link tracking
- * outlink = http://amazon.org/test
- * (name = the big partners / amazon)
- *
- * * AUTOMATIC External link tracking
- * When a link is not detected as being part of the same website
- * AND when the url extension is not detected as being a file download
- * outlink = http://amazon.org/test
- *
- * When 'name' is not specified,
- * if AUTOMATIC and if anchor not empty => name = link title anchor
- * else name = URL
- * Ex: http://amazon.org/test
- */
- private $actionName;
- private $url;
- private $defaultActionName;
- private $nameDownloadOutlink;
-
- const TYPE_ACTION = 1;
- const TYPE_DOWNLOAD = 3;
- const TYPE_OUTLINK = 2;
-
- function __construct( $db )
- {
- $this->actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string');
-
- $downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
- $this->downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
-
- $outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
- $this->outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
-
- $nameVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_outlink_name_var'];
- $this->nameDownloadOutlink = Piwik_Common::getRequestVar( $nameVariableName, '', 'string');
-
- $this->url = Piwik_Common::getRequestVar( 'url', '', 'string');
- $this->db = $db;
- $this->defaultActionName = Piwik_LogStats_Config::getInstance()->LogStats['default_action_name'];
- }
-
- /**
- * About the Action concept:
- *
- * - An action is defined by a name.
- * - The name can be specified in the JS Code in the variable 'action_name'
- * - Handling UTF8 in the action name
- * PLUGIN_IDEA - An action is associated to URLs and link to the URL from the interface
- * PLUGIN_IDEA - An action hit by a visitor is associated to the HTML title of the page that triggered the action
- *
- * + If the name is not specified, we use the URL(path+query) to build a default name.
- * For example for "http://piwik.org/test/my_page/test.html"
- * the name would be "test/my_page/test.html"
- *
- * We make sure it is clean and displayable.
- * If the name is empty we set it to a default name.
- *
- * TODO UTF8 handling to test
- *
- */
- private function generateInfo()
- {
- if(!empty($this->downloadUrl))
- {
- $this->actionType = self::TYPE_DOWNLOAD;
- $url = $this->downloadUrl;
- $actionName = $this->nameDownloadOutlink;
- }
- elseif(!empty($this->outlinkUrl))
- {
- $this->actionType = self::TYPE_OUTLINK;
- $url = $this->outlinkUrl;
- $actionName = $this->nameDownloadOutlink;
- if( empty($actionName) )
- {
- $actionName = $url;
- }
- }
- else
- {
- $this->actionType = self::TYPE_ACTION;
- $url = $this->url;
- $actionName = $this->actionName;
- }
-
- // the ActionName wasn't specified
- if( empty($actionName) )
- {
- $parsedUrl = parse_url( $url );
-
- $actionName = '';
-
- if(isset($parsedUrl['path']))
- {
- $actionName .= substr($parsedUrl['path'], 1);
- }
-
- if(isset($parsedUrl['query']))
- {
- $actionName .= '?'.$parsedUrl['query'];
- }
- }
-
- // clean the name
- $actionName = str_replace(array("\n", "\r"), '', $actionName);
-
- if(empty($actionName))
- {
- $actionName = $this->defaultActionName;
- }
-
- $this->finalActionName = $actionName;
- }
-
- /**
- * Returns the idaction of the current action name.
- * This idaction is used in the visitor logging table to link the visit information
- * (entry action, exit action) to the actions.
- * This idaction is also used in the table that links the visits and their actions.
- *
- * The methods takes care of creating a new record in the action table if the existing
- * action name doesn't exist yet.
- *
- * @return int Id action
- */
- function getActionId()
- {
- $this->loadActionId();
- return $this->idAction;
- }
-
- /**
- * @see getActionId()
- */
- private function loadActionId()
- {
- $this->generateInfo();
-
- $name = $this->finalActionName;
- $type = $this->actionType;
-
- $idAction = $this->db->fetch(" SELECT idaction
- FROM ".$this->db->prefixTable('log_action')
- ." WHERE name = ? AND type = ?", array($name, $type) );
-
- // the action name has not been found, create it
- if($idAction === false)
- {
- $this->db->query("INSERT INTO ". $this->db->prefixTable('log_action'). "( name, type )
- VALUES (?,?)",array($name,$type) );
- $idAction = $this->db->lastInsertId();
- }
- else
- {
- $idAction = $idAction['idaction'];
- }
-
- $this->idAction = $idAction;
- }
-
- /**
- * Records in the DB the association between the visit and this action.
- */
- public function record( $idVisit, $idRefererAction, $timeSpentRefererAction)
- {
- $this->db->query("INSERT INTO ".$this->db->prefixTable('log_link_visit_action')
- ." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)",
- array($idVisit, $this->idAction, $idRefererAction, $timeSpentRefererAction)
- );
- }
-}
-
-class Piwik_LogStats_Visit
-{
- protected $cookieLog = null;
- protected $visitorInfo = array();
- protected $userSettingsInformation = null;
-
- function __construct( $db )
- {
- $this->db = $db;
-
- $idsite = Piwik_Common::getRequestVar('idsite', 0, 'int');
- if($idsite <= 0)
- {
- throw new Exception("The 'idsite' in the request is invalide.");
- }
-
- $this->idsite = $idsite;
- }
-
- protected function getCurrentDate( $format = "Y-m-d")
- {
- return date($format, $this->getCurrentTimestamp() );
- }
-
- protected function getCurrentTimestamp()
- {
- return time();
- }
-
- protected function getDatetimeFromTimestamp($timestamp)
- {
- return date("Y-m-d H:i:s",$timestamp);
- }
-
-
-
- // test if the visitor is excluded because of
- // - IP
- // - cookie
- // - configuration option?
- private function isExcluded()
- {
- $excluded = 0;
-
- if($excluded)
- {
- printDebug("Visitor excluded.");
- return true;
- }
-
- return false;
- }
-
- private function getCookieName()
- {
- return Piwik_LogStats_Config::getInstance()->LogStats['cookie_name'] . $this->idsite;
- }
-
-
- /**
- * This methods tries to see if the visitor has visited the website before.
- *
- * We have to split the visitor into one of the category
- * - Known visitor
- * - New visitor
- *
- * A known visitor is a visitor that has already visited the website in the current month.
- * We define a known visitor using the algorithm:
- *
- * 1) Checking if a cookie contains
- * // a unique id for the visitor
- * - id_visitor
- *
- * // the timestamp of the last action in the most recent visit
- * - timestamp_last_action
- *
- * // the timestamp of the first action in the most recent visit
- * - timestamp_first_action
- *
- * // the ID of the most recent visit (which could be in the past or the current visit)
- * - id_visit
- *
- * // the ID of the most recent action
- * - id_last_action
- *
- * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration.
- * We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website.
- */
- private function recognizeTheVisitor()
- {
- $this->visitorKnown = false;
-
- $this->cookieLog = new Piwik_LogStats_Cookie( $this->getCookieName() );
- /*
- * Case the visitor has the piwik cookie.
- * We make sure all the data that should saved in the cookie is available.
- */
-
- if( false !== ($idVisitor = $this->cookieLog->get( Piwik_LogStats_Controller::COOKIE_INDEX_IDVISITOR )) )
- {
- $timestampLastAction = $this->cookieLog->get( Piwik_LogStats_Controller::COOKIE_INDEX_TIMESTAMP_LAST_ACTION );
- $timestampFirstAction = $this->cookieLog->get( Piwik_LogStats_Controller::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION );
- $idVisit = $this->cookieLog->get( Piwik_LogStats_Controller::COOKIE_INDEX_ID_VISIT );
- $idLastAction = $this->cookieLog->get( Piwik_LogStats_Controller::COOKIE_INDEX_ID_LAST_ACTION );
-
- if( $timestampLastAction !== false && is_numeric($timestampLastAction)
- && $timestampFirstAction !== false && is_numeric($timestampFirstAction)
- && $idVisit !== false && is_numeric($idVisit)
- && $idLastAction !== false && is_numeric($idLastAction)
- )
- {
- $this->visitorInfo['visitor_idcookie'] = $idVisitor;
- $this->visitorInfo['visit_last_action_time'] = $timestampLastAction;
- $this->visitorInfo['visit_first_action_time'] = $timestampFirstAction;
- $this->visitorInfo['idvisit'] = $idVisit;
- $this->visitorInfo['visit_exit_idaction'] = $idLastAction;
-
- $this->visitorKnown = true;
-
- printDebug("The visitor is known because he has the piwik cookie (idcookie = {$this->visitorInfo['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
- }
- }
-
- /*
- * If the visitor doesn't have the piwik cookie, we look for a visitor that has exactly the same configuration
- * and that visited the website today.
- */
- if( !$this->visitorKnown )
- {
- $userInfo = $this->getUserSettingsInformation();
- $md5Config = $userInfo['config_md5config'];
-
- $visitRow = $this->db->fetch(
- " SELECT visitor_idcookie,
- UNIX_TIMESTAMP(visit_last_action_time) as visit_last_action_time,
- UNIX_TIMESTAMP(visit_first_action_time) as visit_first_action_time,
- idvisit,
- visit_exit_idaction
- FROM ".$this->db->prefixTable('log_visit').
- " WHERE visit_server_date = ?
- AND idsite = ?
- AND config_md5config = ?
- ORDER BY visit_last_action_time DESC
- LIMIT 1",
- array( $this->getCurrentDate(), $this->idsite, $md5Config));
- if($visitRow
- && count($visitRow) > 0)
- {
- $this->visitorInfo['visitor_idcookie'] = $visitRow['visitor_idcookie'];
- $this->visitorInfo['visit_last_action_time'] = $visitRow['visit_last_action_time'];
- $this->visitorInfo['visit_first_action_time'] = $visitRow['visit_first_action_time'];
- $this->visitorInfo['idvisit'] = $visitRow['idvisit'];
- $this->visitorInfo['visit_exit_idaction'] = $visitRow['visit_exit_idaction'];
-
- $this->visitorKnown = true;
-
- printDebug("The visitor is known because of his userSettings+IP (idcookie = {$visitRow['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
- }
- }
- }
-
- private function getUserSettingsInformation()
- {
- // we already called this method before, simply returns the result
- if(is_array($this->userSettingsInformation))
- {
- return $this->userSettingsInformation;
- }
-
-
- $plugin_Flash = Piwik_Common::getRequestVar( 'fla', 0, 'int');
- $plugin_Director = Piwik_Common::getRequestVar( 'dir', 0, 'int');
- $plugin_Quicktime = Piwik_Common::getRequestVar( 'qt', 0, 'int');
- $plugin_RealPlayer = Piwik_Common::getRequestVar( 'realp', 0, 'int');
- $plugin_Pdf = Piwik_Common::getRequestVar( 'pdf', 0, 'int');
- $plugin_WindowsMedia = Piwik_Common::getRequestVar( 'wma', 0, 'int');
- $plugin_Java = Piwik_Common::getRequestVar( 'java', 0, 'int');
- $plugin_Cookie = Piwik_Common::getRequestVar( 'cookie', 0, 'int');
-
- $userAgent = Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_USER_AGENT']);
- $aBrowserInfo = Piwik_Common::getBrowserInfo($userAgent);
- $browserName = $aBrowserInfo['name'];
- $browserVersion = $aBrowserInfo['version'];
-
- $os = Piwik_Common::getOs($userAgent);
-
- $resolution = Piwik_Common::getRequestVar('res', 'unknown', 'string');
- $colorDepth = Piwik_Common::getRequestVar('col', 32, 'numeric');
-
-
- $ip = Piwik_Common::getIp();
- $ip = ip2long($ip);
-
- $browserLang = Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
- if(is_null($browserLang))
- {
- $browserLang = '';
- }
-
-
- $configurationHash = $this->getConfigHash(
- $os,
- $browserName,
- $browserVersion,
- $resolution,
- $colorDepth,
- $plugin_Flash,
- $plugin_Director,
- $plugin_RealPlayer,
- $plugin_Pdf,
- $plugin_WindowsMedia,
- $plugin_Java,
- $plugin_Cookie,
- $ip,
- $browserLang);
-
- $this->userSettingsInformation = array(
- 'config_md5config' => $configurationHash,
- 'config_os' => $os,
- 'config_browser_name' => $browserName,
- 'config_browser_version' => $browserVersion,
- 'config_resolution' => $resolution,
- 'config_color_depth' => $colorDepth,
- 'config_pdf' => $plugin_Pdf,
- 'config_flash' => $plugin_Flash,
- 'config_java' => $plugin_Java,
- 'config_director' => $plugin_Director,
- 'config_quicktime' => $plugin_Quicktime,
- 'config_realplayer' => $plugin_RealPlayer,
- 'config_windowsmedia' => $plugin_WindowsMedia,
- 'config_cookie' => $plugin_RealPlayer,
- 'location_ip' => $ip,
- 'location_browser_lang' => $browserLang,
- );
-
- return $this->userSettingsInformation;
- }
-
- /**
- * Returns true if the last action was done during the last 30 minutes
- */
- private function isLastActionInTheSameVisit()
- {
- return $this->visitorInfo['visit_last_action_time']
- >= ($this->getCurrentTimestamp() - Piwik_LogStats_Controller::VISIT_STANDARD_LENGTH);
- }
-
- private function isVisitorKnown()
- {
- return $this->visitorKnown === true;
- }
-
- /**
- * Once we have the visitor information, we have to define if the visit is a new or a known visit.
- *
- * 1) When the last action was done more than 30min ago,
- * or if the visitor is new, then this is a new visit.
- *
- * 2) If the last action is less than 30min ago, then the same visit is going on.
- * Because the visit goes on, we can get the time spent during the last action.
- *
- * NB:
- * - In the case of a new visit, then the time spent
- * during the last action of the previous visit is unknown.
- *
- * - In the case of a new visit but with a known visitor,
- * we can set the 'returning visitor' flag.
- *
- */
-
- /**
- * In all the cases we set a cookie to the visitor with the new information.
- */
- public function handle()
- {
- if(!$this->isExcluded())
- {
- $this->recognizeTheVisitor();
-
- // known visitor
- if($this->isVisitorKnown())
- {
- // the same visit is going on
- if($this->isLastActionInTheSameVisit())
- {
- $this->handleKnownVisit();
- }
- // new visit
- else
- {
- $this->handleNewVisit();
- }
- }
- // new visitor => new visit
- else
- {
- $this->handleNewVisit();
- }
-
- // we update the cookie with the new visit information
- $this->updateCookie();
-
- }
- }
-
- private function updateCookie()
- {
- printDebug("We manage the cookie...");
-
- // idcookie has been generated in handleNewVisit or we simply propagate the old value
- $this->cookieLog->set( Piwik_LogStats_Controller::COOKIE_INDEX_IDVISITOR,
- $this->visitorInfo['visitor_idcookie'] );
-
- // the last action timestamp is the current timestamp
- $this->cookieLog->set( Piwik_LogStats_Controller::COOKIE_INDEX_TIMESTAMP_LAST_ACTION,
- $this->visitorInfo['visit_last_action_time'] );
-
- // the first action timestamp is the timestamp of the first action of the current visit
- $this->cookieLog->set( Piwik_LogStats_Controller::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION,
- $this->visitorInfo['visit_first_action_time'] );
-
- // the idvisit has been generated by mysql in handleNewVisit or simply propagated here
- $this->cookieLog->set( Piwik_LogStats_Controller::COOKIE_INDEX_ID_VISIT,
- $this->visitorInfo['idvisit'] );
-
- // the last action ID is the current exit idaction
- $this->cookieLog->set( Piwik_LogStats_Controller::COOKIE_INDEX_ID_LAST_ACTION,
- $this->visitorInfo['visit_exit_idaction'] );
-
- $this->cookieLog->save();
- }
-
-
- /**
- * In the case of a known visit, we have to do the following actions:
- *
- * 1) Insert the new action
- *
- * 2) Update the visit information
- */
- private function handleKnownVisit()
- {
- printDebug("Visit known.");
-
- /**
- * Init the action
- */
- $action = new Piwik_LogStats_Action( $this->db );
-
- $actionId = $action->getActionId();
-
- printDebug("idAction = $actionId");
-
- $serverTime = $this->getCurrentTimestamp();
- $datetimeServer = $this->getDatetimeFromTimestamp($serverTime);
-
- $this->db->query("UPDATE ". $this->db->prefixTable('log_visit')."
- SET visit_last_action_time = ?,
- visit_exit_idaction = ?,
- visit_total_actions = visit_total_actions + 1,
- visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time)
- WHERE idvisit = ?
- LIMIT 1",
- array( $datetimeServer,
- $actionId,
- $this->visitorInfo['idvisit'] )
- );
- /**
- * Save the action
- */
- $timespentLastAction = $serverTime - $this->visitorInfo['visit_last_action_time'];
-
- $action->record( $this->visitorInfo['idvisit'],
- $this->visitorInfo['visit_exit_idaction'],
- $timespentLastAction
- );
-
-
- /**
- * Cookie fields to be updated
- */
- $this->visitorInfo['visit_last_action_time'] = $serverTime;
- $this->visitorInfo['visit_exit_idaction'] = $actionId;
-
-
- }
-
- /**
- * In the case of a new visit, we have to do the following actions:
- *
- * 1) Insert the new action
- *
- * 2) Insert the visit information
- */
- private function handleNewVisit()
- {
- printDebug("New Visit.");
-
- /**
- * Get the variables from the REQUEST
- */
-
- // Configuration settings
- $userInfo = $this->getUserSettingsInformation();
-
- // General information
- $localTime = Piwik_Common::getRequestVar( 'h', $this->getCurrentDate("H"), 'numeric')
- .':'. Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'numeric')
- .':'. Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'numeric');
- $serverDate = $this->getCurrentDate();
- $serverTime = $this->getCurrentTimestamp();
-
- if($this->isVisitorKnown())
- {
- $idcookie = $this->visitorInfo['visitor_idcookie'];
- $returningVisitor = 1;
- }
- else
- {
- $idcookie = $this->getVisitorUniqueId();
- $returningVisitor = 0;
- }
-
- $defaultTimeOnePageVisit = Piwik_LogStats_Config::getInstance()->LogStats['default_time_one_page_visit'];
-
- // Location information
- $country = Piwik_Common::getCountry($userInfo['location_browser_lang']);
- $continent = Piwik_Common::getContinent( $country );
-
- //Referer information
- $refererInfo = $this->getRefererInformation();
-
- /**
- * Init the action
- */
- $action = new Piwik_LogStats_Action( $this->db );
-
- $actionId = $action->getActionId();
-
- printDebug("idAction = $actionId");
-
-
- /**
- * Save the visitor
- */
- $informationToSave = array(
- //'idvisit' => ,
- 'idsite' => $this->idsite,
- 'visitor_localtime' => $localTime,
- 'visitor_idcookie' => $idcookie,
- 'visitor_returning' => $returningVisitor,
- 'visit_first_action_time' => $this->getDatetimeFromTimestamp($serverTime),
- 'visit_last_action_time' => $this->getDatetimeFromTimestamp($serverTime),
- 'visit_server_date' => $serverDate,
- 'visit_entry_idaction' => $actionId,
- 'visit_exit_idaction' => $actionId,
- 'visit_total_actions' => 1,
- 'visit_total_time' => $defaultTimeOnePageVisit,
- 'referer_type' => $refererInfo['referer_type'],
- 'referer_name' => $refererInfo['referer_name'],
- 'referer_url' => $refererInfo['referer_url'],
- 'referer_keyword' => $refererInfo['referer_keyword'],
- 'config_md5config' => $userInfo['config_md5config'],
- 'config_os' => $userInfo['config_os'],
- 'config_browser_name' => $userInfo['config_browser_name'],
- 'config_browser_version' => $userInfo['config_browser_version'],
- 'config_resolution' => $userInfo['config_resolution'],
- 'config_color_depth' => $userInfo['config_color_depth'],
- 'config_pdf' => $userInfo['config_pdf'],
- 'config_flash' => $userInfo['config_flash'],
- 'config_java' => $userInfo['config_java'],
- 'config_director' => $userInfo['config_director'],
- 'config_quicktime' => $userInfo['config_quicktime'],
- 'config_realplayer' => $userInfo['config_realplayer'],
- 'config_windowsmedia' => $userInfo['config_windowsmedia'],
- 'config_cookie' => $userInfo['config_cookie'],
- 'location_ip' => $userInfo['location_ip'],
- 'location_browser_lang' => $userInfo['location_browser_lang'],
- 'location_country' => $country,
- 'location_continent' => $continent,
- );
-
-
- $fields = implode(", ", array_keys($informationToSave));
- $values = substr(str_repeat( "?,",count($informationToSave)),0,-1);
-
- $this->db->query( "INSERT INTO ".$this->db->prefixTable('log_visit').
- " ($fields) VALUES ($values)", array_values($informationToSave));
-
- $idVisit = $this->db->lastInsertId();
-
- // Update the visitor information attribute with this information array
- $this->visitorInfo = $informationToSave;
- $this->visitorInfo['idvisit'] = $idVisit;
-
- // we have to save timestamp in the object properties, whereas mysql eats some other datetime format
- $this->visitorInfo['visit_first_action_time'] = $serverTime;
- $this->visitorInfo['visit_last_action_time'] = $serverTime;
-
- /**
- * Save the action
- */
- $action->record( $idVisit, 0, 0 );
-
- }
-
- /**
- * Returns an array containing the following information:
- * - referer_type
- * - direct -- absence of referer URL OR referer URL has the same host
- * - site -- based on the referer URL
- * - search_engine -- based on the referer URL
- * - campaign -- based on campaign URL parameter
- * - newsletter -- based on newsletter URL parameter
- * - partner -- based on partner URL parameter
- *
- * - referer_name
- * - ()
- * - piwik.net -- site host name
- * - google.fr -- search engine host name
- * - adwords-search -- campaign name
- * - beta-release -- newsletter name
- * - my-nice-partner -- partner name
- *
- * - referer_keyword
- * - ()
- * - ()
- * - my keyword
- * - my paid keyword
- * - ()
- * - ()
- *
- * - referer_url : the same for all the referer types
- *
- */
- private function getRefererInformation()
- {
- // bool that says if the referer detection is done
- $refererAnalyzed = false;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- $nameRefererAnalyzed = '';
- $keywordRefererAnalyzed = '';
-
- $refererUrl = Piwik_Common::getRequestVar( 'urlref', '', 'string');
- $currentUrl = Piwik_Common::getRequestVar( 'url', '', 'string');
-
- $refererUrlParse = @parse_url($refererUrl);
- $currentUrlParse = @parse_url($currentUrl);
-
- if(isset($refererUrlParse['host'])
- && !empty($refererUrlParse['host']))
- {
-
- $refererHost = $refererUrlParse['host'];
- $refererSH = $refererUrlParse['scheme'].'://'.$refererUrlParse['host'];
-
- /*
- * Search engine detection
- */
- if( !$refererAnalyzed )
- {
- /*
- * A referer is a search engine if the URL's host is in the SearchEngines array
- * and if we found the keyword in the URL.
- *
- * For example if someone comes from http://www.google.com/partners.html this will not
- * be counted as a search engines, but as a website referer from google.com (because the
- * keyword couldn't be found in the URL)
- */
- require_once PIWIK_DATAFILES_INCLUDE_PATH . "/SearchEngines.php";
-
- if(array_key_exists($refererHost, $GLOBALS['Piwik_SearchEngines']))
- {
- // which search engine ?
- $searchEngineName = $GLOBALS['Piwik_SearchEngines'][$refererHost][0];
- $variableName = $GLOBALS['Piwik_SearchEngines'][$refererHost][1];
-
- // if there is a query, there may be a keyword...
- if(isset($refererUrlParse['query']))
- {
- $query = $refererUrlParse['query'];
-
- //TODO: change the search engine file and use REGEXP; performance downside?
- //TODO: port the phpmyvisites google-images hack here
-
- // search for keywords now &vname=keyword
- $key = strtolower(Piwik_Common::getParameterFromQueryString($query, $variableName));
-
- //TODO test the search engine non-utf8 support
- // for search engines that don't use utf-8
- if((function_exists('iconv'))
- && (isset($GLOBALS['Piwik_SearchEngines'][$refererHost][2])))
- {
- $charset = trim($GLOBALS['searchEngines'][$refererHost][2]);
-
- if(!empty($charset))
- {
- $key = htmlspecialchars(
- @iconv( $charset,
- 'utf-8//TRANSLIT',
- htmlspecialchars_decode($key, Piwik_Common::HTML_ENCODING_QUOTE_STYLE))
- , Piwik_Common::HTML_ENCODING_QUOTE_STYLE);
- }
- }
-
-
- if(!empty($key))
- {
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_SEARCH_ENGINE;
- $nameRefererAnalyzed = $searchEngineName;
- $keywordRefererAnalyzed = $key;
- }
- }
- }
- }
-
- /*
- * Newsletter analysis
- */
- if( !$refererAnalyzed )
- {
- if(isset($currentUrlParse['query']))
- {
- $newsletterVariableName = Piwik_LogStats_Config::getInstance()->LogStats['newsletter_var_name'];
- $newsletterVar = Piwik_Common::getParameterFromQueryString( $currentUrlParse['query'], $newsletterVariableName);
-
- if($newsletterVar !== false && !empty($newsletterVar))
- {
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_NEWSLETTER;
- $nameRefererAnalyzed = $newsletterVar;
- }
- }
- }
-
- /*
- * Partner analysis
- */
- //TODO handle partner from a list of known partner URLs
- if( !$refererAnalyzed )
- {
- if(isset($currentUrlParse['query']))
- {
- $partnerVariableName = Piwik_LogStats_Config::getInstance()->LogStats['partner_var_name'];
- $partnerVar = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $partnerVariableName);
-
- if($partnerVar !== false && !empty($partnerVar))
- {
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_PARTNER;
- $nameRefererAnalyzed = $partnerVar;
- }
- }
- }
-
- /*
- * Campaign analysis
- */
- if( !$refererAnalyzed )
- {
- if(isset($currentUrlParse['query']))
- {
- $campaignVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_var_name'];
- $campaignName = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignVariableName);
-
- if( $campaignName !== false && !empty($campaignName))
- {
- $campaignKeywordVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_keyword_var_name'];
- $campaignKeyword = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignKeywordVariableName);
-
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_CAMPAIGN;
- $nameRefererAnalyzed = $campaignName;
-
- if(!empty($campaignKeyword))
- {
- $keywordRefererAnalyzed = $campaignKeyword;
- }
- }
- }
- }
-
- /*
- * Direct entry (referer host is similar to current host)
- * And we have previously tried to detect the newsletter/partner/campaign variables in the URL
- * so it can only be a direct access
- */
- if( !$refererAnalyzed )
- {
- $currentUrlParse = @parse_url($currentUrl);
-
- if(isset($currentUrlParse['host']))
- {
- $currentHost = $currentUrlParse['host'];
- $currentSH = $currentUrlParse['scheme'].'://'.$currentUrlParse['host'];
-
- if($currentHost == $refererHost)
- {
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- }
- }
-
- }
-
- /*
- * Normal website referer
- */
- if( !$refererAnalyzed )
- {
- $refererAnalyzed = true;
- $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_WEBSITE;
- $nameRefererAnalyzed = $refererHost;
- }
- }
-
-
- $refererInformation = array(
- 'referer_type' => $typeRefererAnalyzed,
- 'referer_name' => $nameRefererAnalyzed,
- 'referer_keyword' => $keywordRefererAnalyzed,
- 'referer_url' => $refererUrl,
- );
-
- return $refererInformation;
- }
-
- private function getConfigHash( $os, $browserName, $browserVersion, $resolution, $colorDepth, $plugin_Flash, $plugin_Director, $plugin_RealPlayer, $plugin_Pdf, $plugin_WindowsMedia, $plugin_Java, $plugin_Cookie, $ip, $browserLang)
- {
- return md5( $os . $browserName . $browserVersion . $resolution . $colorDepth . $plugin_Flash . $plugin_Director . $plugin_RealPlayer . $plugin_Pdf . $plugin_WindowsMedia . $plugin_Java . $plugin_Cookie . $ip . $browserLang );
- }
-
- private function getVisitorUniqueId()
- {
- if($this->isVisitorKnown())
- {
- return -1;
- }
- else
- {
- return Piwik_Common::generateUniqId();
- }
- }
-
-}
-class Piwik_LogStats_Controller
+class Piwik_LogStats
{
private $stateValid;
diff --git a/modules/LogStats/Action.php b/modules/LogStats/Action.php
new file mode 100644
index 0000000000..021ff35328
--- /dev/null
+++ b/modules/LogStats/Action.php
@@ -0,0 +1,198 @@
+<?php
+
+class Piwik_LogStats_Action
+{
+
+ /*
+ * Specifications
+ *
+ * - External file tracking
+ *
+ * * MANUAL Download tracking
+ * download = http://piwik.org/hellokity.zip
+ * (name = dir1/file alias name)
+ *
+ * * AUTOMATIC Download tracking for a known list of file extensions.
+ * Make a hit to the piwik.php with the parameter:
+ * download = http://piwik.org/hellokity.zip
+ *
+ * When 'name' is not specified,
+ * if AUTOMATIC and if anchor not empty => name = link title anchor
+ * else name = path+query of the URL
+ * Ex: myfiles/beta.zip
+ *
+ * - External link tracking
+ *
+ * * MANUAL External link tracking
+ * outlink = http://amazon.org/test
+ * (name = the big partners / amazon)
+ *
+ * * AUTOMATIC External link tracking
+ * When a link is not detected as being part of the same website
+ * AND when the url extension is not detected as being a file download
+ * outlink = http://amazon.org/test
+ *
+ * When 'name' is not specified,
+ * if AUTOMATIC and if anchor not empty => name = link title anchor
+ * else name = URL
+ * Ex: http://amazon.org/test
+ */
+ private $actionName;
+ private $url;
+ private $defaultActionName;
+ private $nameDownloadOutlink;
+
+ const TYPE_ACTION = 1;
+ const TYPE_DOWNLOAD = 3;
+ const TYPE_OUTLINK = 2;
+
+ function __construct( $db )
+ {
+ $this->actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string');
+
+ $downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
+ $this->downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
+
+ $outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
+ $this->outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
+
+ $nameVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_outlink_name_var'];
+ $this->nameDownloadOutlink = Piwik_Common::getRequestVar( $nameVariableName, '', 'string');
+
+ $this->url = Piwik_Common::getRequestVar( 'url', '', 'string');
+ $this->db = $db;
+ $this->defaultActionName = Piwik_LogStats_Config::getInstance()->LogStats['default_action_name'];
+ }
+
+ /**
+ * About the Action concept:
+ *
+ * - An action is defined by a name.
+ * - The name can be specified in the JS Code in the variable 'action_name'
+ * - Handling UTF8 in the action name
+ * PLUGIN_IDEA - An action is associated to URLs and link to the URL from the interface
+ * PLUGIN_IDEA - An action hit by a visitor is associated to the HTML title of the page that triggered the action
+ *
+ * + If the name is not specified, we use the URL(path+query) to build a default name.
+ * For example for "http://piwik.org/test/my_page/test.html"
+ * the name would be "test/my_page/test.html"
+ *
+ * We make sure it is clean and displayable.
+ * If the name is empty we set it to a default name.
+ *
+ * TODO UTF8 handling to test
+ *
+ */
+ private function generateInfo()
+ {
+ if(!empty($this->downloadUrl))
+ {
+ $this->actionType = self::TYPE_DOWNLOAD;
+ $url = $this->downloadUrl;
+ $actionName = $this->nameDownloadOutlink;
+ }
+ elseif(!empty($this->outlinkUrl))
+ {
+ $this->actionType = self::TYPE_OUTLINK;
+ $url = $this->outlinkUrl;
+ $actionName = $this->nameDownloadOutlink;
+ if( empty($actionName) )
+ {
+ $actionName = $url;
+ }
+ }
+ else
+ {
+ $this->actionType = self::TYPE_ACTION;
+ $url = $this->url;
+ $actionName = $this->actionName;
+ }
+
+ // the ActionName wasn't specified
+ if( empty($actionName) )
+ {
+ $parsedUrl = parse_url( $url );
+
+ $actionName = '';
+
+ if(isset($parsedUrl['path']))
+ {
+ $actionName .= substr($parsedUrl['path'], 1);
+ }
+
+ if(isset($parsedUrl['query']))
+ {
+ $actionName .= '?'.$parsedUrl['query'];
+ }
+ }
+
+ // clean the name
+ $actionName = str_replace(array("\n", "\r"), '', $actionName);
+
+ if(empty($actionName))
+ {
+ $actionName = $this->defaultActionName;
+ }
+
+ $this->finalActionName = $actionName;
+ }
+
+ /**
+ * Returns the idaction of the current action name.
+ * This idaction is used in the visitor logging table to link the visit information
+ * (entry action, exit action) to the actions.
+ * This idaction is also used in the table that links the visits and their actions.
+ *
+ * The methods takes care of creating a new record in the action table if the existing
+ * action name doesn't exist yet.
+ *
+ * @return int Id action
+ */
+ function getActionId()
+ {
+ $this->loadActionId();
+ return $this->idAction;
+ }
+
+ /**
+ * @see getActionId()
+ */
+ private function loadActionId()
+ {
+ $this->generateInfo();
+
+ $name = $this->finalActionName;
+ $type = $this->actionType;
+
+ $idAction = $this->db->fetch(" SELECT idaction
+ FROM ".$this->db->prefixTable('log_action')
+ ." WHERE name = ? AND type = ?", array($name, $type) );
+
+ // the action name has not been found, create it
+ if($idAction === false)
+ {
+ $this->db->query("INSERT INTO ". $this->db->prefixTable('log_action'). "( name, type )
+ VALUES (?,?)",array($name,$type) );
+ $idAction = $this->db->lastInsertId();
+ }
+ else
+ {
+ $idAction = $idAction['idaction'];
+ }
+
+ $this->idAction = $idAction;
+ }
+
+ /**
+ * Records in the DB the association between the visit and this action.
+ */
+ public function record( $idVisit, $idRefererAction, $timeSpentRefererAction)
+ {
+ $this->db->query("INSERT INTO ".$this->db->prefixTable('log_link_visit_action')
+ ." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)",
+ array($idVisit, $this->idAction, $idRefererAction, $timeSpentRefererAction)
+ );
+ }
+}
+
+?>
diff --git a/modules/LogStats/Config.php b/modules/LogStats/Config.php
new file mode 100644
index 0000000000..bd486ac519
--- /dev/null
+++ b/modules/LogStats/Config.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Simple class to access the configuration file
+ */
+class Piwik_LogStats_Config
+{
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public $config = array();
+
+ private function __construct()
+ {
+ $pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
+ $this->config = parse_ini_file($pathIniFile, true);
+ }
+
+ public function __get( $name )
+ {
+ if(isset($this->config[$name]))
+ {
+ return $this->config[$name];
+ }
+ else
+ {
+ throw new Exception("The config element $name is not available in the configuration (check the configuration file).");
+ }
+ }
+}
+
+?>
diff --git a/modules/LogStats/Cookie.php b/modules/LogStats/Cookie.php
new file mode 100644
index 0000000000..4799e0367f
--- /dev/null
+++ b/modules/LogStats/Cookie.php
@@ -0,0 +1,228 @@
+<?php
+
+/**
+ * Simple class to handle the cookies.
+ * Its features are:
+ *
+ * - read a cookie values
+ * - edit an existing cookie and save it
+ * - create a new cookie, set values, expiration date, etc. and save it
+ *
+ * The cookie content is saved in an optimized way.
+ */
+class Piwik_LogStats_Cookie
+{
+ /**
+ * The name of the cookie
+ */
+ protected $name = null;
+
+ /**
+ * The expire time for the cookie (expressed in UNIX Timestamp)
+ */
+ protected $expire = null;
+
+ /**
+ * The content of the cookie
+ */
+ protected $value = array();
+
+ const VALUE_SEPARATOR = ':';
+
+ public function __construct( $cookieName, $expire = null)
+ {
+ $this->name = $cookieName;
+
+ if(is_null($expire)
+ || !is_numeric($expire)
+ || $expire <= 0)
+ {
+ $this->expire = $this->getDefaultExpire();
+ }
+
+ if($this->isCookieFound())
+ {
+ $this->loadContentFromCookie();
+ }
+ }
+
+ public function isCookieFound()
+ {
+ return isset($_COOKIE[$this->name]);
+ }
+
+ protected function getDefaultExpire()
+ {
+ return time() + 86400*365*10;
+ }
+
+ /**
+ * taken from http://usphp.com/manual/en/function.setcookie.php
+ * fix expires bug for IE users (should i say expected to fix the bug in 2.3 b2)
+ * TODO setCookie: use the other parameters of the function
+ */
+ protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
+ {
+ if (!empty($Domain))
+ {
+ // Fix the domain to accept domains with and without 'www.'.
+ if (strtolower(substr($Domain, 0, 4)) == 'www.') $Domain = substr($Domain, 4);
+
+ $Domain = '.' . $Domain;
+
+ // Remove port information.
+ $Port = strpos($Domain, ':');
+ if ($Port !== false) $Domain = substr($Domain, 0, $Port);
+ }
+
+ $header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
+ . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
+ . (empty($Path) ? '' : '; path=' . $Path)
+ . (empty($Domain) ? '' : '; domain=' . $Domain)
+ . (!$Secure ? '' : '; secure')
+ . (!$HTTPOnly ? '' : '; HttpOnly');
+
+ header($header, false);
+ }
+
+ protected function setP3PHeader()
+ {
+ header("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
+ }
+
+ public function deleteCookie()
+ {
+ $this->setP3PHeader();
+ setcookie($this->name, false, time() - 86400);
+ }
+
+ public function save()
+ {
+ $this->setP3PHeader();
+ $this->setCookie( $this->name, $this->generateContentString(), $this->expire);
+ }
+
+ /**
+ * Load the cookie content into a php array
+ */
+ protected function loadContentFromCookie()
+ {
+ $cookieStr = $_COOKIE[$this->name];
+
+ $values = explode( self::VALUE_SEPARATOR, $cookieStr);
+ foreach($values as $nameValue)
+ {
+ $equalPos = strpos($nameValue, '=');
+ $varName = substr($nameValue,0,$equalPos);
+ $varValue = substr($nameValue,$equalPos+1);
+
+ // no numeric value are base64 encoded so we need to decode them
+ if(!is_numeric($varValue))
+ {
+ $varValue = base64_decode($varValue);
+
+ // some of the values may be serialized array so we try to unserialize it
+ if( ($arrayValue = @unserialize($varValue)) !== false
+ // we set the unserialized version only for arrays as you can have set a serialized string on purpose
+ && is_array($arrayValue)
+ )
+ {
+ $varValue = $arrayValue;
+ }
+ }
+
+ $this->set($varName, $varValue);
+ }
+ }
+
+ /**
+ * Returns the string to save in the cookie frpm the $this->value array of values
+ *
+ */
+ public function generateContentString()
+ {
+ $cookieStr = '';
+ foreach($this->value as $name=>$value)
+ {
+ if(is_array($value))
+ {
+ $value = base64_encode(serialize($value));
+ }
+ elseif(is_string($value))
+ {
+ $value = base64_encode($value);
+ }
+
+ $cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
+ }
+ $cookieStr = substr($cookieStr, 0, strlen($cookieStr)-1);
+ return $cookieStr;
+ }
+
+ /**
+ * Registers a new name => value association in the cookie.
+ *
+ * Registering new values is optimal if the value is a numeric value.
+ * If the value is a string, it will be saved as a base64 encoded string.
+ * If the value is an array, it will be saved as a serialized and base64 encoded
+ * string which is not very good in terms of bytes usage.
+ * You should save arrays only when you are sure about their maximum data size.
+ *
+ * @param string Name of the value to save; the name will be used to retrieve this value
+ * @param string|array|numeric Value to save
+ *
+ */
+ public function set( $name, $value )
+ {
+ $name = self::escapeValue($name);
+ $this->value[$name] = $value;
+ }
+
+ /**
+ * Returns the value defined by $name from the cookie.
+ *
+ * @param string|integer Index name of the value to return
+ * @return mixed The value if found, false if the value is not found
+ */
+ public function get( $name )
+ {
+ $name = self::escapeValue($name);
+ return isset($this->value[$name]) ? self::escapeValue($this->value[$name]) : false;
+ }
+
+ public function __toString()
+ {
+ $str = "<-- Content of the cookie '{$this->name}' <br>\n";
+ foreach($this->value as $name => $value )
+ {
+ $str .= $name . " = " . var_export($this->get($name), true) . "<br>\n";
+ }
+ $str .= "--> <br>\n";
+ return $str;
+ }
+
+ static protected function escapeValue( $value )
+ {
+ return Piwik_Common::sanitizeInputValues($value);
+ }
+}
+
+//
+//$c = new Piwik_LogStats_Cookie( 'piwik_logstats', 86400);
+//echo $c;
+//$c->set(1,1);
+//$c->set('test',1);
+//$c->set('test2','test=432:gea785');
+//$c->set('test3',array('test=432:gea785'));
+//$c->set('test4',array(array(0=>1),1=>'test'));
+//echo $c;
+//echo "<br>";
+//echo $c->generateContentString();
+//echo "<br>";
+//$v=$c->get('more!');
+//if(empty($v)) $c->set('more!',1);
+//$c->set('more!', array($c->get('more!')));
+//$c->save();
+//$c->deleteCookie();
+
+?>
diff --git a/modules/LogStats/Db.php b/modules/LogStats/Db.php
new file mode 100644
index 0000000000..1231de5eac
--- /dev/null
+++ b/modules/LogStats/Db.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Simple database PDO wrapper
+ *
+ */
+class Piwik_LogStats_Db
+{
+ private $connection;
+ private $username;
+ private $password;
+
+ public function __construct( $host, $username, $password, $dbname)
+ {
+ $this->dsn = "mysql:dbname=$dbname;host=$host";
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ public function connect()
+ {
+ try {
+ $pdoConnect = new PDO($this->dsn, $this->username, $this->password);
+ $pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->connection = $pdoConnect;
+ } catch (PDOException $e) {
+ throw new Exception("Error connecting database: ".$e->getMessage());
+ }
+ }
+
+ public function prefixTable( $suffix )
+ {
+ $prefix = Piwik_LogStats_Config::getInstance()->database['tables_prefix'];
+
+ return $prefix . $suffix;
+ }
+
+ public function fetchAll( $query, $parameters )
+ {
+ try {
+ $sth = $this->query( $query, $parameters );
+ return $sth->fetchAll(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ throw new Exception("Error query: ".$e->getMessage());
+ }
+ }
+
+ public function fetch( $query, $parameters )
+ {
+ try {
+ $sth = $this->query( $query, $parameters );
+ return $sth->fetch(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ throw new Exception("Error query: ".$e->getMessage());
+ }
+ }
+
+ public function query($query, $parameters = array())
+ {
+ try {
+ $sth = $this->connection->prepare($query);
+ $sth->execute( $parameters );
+ return $sth;
+ } catch (PDOException $e) {
+ throw new Exception("Error query: ".$e->getMessage());
+ }
+ }
+
+ public function lastInsertId()
+ {
+ return $this->connection->lastInsertId();
+ }
+}
+?>
diff --git a/modules/LogStats/Visit.php b/modules/LogStats/Visit.php
new file mode 100644
index 0000000000..026b17636a
--- /dev/null
+++ b/modules/LogStats/Visit.php
@@ -0,0 +1,749 @@
+<?php
+
+class Piwik_LogStats_Visit
+{
+ protected $cookieLog = null;
+ protected $visitorInfo = array();
+ protected $userSettingsInformation = null;
+
+ function __construct( $db )
+ {
+ $this->db = $db;
+
+ $idsite = Piwik_Common::getRequestVar('idsite', 0, 'int');
+ if($idsite <= 0)
+ {
+ throw new Exception("The 'idsite' in the request is invalide.");
+ }
+
+ $this->idsite = $idsite;
+ }
+
+ protected function getCurrentDate( $format = "Y-m-d")
+ {
+ return date($format, $this->getCurrentTimestamp() );
+ }
+
+ protected function getCurrentTimestamp()
+ {
+ return time();
+ }
+
+ protected function getDatetimeFromTimestamp($timestamp)
+ {
+ return date("Y-m-d H:i:s",$timestamp);
+ }
+
+
+
+ // test if the visitor is excluded because of
+ // - IP
+ // - cookie
+ // - configuration option?
+ private function isExcluded()
+ {
+ $excluded = 0;
+
+ if($excluded)
+ {
+ printDebug("Visitor excluded.");
+ return true;
+ }
+
+ return false;
+ }
+
+ private function getCookieName()
+ {
+ return Piwik_LogStats_Config::getInstance()->LogStats['cookie_name'] . $this->idsite;
+ }
+
+
+ /**
+ * This methods tries to see if the visitor has visited the website before.
+ *
+ * We have to split the visitor into one of the category
+ * - Known visitor
+ * - New visitor
+ *
+ * A known visitor is a visitor that has already visited the website in the current month.
+ * We define a known visitor using the algorithm:
+ *
+ * 1) Checking if a cookie contains
+ * // a unique id for the visitor
+ * - id_visitor
+ *
+ * // the timestamp of the last action in the most recent visit
+ * - timestamp_last_action
+ *
+ * // the timestamp of the first action in the most recent visit
+ * - timestamp_first_action
+ *
+ * // the ID of the most recent visit (which could be in the past or the current visit)
+ * - id_visit
+ *
+ * // the ID of the most recent action
+ * - id_last_action
+ *
+ * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration.
+ * We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website.
+ */
+ private function recognizeTheVisitor()
+ {
+ $this->visitorKnown = false;
+
+ $this->cookieLog = new Piwik_LogStats_Cookie( $this->getCookieName() );
+ /*
+ * Case the visitor has the piwik cookie.
+ * We make sure all the data that should saved in the cookie is available.
+ */
+
+ if( false !== ($idVisitor = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_IDVISITOR )) )
+ {
+ $timestampLastAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_LAST_ACTION );
+ $timestampFirstAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION );
+ $idVisit = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_ID_VISIT );
+ $idLastAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_ID_LAST_ACTION );
+
+ if( $timestampLastAction !== false && is_numeric($timestampLastAction)
+ && $timestampFirstAction !== false && is_numeric($timestampFirstAction)
+ && $idVisit !== false && is_numeric($idVisit)
+ && $idLastAction !== false && is_numeric($idLastAction)
+ )
+ {
+ $this->visitorInfo['visitor_idcookie'] = $idVisitor;
+ $this->visitorInfo['visit_last_action_time'] = $timestampLastAction;
+ $this->visitorInfo['visit_first_action_time'] = $timestampFirstAction;
+ $this->visitorInfo['idvisit'] = $idVisit;
+ $this->visitorInfo['visit_exit_idaction'] = $idLastAction;
+
+ $this->visitorKnown = true;
+
+ printDebug("The visitor is known because he has the piwik cookie (idcookie = {$this->visitorInfo['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
+ }
+ }
+
+ /*
+ * If the visitor doesn't have the piwik cookie, we look for a visitor that has exactly the same configuration
+ * and that visited the website today.
+ */
+ if( !$this->visitorKnown )
+ {
+ $userInfo = $this->getUserSettingsInformation();
+ $md5Config = $userInfo['config_md5config'];
+
+ $visitRow = $this->db->fetch(
+ " SELECT visitor_idcookie,
+ UNIX_TIMESTAMP(visit_last_action_time) as visit_last_action_time,
+ UNIX_TIMESTAMP(visit_first_action_time) as visit_first_action_time,
+ idvisit,
+ visit_exit_idaction
+ FROM ".$this->db->prefixTable('log_visit').
+ " WHERE visit_server_date = ?
+ AND idsite = ?
+ AND config_md5config = ?
+ ORDER BY visit_last_action_time DESC
+ LIMIT 1",
+ array( $this->getCurrentDate(), $this->idsite, $md5Config));
+ if($visitRow
+ && count($visitRow) > 0)
+ {
+ $this->visitorInfo['visitor_idcookie'] = $visitRow['visitor_idcookie'];
+ $this->visitorInfo['visit_last_action_time'] = $visitRow['visit_last_action_time'];
+ $this->visitorInfo['visit_first_action_time'] = $visitRow['visit_first_action_time'];
+ $this->visitorInfo['idvisit'] = $visitRow['idvisit'];
+ $this->visitorInfo['visit_exit_idaction'] = $visitRow['visit_exit_idaction'];
+
+ $this->visitorKnown = true;
+
+ printDebug("The visitor is known because of his userSettings+IP (idcookie = {$visitRow['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
+ }
+ }
+ }
+
+ private function getUserSettingsInformation()
+ {
+ // we already called this method before, simply returns the result
+ if(is_array($this->userSettingsInformation))
+ {
+ return $this->userSettingsInformation;
+ }
+
+
+ $plugin_Flash = Piwik_Common::getRequestVar( 'fla', 0, 'int');
+ $plugin_Director = Piwik_Common::getRequestVar( 'dir', 0, 'int');
+ $plugin_Quicktime = Piwik_Common::getRequestVar( 'qt', 0, 'int');
+ $plugin_RealPlayer = Piwik_Common::getRequestVar( 'realp', 0, 'int');
+ $plugin_Pdf = Piwik_Common::getRequestVar( 'pdf', 0, 'int');
+ $plugin_WindowsMedia = Piwik_Common::getRequestVar( 'wma', 0, 'int');
+ $plugin_Java = Piwik_Common::getRequestVar( 'java', 0, 'int');
+ $plugin_Cookie = Piwik_Common::getRequestVar( 'cookie', 0, 'int');
+
+ $userAgent = Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_USER_AGENT']);
+ $aBrowserInfo = Piwik_Common::getBrowserInfo($userAgent);
+ $browserName = $aBrowserInfo['name'];
+ $browserVersion = $aBrowserInfo['version'];
+
+ $os = Piwik_Common::getOs($userAgent);
+
+ $resolution = Piwik_Common::getRequestVar('res', 'unknown', 'string');
+ $colorDepth = Piwik_Common::getRequestVar('col', 32, 'numeric');
+
+
+ $ip = Piwik_Common::getIp();
+ $ip = ip2long($ip);
+
+ $browserLang = Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ if(is_null($browserLang))
+ {
+ $browserLang = '';
+ }
+
+
+ $configurationHash = $this->getConfigHash(
+ $os,
+ $browserName,
+ $browserVersion,
+ $resolution,
+ $colorDepth,
+ $plugin_Flash,
+ $plugin_Director,
+ $plugin_RealPlayer,
+ $plugin_Pdf,
+ $plugin_WindowsMedia,
+ $plugin_Java,
+ $plugin_Cookie,
+ $ip,
+ $browserLang);
+
+ $this->userSettingsInformation = array(
+ 'config_md5config' => $configurationHash,
+ 'config_os' => $os,
+ 'config_browser_name' => $browserName,
+ 'config_browser_version' => $browserVersion,
+ 'config_resolution' => $resolution,
+ 'config_color_depth' => $colorDepth,
+ 'config_pdf' => $plugin_Pdf,
+ 'config_flash' => $plugin_Flash,
+ 'config_java' => $plugin_Java,
+ 'config_director' => $plugin_Director,
+ 'config_quicktime' => $plugin_Quicktime,
+ 'config_realplayer' => $plugin_RealPlayer,
+ 'config_windowsmedia' => $plugin_WindowsMedia,
+ 'config_cookie' => $plugin_RealPlayer,
+ 'location_ip' => $ip,
+ 'location_browser_lang' => $browserLang,
+ );
+
+ return $this->userSettingsInformation;
+ }
+
+ /**
+ * Returns true if the last action was done during the last 30 minutes
+ */
+ private function isLastActionInTheSameVisit()
+ {
+ return $this->visitorInfo['visit_last_action_time']
+ >= ($this->getCurrentTimestamp() - Piwik_LogStats::VISIT_STANDARD_LENGTH);
+ }
+
+ private function isVisitorKnown()
+ {
+ return $this->visitorKnown === true;
+ }
+
+ /**
+ * Once we have the visitor information, we have to define if the visit is a new or a known visit.
+ *
+ * 1) When the last action was done more than 30min ago,
+ * or if the visitor is new, then this is a new visit.
+ *
+ * 2) If the last action is less than 30min ago, then the same visit is going on.
+ * Because the visit goes on, we can get the time spent during the last action.
+ *
+ * NB:
+ * - In the case of a new visit, then the time spent
+ * during the last action of the previous visit is unknown.
+ *
+ * - In the case of a new visit but with a known visitor,
+ * we can set the 'returning visitor' flag.
+ *
+ */
+
+ /**
+ * In all the cases we set a cookie to the visitor with the new information.
+ */
+ public function handle()
+ {
+ if(!$this->isExcluded())
+ {
+ $this->recognizeTheVisitor();
+
+ // known visitor
+ if($this->isVisitorKnown())
+ {
+ // the same visit is going on
+ if($this->isLastActionInTheSameVisit())
+ {
+ $this->handleKnownVisit();
+ }
+ // new visit
+ else
+ {
+ $this->handleNewVisit();
+ }
+ }
+ // new visitor => new visit
+ else
+ {
+ $this->handleNewVisit();
+ }
+
+ // we update the cookie with the new visit information
+ $this->updateCookie();
+
+ }
+ }
+
+ private function updateCookie()
+ {
+ printDebug("We manage the cookie...");
+
+ // idcookie has been generated in handleNewVisit or we simply propagate the old value
+ $this->cookieLog->set( Piwik_LogStats::COOKIE_INDEX_IDVISITOR,
+ $this->visitorInfo['visitor_idcookie'] );
+
+ // the last action timestamp is the current timestamp
+ $this->cookieLog->set( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_LAST_ACTION,
+ $this->visitorInfo['visit_last_action_time'] );
+
+ // the first action timestamp is the timestamp of the first action of the current visit
+ $this->cookieLog->set( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION,
+ $this->visitorInfo['visit_first_action_time'] );
+
+ // the idvisit has been generated by mysql in handleNewVisit or simply propagated here
+ $this->cookieLog->set( Piwik_LogStats::COOKIE_INDEX_ID_VISIT,
+ $this->visitorInfo['idvisit'] );
+
+ // the last action ID is the current exit idaction
+ $this->cookieLog->set( Piwik_LogStats::COOKIE_INDEX_ID_LAST_ACTION,
+ $this->visitorInfo['visit_exit_idaction'] );
+
+ $this->cookieLog->save();
+ }
+
+
+ /**
+ * In the case of a known visit, we have to do the following actions:
+ *
+ * 1) Insert the new action
+ *
+ * 2) Update the visit information
+ */
+ private function handleKnownVisit()
+ {
+ printDebug("Visit known.");
+
+ /**
+ * Init the action
+ */
+ $action = new Piwik_LogStats_Action( $this->db );
+
+ $actionId = $action->getActionId();
+
+ printDebug("idAction = $actionId");
+
+ $serverTime = $this->getCurrentTimestamp();
+ $datetimeServer = $this->getDatetimeFromTimestamp($serverTime);
+
+ $this->db->query("UPDATE ". $this->db->prefixTable('log_visit')."
+ SET visit_last_action_time = ?,
+ visit_exit_idaction = ?,
+ visit_total_actions = visit_total_actions + 1,
+ visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time)
+ WHERE idvisit = ?
+ LIMIT 1",
+ array( $datetimeServer,
+ $actionId,
+ $this->visitorInfo['idvisit'] )
+ );
+ /**
+ * Save the action
+ */
+ $timespentLastAction = $serverTime - $this->visitorInfo['visit_last_action_time'];
+
+ $action->record( $this->visitorInfo['idvisit'],
+ $this->visitorInfo['visit_exit_idaction'],
+ $timespentLastAction
+ );
+
+
+ /**
+ * Cookie fields to be updated
+ */
+ $this->visitorInfo['visit_last_action_time'] = $serverTime;
+ $this->visitorInfo['visit_exit_idaction'] = $actionId;
+
+
+ }
+
+ /**
+ * In the case of a new visit, we have to do the following actions:
+ *
+ * 1) Insert the new action
+ *
+ * 2) Insert the visit information
+ */
+ private function handleNewVisit()
+ {
+ printDebug("New Visit.");
+
+ /**
+ * Get the variables from the REQUEST
+ */
+
+ // Configuration settings
+ $userInfo = $this->getUserSettingsInformation();
+
+ // General information
+ $localTime = Piwik_Common::getRequestVar( 'h', $this->getCurrentDate("H"), 'numeric')
+ .':'. Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'numeric')
+ .':'. Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'numeric');
+ $serverDate = $this->getCurrentDate();
+ $serverTime = $this->getCurrentTimestamp();
+
+ if($this->isVisitorKnown())
+ {
+ $idcookie = $this->visitorInfo['visitor_idcookie'];
+ $returningVisitor = 1;
+ }
+ else
+ {
+ $idcookie = $this->getVisitorUniqueId();
+ $returningVisitor = 0;
+ }
+
+ $defaultTimeOnePageVisit = Piwik_LogStats_Config::getInstance()->LogStats['default_time_one_page_visit'];
+
+ // Location information
+ $country = Piwik_Common::getCountry($userInfo['location_browser_lang']);
+ $continent = Piwik_Common::getContinent( $country );
+
+ //Referer information
+ $refererInfo = $this->getRefererInformation();
+
+ /**
+ * Init the action
+ */
+ $action = new Piwik_LogStats_Action( $this->db );
+
+ $actionId = $action->getActionId();
+
+ printDebug("idAction = $actionId");
+
+
+ /**
+ * Save the visitor
+ */
+ $informationToSave = array(
+ //'idvisit' => ,
+ 'idsite' => $this->idsite,
+ 'visitor_localtime' => $localTime,
+ 'visitor_idcookie' => $idcookie,
+ 'visitor_returning' => $returningVisitor,
+ 'visit_first_action_time' => $this->getDatetimeFromTimestamp($serverTime),
+ 'visit_last_action_time' => $this->getDatetimeFromTimestamp($serverTime),
+ 'visit_server_date' => $serverDate,
+ 'visit_entry_idaction' => $actionId,
+ 'visit_exit_idaction' => $actionId,
+ 'visit_total_actions' => 1,
+ 'visit_total_time' => $defaultTimeOnePageVisit,
+ 'referer_type' => $refererInfo['referer_type'],
+ 'referer_name' => $refererInfo['referer_name'],
+ 'referer_url' => $refererInfo['referer_url'],
+ 'referer_keyword' => $refererInfo['referer_keyword'],
+ 'config_md5config' => $userInfo['config_md5config'],
+ 'config_os' => $userInfo['config_os'],
+ 'config_browser_name' => $userInfo['config_browser_name'],
+ 'config_browser_version' => $userInfo['config_browser_version'],
+ 'config_resolution' => $userInfo['config_resolution'],
+ 'config_color_depth' => $userInfo['config_color_depth'],
+ 'config_pdf' => $userInfo['config_pdf'],
+ 'config_flash' => $userInfo['config_flash'],
+ 'config_java' => $userInfo['config_java'],
+ 'config_director' => $userInfo['config_director'],
+ 'config_quicktime' => $userInfo['config_quicktime'],
+ 'config_realplayer' => $userInfo['config_realplayer'],
+ 'config_windowsmedia' => $userInfo['config_windowsmedia'],
+ 'config_cookie' => $userInfo['config_cookie'],
+ 'location_ip' => $userInfo['location_ip'],
+ 'location_browser_lang' => $userInfo['location_browser_lang'],
+ 'location_country' => $country,
+ 'location_continent' => $continent,
+ );
+
+
+ $fields = implode(", ", array_keys($informationToSave));
+ $values = substr(str_repeat( "?,",count($informationToSave)),0,-1);
+
+ $this->db->query( "INSERT INTO ".$this->db->prefixTable('log_visit').
+ " ($fields) VALUES ($values)", array_values($informationToSave));
+
+ $idVisit = $this->db->lastInsertId();
+
+ // Update the visitor information attribute with this information array
+ $this->visitorInfo = $informationToSave;
+ $this->visitorInfo['idvisit'] = $idVisit;
+
+ // we have to save timestamp in the object properties, whereas mysql eats some other datetime format
+ $this->visitorInfo['visit_first_action_time'] = $serverTime;
+ $this->visitorInfo['visit_last_action_time'] = $serverTime;
+
+ /**
+ * Save the action
+ */
+ $action->record( $idVisit, 0, 0 );
+
+ }
+
+ /**
+ * Returns an array containing the following information:
+ * - referer_type
+ * - direct -- absence of referer URL OR referer URL has the same host
+ * - site -- based on the referer URL
+ * - search_engine -- based on the referer URL
+ * - campaign -- based on campaign URL parameter
+ * - newsletter -- based on newsletter URL parameter
+ * - partner -- based on partner URL parameter
+ *
+ * - referer_name
+ * - ()
+ * - piwik.net -- site host name
+ * - google.fr -- search engine host name
+ * - adwords-search -- campaign name
+ * - beta-release -- newsletter name
+ * - my-nice-partner -- partner name
+ *
+ * - referer_keyword
+ * - ()
+ * - ()
+ * - my keyword
+ * - my paid keyword
+ * - ()
+ * - ()
+ *
+ * - referer_url : the same for all the referer types
+ *
+ */
+ private function getRefererInformation()
+ {
+ // bool that says if the referer detection is done
+ $refererAnalyzed = false;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ $nameRefererAnalyzed = '';
+ $keywordRefererAnalyzed = '';
+
+ $refererUrl = Piwik_Common::getRequestVar( 'urlref', '', 'string');
+ $currentUrl = Piwik_Common::getRequestVar( 'url', '', 'string');
+
+ $refererUrlParse = @parse_url($refererUrl);
+ $currentUrlParse = @parse_url($currentUrl);
+
+ if(isset($refererUrlParse['host'])
+ && !empty($refererUrlParse['host']))
+ {
+
+ $refererHost = $refererUrlParse['host'];
+ $refererSH = $refererUrlParse['scheme'].'://'.$refererUrlParse['host'];
+
+ /*
+ * Search engine detection
+ */
+ if( !$refererAnalyzed )
+ {
+ /*
+ * A referer is a search engine if the URL's host is in the SearchEngines array
+ * and if we found the keyword in the URL.
+ *
+ * For example if someone comes from http://www.google.com/partners.html this will not
+ * be counted as a search engines, but as a website referer from google.com (because the
+ * keyword couldn't be found in the URL)
+ */
+ require_once PIWIK_DATAFILES_INCLUDE_PATH . "/SearchEngines.php";
+
+ if(array_key_exists($refererHost, $GLOBALS['Piwik_SearchEngines']))
+ {
+ // which search engine ?
+ $searchEngineName = $GLOBALS['Piwik_SearchEngines'][$refererHost][0];
+ $variableName = $GLOBALS['Piwik_SearchEngines'][$refererHost][1];
+
+ // if there is a query, there may be a keyword...
+ if(isset($refererUrlParse['query']))
+ {
+ $query = $refererUrlParse['query'];
+
+ //TODO: change the search engine file and use REGEXP; performance downside?
+ //TODO: port the phpmyvisites google-images hack here
+
+ // search for keywords now &vname=keyword
+ $key = strtolower(Piwik_Common::getParameterFromQueryString($query, $variableName));
+
+ //TODO test the search engine non-utf8 support
+ // for search engines that don't use utf-8
+ if((function_exists('iconv'))
+ && (isset($GLOBALS['Piwik_SearchEngines'][$refererHost][2])))
+ {
+ $charset = trim($GLOBALS['searchEngines'][$refererHost][2]);
+
+ if(!empty($charset))
+ {
+ $key = htmlspecialchars(
+ @iconv( $charset,
+ 'utf-8//TRANSLIT',
+ htmlspecialchars_decode($key, Piwik_Common::HTML_ENCODING_QUOTE_STYLE))
+ , Piwik_Common::HTML_ENCODING_QUOTE_STYLE);
+ }
+ }
+
+
+ if(!empty($key))
+ {
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_SEARCH_ENGINE;
+ $nameRefererAnalyzed = $searchEngineName;
+ $keywordRefererAnalyzed = $key;
+ }
+ }
+ }
+ }
+
+ /*
+ * Newsletter analysis
+ */
+ if( !$refererAnalyzed )
+ {
+ if(isset($currentUrlParse['query']))
+ {
+ $newsletterVariableName = Piwik_LogStats_Config::getInstance()->LogStats['newsletter_var_name'];
+ $newsletterVar = Piwik_Common::getParameterFromQueryString( $currentUrlParse['query'], $newsletterVariableName);
+
+ if($newsletterVar !== false && !empty($newsletterVar))
+ {
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_NEWSLETTER;
+ $nameRefererAnalyzed = $newsletterVar;
+ }
+ }
+ }
+
+ /*
+ * Partner analysis
+ */
+ //TODO handle partner from a list of known partner URLs
+ if( !$refererAnalyzed )
+ {
+ if(isset($currentUrlParse['query']))
+ {
+ $partnerVariableName = Piwik_LogStats_Config::getInstance()->LogStats['partner_var_name'];
+ $partnerVar = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $partnerVariableName);
+
+ if($partnerVar !== false && !empty($partnerVar))
+ {
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_PARTNER;
+ $nameRefererAnalyzed = $partnerVar;
+ }
+ }
+ }
+
+ /*
+ * Campaign analysis
+ */
+ if( !$refererAnalyzed )
+ {
+ if(isset($currentUrlParse['query']))
+ {
+ $campaignVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_var_name'];
+ $campaignName = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignVariableName);
+
+ if( $campaignName !== false && !empty($campaignName))
+ {
+ $campaignKeywordVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_keyword_var_name'];
+ $campaignKeyword = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignKeywordVariableName);
+
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_CAMPAIGN;
+ $nameRefererAnalyzed = $campaignName;
+
+ if(!empty($campaignKeyword))
+ {
+ $keywordRefererAnalyzed = $campaignKeyword;
+ }
+ }
+ }
+ }
+
+ /*
+ * Direct entry (referer host is similar to current host)
+ * And we have previously tried to detect the newsletter/partner/campaign variables in the URL
+ * so it can only be a direct access
+ */
+ if( !$refererAnalyzed )
+ {
+ $currentUrlParse = @parse_url($currentUrl);
+
+ if(isset($currentUrlParse['host']))
+ {
+ $currentHost = $currentUrlParse['host'];
+ $currentSH = $currentUrlParse['scheme'].'://'.$currentUrlParse['host'];
+
+ if($currentHost == $refererHost)
+ {
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ }
+ }
+
+ }
+
+ /*
+ * Normal website referer
+ */
+ if( !$refererAnalyzed )
+ {
+ $refererAnalyzed = true;
+ $typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_WEBSITE;
+ $nameRefererAnalyzed = $refererHost;
+ }
+ }
+
+
+ $refererInformation = array(
+ 'referer_type' => $typeRefererAnalyzed,
+ 'referer_name' => $nameRefererAnalyzed,
+ 'referer_keyword' => $keywordRefererAnalyzed,
+ 'referer_url' => $refererUrl,
+ );
+
+ return $refererInformation;
+ }
+
+ private function getConfigHash( $os, $browserName, $browserVersion, $resolution, $colorDepth, $plugin_Flash, $plugin_Director, $plugin_RealPlayer, $plugin_Pdf, $plugin_WindowsMedia, $plugin_Java, $plugin_Cookie, $ip, $browserLang)
+ {
+ return md5( $os . $browserName . $browserVersion . $resolution . $colorDepth . $plugin_Flash . $plugin_Director . $plugin_RealPlayer . $plugin_Pdf . $plugin_WindowsMedia . $plugin_Java . $plugin_Cookie . $ip . $browserLang );
+ }
+
+ private function getVisitorUniqueId()
+ {
+ if($this->isVisitorKnown())
+ {
+ return -1;
+ }
+ else
+ {
+ return Piwik_Common::generateUniqId();
+ }
+ }
+
+}
+?>
diff --git a/modules/Piwik.php b/modules/Piwik.php
index 80c0ca1015..5cc09f234c 100755
--- a/modules/Piwik.php
+++ b/modules/Piwik.php
@@ -9,6 +9,18 @@ class Piwik
Zend_Registry::get('logger_message')->log($message . "<br>" . PHP_EOL);
}
+ static public function getTableCreateSql( $tableName )
+ {
+ $tables = Piwik::getTablesCreateSql();
+
+ if(!isset($tables[$tableName]))
+ {
+ throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
+ }
+
+ return $tables[$tableName];
+ }
+
static public function getTablesCreateSql()
{
$config = Zend_Registry::get('config');
@@ -217,8 +229,8 @@ class Piwik
if(!is_dir($path))
{
mkdir($path, $mode, true);
-
}
+
if($denyAccess)
{
Piwik::createHtAccess($path);
@@ -250,11 +262,12 @@ class Piwik
$allMyTables = self::getTablesNames();
$db = Zend_Registry::get('db');
- $allTables = $db->fetchCol('SHOW TABLES');
-
- $intersect = array_intersect($allTables, $allMyTables);
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
- return $intersect;
+ $allTables = $db->fetchCol("SHOW TABLES LIKE '$prefixTables%'");
+
+ return $allTables;
}
static public function createDatabase()
@@ -286,7 +299,7 @@ class Piwik
$configAPI = Zend_Registry::get('config')->log;
$aLoggers = array(
- //'logger_query_profile' => new Piwik_Log_QueryProfile, // TODO Piwik_Log_QueryProfile
+// 'logger_query_profile' => new Piwik_Log_QueryProfile, // TODO Piwik_Log_QueryProfile
'logger_api_call' => new Piwik_Log_APICall,
'logger_exception' => new Piwik_Log_Exception,
'logger_error' => new Piwik_Log_Error,
@@ -333,9 +346,9 @@ class Piwik
}
}
- static public function createConfigObject()
+ static public function createConfigObject( $pathConfigFile = null )
{
- $config = new Piwik_Config;
+ $config = new Piwik_Config($pathConfigFile);
assert(count($config) != 0);
}
diff --git a/modules/TablePartitioning.php b/modules/TablePartitioning.php
new file mode 100644
index 0000000000..0b5d62d888
--- /dev/null
+++ b/modules/TablePartitioning.php
@@ -0,0 +1,100 @@
+<?php
+
+abstract class Piwik_TablePartitioning
+{
+ protected $tableName = null;
+ protected $generatedTableName = null;
+ protected $timestamp = null;
+
+ public function __construct( $tableName )
+ {
+ $this->tableName = $tableName;
+ }
+
+ abstract protected function generateTableName() ;
+
+
+ public function setDate( $timestamp )
+ {
+ $this->timestamp = $timestamp;
+ $this->generatedTableName = null;
+ }
+
+ public function getTableName()
+ {
+ // table name already processed
+ if(!is_null($this->generatedTableName))
+ {
+ return $this->generatedTableName;
+ }
+
+ if(is_null($this->timestamp))
+ {
+ throw new Exception("You have to specify a timestamp for a Table Partitioning by date.");
+ }
+
+ // generate table name
+ $this->generatedTableName = $this->generateTableName();
+
+ // we make sure the table already exists
+ $this->checkTableExists();
+ }
+
+ protected function checkTableExists()
+ {
+ $tablesAlreadyInstalled = Piwik::getTablesInstalled();
+
+ if(!in_array($this->generatedTableName, $tablesAlreadyInstalled))
+ {
+ $db = Zend_Registry::get('db');
+ $sql = Piwik::getTableCreateSql($this->tableName);
+
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $sql = str_replace( $prefixTables . $this->tableName, $this->generatedTableName, $sql);
+
+ $db->query( $sql );
+ }
+ }
+
+ protected function __toString()
+ {
+ return $this->getTableName();
+ }
+}
+
+class Piwik_TablePartitioning_Monthly extends Piwik_TablePartitioning
+{
+ public function __construct( $tableName )
+ {
+ parent::__construct($tableName);
+ }
+ protected function generateTableName()
+ {
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+
+ $date = date("Y_m", $this->timestamp);
+
+ return $prefixTables . $this->tableName . "_" . $date;
+ }
+
+}
+class Piwik_TablePartitioning_Daily extends Piwik_TablePartitioning
+{
+ public function __construct( $tableName )
+ {
+ parent::__construct($tableName);
+ }
+ protected function generateTableName()
+ {
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+
+ $date = date("Y_m_d", $this->timestamp);
+
+ return $prefixTables . $this->tableName . "_" . $date;
+ }
+
+}
+?>
diff --git a/piwik.php b/piwik.php
index df699094e9..b0136fd747 100644
--- a/piwik.php
+++ b/piwik.php
@@ -23,9 +23,15 @@ set_include_path(PIWIK_INCLUDE_PATH
require_once "Event/Dispatcher.php";
require_once "Common.php";
-require_once "LogStats.php";
require_once "PluginManager.php";
+
+require_once "LogStats.php";
require_once "LogStats/Plugins.php";
+require_once "LogStats/Config.php";
+require_once "LogStats/Action.php";
+require_once "LogStats/Cookie.php";
+require_once "LogStats/Db.php";
+require_once "LogStats/Visit.php";
$GLOBALS['DEBUGPIWIK'] = true;
@@ -42,8 +48,7 @@ $GLOBALS['DEBUGPIWIK'] = true;
ob_start();
printDebug($_GET);
-$process = new Piwik_LogStats_Controller;
-Piwik_PostEvent( 'LogsStats.NewVisitor' );
+$process = new Piwik_LogStats;
$process->main();
// yet to do
diff --git a/tests/config_test.php b/tests/config_test.php
index f36342e7ba..041490aff1 100755
--- a/tests/config_test.php
+++ b/tests/config_test.php
@@ -3,41 +3,48 @@ if(!defined("PATH_TEST_TO_ROOT"))
{
define('PATH_TEST_TO_ROOT', '..');
}
-
-if(!defined('PIWIK_INCLUDE_PATH'))
+if(!defined("PATH_TEST_TO_ROOT2"))
{
- define('PIWIK_INCLUDE_PATH', PATH_TEST_TO_ROOT);
+ define('PATH_TEST_TO_ROOT2', '../..');
}
-if (! defined('SIMPLE_TEST'))
+if(!defined('PIWIK_INCLUDE_PATH'))
{
- define('SIMPLE_TEST', PATH_TEST_TO_ROOT . '/tests/simpletest/');
+ define('PIWIK_INCLUDE_PATH', PATH_TEST_TO_ROOT);
}
-require_once SIMPLE_TEST . 'autorun.php';
-require_once SIMPLE_TEST . 'mock_objects.php';
+set_include_path(PATH_TEST_TO_ROOT .'/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/libs/'
+ . PATH_SEPARATOR . getcwd() . '/../../libs/'
+ . PATH_SEPARATOR . getcwd() . '/../../config/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/core/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/config/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/modules/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT2 . '/libs/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT2 . '/config/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT2 . '/core/'
+ . PATH_SEPARATOR . PATH_TEST_TO_ROOT2 . '/modules/'
+ . PATH_SEPARATOR . get_include_path()
+ );
+
+require_once 'simpletest/autorun.php';
+require_once 'simpletest/mock_objects.php';
SimpleTest :: prefer(new HtmlReporter());
error_reporting(E_ALL|E_NOTICE);
date_default_timezone_set('Europe/London');
-set_include_path(PATH_TEST_TO_ROOT
- . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/libs/'
- . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/core/'
- . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/modules'
- . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/core/models'
- . PATH_SEPARATOR . get_include_path());
require_once "Zend/Exception.php";
require_once "Zend/Loader.php";
-
-require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php";
+require_once "ErrorHandler.php";
//set_error_handler('Piwik_ErrorHandler');
Zend_Loader::loadClass('Zend_Registry');
Zend_Loader::loadClass('Zend_Config_Ini');
+Zend_Loader::loadClass('Zend_Config');
Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Db_Table');
Zend_Loader::loadClass('Zend_Debug');
diff --git a/tests/modules/Database.test.php b/tests/modules/Database.test.php
index 2b0c07f767..df274fccc0 100755
--- a/tests/modules/Database.test.php
+++ b/tests/modules/Database.test.php
@@ -2,7 +2,7 @@
if(!defined("PATH_TEST_TO_ROOT")) {
define('PATH_TEST_TO_ROOT', '../..');
}
-require_once PATH_TEST_TO_ROOT ."/tests/config_test.php";
+require_once "config_test.php";
Mock::generate('Piwik_Access');
@@ -139,13 +139,15 @@ class Test_Database extends UnitTestCase
public function setUp()
{
- Piwik::createConfigObject();
+ Piwik::createConfigObject('config.ini.php');
// setup database
Piwik::createDatabaseObject();
- Piwik::createLogObject();
Zend_Registry::get('config')->setTestEnvironment();
+
+ Piwik::createLogObject();
+
Piwik::dropDatabase();
Piwik::createDatabase();
Piwik::createDatabaseObject();
diff --git a/tests/modules/TablePartitioning.test.php b/tests/modules/TablePartitioning.test.php
new file mode 100755
index 0000000000..7f000d1212
--- /dev/null
+++ b/tests/modules/TablePartitioning.test.php
@@ -0,0 +1,138 @@
+<?php
+if(!defined("PATH_TEST_TO_ROOT")) {
+ define('PATH_TEST_TO_ROOT', '..');
+}
+require_once PATH_TEST_TO_ROOT ."/../tests/config_test.php";
+require_once "Database.test.php";
+
+Zend_Loader::loadClass('Piwik_TablePartitioning');
+class Test_Piwik_TablePartitioning extends Test_Database
+{
+ function __construct()
+ {
+ parent::__construct('');
+ }
+ public function setUp()
+ {
+ parent::setUp();
+ }
+
+ // test no timestamp => exception
+ function test_noTimestamp()
+ {
+ $p = new Piwik_TablePartitioning_Monthly('testtable');
+
+ try {
+ $p->getTableName();
+ $this->fail("Exception not raised.");
+ }
+ catch (Exception $expected) {
+ return;
+ }
+ }
+
+ // test table absent => create
+ function test_noTable()
+ {
+ $tableName ='log_visit';
+ $p = new Piwik_TablePartitioning_Monthly($tableName);
+ $timestamp = strtotime("10 September 2000");
+ $suffixShouldBe = "_2000_09";
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $tablename = $prefixTables.$tableName.$suffixShouldBe;
+
+ $p->setDate( $timestamp );
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( !in_array($tablename, $allTablesInstalled));
+ $this->assertTrue( $tablename, $p->getTableName());
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( in_array($tablename, $allTablesInstalled));
+ $this->assertEqual( $tablename, (string)$p);
+ }
+
+ // test table present => nothing
+ function test_tablePresent()
+ {
+ $tableName ='log_visit';
+ $p = new Piwik_TablePartitioning_Monthly($tableName);
+ $timestamp = strtotime("10 September 2000");
+ $suffixShouldBe = "_2000_09";
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $tablename = $prefixTables.$tableName.$suffixShouldBe;
+
+ Zend_Registry::get('db')->query("CREATE TABLE $tablename (`test` VARCHAR( 255 ) NOT NULL)");
+
+ $p->setDate( $timestamp );
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( in_array($tablename, $allTablesInstalled));
+ $this->assertTrue( $tablename, $p->getTableName());
+ }
+
+ // test monthly
+ function test_monthlyPartition()
+ {
+
+ $tableName ='log_visit';
+ $p = new Piwik_TablePartitioning_Monthly($tableName);
+ $timestamp = strtotime("10 September 2000");
+ $suffixShouldBe = "_2000_09";
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $tablename = $prefixTables.$tableName.$suffixShouldBe;
+
+ $p->setDate( $timestamp );
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( !in_array($tablename, $allTablesInstalled));
+ $this->assertTrue( $tablename, $p->getTableName());
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( in_array($tablename, $allTablesInstalled));
+ $this->assertEqual( $tablename, (string)$p);
+ }
+
+ // test daily
+ function test_dailyPartition()
+ {
+
+ $tableName ='log_visit';
+ $p = new Piwik_TablePartitioning_Daily($tableName);
+ $timestamp = strtotime("10 September 2000");
+ $suffixShouldBe = "_2000_09_10";
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $tablename = $prefixTables.$tableName.$suffixShouldBe;
+
+ $p->setDate( $timestamp );
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( !in_array($tablename, $allTablesInstalled));
+ $this->assertTrue( $tablename, $p->getTableName());
+
+ $allTablesInstalled = Piwik::getTablesInstalled();
+ $this->assertTrue( in_array($tablename, $allTablesInstalled));
+ $this->assertEqual( $tablename, (string)$p);
+ }
+
+
+ /**
+ * -> exception
+ */
+ public function _test_()
+ {
+ try {
+ test();
+ $this->fail("Exception not raised.");
+ }
+ catch (Exception $expected) {
+ $this->assertPattern("()", $expected->getMessage());
+ return;
+ }
+ }
+}
+?>
diff --git a/tests/modules/blank.test.php b/tests/modules/blank.test.php
index b26a8f64aa..68522ef449 100755
--- a/tests/modules/blank.test.php
+++ b/tests/modules/blank.test.php
@@ -1,15 +1,14 @@
<?php
-if (! defined('SIMPLE_TEST')) {
- define('SIMPLE_TEST', '../simpletest/');
+if(!defined("PATH_TEST_TO_ROOT")) {
+ define('PATH_TEST_TO_ROOT', '../..');
}
-require_once(SIMPLE_TEST.'autorun.php');
-SimpleTest :: prefer(new HtmlReporter());
+require_once PATH_TEST_TO_ROOT ."/tests/config_test.php";
class Test_Piwik_Blank extends UnitTestCase
{
function __construct()
{
- parent::__construct('Log class test');
+ parent::__construct('');
}
/**