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:
authorThomas Steur <thomas.steur@googlemail.com>2014-12-04 05:04:02 +0300
committerThomas Steur <thomas.steur@googlemail.com>2014-12-04 05:04:02 +0300
commit9d71fc8e92e5b434fd69c7ab4b83a69169064cf3 (patch)
tree8cb2798746e4defb1a978cd758df72006ec419ae
parent784b738f9c4a92c42a9d8e6b85c28a82a3af0a2f (diff)
Tracker refactoring
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml3
-rw-r--r--config/global.ini.php8
-rw-r--r--core/CliMulti/RequestCommand.php6
-rw-r--r--core/Common.php16
-rw-r--r--core/Cookie.php3
-rw-r--r--core/Date.php11
-rw-r--r--core/Piwik.php6
-rw-r--r--core/Plugin.php28
-rw-r--r--core/Plugin/Manager.php48
-rw-r--r--core/Plugin/Settings.php17
-rw-r--r--core/Settings/Storage/StaticStorage.php34
-rw-r--r--core/SettingsServer.php16
-rw-r--r--core/Tracker.php862
-rw-r--r--core/Tracker/Db.php64
-rw-r--r--core/Tracker/Db/Mysqli.php12
-rw-r--r--core/Tracker/Db/Pdo/Mysql.php10
-rw-r--r--core/Tracker/GoalManager.php3
-rw-r--r--core/Tracker/Handler.php118
-rw-r--r--core/Tracker/Handler/Factory.php42
-rw-r--r--core/Tracker/Request.php116
-rw-r--r--core/Tracker/RequestSet.php248
-rw-r--r--core/Tracker/Response.php175
-rw-r--r--core/Tracker/ScheduledTasksRunner.php107
-rw-r--r--core/Tracker/SettingsStorage.php28
-rw-r--r--core/Tracker/TrackerConfig.php39
-rw-r--r--core/Tracker/Visit/Factory.php48
-rw-r--r--core/Updates/2.9.1.php33
-rw-r--r--core/Url.php26
-rw-r--r--misc/others/cli-script-bootstrap.php2
-rwxr-xr-xmisc/others/geoipUpdateRows.php6
-rw-r--r--piwik.php66
-rw-r--r--plugins/BulkTracking/.gitignore1
-rw-r--r--plugins/BulkTracking/BulkTracking.php84
-rw-r--r--plugins/BulkTracking/README.md18
-rw-r--r--plugins/BulkTracking/Tracker/Handler.php82
-rw-r--r--plugins/BulkTracking/Tracker/Requests.php111
-rw-r--r--plugins/BulkTracking/Tracker/Response.php77
-rw-r--r--plugins/BulkTracking/screenshots/.gitkeep0
-rw-r--r--plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php77
-rw-r--r--plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php42
-rw-r--r--plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php21
-rw-r--r--plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php96
-rw-r--r--plugins/BulkTracking/tests/Integration/BulkTrackingTest.php147
-rw-r--r--plugins/BulkTracking/tests/Integration/HandlerTest.php161
-rw-r--r--plugins/BulkTracking/tests/Integration/RequestsTest.php127
-rw-r--r--plugins/BulkTracking/tests/Integration/TrackerTest.php133
-rw-r--r--plugins/BulkTracking/tests/System/TrackerTest.php53
-rw-r--r--plugins/BulkTracking/tests/Unit/RequestsTest.php172
-rw-r--r--plugins/BulkTracking/tests/Unit/ResponseTest.php93
-rw-r--r--plugins/CoreAdminHome/stylesheets/pluginSettings.less4
-rw-r--r--plugins/CoreAdminHome/templates/pluginSettings.twig1
-rw-r--r--plugins/CoreHome/Columns/ServerTime.php3
-rw-r--r--plugins/CoreHome/Columns/VisitFirstActionTime.php3
-rw-r--r--plugins/CoreHome/Columns/VisitLastActionTime.php3
-rw-r--r--plugins/ExampleSettingsPlugin/Settings.php2
-rw-r--r--plugins/Provider/Columns/Provider.php7
m---------plugins/QueuedTracking0
-rw-r--r--plugins/ScheduledReports/API.php4
-rw-r--r--plugins/ScheduledReports/ScheduledReports.php4
-rw-r--r--plugins/TestRunner/README.md9
-rw-r--r--plugins/TestRunner/TravisYmlView.php1
-rw-r--r--plugins/TestRunner/templates/travis.yml.twig3
-rwxr-xr-xtests/LocalTracker.php18
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker.php35
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker/Db.php53
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker/Handler.php70
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker/RequestSet.php32
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker/Response.php51
-rw-r--r--tests/PHPUnit/Framework/Mock/Tracker/ScheduledTasksRunner.php28
-rwxr-xr-xtests/PHPUnit/Framework/TestCase/UnitTestCase.php2
-rw-r--r--tests/PHPUnit/Integration/Plugin/ManagerTest.php90
-rw-r--r--tests/PHPUnit/Integration/Plugin/SettingsTest.php5
-rw-r--r--tests/PHPUnit/Integration/ReleaseCheckListTest.php4
-rw-r--r--tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php5
-rw-r--r--tests/PHPUnit/Integration/Tracker/Handler/FactoryTest.php75
-rw-r--r--tests/PHPUnit/Integration/Tracker/HandlerTest.php246
-rw-r--r--tests/PHPUnit/Integration/Tracker/RequestSetTest.php175
-rw-r--r--tests/PHPUnit/Integration/Tracker/RequestTest.php393
-rw-r--r--tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php54
-rw-r--r--tests/PHPUnit/Integration/Tracker/Visit/FactoryTest.php76
-rw-r--r--tests/PHPUnit/Integration/TrackerTest.php341
-rwxr-xr-xtests/PHPUnit/System/TrackerResponseTest.php106
-rw-r--r--[-rwxr-xr-x]tests/PHPUnit/System/TrackerTest.php186
-rw-r--r--tests/PHPUnit/Unit/Tracker/RequestSetTest.php470
-rw-r--r--tests/PHPUnit/Unit/Tracker/RequestTest.php595
-rw-r--r--tests/PHPUnit/Unit/Tracker/ResponseTest.php178
-rw-r--r--tests/PHPUnit/Unit/TrackerTest.php274
-rwxr-xr-xtests/PHPUnit/proxy/piwik.php6
-rwxr-xr-xtests/travis/prepare.sh6
90 files changed, 6207 insertions, 1110 deletions
diff --git a/.gitmodules b/.gitmodules
index 26ff2ad741..002cd98f55 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -30,3 +30,6 @@
path = plugins/LoginHttpAuth
url = https://github.com/piwik/plugin-LoginHttpAuth.git
branch = master
+[submodule "plugins/QueuedTracking"]
+ path = plugins/QueuedTracking
+ url = https://github.com/piwik/plugin-QueuedTracking.git
diff --git a/.travis.yml b/.travis.yml
index e5f317ffef..54575ea797 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,6 +14,9 @@ php:
- 5.3.3
# - hhvm
+services:
+ - redis-server
+
# Separate different test suites
env:
matrix:
diff --git a/config/global.ini.php b/config/global.ini.php
index 1c5dc8dd39..6667af1b80 100644
--- a/config/global.ini.php
+++ b/config/global.ini.php
@@ -509,7 +509,14 @@ pivot_by_filter_enable_fetch_by_segment = 0
; on a per-request basis;
pivot_by_filter_default_column_limit = 10
+
+[Redis]
+host = 127.0.0.1
+port = 6379
+timeout = 0.0
+
[Tracker]
+
; Piwik uses first party cookies by default. If set to 1,
; the visit ID cookie will be set on the Piwik server domain as well
; this is useful when you want to do cross websites analysis
@@ -709,6 +716,7 @@ Plugins[] = LeftMenu
Plugins[] = Morpheus
Plugins[] = Contents
Plugins[] = TestRunner
+Plugins[] = BulkTracking
[PluginsInstalled]
PluginsInstalled[] = Login
diff --git a/core/CliMulti/RequestCommand.php b/core/CliMulti/RequestCommand.php
index a13b10c488..948835ba1f 100644
--- a/core/CliMulti/RequestCommand.php
+++ b/core/CliMulti/RequestCommand.php
@@ -35,11 +35,13 @@ class RequestCommand extends ConsoleCommand
if ($this->isTestModeEnabled()) {
Config::getInstance()->setTestEnvironment();
- $indexFile = '/tests/PHPUnit/proxy/index.php';
+ $indexFile = '/tests/PHPUnit/proxy/';
} else {
- $indexFile = '/index.php';
+ $indexFile = '/';
}
+ $indexFile .= 'index.php';
+
if (!empty($_GET['pid'])) {
$process = new Process($_GET['pid']);
diff --git a/core/Common.php b/core/Common.php
index b5ed581d82..f228053da0 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -518,7 +518,13 @@ class Common
*/
public static function generateUniqId()
{
- return md5(uniqid(rand(), true));
+ if (function_exists('mt_rand')) {
+ $rand = mt_rand();
+ } else {
+ $rand = rand();
+ }
+
+ return md5(uniqid($rand, true));
}
/**
@@ -1182,10 +1188,12 @@ class Common
}
if (strpos(PHP_SAPI, '-fcgi') === false) {
- $key = $_SERVER['SERVER_PROTOCOL'];
+ $key = 'HTTP/1.1';
- if (strlen($key) > 15 || empty($key)) {
- $key = 'HTTP/1.1';
+ if (array_key_exists('SERVER_PROTOCOL', $_SERVER)
+ && strlen($_SERVER['SERVER_PROTOCOL']) < 15
+ && strlen($_SERVER['SERVER_PROTOCOL']) > 1) {
+ $key = $_SERVER['SERVER_PROTOCOL'];
}
} else {
diff --git a/core/Cookie.php b/core/Cookie.php
index 993d97f1e2..39c0de95e4 100644
--- a/core/Cookie.php
+++ b/core/Cookie.php
@@ -374,7 +374,8 @@ class Cookie
*/
public function __toString()
{
- $str = 'COOKIE ' . $this->name . ', rows count: ' . count($this->value) . ', cookie size = ' . strlen($this->generateContentString()) . " bytes\n";
+ $str = 'COOKIE ' . $this->name . ', rows count: ' . count($this->value) . ', cookie size = ' . strlen($this->generateContentString()) . " bytes, ";
+ $str .= 'path: ' . $this->path. ', expire: ' . $this->expire . "\n";
$str .= var_export($this->value, $return = true);
return $str;
diff --git a/core/Date.php b/core/Date.php
index 5b8e0fab6e..abc43c6971 100644
--- a/core/Date.php
+++ b/core/Date.php
@@ -243,6 +243,17 @@ class Date
}
/**
+ * Returns the date in the "Y-m-d H:i:s" PHP format
+ *
+ * @param int $timestamp
+ * @return string
+ */
+ public static function getDatetimeFromTimestamp($timestamp)
+ {
+ return date("Y-m-d H:i:s", $timestamp);
+ }
+
+ /**
* Returns the Unix timestamp of the date in UTC.
*
* @return int
diff --git a/core/Piwik.php b/core/Piwik.php
index bc5244dd52..b4ce8e0694 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -299,8 +299,10 @@ class Piwik
public static function hasUserSuperUserAccess()
{
try {
- self::checkUserHasSuperUserAccess();
- return true;
+ $hasAccess = Access::getInstance()->hasSuperUserAccess();
+
+ return $hasAccess;
+
} catch (Exception $e) {
return false;
}
diff --git a/core/Plugin.php b/core/Plugin.php
index 55a70ab501..f30c3ed23a 100644
--- a/core/Plugin.php
+++ b/core/Plugin.php
@@ -131,14 +131,28 @@ class Plugin
}
$this->pluginName = $pluginName;
- $metadataLoader = new MetadataLoader($pluginName);
- $this->pluginInformation = $metadataLoader->load();
+ $cache = new PersistentCache('Plugin' . $pluginName . 'Metadata');
- if ($this->hasDefinedPluginInformationInPluginClass() && $metadataLoader->hasPluginJson()) {
- throw new \Exception('Plugin ' . $pluginName . ' has defined the method getInformation() and as well as having a plugin.json file. Please delete the getInformation() method from the plugin class. Alternatively, you may delete the plugin directory from plugins/' . $pluginName);
+ if ($cache->has()) {
+ $this->pluginInformation = $cache->get();
+ } else {
+
+ $metadataLoader = new MetadataLoader($pluginName);
+ $this->pluginInformation = $metadataLoader->load();
+
+ if ($this->hasDefinedPluginInformationInPluginClass() && $metadataLoader->hasPluginJson()) {
+ throw new \Exception('Plugin ' . $pluginName . ' has defined the method getInformation() and as well as having a plugin.json file. Please delete the getInformation() method from the plugin class. Alternatively, you may delete the plugin directory from plugins/' . $pluginName);
+ }
+
+ $cache->set($this->pluginInformation);
}
+ }
- $this->cache = new PersistentCache('Plugin' . $pluginName);
+ private function createCacheIfNeeded()
+ {
+ if (is_null($this->cache)) {
+ $this->cache = new PersistentCache('Plugin' . $this->pluginName);
+ }
}
private function hasDefinedPluginInformationInPluginClass()
@@ -305,6 +319,8 @@ class Plugin
*/
public function findComponent($componentName, $expectedSubclass)
{
+ $this->createCacheIfNeeded();
+
$this->cache->setCacheKey('Plugin' . $this->pluginName . $componentName . $expectedSubclass);
$componentFile = sprintf('%s/plugins/%s/%s.php', PIWIK_INCLUDE_PATH, $this->pluginName, $componentName);
@@ -349,6 +365,8 @@ class Plugin
public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass)
{
+ $this->createCacheIfNeeded();
+
$this->cache->setCacheKey('Plugin' . $this->pluginName . $directoryWithinPlugin . $expectedSubclass);
if ($this->cache->has()) {
diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php
index 9f8941170b..c1e8da7d7f 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -93,6 +93,8 @@ class Manager extends Singleton
'ExampleTheme'
);
+ private $trackerPluginsNotToLoad = array();
+
/**
* Loads plugin that are enabled
*/
@@ -108,7 +110,7 @@ class Manager extends Singleton
public function loadCorePluginsDuringTracker()
{
$pluginsToLoad = Config::getInstance()->Plugins['Plugins'];
- $pluginsToLoad = array_diff($pluginsToLoad, Tracker::getPluginsNotToLoad());
+ $pluginsToLoad = array_diff($pluginsToLoad, $this->getTrackerPluginsNotToLoad());
$this->loadPlugins($pluginsToLoad);
}
@@ -139,18 +141,42 @@ class Manager extends Singleton
}
}
- $this->unloadPlugins();
-
if (empty($pluginsTracker)) {
+ $this->unloadPlugins();
return array();
}
- $pluginsTracker = array_diff($pluginsTracker, Tracker::getPluginsNotToLoad());
+ $pluginsTracker = array_diff($pluginsTracker, $this->getTrackerPluginsNotToLoad());
$this->doNotLoadAlwaysActivatedPlugins();
$this->loadPlugins($pluginsTracker);
+
+ // we could simply unload all plugins first before loading plugins but this way it is much faster
+ // since we won't have to create each plugin again and we won't have to parse each plugin metadata file
+ // again etc
+ $this->makeSureOnlyActivatedPluginsAreLoaded();
+
return $pluginsTracker;
}
+ /**
+ * Do not load the specified plugins (used during testing, to disable Provider plugin)
+ * @param array $plugins
+ */
+ public function setTrackerPluginsNotToLoad($plugins)
+ {
+ $this->trackerPluginsNotToLoad = $plugins;
+ }
+
+ /**
+ * Get list of plugins to not load
+ *
+ * @return array
+ */
+ public function getTrackerPluginsNotToLoad()
+ {
+ return $this->trackerPluginsNotToLoad;
+ }
+
public function getCorePluginsDisabledByDefault()
{
return array_merge( $this->corePluginsDisabledByDefault, $this->coreThemesDisabledByDefault);
@@ -237,7 +263,7 @@ class Manager extends Singleton
public function isPluginActivated($name)
{
return in_array($name, $this->pluginsToLoad)
- || $this->isPluginAlwaysActivated($name);
+ || ($this->doLoadAlwaysActivatedPlugins && $this->isPluginAlwaysActivated($name));
}
/**
@@ -1005,8 +1031,9 @@ class Manager extends Singleton
*
* @param string $pluginName plugin name without prefix (eg. 'UserCountry')
* @param Plugin $newPlugin
+ * @internal
*/
- private function addLoadedPlugin($pluginName, Plugin $newPlugin)
+ public function addLoadedPlugin($pluginName, Plugin $newPlugin)
{
$this->loadedPlugins[$pluginName] = $newPlugin;
}
@@ -1335,6 +1362,15 @@ class Manager extends Singleton
{
Option::delete('version_' . $version);
}
+
+ private function makeSureOnlyActivatedPluginsAreLoaded()
+ {
+ foreach ($this->getLoadedPlugins() as $pluginName => $plugin) {
+ if (!in_array($pluginName, $this->pluginsToLoad)) {
+ $this->unloadPlugin($plugin);
+ }
+ }
+ }
}
/**
diff --git a/core/Plugin/Settings.php b/core/Plugin/Settings.php
index 23d1472ca9..8a674ae370 100644
--- a/core/Plugin/Settings.php
+++ b/core/Plugin/Settings.php
@@ -180,12 +180,14 @@ abstract class Settings
*/
protected function addSetting(Setting $setting)
{
- if (!ctype_alnum($setting->getName())) {
+ $name = $setting->getName();
+
+ if (!ctype_alnum($name)) {
$msg = sprintf('The setting name "%s" in plugin "%s" is not valid. Only alpha and numerical characters are allowed', $setting->getName(), $this->pluginName);
throw new \Exception($msg);
}
- if (array_key_exists($setting->getName(), $this->settings)) {
+ if (array_key_exists($name, $this->settings)) {
throw new \Exception(sprintf('A setting with name "%s" does already exist for plugin "%s"', $setting->getName(), $this->pluginName));
}
@@ -195,7 +197,7 @@ abstract class Settings
$setting->setStorage($this->storage);
$setting->setPluginName($this->pluginName);
- $this->settings[$setting->getName()] = $setting;
+ $this->settings[$name] = $setting;
}
/**
@@ -265,11 +267,14 @@ abstract class Settings
private function setDefaultTypeAndFieldIfNeeded(Setting $setting)
{
- if (!is_null($setting->uiControlType) && is_null($setting->type)) {
+ $hasControl = !is_null($setting->uiControlType);
+ $hasType = !is_null($setting->type);
+
+ if ($hasControl && !$hasType) {
$setting->type = $this->getDefaultType($setting->uiControlType);
- } elseif (!is_null($setting->type) && is_null($setting->uiControlType)) {
+ } elseif ($hasType && !$hasControl) {
$setting->uiControlType = $this->getDefaultCONTROL($setting->type);
- } elseif (is_null($setting->uiControlType) && is_null($setting->type)) {
+ } elseif (!$hasControl && !$hasType) {
$setting->type = static::TYPE_STRING;
$setting->uiControlType = static::CONTROL_TEXT;
}
diff --git a/core/Settings/Storage/StaticStorage.php b/core/Settings/Storage/StaticStorage.php
new file mode 100644
index 0000000000..ada437fa1c
--- /dev/null
+++ b/core/Settings/Storage/StaticStorage.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Settings\Storage;
+use Piwik\Settings\Storage;
+
+/**
+ * Static / temporary storage where a value will never be persisted meaning it will use the default value
+ * for each request until configured differently. Useful for tests.
+ *
+ * @api
+ */
+class StaticStorage extends Storage
+{
+
+ protected function loadSettings()
+ {
+ return array();
+ }
+
+ /**
+ * Saves (persists) the current setting values in the database.
+ */
+ public function save()
+ {
+ }
+
+}
diff --git a/core/SettingsServer.php b/core/SettingsServer.php
index fc9d5f6bc1..3f6fbb8878 100644
--- a/core/SettingsServer.php
+++ b/core/SettingsServer.php
@@ -41,6 +41,22 @@ class SettingsServer
}
/**
+ * Mark the current request as a Tracker API request
+ */
+ public static function setIsTrackerApiRequest()
+ {
+ $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+ }
+
+ /**
+ * Set the current request is not a tracker API request
+ */
+ public static function setIsNotTrackerApiRequest()
+ {
+ $GLOBALS['PIWIK_TRACKER_MODE'] = false;
+ }
+
+ /**
* Returns `true` if running on Microsoft IIS 7 (or above), `false` if otherwise.
*
* @return bool
diff --git a/core/Tracker.php b/core/Tracker.php
index d6b2d9236f..99a712a887 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -9,17 +9,16 @@
namespace Piwik;
use Exception;
-use Piwik\Exception\InvalidRequestParameterException;
-use Piwik\Exception\UnexpectedWebsiteFoundException;
+use Piwik\Plugins\BulkTracking\Tracker\Requests;
use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
-use Piwik\Plugins\SitesManager\SiteUrls;
-use Piwik\Tracker\Cache;
+use Piwik\Tracker\Db as TrackerDb;
use Piwik\Tracker\Db\DbException;
-use Piwik\Tracker\Db\Mysqli;
-use Piwik\Tracker\Db\Pdo\Mysql;
+use Piwik\Tracker\Handler;
use Piwik\Tracker\Request;
+use Piwik\Tracker\RequestSet;
+use Piwik\Tracker\TrackerConfig;
use Piwik\Tracker\Visit;
-use Piwik\Tracker\VisitInterface;
+use Piwik\Plugin\Manager as PluginManager;
/**
* Class used by the logging script piwik.php called by the javascript tag.
@@ -27,363 +26,124 @@ use Piwik\Tracker\VisitInterface;
* saves information in the cookie, etc.
*
* We try to include as little files as possible (no dependency on 3rd party modules).
- *
*/
class Tracker
{
- protected $stateValid = self::STATE_NOTHING_TO_NOTICE;
/**
* @var Db
*/
- protected static $db = null;
-
- const STATE_NOTHING_TO_NOTICE = 1;
- const STATE_LOGGING_DISABLE = 10;
- const STATE_EMPTY_REQUEST = 11;
- const STATE_NOSCRIPT_REQUEST = 13;
+ private static $db = null;
// We use hex ID that are 16 chars in length, ie. 64 bits IDs
const LENGTH_HEX_ID_STRING = 16;
const LENGTH_BINARY_ID = 8;
- protected static $pluginsNotToLoad = array();
- protected static $pluginsToLoad = array();
-
- /**
- * The set of visits to track.
- *
- * @var array
- */
- private $requests = array();
-
- /**
- * The token auth supplied with a bulk visits POST.
- *
- * @var string
- */
- private $tokenAuth = null;
-
- /**
- * Whether we're currently using bulk tracking or not.
- *
- * @var bool
- */
- private $usingBulkTracking = false;
+ public static $initTrackerMode = false;
- /**
- * The number of requests that have been successfully logged.
- *
- * @var int
- */
private $countOfLoggedRequests = 0;
+ protected $isInstalled = null;
- protected function outputAccessControlHeaders()
- {
- $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
- if ($requestMethod !== 'GET') {
- $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
- Common::sendHeader('Access-Control-Allow-Origin: ' . $origin);
- Common::sendHeader('Access-Control-Allow-Credentials: true');
- }
- }
-
- public function clear()
+ public function isDebugModeEnabled()
{
- $this->stateValid = self::STATE_NOTHING_TO_NOTICE;
+ return array_key_exists('PIWIK_TRACKER_DEBUG', $GLOBALS) && $GLOBALS['PIWIK_TRACKER_DEBUG'] === true;
}
- /**
- * Do not load the specified plugins (used during testing, to disable Provider plugin)
- * @param array $plugins
- */
- public static function setPluginsNotToLoad($plugins)
+ public function shouldRecordStatistics()
{
- self::$pluginsNotToLoad = $plugins;
- }
+ $record = TrackerConfig::getConfigValue('record_statistics') != 0;
- /**
- * Get list of plugins to not load
- *
- * @return array
- */
- public static function getPluginsNotToLoad()
- {
- return self::$pluginsNotToLoad;
- }
+ if (!$record) {
+ Common::printDebug('Tracking is disabled in the config.ini.php via record_statistics=0');
+ }
- /**
- * Update Tracker config
- *
- * @param string $name Setting name
- * @param mixed $value Value
- */
- private static function updateTrackerConfig($name, $value)
- {
- $section = Config::getInstance()->Tracker;
- $section[$name] = $value;
- Config::getInstance()->Tracker = $section;
+ return $record && $this->isInstalled();
}
- protected function initRequests($args)
+ public static function loadTrackerEnvironment()
{
- $rawData = self::getRawBulkRequest();
- if (!empty($rawData)) {
- $this->usingBulkTracking = strpos($rawData, '"requests"') || strpos($rawData, "'requests'");
- if ($this->usingBulkTracking) {
- return $this->authenticateBulkTrackingRequests($rawData);
- }
- }
-
- // Not using bulk tracking
- $this->requests = $args ? $args : (!empty($_GET) || !empty($_POST) ? array($_GET + $_POST) : array());
+ SettingsServer::setIsTrackerApiRequest();
+ $GLOBALS['PIWIK_TRACKER_DEBUG'] = (bool) TrackerConfig::getConfigValue('debug');
+ PluginManager::getInstance()->loadTrackerPlugins();
}
- private static function getRequestsArrayFromBulkRequest($rawData)
+ private function init()
{
- $rawData = trim($rawData);
- $rawData = Common::sanitizeLineBreaks($rawData);
-
- // POST data can be array of string URLs or array of arrays w/ visit info
- $jsonData = json_decode($rawData, $assoc = true);
+ \Piwik\FrontController::createConfigObject();
- $tokenAuth = Common::getRequestVar('token_auth', false, 'string', $jsonData);
+ if ($this->isDebugModeEnabled()) {
+ Error::setErrorHandler();
+ ExceptionHandler::setUp();
- $requests = array();
- if (isset($jsonData['requests'])) {
- $requests = $jsonData['requests'];
+ Common::printDebug("Debug enabled - Input parameters: ");
+ Common::printDebug(var_export($_GET, true));
}
-
- return array($requests, $tokenAuth);
- }
-
- private function isBulkTrackingRequireTokenAuth()
- {
- return !empty(Config::getInstance()->Tracker['bulk_requests_require_authentication']);
}
- private function authenticateBulkTrackingRequests($rawData)
+ public function isInstalled()
{
- list($this->requests, $tokenAuth) = $this->getRequestsArrayFromBulkRequest($rawData);
-
- $bulkTrackingRequireTokenAuth = $this->isBulkTrackingRequireTokenAuth();
- if ($bulkTrackingRequireTokenAuth) {
- if (empty($tokenAuth)) {
- throw new Exception("token_auth must be specified when using Bulk Tracking Import. "
- . " See <a href='http://developer.piwik.org/api-reference/tracking-api'>Tracking Doc</a>");
- }
+ if (is_null($this->isInstalled)) {
+ $this->isInstalled = SettingsPiwik::isPiwikInstalled();
}
- if (!empty($this->requests)) {
- foreach ($this->requests as &$request) {
- // if a string is sent, we assume its a URL and try to parse it
- if (is_string($request)) {
- $params = array();
-
- $url = @parse_url($request);
- if (!empty($url)) {
- @parse_str($url['query'], $params);
- $request = $params;
- }
- }
-
- $requestObj = new Request($request, $tokenAuth);
- $this->loadTrackerPlugins($requestObj);
-
- if ($bulkTrackingRequireTokenAuth
- && !$requestObj->isAuthenticated()
- ) {
- throw new Exception(sprintf("token_auth specified does not have Admin permission for idsite=%s", $requestObj->getIdSite()));
- }
- $request = $requestObj;
- }
- }
-
- return $tokenAuth;
+ return $this->isInstalled;
}
- /**
- * Main - tracks the visit/action
- *
- * @param array $args Optional Request Array
- */
- public function main($args = null)
+ public function main(Handler $handler, RequestSet $requestSet)
{
- if (!SettingsPiwik::isPiwikInstalled()) {
- return $this->handleEmptyRequest();
- }
try {
- $tokenAuth = $this->initRequests($args);
- } catch (Exception $ex) {
- $this->exitWithException($ex, true);
- }
-
- $this->initOutputBuffer();
-
- if (!empty($this->requests)) {
- $this->beginTransaction();
-
- try {
- foreach ($this->requests as $params) {
- $isAuthenticated = $this->trackRequest($params, $tokenAuth);
- }
- $this->runScheduledTasksIfAllowed($isAuthenticated);
- $this->commitTransaction();
- } catch (DbException $e) {
- Common::printDebug($e->getMessage());
- $this->rollbackTransaction();
- }
-
- } else {
- $this->handleEmptyRequest();
+ $this->init();
+ $handler->init($this, $requestSet);
+ $this->track($handler, $requestSet);
+ } catch (Exception $e) {
+ $handler->onException($this, $requestSet, $e);
}
Piwik::postEvent('Tracker.end');
+ $response = $handler->finish($this, $requestSet);
- $this->end();
-
- $this->flushOutputBuffer();
-
- $this->performRedirectToUrlIfSet();
- }
-
- protected function initOutputBuffer()
- {
- ob_start();
- }
-
- protected function flushOutputBuffer()
- {
- ob_end_flush();
- }
+ $this->disconnectDatabase();
- protected function getOutputBuffer()
- {
- return ob_get_contents();
+ return $response;
}
- protected function beginTransaction()
+ public function track(Handler $handler, RequestSet $requestSet)
{
- $this->transactionId = null;
- if (!$this->shouldUseTransactions()) {
+ if (!$this->shouldRecordStatistics()) {
return;
}
- $this->transactionId = self::getDatabase()->beginTransaction();
- }
- protected function commitTransaction()
- {
- if (empty($this->transactionId)) {
- return;
- }
- self::getDatabase()->commit($this->transactionId);
- }
+ $requestSet->initRequestsAndTokenAuth();
- protected function rollbackTransaction()
- {
- if (empty($this->transactionId)) {
- return;
+ if ($requestSet->hasRequests()) {
+ $handler->onStartTrackRequests($this, $requestSet);
+ $handler->process($this, $requestSet);
+ $handler->onAllRequestsTracked($this, $requestSet);
}
- self::getDatabase()->rollback($this->transactionId);
}
/**
- * @return bool
- */
- protected function shouldUseTransactions()
- {
- $isBulkRequest = count($this->requests) > 1;
- return $isBulkRequest && $this->isTransactionSupported();
- }
-
- /**
- * @return bool
- */
- protected function isTransactionSupported()
- {
- return (bool)Config::getInstance()->Tracker['bulk_requests_use_transaction'];
- }
-
- protected function shouldRunScheduledTasks()
- {
- // don't run scheduled tasks in CLI mode from Tracker, this is the case
- // where we bulk load logs & don't want to lose time with tasks
- return !Common::isPhpCliMode()
- && $this->getState() != self::STATE_LOGGING_DISABLE;
- }
-
- /**
- * Tracker requests will automatically trigger the Scheduled tasks.
- * This is useful for users who don't setup the cron,
- * but still want daily/weekly/monthly PDF reports emailed automatically.
- *
- * This is similar to calling the API CoreAdminHome.runScheduledTasks
+ * @param Request $request
+ * @return array
*/
- protected static function runScheduledTasks()
+ public function trackRequest(Request $request)
{
- $now = time();
-
- // Currently, there are no hourly tasks. When there are some,
- // this could be too aggressive minimum interval (some hours would be skipped in case of low traffic)
- $minimumInterval = Config::getInstance()->Tracker['scheduled_tasks_min_interval'];
+ if ($request->isEmptyRequest()) {
+ Common::printDebug("The request is empty");
+ } else {
+ $this->loadTrackerPlugins();
- // If the user disabled browser archiving, he has already setup a cron
- // To avoid parallel requests triggering the Scheduled Tasks,
- // Get last time tasks started executing
- $cache = Cache::getCacheGeneral();
+ Common::printDebug("Current datetime: " . date("Y-m-d H:i:s", $request->getCurrentTimestamp()));
- if ($minimumInterval <= 0
- || empty($cache['isBrowserTriggerEnabled'])
- ) {
- Common::printDebug("-> Scheduled tasks not running in Tracker: Browser archiving is disabled.");
- return;
+ $visit = Visit\Factory::make();
+ $visit->setRequest($request);
+ $visit->handle();
}
- $nextRunTime = $cache['lastTrackerCronRun'] + $minimumInterval;
-
- if ((defined('DEBUG_FORCE_SCHEDULED_TASKS') && DEBUG_FORCE_SCHEDULED_TASKS)
- || $cache['lastTrackerCronRun'] === false
- || $nextRunTime < $now
- ) {
- $cache['lastTrackerCronRun'] = $now;
- Cache::setCacheGeneral($cache);
- self::initCorePiwikInTrackerMode();
- Option::set('lastTrackerCronRun', $cache['lastTrackerCronRun']);
- Common::printDebug('-> Scheduled Tasks: Starting...');
-
- // save current user privilege and temporarily assume Super User privilege
- $isSuperUser = Piwik::hasUserSuperUserAccess();
-
- // Scheduled tasks assume Super User is running
- Piwik::setUserHasSuperUserAccess();
-
- // While each plugins should ensure that necessary languages are loaded,
- // we ensure English translations at least are loaded
- Translate::loadEnglishTranslation();
-
- ob_start();
- CronArchive::$url = SettingsPiwik::getPiwikUrl();
- $cronArchive = new CronArchive();
- $cronArchive->runScheduledTasksInTrackerMode();
-
- $resultTasks = ob_get_contents();
- ob_clean();
-
- // restore original user privilege
- Piwik::setUserHasSuperUserAccess($isSuperUser);
-
- foreach (explode('</pre>', $resultTasks) as $resultTask) {
- Common::printDebug(str_replace('<pre>', '', $resultTask));
- }
-
- Common::printDebug('Finished Scheduled Tasks.');
- } else {
- Common::printDebug("-> Scheduled tasks not triggered.");
- }
- Common::printDebug("Next run will be from: " . date('Y-m-d H:i:s', $nextRunTime) . ' UTC');
+ // increment successfully logged request count. make sure to do this after try-catch,
+ // since an excluded visit is considered 'successfully logged'
+ ++$this->countOfLoggedRequests;
}
- public static $initTrackerMode = false;
-
/**
* Used to initialize core Piwik components on a piwik.php request
* Eg. when cache is missed and we will be calling some APIs to generate cache
@@ -405,356 +165,67 @@ class Tracker
Db::createDatabaseObject();
}
- \Piwik\Plugin\Manager::getInstance()->loadCorePluginsDuringTracker();
+ PluginManager::getInstance()->loadCorePluginsDuringTracker();
}
}
- /**
- * Echos an error message & other information, then exits.
- *
- * @param Exception $e
- * @param bool $authenticated
- * @param int $statusCode eg 500
- */
- protected function exitWithException($e, $authenticated = false, $statusCode = 500)
+ public function getCountOfLoggedRequests()
{
- if ($this->hasRedirectUrl()) {
- $this->performRedirectToUrlIfSet();
- exit;
- }
-
- Common::sendResponseCode($statusCode);
- error_log(sprintf("Error in Piwik (tracker): %s", str_replace("\n", " ", $this->getMessageFromException($e))));
-
- if ($this->usingBulkTracking) {
- // when doing bulk tracking we return JSON so the caller will know how many succeeded
- $result = array(
- 'status' => 'error',
- 'tracked' => $this->countOfLoggedRequests
- );
- // send error when in debug mode or when authenticated (which happens when doing log importing,
- if ((isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'])
- || $authenticated
- ) {
- $result['message'] = $this->getMessageFromException($e);
- }
- Common::sendHeader('Content-Type: application/json');
- echo json_encode($result);
- die(1);
- exit;
- }
-
- if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
- Common::sendHeader('Content-Type: text/html; charset=utf-8');
- $trailer = '<span style="color: #888888">Backtrace:<br /><pre>' . $e->getTraceAsString() . '</pre></span>';
- $headerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/simpleLayoutHeader.tpl');
- $footerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/simpleLayoutFooter.tpl');
- $headerPage = str_replace('{$HTML_TITLE}', 'Piwik &rsaquo; Error', $headerPage);
-
- echo $headerPage . '<p>' . $this->getMessageFromException($e) . '</p>' . $trailer . $footerPage;
- } // If not debug, but running authenticated (eg. during log import) then we display raw errors
- elseif ($authenticated) {
- Common::sendHeader('Content-Type: text/html; charset=utf-8');
- echo $this->getMessageFromException($e);
- } else {
- $this->sendResponse();
- }
-
- die(1);
- exit;
+ return $this->countOfLoggedRequests;
}
- /**
- * Returns the date in the "Y-m-d H:i:s" PHP format
- *
- * @param int $timestamp
- * @return string
- */
- public static function getDatetimeFromTimestamp($timestamp)
+ public function setCountOfLoggedRequests($numLoggedRequests)
{
- return date("Y-m-d H:i:s", $timestamp);
+ $this->countOfLoggedRequests = $numLoggedRequests;
}
- /**
- * Initialization
- * @param Request $request
- */
- protected function init(Request $request)
+ public function hasLoggedRequests()
{
- $this->loadTrackerPlugins($request);
- $this->handleDisabledTracker();
- $this->handleEmptyRequest($request);
+ return 0 !== $this->countOfLoggedRequests;
}
/**
- * Cleanup
+ * @deprecated since 2.10.0 use {@link Date::getDatetimeFromTimestamp()} instead
*/
- protected function end()
+ public static function getDatetimeFromTimestamp($timestamp)
{
- if ($this->usingBulkTracking) {
- $result = array(
- 'status' => 'success',
- 'tracked' => $this->countOfLoggedRequests
- );
-
- $this->outputAccessControlHeaders();
-
- Common::sendHeader('Content-Type: application/json');
- echo json_encode($result);
- exit;
- }
- switch ($this->getState()) {
- case self::STATE_LOGGING_DISABLE:
- $this->sendResponse();
- Common::printDebug("Logging disabled, display transparent logo");
- break;
-
- case self::STATE_EMPTY_REQUEST:
- Common::printDebug("Empty request => Piwik page");
- echo "<a href='/'>Piwik</a> is a free/libre web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
- break;
-
- case self::STATE_NOSCRIPT_REQUEST:
- case self::STATE_NOTHING_TO_NOTICE:
- default:
- $this->sendResponse();
- Common::printDebug("Nothing to notice => default behaviour");
- break;
- }
- Common::printDebug("End of the page.");
-
- if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
- if (isset(self::$db)) {
- self::$db->recordProfiling();
- Profiler::displayDbTrackerProfile(self::$db);
- }
- }
-
- self::disconnectDatabase();
+ return Date::getDatetimeFromTimestamp($timestamp);
}
- /**
- * Factory to create database objects
- *
- * @param array $configDb Database configuration
- * @throws Exception
- * @return \Piwik\Tracker\Db\Mysqli|\Piwik\Tracker\Db\Pdo\Mysql
- */
- public static function factory($configDb)
+ public function isDatabaseConnected()
{
- /**
- * Triggered before a connection to the database is established by the Tracker.
- *
- * This event can be used to change the database connection settings used by the Tracker.
- *
- * @param array $dbInfos Reference to an array containing database connection info,
- * including:
- *
- * - **host**: The host name or IP address to the MySQL database.
- * - **username**: The username to use when connecting to the
- * database.
- * - **password**: The password to use when connecting to the
- * database.
- * - **dbname**: The name of the Piwik MySQL database.
- * - **port**: The MySQL database port to use.
- * - **adapter**: either `'PDO\MYSQL'` or `'MYSQLI'`
- * - **type**: The MySQL engine to use, for instance 'InnoDB'
- */
- Piwik::postEvent('Tracker.getDatabaseConfig', array(&$configDb));
-
- switch ($configDb['adapter']) {
- case 'PDO\MYSQL':
- case 'PDO_MYSQL': // old format pre Piwik 2
- require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Pdo/Mysql.php';
- return new Mysql($configDb);
-
- case 'MYSQLI':
- require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Mysqli.php';
- return new Mysqli($configDb);
- }
-
- throw new Exception('Unsupported database adapter ' . $configDb['adapter']);
+ return !is_null(self::$db);
}
- public static function connectPiwikTrackerDb()
- {
- $db = null;
- $configDb = Config::getInstance()->database;
-
- if (!isset($configDb['port'])) {
- // before 0.2.4 there is no port specified in config file
- $configDb['port'] = '3306';
- }
-
- $db = Tracker::factory($configDb);
- $db->connect();
-
- return $db;
- }
-
- protected static function connectDatabaseIfNotConnected()
+ public static function getDatabase()
{
- if (!is_null(self::$db)) {
- return;
- }
-
- try {
- self::$db = self::connectPiwikTrackerDb();
- } catch (Exception $e) {
- throw new DbException($e->getMessage(), $e->getCode());
+ if (is_null(self::$db)) {
+ try {
+ self::$db = TrackerDb::connectPiwikTrackerDb();
+ } catch (Exception $e) {
+ throw new DbException($e->getMessage(), $e->getCode());
+ }
}
- }
- /**
- * @return Db
- */
- public static function getDatabase()
- {
- self::connectDatabaseIfNotConnected();
return self::$db;
}
- public static function disconnectDatabase()
+ protected function disconnectDatabase()
{
- if (isset(self::$db)) {
+ if ($this->isDatabaseConnected()) { // note: I think we do this only for the tests
self::$db->disconnect();
self::$db = null;
}
}
- /**
- * Returns the Tracker_Visit object.
- * This method can be overwritten to use a different Tracker_Visit object
- *
- * @throws Exception
- * @return \Piwik\Tracker\Visit
- */
- protected function getNewVisitObject()
- {
- $visit = null;
-
- /**
- * Triggered before a new **visit tracking object** is created. Subscribers to this
- * event can force the use of a custom visit tracking object that extends from
- * {@link Piwik\Tracker\VisitInterface}.
- *
- * @param \Piwik\Tracker\VisitInterface &$visit Initialized to null, but can be set to
- * a new visit object. If it isn't modified
- * Piwik uses the default class.
- */
- Piwik::postEvent('Tracker.makeNewVisitObject', array(&$visit));
-
- if (is_null($visit)) {
- $visit = new Visit();
- } elseif (!($visit instanceof VisitInterface)) {
- throw new Exception("The Visit object set in the plugin must implement VisitInterface");
- }
- return $visit;
- }
-
- private function sendResponse()
- {
- if (isset($GLOBALS['PIWIK_TRACKER_DEBUG'])
- && $GLOBALS['PIWIK_TRACKER_DEBUG']
- ) {
- return;
- }
-
- if (strlen($this->getOutputBuffer()) > 0) {
- // If there was an error during tracker, return so errors can be flushed
- return;
- }
-
- $this->outputAccessControlHeaders();
-
- $request = $_GET + $_POST;
-
- if (array_key_exists('send_image', $request) && $request['send_image'] === '0') {
- Common::sendResponseCode(204);
-
- return;
- }
-
- $this->outputTransparentGif();
- }
-
- protected function outputTransparentGif ()
- {
- $transGifBase64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
- Common::sendHeader('Content-Type: image/gif');
-
- print(base64_decode($transGifBase64));
- }
-
- protected function isVisitValid()
- {
- return $this->stateValid !== self::STATE_LOGGING_DISABLE
- && $this->stateValid !== self::STATE_EMPTY_REQUEST;
- }
-
- protected function getState()
- {
- return $this->stateValid;
- }
-
- protected function setState($value)
- {
- $this->stateValid = $value;
- }
-
- protected function loadTrackerPlugins(Request $request)
- {
- // Adding &dp=1 will disable the provider plugin, if token_auth is used (used to speed up bulk imports)
- $disableProvider = $request->getParam('dp');
- if (!empty($disableProvider)) {
- Tracker::setPluginsNotToLoad(array('Provider'));
- }
-
- try {
- $pluginsTracker = \Piwik\Plugin\Manager::getInstance()->loadTrackerPlugins();
- Common::printDebug("Loading plugins: { " . implode(", ", $pluginsTracker) . " }");
- } catch (Exception $e) {
- Common::printDebug("ERROR: " . $e->getMessage());
- }
- }
-
- protected function handleEmptyRequest(Request $request = null)
- {
- if (is_null($request)) {
- $request = new Request($_GET + $_POST);
- }
- $countParameters = $request->getParamsCount();
- if ($countParameters == 0) {
- $this->setState(self::STATE_EMPTY_REQUEST);
- }
- if ($countParameters == 1) {
- $this->setState(self::STATE_NOSCRIPT_REQUEST);
- }
- }
-
- protected function handleDisabledTracker()
- {
- $saveStats = Config::getInstance()->Tracker['record_statistics'];
- if ($saveStats == 0) {
- $this->setState(self::STATE_LOGGING_DISABLE);
- }
- }
-
- protected function getTokenAuth()
- {
- if (!is_null($this->tokenAuth)) {
- return $this->tokenAuth;
- }
-
- return Common::getRequestVar('token_auth', false);
- }
-
public static function setTestEnvironment($args = null, $requestMethod = null)
{
if (is_null($args)) {
- $postData = self::getRequestsArrayFromBulkRequest(self::getRawBulkRequest());
- $args = $_GET + $postData;
+ $requests = new Requests();
+ $args = $requests->getRequestsArrayFromBulkRequest($requests->getRawBulkRequest());
+ $args = $_GET + $args;
}
+
if (is_null($requestMethod) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
$requestMethod = $_SERVER['REQUEST_METHOD'];
} else if (is_null($requestMethod)) {
@@ -762,17 +233,17 @@ class Tracker
}
// Do not run scheduled tasks during tests
- self::updateTrackerConfig('scheduled_tasks_min_interval', 0);
+ TrackerConfig::setConfigValue('scheduled_tasks_min_interval', 0);
// if nothing found in _GET/_POST and we're doing a POST, assume bulk request. in which case,
// we have to bypass authentication
if (empty($args) && $requestMethod == 'POST') {
- self::updateTrackerConfig('tracking_requests_require_authentication', 0);
+ TrackerConfig::setConfigValue('tracking_requests_require_authentication', 0);
}
// Tests can force the use of 3rd party cookie for ID visitor
if (Common::getRequestVar('forceUseThirdPartyCookie', false, null, $args) == 1) {
- self::updateTrackerConfig('use_third_party_id_cookie', 1);
+ TrackerConfig::setConfigValue('use_third_party_id_cookie', 1);
}
// Tests using window_look_back_for_visitor
@@ -780,13 +251,13 @@ class Tracker
// also look for this in bulk requests (see fake_logs_replay.log)
|| strpos(json_encode($args, true), '"forceLargeWindowLookBackForVisitor":"1"') !== false
) {
- self::updateTrackerConfig('window_look_back_for_visitor', 2678400);
+ TrackerConfig::setConfigValue('window_look_back_for_visitor', 2678400);
}
// Tests can force the enabling of IP anonymization
if (Common::getRequestVar('forceIpAnonymization', false, null, $args) == 1) {
- self::connectDatabaseIfNotConnected();
+ self::getDatabase(); // make sure db is initialized
$privacyConfig = new PrivacyManagerConfig();
$privacyConfig->ipAddressMaskLength = 2;
@@ -797,155 +268,18 @@ class Tracker
$pluginsDisabled = array('Provider');
// Disable provider plugin, because it is so slow to do many reverse ip lookups
- self::setPluginsNotToLoad($pluginsDisabled);
- }
-
- /**
- * Gets the error message to output when a tracking request fails.
- *
- * @param Exception $e
- * @return string
- */
- private function getMessageFromException($e)
- {
- // Note: duplicated from FormDatabaseSetup.isAccessDenied
- // Avoid leaking the username/db name when access denied
- if ($e->getCode() == 1044 || $e->getCode() == 42000) {
- return "Error while connecting to the Piwik database - please check your credentials in config/config.ini.php file";
- }
- if(Common::isPhpCliMode()) {
- return $e->getMessage() . "\n" . $e->getTraceAsString();
- }
- return $e->getMessage();
- }
-
- /**
- * @param $params
- * @param $tokenAuth
- * @return array
- */
- protected function trackRequest($params, $tokenAuth)
- {
- if ($params instanceof Request) {
- $request = $params;
- } else {
- $request = new Request($params, $tokenAuth);
- }
-
- $this->init($request);
-
- $isAuthenticated = $request->isAuthenticated();
-
- try {
- if ($this->isVisitValid()) {
- Common::printDebug("Current datetime: " . date("Y-m-d H:i:s", $request->getCurrentTimestamp()));
-
- $visit = $this->getNewVisitObject();
- $visit->setRequest($request);
- $visit->handle();
- } else {
- Common::printDebug("The request is invalid: empty request, or maybe tracking is disabled in the config.ini.php via record_statistics=0");
- }
- } catch (UnexpectedWebsiteFoundException $e) {
- Common::printDebug("Exception: " . $e->getMessage());
- $this->exitWithException($e, $isAuthenticated, 400);
- } catch (InvalidRequestParameterException $e) {
- Common::printDebug("Exception: " . $e->getMessage());
- $this->exitWithException($e, $isAuthenticated, 400);
- } catch (DbException $e) {
- Common::printDebug("Exception: " . $e->getMessage());
- $this->exitWithException($e, $isAuthenticated);
- } catch (Exception $e) {
- $this->exitWithException($e, $isAuthenticated);
- }
- $this->clear();
-
- // increment successfully logged request count. make sure to do this after try-catch,
- // since an excluded visit is considered 'successfully logged'
- ++$this->countOfLoggedRequests;
- return $isAuthenticated;
+ PluginManager::getInstance()->setTrackerPluginsNotToLoad($pluginsDisabled);
}
- protected function runScheduledTasksIfAllowed($isAuthenticated)
+ protected function loadTrackerPlugins()
{
- // Do not run schedule task if we are importing logs
- // or doing custom tracking (as it could slow down)
try {
- if (!$isAuthenticated
- && $this->shouldRunScheduledTasks()
- ) {
- self::runScheduledTasks();
- }
+ $pluginManager = PluginManager::getInstance();
+ $pluginsTracker = $pluginManager->loadTrackerPlugins();
+ Common::printDebug("Loading plugins: { " . implode(", ", $pluginsTracker) . " }");
} catch (Exception $e) {
- $this->exitWithException($e);
- }
- }
-
- /**
- * @return string
- */
- protected static function getRawBulkRequest()
- {
- return file_get_contents("php://input");
- }
-
- private function getRedirectUrl()
- {
- return Common::getRequestVar('redirecturl', false, 'string');
- }
-
- private function hasRedirectUrl()
- {
- $redirectUrl = $this->getRedirectUrl();
-
- return !empty($redirectUrl);
- }
-
- private function performRedirectToUrlIfSet()
- {
- if (!$this->hasRedirectUrl()) {
- return;
- }
-
- if (empty($this->requests)) {
- return;
- }
-
- $redirectUrl = $this->getRedirectUrl();
- $host = Url::getHostFromUrl($redirectUrl);
-
- if (empty($host)) {
- return;
- }
-
- $urls = new SiteUrls();
- $siteUrls = $urls->getAllCachedSiteUrls();
- $siteIds = $this->getAllSiteIdsWithinRequest();
-
- foreach ($siteIds as $siteId) {
- if (empty($siteUrls[$siteId])) {
- continue;
- }
-
- if (Url::isHostInUrls($host, $siteUrls[$siteId])) {
- Url::redirectToUrl($redirectUrl);
- }
- }
- }
-
- private function getAllSiteIdsWithinRequest()
- {
- if (empty($this->requests)) {
- return array();
- }
-
- $siteIds = array();
-
- foreach ($this->requests as $request) {
- $siteIds[] = (int) $request['idsite'];
+ Common::printDebug("ERROR: " . $e->getMessage());
}
-
- return array_unique($siteIds);
}
}
diff --git a/core/Tracker/Db.php b/core/Tracker/Db.php
index 0c419d8f6e..e5ec25f57f 100644
--- a/core/Tracker/Db.php
+++ b/core/Tracker/Db.php
@@ -11,8 +11,13 @@ namespace Piwik\Tracker;
use Exception;
use PDOStatement;
use Piwik\Common;
+use Piwik\Config;
+use Piwik\Piwik;
use Piwik\Timer;
+use Piwik\Tracker;
use Piwik\Tracker\Db\DbException;
+use Piwik\Tracker\Db\Mysqli;
+use Piwik\Tracker\Db\Pdo\Mysql;
/**
* Simple database wrapper.
@@ -226,4 +231,63 @@ abstract class Db
* @return bool True if error number matches; false otherwise
*/
abstract public function isErrNo($e, $errno);
+
+ /**
+ * Factory to create database objects
+ *
+ * @param array $configDb Database configuration
+ * @throws Exception
+ * @return \Piwik\Tracker\Db\Mysqli|\Piwik\Tracker\Db\Pdo\Mysql
+ */
+ public static function factory($configDb)
+ {
+ /**
+ * Triggered before a connection to the database is established by the Tracker.
+ *
+ * This event can be used to change the database connection settings used by the Tracker.
+ *
+ * @param array $dbInfos Reference to an array containing database connection info,
+ * including:
+ *
+ * - **host**: The host name or IP address to the MySQL database.
+ * - **username**: The username to use when connecting to the
+ * database.
+ * - **password**: The password to use when connecting to the
+ * database.
+ * - **dbname**: The name of the Piwik MySQL database.
+ * - **port**: The MySQL database port to use.
+ * - **adapter**: either `'PDO\MYSQL'` or `'MYSQLI'`
+ * - **type**: The MySQL engine to use, for instance 'InnoDB'
+ */
+ Piwik::postEvent('Tracker.getDatabaseConfig', array(&$configDb));
+
+ switch ($configDb['adapter']) {
+ case 'PDO\MYSQL':
+ case 'PDO_MYSQL': // old format pre Piwik 2
+ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Pdo/Mysql.php';
+ return new Mysql($configDb);
+
+ case 'MYSQLI':
+ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Mysqli.php';
+ return new Mysqli($configDb);
+ }
+
+ throw new Exception('Unsupported database adapter ' . $configDb['adapter']);
+ }
+
+ public static function connectPiwikTrackerDb()
+ {
+ $db = null;
+ $configDb = Config::getInstance()->database;
+
+ if (!isset($configDb['port'])) {
+ // before 0.2.4 there is no port specified in config file
+ $configDb['port'] = '3306';
+ }
+
+ $db = self::factory($configDb);
+ $db->connect();
+
+ return $db;
+ }
}
diff --git a/core/Tracker/Db/Mysqli.php b/core/Tracker/Db/Mysqli.php
index 9452551683..e1922e11e5 100644
--- a/core/Tracker/Db/Mysqli.php
+++ b/core/Tracker/Db/Mysqli.php
@@ -291,11 +291,11 @@ class Mysqli extends Db
*/
public function beginTransaction()
{
- if (!$this->activeTransaction === false ) {
+ if (!$this->activeTransaction === false) {
return;
}
- if ( $this->connection->autocommit(false) ) {
+ if ( $this->connection->autocommit(false)) {
$this->activeTransaction = uniqid();
return $this->activeTransaction;
}
@@ -309,15 +309,17 @@ class Mysqli extends Db
*/
public function commit($xid)
{
- if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
return;
}
+
$this->activeTransaction = false;
if (!$this->connection->commit() ) {
throw new DbException("Commit failed");
}
+
$this->connection->autocommit(true);
}
@@ -329,14 +331,16 @@ class Mysqli extends Db
*/
public function rollBack($xid)
{
- if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
return;
}
+
$this->activeTransaction = false;
if (!$this->connection->rollback() ) {
throw new DbException("Rollback failed");
}
+
$this->connection->autocommit(true);
}
}
diff --git a/core/Tracker/Db/Pdo/Mysql.php b/core/Tracker/Db/Pdo/Mysql.php
index 1cb72c11a6..4d6094b478 100644
--- a/core/Tracker/Db/Pdo/Mysql.php
+++ b/core/Tracker/Db/Pdo/Mysql.php
@@ -270,12 +270,13 @@ class Mysql extends Db
*/
public function commit($xid)
{
- if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
return;
}
+
$this->activeTransaction = false;
- if (!$this->connection->commit() ) {
+ if (!$this->connection->commit()) {
throw new DbException("Commit failed");
}
}
@@ -288,12 +289,13 @@ class Mysql extends Db
*/
public function rollBack($xid)
{
- if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
return;
}
+
$this->activeTransaction = false;
- if (!$this->connection->rollBack() ) {
+ if (!$this->connection->rollBack()) {
throw new DbException("Rollback failed");
}
}
diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index 0d4bd2bfcb..913522696a 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -10,6 +10,7 @@ namespace Piwik\Tracker;
use Exception;
use Piwik\Common;
+use Piwik\Date;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ConversionDimension;
use Piwik\Plugin\Dimension\VisitDimension;
@@ -837,7 +838,7 @@ class GoalManager
$goal = array(
'idvisit' => $visitorInformation['idvisit'],
'idvisitor' => $visitorInformation['idvisitor'],
- 'server_time' => Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time'])
+ 'server_time' => Date::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time'])
);
$visitDimensions = VisitDimension::getAllDimensions();
diff --git a/core/Tracker/Handler.php b/core/Tracker/Handler.php
new file mode 100644
index 0000000000..3970b910d1
--- /dev/null
+++ b/core/Tracker/Handler.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Tracker;
+
+use Piwik\Common;
+use Piwik\Exception\InvalidRequestParameterException;
+use Piwik\Exception\UnexpectedWebsiteFoundException;
+use Piwik\Tracker;
+use Exception;
+use Piwik\Url;
+
+class Handler
+{
+ /**
+ * @var Response
+ */
+ private $response;
+
+ /**
+ * @var ScheduledTasksRunner
+ */
+ private $tasksRunner;
+
+ public function __construct()
+ {
+ $this->setResponse(new Response());
+ }
+
+ public function setResponse($response)
+ {
+ $this->response = $response;
+ }
+
+ public function init(Tracker $tracker, RequestSet $requestSet)
+ {
+ $this->response->init($tracker);
+ }
+
+ public function process(Tracker $tracker, RequestSet $requestSet)
+ {
+ foreach ($requestSet->getRequests() as $request) {
+ $tracker->trackRequest($request);
+ }
+ }
+
+ public function onStartTrackRequests(Tracker $tracker, RequestSet $requestSet)
+ {
+ }
+
+ public function onAllRequestsTracked(Tracker $tracker, RequestSet $requestSet)
+ {
+ $tasks = $this->getScheduledTasksRunner();
+ if ($tasks->shouldRun($tracker)) {
+ $tasks->runScheduledTasks();
+ }
+ }
+
+ private function getScheduledTasksRunner()
+ {
+ if (is_null($this->tasksRunner)) {
+ $this->tasksRunner = new ScheduledTasksRunner();
+ }
+
+ return $this->tasksRunner;
+ }
+
+ /**
+ * @internal
+ */
+ public function setScheduledTasksRunner(ScheduledTasksRunner $runner)
+ {
+ $this->tasksRunner = $runner;
+ }
+
+ public function onException(Tracker $tracker, RequestSet $requestSet, Exception $e)
+ {
+ Common::printDebug("Exception: " . $e->getMessage());
+
+ $statusCode = 500;
+ if ($e instanceof UnexpectedWebsiteFoundException) {
+ $statusCode = 400;
+ } elseif ($e instanceof InvalidRequestParameterException) {
+ $statusCode = 400;
+ }
+
+ $this->response->outputException($tracker, $e, $statusCode);
+ $this->redirectIfNeeded($requestSet);
+ }
+
+ public function finish(Tracker $tracker, RequestSet $requestSet)
+ {
+ $this->response->outputResponse($tracker);
+ $this->redirectIfNeeded($requestSet);
+ return $this->response->getOutput();
+ }
+
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ protected function redirectIfNeeded(RequestSet $requestSet)
+ {
+ $redirectUrl = $requestSet->shouldPerformRedirectToUrl();
+
+ if (!empty($redirectUrl)) {
+ Url::redirectToUrl($redirectUrl);
+ }
+ }
+
+}
diff --git a/core/Tracker/Handler/Factory.php b/core/Tracker/Handler/Factory.php
new file mode 100644
index 0000000000..0f1421d0a2
--- /dev/null
+++ b/core/Tracker/Handler/Factory.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Tracker\Handler;
+
+use Exception;
+use Piwik\Piwik;
+use Piwik\Tracker\Handler;
+
+class Factory
+{
+ public static function make()
+ {
+ $handler = null;
+
+ /**
+ * Triggered before a new **handler tracking object** is created. Subscribers to this
+ * event can force the use of a custom handler tracking object that extends from
+ * {@link Piwik\Tracker\Handler} and customize any tracking behavior.
+ *
+ * @param \Piwik\Tracker\Handler &$handler Initialized to null, but can be set to
+ * a new handler object. If it isn't modified
+ * Piwik uses the default class.
+ */
+ Piwik::postEvent('Tracker.newHandler', array(&$handler));
+
+ if (is_null($handler)) {
+ $handler = new Handler();
+ } elseif (!($handler instanceof Handler)) {
+ throw new Exception("The Handler object set in the plugin must be an instance of Piwik\\Tracker\\Handler");
+ }
+
+ return $handler;
+ }
+
+}
diff --git a/core/Tracker/Request.php b/core/Tracker/Request.php
index 8853800fa6..a38e599b28 100644
--- a/core/Tracker/Request.php
+++ b/core/Tracker/Request.php
@@ -31,8 +31,10 @@ class Request
* @var array
*/
protected $params;
+ protected $rawParams;
protected $isAuthenticated = null;
+ private $isEmptyRequest = false;
protected $tokenAuth;
@@ -50,16 +52,19 @@ class Request
$params = array();
}
$this->params = $params;
+ $this->rawParams = $params;
$this->tokenAuth = $tokenAuth;
$this->timestamp = time();
+ $this->isEmptyRequest = empty($params);
// When the 'url' and referrer url parameter are not given, we might be in the 'Simple Image Tracker' mode.
// The URL can default to the Referrer, which will be in this case
// the URL of the page containing the Simple Image beacon
if (empty($this->params['urlref'])
&& empty($this->params['url'])
+ && array_key_exists('HTTP_REFERER', $_SERVER)
) {
- $url = @$_SERVER['HTTP_REFERER'];
+ $url = $_SERVER['HTTP_REFERER'];
if (!empty($url)) {
$this->params['url'] = $url;
}
@@ -67,6 +72,21 @@ class Request
}
/**
+ * Get the params that were originally passed to the instance. These params do not contain any params that were added
+ * within this object.
+ * @return array
+ */
+ public function getRawParams()
+ {
+ return $this->rawParams;
+ }
+
+ public function getTokenAuth()
+ {
+ return $this->tokenAuth;
+ }
+
+ /**
* @return bool
*/
public function isAuthenticated()
@@ -82,21 +102,27 @@ class Request
* This method allows to set custom IP + server time + visitor ID, when using Tracking API.
* These two attributes can be only set by the Super User (passing token_auth).
*/
- protected function authenticateTrackingApi($tokenAuthFromBulkRequest)
+ protected function authenticateTrackingApi($tokenAuth)
{
- $shouldAuthenticate = Config::getInstance()->Tracker['tracking_requests_require_authentication'];
+ $shouldAuthenticate = TrackerConfig::getConfigValue('tracking_requests_require_authentication');
+
if ($shouldAuthenticate) {
- $tokenAuth = $tokenAuthFromBulkRequest ? $tokenAuthFromBulkRequest : Common::getRequestVar('token_auth', false, 'string', $this->params);
+
+ if (empty($tokenAuth)) {
+ $tokenAuth = Common::getRequestVar('token_auth', false, 'string', $this->params);
+ }
+
try {
$idSite = $this->getIdSite();
- $this->isAuthenticated = $this->authenticateSuperUserOrAdmin($tokenAuth, $idSite);
+ $this->isAuthenticated = self::authenticateSuperUserOrAdmin($tokenAuth, $idSite);
} catch (Exception $e) {
$this->isAuthenticated = false;
}
- if (!$this->isAuthenticated) {
- return;
+
+ if ($this->isAuthenticated) {
+ Common::printDebug("token_auth is authenticated!");
}
- Common::printDebug("token_auth is authenticated!");
+
} else {
$this->isAuthenticated = true;
Common::printDebug("token_auth authentication not required");
@@ -124,10 +150,12 @@ class Request
// Now checking the list of admin token_auth cached in the Tracker config file
if (!empty($idSite) && $idSite > 0) {
$website = Cache::getCacheWebsiteAttributes($idSite);
+
if (array_key_exists('admin_token_auth', $website) && in_array($tokenAuth, $website['admin_token_auth'])) {
return true;
}
}
+
Common::printDebug("WARNING! token_auth = $tokenAuth is not valid, Super User / Admin was NOT authenticated");
return false;
@@ -139,11 +167,13 @@ class Request
public function getDaysSinceFirstVisit()
{
$cookieFirstVisitTimestamp = $this->getParam('_idts');
+
if (!$this->isTimestampValid($cookieFirstVisitTimestamp)) {
$cookieFirstVisitTimestamp = $this->getCurrentTimestamp();
}
$daysSinceFirstVisit = round(($this->getCurrentTimestamp() - $cookieFirstVisitTimestamp) / 86400, $precision = 0);
+
if ($daysSinceFirstVisit < 0) {
$daysSinceFirstVisit = 0;
}
@@ -324,21 +354,31 @@ class Request
public function getCurrentTimestamp()
{
$cdt = $this->getCustomTimestamp();
- if(!empty($cdt)) {
+
+ if (!empty($cdt)) {
return $cdt;
}
+
return $this->timestamp;
}
+ public function setCurrentTimestamp($timestamp)
+ {
+ $this->timestamp = $timestamp;
+ }
+
protected function getCustomTimestamp()
{
$cdt = $this->getParam('cdt');
+
if (empty($cdt)) {
return false;
}
+
if (!is_numeric($cdt)) {
$cdt = strtotime($cdt);
}
+
if (!$this->isTimestampValid($cdt, $this->timestamp)) {
Common::printDebug(sprintf("Datetime %s is not valid", date("Y-m-d H:i:m", $cdt)));
return false;
@@ -347,6 +387,7 @@ class Request
// If timestamp in the past, token_auth is required
$timeFromNow = $this->timestamp - $cdt;
$isTimestampRecent = $timeFromNow < self::CUSTOM_TIMESTAMP_DOES_NOT_REQUIRE_TOKENAUTH_WHEN_NEWER_THAN;
+
if (!$isTimestampRecent) {
if(!$this->isAuthenticated()) {
Common::printDebug(sprintf("Custom timestamp is %s seconds old, requires &token_auth...", $timeFromNow));
@@ -354,6 +395,7 @@ class Request
return false;
}
}
+
return $cdt;
}
@@ -366,9 +408,10 @@ class Request
*/
protected function isTimestampValid($time, $now = null)
{
- if(empty($now)) {
+ if (empty($now)) {
$now = $this->getCurrentTimestamp();
}
+
return $time <= $now
&& $time > $now - 10 * 365 * 86400;
}
@@ -400,10 +443,29 @@ class Request
public function getUserAgent()
{
- $default = @$_SERVER['HTTP_USER_AGENT'];
- return Common::getRequestVar('ua', is_null($default) ? false : $default, 'string', $this->params);
+ $default = false;
+
+ if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
+ $default = $_SERVER['HTTP_USER_AGENT'];
+ }
+
+ return Common::getRequestVar('ua', $default, 'string', $this->params);
}
+ public function getCustomVariablesInVisitScope()
+ {
+ return $this->getCustomVariables('visit');
+ }
+
+ public function getCustomVariablesInPageScope()
+ {
+ return $this->getCustomVariables('page');
+ }
+
+ /**
+ * @deprecated since Piwik 2.10.0. Use Request::getCustomVariablesInPageScope() or Request::getCustomVariablesInVisitScope() instead.
+ * When we "remove" this method we will only set visibility to "private" and pass $parameter = _cvar|cvar as an argument instead of $scope
+ */
public function getCustomVariables($scope)
{
if ($scope == 'visit') {
@@ -412,16 +474,19 @@ class Request
$parameter = 'cvar';
}
- $customVar = Common::unsanitizeInputValues(Common::getRequestVar($parameter, '', 'json', $this->params));
+ $cvar = Common::getRequestVar($parameter, '', 'json', $this->params);
+ $customVar = Common::unsanitizeInputValues($cvar);
if (!is_array($customVar)) {
return array();
}
$customVariables = array();
- $maxCustomVars = CustomVariables::getMaxCustomVariables();
+ $maxCustomVars = CustomVariables::getMaxCustomVariables();
+
foreach ($customVar as $id => $keyValue) {
$id = (int)$id;
+
if ($id < 1
|| $id > $maxCustomVars
|| count($keyValue) != 2
@@ -437,10 +502,8 @@ class Request
// We keep in the URL when Custom Variable have empty names
// and values, as it means they can be deleted server side
- $key = self::truncateCustomVariable($keyValue[0]);
- $value = self::truncateCustomVariable($keyValue[1]);
- $customVariables['custom_var_k' . $id] = $key;
- $customVariables['custom_var_v' . $id] = $value;
+ $customVariables['custom_var_k' . $id] = self::truncateCustomVariable($keyValue[0]);
+ $customVariables['custom_var_v' . $id] = self::truncateCustomVariable($keyValue[1]);
}
return $customVariables;
@@ -485,17 +548,17 @@ class Request
protected function getCookieName()
{
- return Config::getInstance()->Tracker['cookie_name'];
+ return TrackerConfig::getConfigValue('cookie_name');
}
protected function getCookieExpire()
{
- return $this->getCurrentTimestamp() + Config::getInstance()->Tracker['cookie_expire'];
+ return $this->getCurrentTimestamp() + TrackerConfig::getConfigValue('cookie_expire');
}
protected function getCookiePath()
{
- return Config::getInstance()->Tracker['cookie_path'];
+ return TrackerConfig::getConfigValue('cookie_path');
}
/**
@@ -594,9 +657,9 @@ class Request
return $plugins;
}
- public function getParamsCount()
+ public function isEmptyRequest()
{
- return count($this->params);
+ return $this->isEmptyRequest;
}
const GENERATION_TIME_MS_MAXIMUM = 3600000; // 1 hour
@@ -637,18 +700,19 @@ class Request
* @return mixed|string
* @throws Exception
*/
- private function getIpString()
+ public function getIpString()
{
$cip = $this->getParam('cip');
- if(empty($cip)) {
+ if (empty($cip)) {
return IP::getIpFromHeader();
}
- if(!$this->isAuthenticated()) {
+ if (!$this->isAuthenticated()) {
Common::printDebug("WARN: Tracker API 'cip' was used with invalid token_auth");
return IP::getIpFromHeader();
}
+
return $cip;
}
}
diff --git a/core/Tracker/RequestSet.php b/core/Tracker/RequestSet.php
new file mode 100644
index 0000000000..9f09c3ca52
--- /dev/null
+++ b/core/Tracker/RequestSet.php
@@ -0,0 +1,248 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Tracker;
+
+use Piwik\Common;
+use Piwik\Piwik;
+use Piwik\Plugins\SitesManager\SiteUrls;
+use Piwik\Url;
+
+class RequestSet
+{
+ /**
+ * The set of visits to track.
+ *
+ * @var Request[]
+ */
+ private $requests = null;
+
+ /**
+ * The token auth supplied with a bulk visits POST.
+ *
+ * @var string
+ */
+ private $tokenAuth = null;
+
+ private $env = array();
+
+ public function setRequests($requests)
+ {
+ $this->requests = array();
+
+ foreach ($requests as $request) {
+
+ if (empty($request) && !is_array($request)) {
+ continue;
+ }
+
+ if (!$request instanceof Request) {
+ $request = new Request($request, $this->getTokenAuth());
+ }
+
+ $this->requests[] = $request;
+ }
+ }
+
+ public function setTokenAuth($tokenAuth)
+ {
+ $this->tokenAuth = $tokenAuth;
+ }
+
+ public function getNumberOfRequests()
+ {
+ if (is_array($this->requests)) {
+ return count($this->requests);
+ }
+
+ return 0;
+ }
+
+ public function getRequests()
+ {
+ if (!$this->areRequestsInitialized()) {
+ return array();
+ }
+
+ return $this->requests;
+ }
+
+ public function getTokenAuth()
+ {
+ if (!is_null($this->tokenAuth)) {
+ return $this->tokenAuth;
+ }
+
+ return Common::getRequestVar('token_auth', false);
+ }
+
+ private function areRequestsInitialized()
+ {
+ return !is_null($this->requests);
+ }
+
+ public function initRequestsAndTokenAuth()
+ {
+ if ($this->areRequestsInitialized()) {
+ return;
+ }
+
+ Piwik::postEvent('Tracker.initRequestSet', array($this));
+
+ if (!$this->areRequestsInitialized()) {
+ $this->requests = array();
+
+ if (!empty($_GET) || !empty($_POST)) {
+ $this->setRequests(array($_GET + $_POST));
+ }
+ }
+ }
+
+ public function hasRequests()
+ {
+ return !empty($this->requests);
+ }
+
+ protected function getRedirectUrl()
+ {
+ return Common::getRequestVar('redirecturl', false, 'string');
+ }
+
+ protected function hasRedirectUrl()
+ {
+ $redirectUrl = $this->getRedirectUrl();
+
+ return !empty($redirectUrl);
+ }
+
+ protected function getAllSiteIdsWithinRequest()
+ {
+ if (empty($this->requests)) {
+ return array();
+ }
+
+ $siteIds = array();
+ foreach ($this->requests as $request) {
+ $siteIds[] = (int) $request->getIdSite();
+ }
+
+ return array_values(array_unique($siteIds));
+ }
+
+ // TODO maybe move to reponse? or somewhere else? not sure where!
+ public function shouldPerformRedirectToUrl()
+ {
+ if (!$this->hasRedirectUrl()) {
+ return false;
+ }
+
+ if (!$this->hasRequests()) {
+ return false;
+ }
+
+ $redirectUrl = $this->getRedirectUrl();
+ $host = Url::getHostFromUrl($redirectUrl);
+
+ if (empty($host)) {
+ return false;
+ }
+
+ $urls = new SiteUrls();
+ $siteUrls = $urls->getAllCachedSiteUrls();
+ $siteIds = $this->getAllSiteIdsWithinRequest();
+
+ foreach ($siteIds as $siteId) {
+ if (empty($siteUrls[$siteId])) {
+ $siteUrls[$siteId] = array();
+ }
+
+ if (Url::isHostInUrls($host, $siteUrls[$siteId])) {
+ return $redirectUrl;
+ }
+ }
+
+ return false;
+ }
+
+ public function getState()
+ {
+ $requests = array(
+ 'requests' => array(),
+ 'env' => $this->getEnvironment(),
+ 'tokenAuth' => $this->getTokenAuth(),
+ 'time' => time()
+ );
+
+ foreach ($this->getRequests() as $request) {
+ $requests['requests'][] = $request->getRawParams();
+ // todo we maybe need to save cdt (timestamp), tokenAuth, maybe also urlref and IP as well but we need to be
+ // careful with restoring those values etc since we'd probably need to check permissions etc in some cases
+ }
+
+ return $requests;
+ }
+
+ public function restoreState($state)
+ {
+ $backupEnv = $this->getCurrentEnvironment();
+
+ $this->setEnvironment($state['env']);
+ $this->setTokenAuth($state['tokenAuth']);
+
+ $this->restoreEnvironment();
+ $this->setRequests($state['requests']);
+
+ foreach ($this->getRequests() as $request) {
+ $request->setCurrentTimestamp($state['time']);
+ }
+
+ $this->resetEnvironment($backupEnv);
+ }
+
+ public function rememberEnvironment()
+ {
+ $this->setEnvironment($this->getEnvironment());
+ }
+
+ public function setEnvironment($env)
+ {
+ $this->env = $env;
+ }
+
+ protected function getEnvironment()
+ {
+ if (!empty($this->env)) {
+ return $this->env;
+ }
+
+ return $this->getCurrentEnvironment();
+ }
+
+ public function restoreEnvironment()
+ {
+ if (empty($this->env)) {
+ return;
+ }
+
+ $this->resetEnvironment($this->env);
+ }
+
+ private function resetEnvironment($env)
+ {
+ $_SERVER = $env['server'];
+ }
+
+ private function getCurrentEnvironment()
+ {
+ return array(
+ 'server' => $_SERVER
+ );
+ }
+
+
+}
diff --git a/core/Tracker/Response.php b/core/Tracker/Response.php
new file mode 100644
index 0000000000..5258d65cd2
--- /dev/null
+++ b/core/Tracker/Response.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Tracker;
+
+use Exception;
+use Piwik\Common;
+use Piwik\Profiler;
+use Piwik\Timer;
+use Piwik\Tracker;
+use Piwik\Tracker\Db as TrackerDb;
+
+class Response
+{
+ private $timer;
+
+ private $content;
+
+ public function init(Tracker $tracker)
+ {
+ ob_start(); // we use ob_start only because of Common::printDebug, we should actually not really use ob_start
+
+ if ($tracker->isDebugModeEnabled()) {
+ $this->timer = new Timer();
+
+ TrackerDb::enableProfiling();
+ }
+ }
+
+ public function getOutput()
+ {
+ $this->outputAccessControlHeaders();
+
+ if (is_null($this->content) && ob_get_level() > 0) {
+ $this->content = ob_get_clean();
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * Echos an error message & other information, then exits.
+ *
+ * @param Tracker $tracker
+ * @param Exception $e
+ * @param int $statusCode eg 500
+ */
+ public function outputException(Tracker $tracker, Exception $e, $statusCode)
+ {
+ Common::sendResponseCode($statusCode);
+ $this->logExceptionToErrorLog($e);
+
+ if ($tracker->isDebugModeEnabled()) {
+ Common::sendHeader('Content-Type: text/html; charset=utf-8');
+ $trailer = '<span style="color: #888888">Backtrace:<br /><pre>' . $e->getTraceAsString() . '</pre></span>';
+ $headerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/simpleLayoutHeader.tpl');
+ $footerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/simpleLayoutFooter.tpl');
+ $headerPage = str_replace('{$HTML_TITLE}', 'Piwik &rsaquo; Error', $headerPage);
+
+ echo $headerPage . '<p>' . $this->getMessageFromException($e) . '</p>' . $trailer . $footerPage;
+ } else {
+ $this->outputApiResponse($tracker);
+ }
+ }
+
+ public function outputResponse(Tracker $tracker)
+ {
+ if (!$tracker->shouldRecordStatistics()) {
+ $this->outputApiResponse($tracker);
+ Common::printDebug("Logging disabled, display transparent logo");
+ } elseif (!$tracker->hasLoggedRequests()) {
+ Common::printDebug("Empty request => Piwik page");
+ echo "<a href='/'>Piwik</a> is a free/libre web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
+ } else {
+ $this->outputApiResponse($tracker);
+ Common::printDebug("Nothing to notice => default behaviour");
+ }
+
+ Common::printDebug("End of the page.");
+
+ if ($tracker->isDebugModeEnabled()
+ && $tracker->isDatabaseConnected()
+ && TrackerDb::isProfilingEnabled()) {
+ $db = Tracker::getDatabase();
+ $db->recordProfiling();
+ Profiler::displayDbTrackerProfile($db);
+ }
+
+ if ($tracker->isDebugModeEnabled()) {
+ Common::printDebug($_COOKIE);
+ Common::printDebug((string)$this->timer);
+ }
+ }
+
+ private function outputAccessControlHeaders()
+ {
+ $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+
+ if ($requestMethod !== 'GET') {
+ $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
+ Common::sendHeader('Access-Control-Allow-Origin: ' . $origin);
+ Common::sendHeader('Access-Control-Allow-Credentials: true');
+ }
+ }
+
+ private function getOutputBuffer()
+ {
+ return ob_get_contents();
+ }
+
+ protected function hasAlreadyPrintedOutput()
+ {
+ return strlen($this->getOutputBuffer()) > 0;
+ }
+
+ private function outputApiResponse(Tracker $tracker)
+ {
+ if ($tracker->isDebugModeEnabled()) {
+ return;
+ }
+
+ if ($this->hasAlreadyPrintedOutput()) {
+ return;
+ }
+
+ $request = $_GET + $_POST;
+
+ if (array_key_exists('send_image', $request) && $request['send_image'] === '0') {
+ Common::sendResponseCode(204);
+ return;
+ }
+
+ $this->outputTransparentGif();
+ }
+
+ private function outputTransparentGif ()
+ {
+ $transGifBase64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
+ Common::sendHeader('Content-Type: image/gif');
+
+ echo base64_decode($transGifBase64);
+ }
+
+ /**
+ * Gets the error message to output when a tracking request fails.
+ *
+ * @param Exception $e
+ * @return string
+ */
+ protected function getMessageFromException($e)
+ {
+ // Note: duplicated from FormDatabaseSetup.isAccessDenied
+ // Avoid leaking the username/db name when access denied
+ if ($e->getCode() == 1044 || $e->getCode() == 42000) {
+ return "Error while connecting to the Piwik database - please check your credentials in config/config.ini.php file";
+ }
+
+ if (Common::isPhpCliMode()) {
+ return $e->getMessage() . "\n" . $e->getTraceAsString();
+ }
+
+ return $e->getMessage();
+ }
+
+ protected function logExceptionToErrorLog(Exception $e)
+ {
+ error_log(sprintf("Error in Piwik (tracker): %s", str_replace("\n", " ", $this->getMessageFromException($e))));
+ }
+
+}
diff --git a/core/Tracker/ScheduledTasksRunner.php b/core/Tracker/ScheduledTasksRunner.php
new file mode 100644
index 0000000000..3f4602ef2b
--- /dev/null
+++ b/core/Tracker/ScheduledTasksRunner.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Tracker;
+
+
+use Piwik\Common;
+use Piwik\Config;
+use Piwik\CronArchive;
+use Piwik\Option;
+use Piwik\Piwik;
+use Piwik\SettingsPiwik;
+use Piwik\Tracker;
+use Piwik\Translate;
+
+class ScheduledTasksRunner
+{
+
+ public function shouldRun(Tracker $tracker)
+ {
+ if (Common::isPhpCliMode()) {
+ // don't run scheduled tasks in CLI mode from Tracker, this is the case
+ // where we bulk load logs & don't want to lose time with tasks
+ return false;
+ }
+
+ return $tracker->shouldRecordStatistics();
+ }
+
+ /**
+ * Tracker requests will automatically trigger the Scheduled tasks.
+ * This is useful for users who don't setup the cron,
+ * but still want daily/weekly/monthly PDF reports emailed automatically.
+ *
+ * This is similar to calling the API CoreAdminHome.runScheduledTasks
+ */
+ public function runScheduledTasks()
+ {
+ $now = time();
+
+ // Currently, there are no hourly tasks. When there are some,
+ // this could be too aggressive minimum interval (some hours would be skipped in case of low traffic)
+ $minimumInterval = TrackerConfig::getConfigValue('scheduled_tasks_min_interval');
+
+ // If the user disabled browser archiving, he has already setup a cron
+ // To avoid parallel requests triggering the Scheduled Tasks,
+ // Get last time tasks started executing
+ $cache = Cache::getCacheGeneral();
+
+ if ($minimumInterval <= 0
+ || empty($cache['isBrowserTriggerEnabled'])
+ ) {
+ Common::printDebug("-> Scheduled tasks not running in Tracker: Browser archiving is disabled.");
+ return;
+ }
+
+ $nextRunTime = $cache['lastTrackerCronRun'] + $minimumInterval;
+
+ if ((defined('DEBUG_FORCE_SCHEDULED_TASKS') && DEBUG_FORCE_SCHEDULED_TASKS)
+ || $cache['lastTrackerCronRun'] === false
+ || $nextRunTime < $now
+ ) {
+ $cache['lastTrackerCronRun'] = $now;
+ Cache::setCacheGeneral($cache);
+ Tracker::initCorePiwikInTrackerMode();
+ Option::set('lastTrackerCronRun', $cache['lastTrackerCronRun']);
+ Common::printDebug('-> Scheduled Tasks: Starting...');
+
+ // save current user privilege and temporarily assume Super User privilege
+ $isSuperUser = Piwik::hasUserSuperUserAccess();
+
+ // Scheduled tasks assume Super User is running
+ Piwik::setUserHasSuperUserAccess();
+
+ // While each plugins should ensure that necessary languages are loaded,
+ // we ensure English translations at least are loaded
+ Translate::loadEnglishTranslation();
+
+ ob_start();
+ CronArchive::$url = SettingsPiwik::getPiwikUrl();
+ $cronArchive = new CronArchive();
+ $cronArchive->runScheduledTasksInTrackerMode();
+
+ $resultTasks = ob_get_contents();
+ ob_clean();
+
+ // restore original user privilege
+ Piwik::setUserHasSuperUserAccess($isSuperUser);
+
+ foreach (explode('</pre>', $resultTasks) as $resultTask) {
+ Common::printDebug(str_replace('<pre>', '', $resultTask));
+ }
+
+ Common::printDebug('Finished Scheduled Tasks.');
+ } else {
+ Common::printDebug("-> Scheduled tasks not triggered.");
+ }
+
+ Common::printDebug("Next run will be from: " . date('Y-m-d H:i:s', $nextRunTime) . ' UTC');
+ }
+}
diff --git a/core/Tracker/SettingsStorage.php b/core/Tracker/SettingsStorage.php
index 6c54b1b993..2e3ac5e17d 100644
--- a/core/Tracker/SettingsStorage.php
+++ b/core/Tracker/SettingsStorage.php
@@ -9,6 +9,7 @@
namespace Piwik\Tracker;
+use Piwik\Cache\PersistentCache;
use Piwik\Settings\Storage;
use Piwik\Tracker;
@@ -17,27 +18,16 @@ use Piwik\Tracker;
*/
class SettingsStorage extends Storage
{
-
protected function loadSettings()
{
- $trackerCache = Cache::getCacheGeneral();
- $settings = null;
-
- if (array_key_exists('settingsStorage', $trackerCache)) {
- $allSettings = $trackerCache['settingsStorage'];
+ $cache = $this->getCache();
- if (is_array($allSettings) && array_key_exists($this->getOptionKey(), $allSettings)) {
- $settings = $allSettings[$this->getOptionKey()];
- }
+ if ($cache->has()) {
+ $settings = $cache->get();
} else {
- $trackerCache['settingsStorage'] = array();
- }
-
- if (is_null($settings)) {
$settings = parent::loadSettings();
- $trackerCache['settingsStorage'][$this->getOptionKey()] = $settings;
- Cache::setCacheGeneral($trackerCache);
+ $cache->set($settings);
}
return $settings;
@@ -49,9 +39,15 @@ class SettingsStorage extends Storage
self::clearCache();
}
+ private function getCache()
+ {
+ return new PersistentCache($this->getOptionKey());
+ }
+
public static function clearCache()
{
- Cache::clearCacheGeneral();
+ Cache::deleteTrackerCache();
+ PersistentCache::_reset();
}
}
diff --git a/core/Tracker/TrackerConfig.php b/core/Tracker/TrackerConfig.php
new file mode 100644
index 0000000000..537dc8f0c9
--- /dev/null
+++ b/core/Tracker/TrackerConfig.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Tracker;
+
+use Piwik\Config;
+use Piwik\Tracker;
+
+class TrackerConfig
+{
+ /**
+ * Update Tracker config
+ *
+ * @param string $name Setting name
+ * @param mixed $value Value
+ */
+ public static function setConfigValue($name, $value)
+ {
+ $section = self::getConfig();
+ $section[$name] = $value;
+ Config::getInstance()->Tracker = $section;
+ }
+
+ public static function getConfigValue($name)
+ {
+ $config = self::getConfig();
+ return $config[$name];
+ }
+
+ private static function getConfig()
+ {
+ return Config::getInstance()->Tracker;
+ }
+}
diff --git a/core/Tracker/Visit/Factory.php b/core/Tracker/Visit/Factory.php
new file mode 100644
index 0000000000..71362dddea
--- /dev/null
+++ b/core/Tracker/Visit/Factory.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Tracker\Visit;
+use Piwik\Piwik;
+use Piwik\Tracker\Visit;
+use Piwik\Tracker\VisitInterface;
+use Exception;
+
+class Factory
+{
+ /**
+ * Returns the Tracker_Visit object.
+ * This method can be overwritten to use a different Tracker_Visit object
+ *
+ * @throws Exception
+ * @return \Piwik\Tracker\Visit
+ */
+ public static function make()
+ {
+ $visit = null;
+
+ /**
+ * Triggered before a new **visit tracking object** is created. Subscribers to this
+ * event can force the use of a custom visit tracking object that extends from
+ * {@link Piwik\Tracker\VisitInterface}.
+ *
+ * @param \Piwik\Tracker\VisitInterface &$visit Initialized to null, but can be set to
+ * a new visit object. If it isn't modified
+ * Piwik uses the default class.
+ */
+ Piwik::postEvent('Tracker.makeNewVisitObject', array(&$visit));
+
+ if (is_null($visit)) {
+ $visit = new Visit();
+ } elseif (!($visit instanceof VisitInterface)) {
+ throw new Exception("The Visit object set in the plugin must implement VisitInterface");
+ }
+
+ return $visit;
+ }
+}
diff --git a/core/Updates/2.9.1.php b/core/Updates/2.9.1.php
new file mode 100644
index 0000000000..caaa92ee4b
--- /dev/null
+++ b/core/Updates/2.9.1.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Updates;
+
+use Piwik\Updates;
+
+/**
+ * Update for version 2.9.1.
+ */
+class Updates_2_9_1 extends Updates
+{
+
+ /**
+ * Here you can define any action that should be performed during the update. For instance executing SQL statements,
+ * renaming config entries, updating files, etc.
+ */
+ static function update()
+ {
+ $pluginManager = \Piwik\Plugin\Manager::getInstance();
+
+ try {
+ $pluginManager->activatePlugin('BulkTracking');
+ } catch(\Exception $e) {
+ }
+ }
+}
diff --git a/core/Url.php b/core/Url.php
index d57276d5a5..daf6c73c95 100644
--- a/core/Url.php
+++ b/core/Url.php
@@ -466,6 +466,21 @@ class Url
self::redirectToUrl(self::getCurrentUrlWithoutQueryString());
}
+ private static function redirectToUrlNoExit($url)
+ {
+ if (UrlHelper::isLookLikeUrl($url)
+ || strpos($url, 'index.php') === 0
+ ) {
+ Common::sendHeader("Location: $url");
+ } else {
+ echo "Invalid URL to redirect to.";
+ }
+
+ if (Common::isPhpCliMode()) {
+ throw new Exception("If you were using a browser, Piwik would redirect you to this URL: $url \n\n");
+ }
+ }
+
/**
* Redirects the user to the specified URL.
*
@@ -480,17 +495,8 @@ class Url
// but it is not always called fast enough
Session::close();
- if (UrlHelper::isLookLikeUrl($url)
- || strpos($url, 'index.php') === 0
- ) {
- Common::sendHeader("Location: $url");
- } else {
- echo "Invalid URL to redirect to.";
- }
+ self::redirectToUrlNoExit($url);
- if (Common::isPhpCliMode()) {
- throw new Exception("If you were using a browser, Piwik would redirect you to this URL: $url \n\n");
- }
exit;
}
diff --git a/misc/others/cli-script-bootstrap.php b/misc/others/cli-script-bootstrap.php
index f26d45abcc..9f89cfe319 100644
--- a/misc/others/cli-script-bootstrap.php
+++ b/misc/others/cli-script-bootstrap.php
@@ -34,7 +34,5 @@ require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
$GLOBALS['PIWIK_TRACKER_DEBUG'] = false;
define('PIWIK_ENABLE_DISPATCH', false);
-Config::getInstance()->log['log_writers'][] = 'screen';
-Config::getInstance()->log['log_level'] = 'VERBOSE';
Config::getInstance()->log['string_message_format'] = "%message%";
FrontController::getInstance()->init(); \ No newline at end of file
diff --git a/misc/others/geoipUpdateRows.php b/misc/others/geoipUpdateRows.php
index 09ae69d3d4..23e72fc413 100755
--- a/misc/others/geoipUpdateRows.php
+++ b/misc/others/geoipUpdateRows.php
@@ -23,6 +23,12 @@ if (!Common::isPhpCliMode()) {
Log::error('[error] You must be logged in as Super User to run this script. Please login in to Piwik and refresh this page.');
exit;
}
+}
+
+Log::getInstance()->setLogLevel(Log::VERBOSE);
+Log::getInstance()->addLogWriter('screen');
+
+if (!Common::isPhpCliMode()) {
// the 'start' query param will be supplied by the AJAX requests, so if it's not there, the
// user is viewing the page in the browser.
if (Common::getRequestVar('start', false) === false) {
diff --git a/piwik.php b/piwik.php
index 636d6f8b22..271825f81a 100644
--- a/piwik.php
+++ b/piwik.php
@@ -8,15 +8,13 @@
* @package Piwik
*/
-use Piwik\Common;
-use Piwik\Timer;
+use Piwik\Tracker\RequestSet;
use Piwik\Tracker;
+use Piwik\Tracker\Handler;
// Note: if you wish to debug the Tracking API please see this documentation:
// http://developer.piwik.org/api-reference/tracking-api#debugging-the-tracker
-define('PIWIK_ENABLE_TRACKING', true);
-
if (!defined('PIWIK_DOCUMENT_ROOT')) {
define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) == '/' ? '' : dirname(__FILE__));
}
@@ -25,7 +23,6 @@ if (file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) {
require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
}
-$GLOBALS['PIWIK_TRACKER_MODE'] = true;
error_reporting(E_ALL | E_NOTICE);
@ini_set('xdebug.show_exception_trace', 0);
@ini_set('magic_quotes_runtime', 0);
@@ -62,62 +59,37 @@ require_once PIWIK_INCLUDE_PATH . '/core/IP.php';
require_once PIWIK_INCLUDE_PATH . '/core/UrlHelper.php';
require_once PIWIK_INCLUDE_PATH . '/core/Url.php';
require_once PIWIK_INCLUDE_PATH . '/core/SettingsPiwik.php';
+require_once PIWIK_INCLUDE_PATH . '/core/SettingsServer.php';
require_once PIWIK_INCLUDE_PATH . '/core/Tracker.php';
require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
require_once PIWIK_INCLUDE_PATH . '/core/Translate.php';
require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Cache.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/DbException.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/IgnoreCookie.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/VisitInterface.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Visit.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/GoalManager.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/PageUrl.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/TableLogAction.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Action.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/ActionPageview.php';
require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Request.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/VisitExcluded.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Tracker/VisitorNotFoundInDb.php';
-require_once PIWIK_INCLUDE_PATH . '/core/CacheFile.php';
-require_once PIWIK_INCLUDE_PATH . '/core/Filesystem.php';
require_once PIWIK_INCLUDE_PATH . '/core/Cookie.php';
+Tracker::loadTrackerEnvironment();
+
session_cache_limiter('nocache');
@date_default_timezone_set('UTC');
-if (!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING) {
- ob_start();
-}
+$tracker = new Tracker();
+$requestSet = new RequestSet();
-\Piwik\FrontController::createConfigObject();
+ob_start();
-$GLOBALS['PIWIK_TRACKER_DEBUG'] = (bool) \Piwik\Config::getInstance()->Tracker['debug'];
-if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
- require_once PIWIK_INCLUDE_PATH . '/core/Error.php';
- \Piwik\Error::setErrorHandler();
- require_once PIWIK_INCLUDE_PATH . '/core/ExceptionHandler.php';
- \Piwik\ExceptionHandler::setUp();
+try {
+ $handler = Handler\Factory::make();
+ $response = $tracker->main($handler, $requestSet);
- $timer = new Timer();
- Common::printDebug("Debug enabled - Input parameters: ");
- Common::printDebug(var_export($_GET, true));
+ if (!is_null($response)) {
+ echo $response;
+ }
- \Piwik\Tracker\Db::enableProfiling();
+} catch (Exception $e) {
+ echo "Error:" . $e->getMessage();
+ exit(1);
}
-if (!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING) {
- $process = new Tracker();
-
- try {
- $process->main();
- } catch (Exception $e) {
- echo "Error:" . $e->getMessage();
- exit(1);
- }
+if (ob_get_level() > 1) {
ob_end_flush();
- if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
- Common::printDebug($_COOKIE);
- Common::printDebug((string)$timer);
- }
-}
+} \ No newline at end of file
diff --git a/plugins/BulkTracking/.gitignore b/plugins/BulkTracking/.gitignore
new file mode 100644
index 0000000000..c8c9480010
--- /dev/null
+++ b/plugins/BulkTracking/.gitignore
@@ -0,0 +1 @@
+tests/System/processed/*xml \ No newline at end of file
diff --git a/plugins/BulkTracking/BulkTracking.php b/plugins/BulkTracking/BulkTracking.php
new file mode 100644
index 0000000000..b55a681030
--- /dev/null
+++ b/plugins/BulkTracking/BulkTracking.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\BulkTracking;
+use Piwik\Plugins\BulkTracking\Tracker\Handler;
+use Piwik\Plugins\BulkTracking\Tracker\Requests;
+use Piwik\Tracker\RequestSet;
+
+class BulkTracking extends \Piwik\Plugin
+{
+ /**
+ * @var Requests
+ */
+ private $requests;
+
+ /**
+ * @see Piwik\Plugin::getListHooksRegistered
+ */
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'Tracker.newHandler' => 'setHandlerIfBulkRequest',
+ 'Tracker.initRequestSet' => 'initRequestSet',
+ );
+ }
+
+ public function setRequests(Requests $requests)
+ {
+ $this->requests = $requests;
+ }
+
+ public function initRequestSet(RequestSet $requestSet)
+ {
+ if ($this->isUsingBulkRequest()) {
+
+ $bulk = $this->buildBulkRequests();
+
+ list($requests, $token) = $bulk->initRequestsAndTokenAuth($bulk->getRawBulkRequest());
+
+ if ($bulk->requiresAuthentication()) {
+ $bulk->authenticateRequests($requests);
+ }
+
+ if (!$requestSet->getTokenAuth()) {
+ $requestSet->setTokenAuth($token);
+ }
+
+ $requestSet->setRequests($requests);
+ }
+ }
+
+ public function setHandlerIfBulkRequest(&$handler)
+ {
+ if (!is_null($handler)) {
+ return;
+ }
+
+ if ($this->isUsingBulkRequest()) {
+ $handler = new Handler();
+ }
+ }
+
+ private function isUsingBulkRequest()
+ {
+ $requests = $this->buildBulkRequests();
+ $rawData = $requests->getRawBulkRequest();
+
+ return $requests->isUsingBulkRequest($rawData);
+ }
+
+ private function buildBulkRequests()
+ {
+ if (!is_null($this->requests)) {
+ return $this->requests;
+ }
+
+ return new Requests();
+ }
+}
diff --git a/plugins/BulkTracking/README.md b/plugins/BulkTracking/README.md
new file mode 100644
index 0000000000..ecb28c2d7b
--- /dev/null
+++ b/plugins/BulkTracking/README.md
@@ -0,0 +1,18 @@
+# Piwik BulkTracking Plugin
+
+## Description
+
+Add your plugin description here.
+
+## FAQ
+
+__My question?__
+My answer
+
+## Changelog
+
+Here goes the changelog text.
+
+## Support
+
+Please direct any feedback to ... \ No newline at end of file
diff --git a/plugins/BulkTracking/Tracker/Handler.php b/plugins/BulkTracking/Tracker/Handler.php
new file mode 100644
index 0000000000..e260237093
--- /dev/null
+++ b/plugins/BulkTracking/Tracker/Handler.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Plugins\BulkTracking\Tracker;
+
+use Piwik\Tracker;
+use Piwik\Tracker\RequestSet;
+use Piwik\Tracker\TrackerConfig;
+use Exception;
+
+class Handler extends Tracker\Handler
+{
+ private $transactionId = null;
+
+ public function __construct()
+ {
+ $this->setResponse(new Response());
+ }
+
+ public function onStartTrackRequests(Tracker $tracker, RequestSet $requestSet)
+ {
+ if ($this->isTransactionSupported()) {
+ $this->beginTransaction();
+ }
+ }
+
+ public function onAllRequestsTracked(Tracker $tracker, RequestSet $requestSet)
+ {
+ $this->commitTransaction();
+
+ // Do not run schedule task if we are importing logs or doing custom tracking (as it could slow down)
+ }
+
+ public function onException(Tracker $tracker, RequestSet $requestSet, Exception $e)
+ {
+ $this->rollbackTransaction();
+ parent::onException($tracker, $requestSet, $e);
+ }
+
+ private function beginTransaction()
+ {
+ if (empty($this->transactionId)) {
+ $this->transactionId = $this->getDb()->beginTransaction();
+ }
+ }
+
+ private function commitTransaction()
+ {
+ if (!empty($this->transactionId)) {
+ $this->getDb()->commit($this->transactionId);
+ $this->transactionId = null;
+ }
+ }
+
+ private function rollbackTransaction()
+ {
+ if (!empty($this->transactionId)) {
+ $this->getDb()->rollback($this->transactionId);
+ $this->transactionId = null;
+ }
+ }
+
+ private function getDb()
+ {
+ return Tracker::getDatabase();
+ }
+
+ /**
+ * @return bool
+ */
+ private function isTransactionSupported()
+ {
+ return (bool) TrackerConfig::getConfigValue('bulk_requests_use_transaction');
+ }
+
+}
diff --git a/plugins/BulkTracking/Tracker/Requests.php b/plugins/BulkTracking/Tracker/Requests.php
new file mode 100644
index 0000000000..af4eb23a15
--- /dev/null
+++ b/plugins/BulkTracking/Tracker/Requests.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\BulkTracking\Tracker;
+
+use Exception;
+use Piwik\Common;
+use Piwik\Tracker\Request;
+use Piwik\Tracker\TrackerConfig;
+
+class Requests
+{
+
+ public function requiresAuthentication()
+ {
+ $requiresAuth = TrackerConfig::getConfigValue('bulk_requests_require_authentication');
+
+ return !empty($requiresAuth);
+ }
+
+ /**
+ * @param Request[] $requests
+ * @throws Exception
+ */
+ public function authenticateRequests($requests)
+ {
+ foreach ($requests as $request) {
+ $this->checkTokenAuthNotEmpty($request->getTokenAuth());
+
+ if (!$request->isAuthenticated()) {
+ $msg = sprintf("token_auth specified does not have Admin permission for idsite=%s", $request->getIdSite());
+ throw new Exception($msg);
+ }
+ }
+ }
+
+ private function checkTokenAuthNotEmpty($token)
+ {
+ if (empty($token)) {
+ throw new Exception("token_auth must be specified when using Bulk Tracking Import. "
+ . " See <a href='http://developer.piwik.org/api-reference/tracking-api'>Tracking Doc</a>");
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getRawBulkRequest()
+ {
+ return file_get_contents("php://input");
+ }
+
+ public function isUsingBulkRequest($rawData)
+ {
+ if (!empty($rawData)) {
+ return strpos($rawData, '"requests"') || strpos($rawData, "'requests'");
+ }
+
+ return false;
+ }
+
+ public function getRequestsArrayFromBulkRequest($rawData)
+ {
+ $rawData = trim($rawData);
+ $rawData = Common::sanitizeLineBreaks($rawData);
+
+ // POST data can be array of string URLs or array of arrays w/ visit info
+ $jsonData = json_decode($rawData, $assoc = true);
+
+ $tokenAuth = Common::getRequestVar('token_auth', false, 'string', $jsonData);
+
+ $requests = array();
+ if (isset($jsonData['requests'])) {
+ $requests = $jsonData['requests'];
+ }
+
+ return array($requests, $tokenAuth);
+ }
+
+ public function initRequestsAndTokenAuth($rawData)
+ {
+ list($requests, $tokenAuth) = $this->getRequestsArrayFromBulkRequest($rawData);
+
+ $validRequests = array();
+
+ if (!empty($requests)) {
+
+ foreach ($requests as $index => $request) {
+ // if a string is sent, we assume its a URL and try to parse it
+ if (is_string($request)) {
+ $params = array();
+
+ $url = @parse_url($request);
+ if (!empty($url['query'])) {
+ @parse_str($url['query'], $params);
+ $validRequests[] = new Request($params, $tokenAuth);
+ }
+ } else {
+ $validRequests[] = new Request($request, $tokenAuth);
+ }
+ }
+ }
+
+ return array($validRequests, $tokenAuth);
+ }
+}
diff --git a/plugins/BulkTracking/Tracker/Response.php b/plugins/BulkTracking/Tracker/Response.php
new file mode 100644
index 0000000000..fd3e304a5e
--- /dev/null
+++ b/plugins/BulkTracking/Tracker/Response.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\BulkTracking\Tracker;
+
+use Exception;
+use Piwik\Common;
+use Piwik\Tracker;
+
+class Response extends Tracker\Response
+{
+ /**
+ * Echos an error message & other information, then exits.
+ *
+ * @param Tracker $tracker
+ * @param Exception $e
+ * @param int $statusCode eg 500
+ */
+ public function outputException(Tracker $tracker, Exception $e, $statusCode)
+ {
+ Common::sendResponseCode($statusCode);
+
+ $this->logExceptionToErrorLog($e);
+
+ $result = $this->formatException($tracker, $e);
+
+ echo json_encode($result);
+ }
+
+ public function outputResponse(Tracker $tracker)
+ {
+ if ($this->hasAlreadyPrintedOutput()) {
+ return;
+ }
+
+ $result = $this->formatResponse($tracker);
+
+ echo json_encode($result);
+ }
+
+ public function getOutput()
+ {
+ Common::sendHeader('Content-Type: application/json');
+
+ return parent::getOutput();
+ }
+
+ private function formatException(Tracker $tracker, Exception $e)
+ {
+ // when doing bulk tracking we return JSON so the caller will know how many succeeded
+ $result = array(
+ 'status' => 'error',
+ 'tracked' => $tracker->getCountOfLoggedRequests()
+ );
+
+ // send error when in debug mode
+ if ($tracker->isDebugModeEnabled()) {
+ $result['message'] = $this->getMessageFromException($e);
+ }
+
+ return $result;
+ }
+
+ private function formatResponse(Tracker $tracker)
+ {
+ return array(
+ 'status' => 'success',
+ 'tracked' => $tracker->getCountOfLoggedRequests()
+ );
+ }
+
+}
diff --git a/plugins/BulkTracking/screenshots/.gitkeep b/plugins/BulkTracking/screenshots/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/plugins/BulkTracking/screenshots/.gitkeep
diff --git a/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php b/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php
new file mode 100644
index 0000000000..aac9c14a2a
--- /dev/null
+++ b/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\BulkTracking\tests\Fixtures;
+
+use Piwik\Date;
+use Piwik\Tests\Framework\Fixture;
+
+/**
+ * Generates tracker testing data for our TrackerTest
+ *
+ * This Simple fixture adds one website and tracks one visit with couple pageviews and an ecommerce conversion
+ */
+class SimpleFixtureTrackFewVisits extends Fixture
+{
+ public $dateTime = '2013-01-23 01:23:45';
+ public $idSite = 1;
+
+ public function setUp()
+ {
+ $this->setUpWebsite();
+ $this->trackFirstVisit();
+ $this->trackSecondVisit();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function setUpWebsite()
+ {
+ if (!self::siteCreated($this->idSite)) {
+ $idSite = self::createWebsite($this->dateTime, $ecommerce = 1);
+ $this->assertSame($this->idSite, $idSite);
+ }
+ }
+
+ protected function trackFirstVisit()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrl('http://example.com/');
+ self::checkResponse($t->doTrackPageView('Viewing homepage'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $t->setUrl('http://example.com/sub/page');
+ self::checkResponse($t->doTrackPageView('Second page view'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
+ $t->addEcommerceItem($sku = 'SKU_ID', $name = 'Test item!', $category = 'Test & Category', $price = 777, $quantity = 33);
+ self::checkResponse($t->doTrackEcommerceOrder('TestingOrder', $grandTotal = 33 * 77));
+ }
+
+ protected function trackSecondVisit()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.73');
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrl('http://example.com/sub/page');
+ self::checkResponse($t->doTrackPageView('Viewing homepage'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $t->setUrl('http://example.com/?search=this is a site search query');
+ self::checkResponse($t->doTrackPageView('Site search query'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
+ $t->addEcommerceItem($sku = 'SKU_ID2', $name = 'A durable item', $category = 'Best seller', $price = 321);
+ self::checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 33 * 77));
+ }
+} \ No newline at end of file
diff --git a/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php
new file mode 100644
index 0000000000..c6c39c4ee9
--- /dev/null
+++ b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php
@@ -0,0 +1,42 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker;
+
+use Piwik\Tracker;
+
+class Requests extends \Piwik\Plugins\BulkTracking\Tracker\Requests
+{
+ private $rawData;
+ private $requiresAuth = false;
+
+ public function setRawData($rawData)
+ {
+ $this->rawData = $rawData;
+ }
+
+ public function getRawBulkRequest()
+ {
+ if (!is_null($this->rawData)) {
+ return $this->rawData;
+ }
+
+ return parent::getRawBulkRequest();
+ }
+
+ public function enableRequiresAuth()
+ {
+ $this->requiresAuth = true;
+ }
+
+ public function requiresAuthentication()
+ {
+ return $this->requiresAuth;
+ }
+
+}
diff --git a/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php
new file mode 100644
index 0000000000..e514745ddb
--- /dev/null
+++ b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php
@@ -0,0 +1,21 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker;
+
+use Piwik\Tracker;
+use Exception;
+
+class Response extends \Piwik\Plugins\BulkTracking\Tracker\Response
+{
+ protected function logExceptionToErrorLog(Exception $e)
+ {
+ // prevent from writing to console in tests
+ }
+
+}
diff --git a/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php b/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php
new file mode 100644
index 0000000000..23b96776ae
--- /dev/null
+++ b/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Framework\TestCase;
+
+use Piwik\Plugin;
+use Piwik\Plugins\BulkTracking\BulkTracking;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker\Requests;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker\Handler as DefaultHandler;
+use Piwik\Tracker\RequestSet;
+
+/**
+ * @group BulkTracking
+ * @group BulkTrackingTest
+ * @group Plugins
+ * @group Tracker
+ */
+class BulkTrackingTestCase extends IntegrationTestCase
+{
+ /**
+ * @var BulkTracking
+ */
+ protected $bulk;
+
+ private $pluginBackup;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->bulk = new BulkTracking();
+
+ $this->pluginBackup = Plugin\Manager::getInstance()->getLoadedPlugin('BulkTracking');
+ Plugin\Manager::getInstance()->addLoadedPlugin('BulkTracking', $this->bulk);
+ }
+
+ public function tearDown()
+ {
+ Plugin\Manager::getInstance()->addLoadedPlugin('BulkTracking', $this->pluginBackup);
+ parent::tearDown();
+ }
+
+ protected function getSuperUserToken()
+ {
+ Fixture::createSuperUser(false);
+ return Fixture::getTokenAuth();
+ }
+
+ protected function injectRawDataToBulk($rawData, $requiresAuth = false)
+ {
+ $requests = new Requests();
+ $requests->setRawData($rawData);
+
+ if ($requiresAuth) {
+ $requests->enableRequiresAuth();
+ }
+
+ $this->bulk->setRequests($requests);
+ }
+
+ protected function initRequestSet($rawData, $requiresAuth = false, $initToken = null)
+ {
+ $requestSet = new RequestSet();
+
+ if (!is_null($initToken)) {
+ $requestSet->setTokenAuth($initToken);
+ }
+
+ $this->injectRawDataToBulk($rawData, $requiresAuth);
+
+ $this->bulk->initRequestSet($requestSet);
+
+ return $requestSet;
+ }
+
+ protected function getDummyRequest($token = null)
+ {
+ $params = array(array('idsite' => '1', 'rec' => '1'), array('idsite' => '2', 'rec' => '1'));
+ $params = array('requests' => $params);
+
+ if (!is_null($token)) {
+ $params['token_auth'] = $token;
+ }
+
+ $request = json_encode($params);
+
+ return $request;
+ }
+}
diff --git a/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php b/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php
new file mode 100644
index 0000000000..c495afcee1
--- /dev/null
+++ b/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Integration;
+
+use Piwik\Plugin;
+use Piwik\Plugins\BulkTracking\tests\Framework\TestCase\BulkTrackingTestCase;
+use Piwik\Plugins\BulkTracking\Tracker\Handler;
+use Piwik\Tracker\Handler as DefaultHandler;
+use Piwik\Tracker\RequestSet;
+
+/**
+ * @group BulkTracking
+ * @group BulkTrackingTest
+ * @group Plugins
+ * @group Tracker
+ */
+class BulkTrackingTest extends BulkTrackingTestCase
+{
+ public function test_initRequestSet_shouldNotSetAnything_IfItIsActuallyNotUsingBulkRequest()
+ {
+ $requestSet = new RequestSet();
+ $this->bulk->initRequestSet($requestSet);
+
+ $this->assertEquals(array(), $requestSet->getRequests());
+ $this->assertEquals(false, $requestSet->getTokenAuth());
+ }
+
+ public function test_initRequestSet_shouldNotSetAnything_IfNotBulkRequestRawDataIsGiven()
+ {
+ $requestSet = $this->initRequestSet('invalid:requests');
+
+ $this->assertEquals(array(), $requestSet->getRequests());
+ $this->assertEquals(false, $requestSet->getTokenAuth());
+ }
+
+ public function test_initRequestSet_shouldInitialize_AsItIsABulkRequest()
+ {
+ $token = $this->getSuperUserToken();
+ $request = $this->getDummyRequest($token);
+
+ $requestSet = $this->initRequestSet($request);
+
+ $requests = $requestSet->getRequests();
+ $this->assertCount(2, $requests);
+ $this->assertEquals(array('idsite' => '1', 'rec' => '1'), $requests[0]->getParams());
+ $this->assertEquals(array('idsite' => '2', 'rec' => '1'), $requests[1]->getParams());
+ $this->assertEquals($token, $requestSet->getTokenAuth());
+ }
+
+ public function test_initRequestSet_shouldNotOverwriteAToken_IfOneIsAlreadySet()
+ {
+ $token = $this->getSuperUserToken();
+ $request = $this->getDummyRequest($token);
+
+ $requestSet = $this->initRequestSet($request, false, 'initialtoken');
+
+ $this->assertEquals('initialtoken', $requestSet->getTokenAuth());
+ $this->assertCount(2, $requestSet->getRequests());
+ }
+
+ public function test_initRequestSet_shouldNotFail_IfNoTokenProvided_AsAuthenticationIsDisabledByDefault()
+ {
+ $request = $this->getDummyRequest();
+
+ $requestSet = $this->initRequestSet($request);
+
+ $requests = $requestSet->getRequests();
+ $this->assertCount(2, $requests);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import
+ */
+ public function test_initRequestSet_shouldTriggerException_InCaseNoValidTokenProvidedAndAuthenticationIsRequired()
+ {
+ $request = $this->getDummyRequest(false);
+
+ $this->initRequestSet($request, true);
+ }
+
+ public function test_setHandlerIfBulkRequest_shouldSetBulkHandler_InCaseNoHandlerIsSetAndItIsABulkRequest()
+ {
+ $this->injectRawDataToBulk($this->getDummyRequest());
+
+ $handler = null;
+ $this->bulk->setHandlerIfBulkRequest($handler);
+
+ $this->assertTrue($handler instanceof Handler);
+ }
+
+ public function test_setHandlerIfBulkRequest_shouldNotSetAHandler_IfOneIsAlreadySetEvenIfItIsABulkRequest()
+ {
+ $this->injectRawDataToBulk($this->getDummyRequest());
+
+ $default = new DefaultHandler();
+ $handler = $default;
+
+ $this->bulk->setHandlerIfBulkRequest($default);
+
+ $this->assertSame($default, $handler);
+ }
+
+ public function test_setHandlerIfBulkRequest_shouldNotSetAHandler_IfItIsNotABulkRequest()
+ {
+ $this->injectRawDataToBulk('{"test":"not a bulk request"}');
+
+ $handler = null;
+
+ $this->bulk->setHandlerIfBulkRequest($handler);
+
+ $this->assertNull($handler);
+ }
+
+ public function test_getListHooksRegistered_shouldListenToNewTrackerEventAndCreateBulkHandler_IfBulkRequest()
+ {
+ $this->injectRawDataToBulk($this->getDummyRequest());
+
+ $handler = DefaultHandler\Factory::make();
+
+ $this->assertTrue($handler instanceof Handler);
+ }
+
+ public function test_getListHooksRegistered_shouldListenToNewTrackerEventAndNotCreateBulkHandler_IfNotBulkRequest()
+ {
+ $handler = DefaultHandler\Factory::make();
+
+ $this->assertTrue($handler instanceof DefaultHandler);
+ }
+
+ public function test_getListHooksRegistered_shouldListenToInitRequestSetEventAndInit_IfBulkRequest()
+ {
+ $this->injectRawDataToBulk($this->getDummyRequest());
+
+ $requestSet = new RequestSet();
+ $requestSet->initRequestsAndTokenAuth();
+
+ $this->assertCount(2, $requestSet->getRequests());
+ }
+
+}
diff --git a/plugins/BulkTracking/tests/Integration/HandlerTest.php b/plugins/BulkTracking/tests/Integration/HandlerTest.php
new file mode 100644
index 0000000000..1b2b46c8cb
--- /dev/null
+++ b/plugins/BulkTracking/tests/Integration/HandlerTest.php
@@ -0,0 +1,161 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Integration;
+
+use Piwik\Exception\InvalidRequestParameterException;
+use Piwik\Exception\UnexpectedWebsiteFoundException;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\Mock\Tracker\Response;
+use Piwik\Tests\Framework\Mock\Tracker\ScheduledTasksRunner;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker;
+use Piwik\Plugins\BulkTracking\Tracker\Handler;
+use Piwik\Tests\Framework\Mock\Tracker\RequestSet;
+use Exception;
+
+/**
+ * @group HandlerTest
+ * @group Handler
+ * @group Tracker
+ */
+class HandlerTest extends IntegrationTestCase
+{
+ /**
+ * @var Handler
+ */
+ private $handler;
+
+ /**
+ * @var Response
+ */
+ private $response;
+
+ /**
+ * @var Tracker
+ */
+ private $tracker;
+
+ /**
+ * @var RequestSet
+ */
+ private $requestSet;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Tracker\Cache::deleteTrackerCache();
+
+ $this->response = new Response();
+ $this->handler = new Handler();
+ $this->handler->setResponse($this->response);
+ $this->tracker = new Tracker();
+ $this->requestSet = new RequestSet();
+ }
+
+ public function test_init_ShouldInitiateResponseInstance()
+ {
+ $this->handler->init($this->tracker, $this->requestSet);
+
+ $this->assertTrue($this->response->isInit);
+ $this->assertFalse($this->response->isResponseOutput);
+ $this->assertFalse($this->response->isSend);
+ }
+
+ public function test_finish_ShouldOutputAndSendResponse()
+ {
+ $response = $this->handler->finish($this->tracker, $this->requestSet);
+
+ $this->assertEquals('My Dummy Content', $response);
+
+ $this->assertFalse($this->response->isInit);
+ $this->assertFalse($this->response->isExceptionOutput);
+ $this->assertTrue($this->response->isResponseOutput);
+ $this->assertTrue($this->response->isSend);
+ }
+
+ public function test_onException_ShouldOutputAndSendResponse()
+ {
+ $this->executeOnException($this->buildException());
+
+ $this->assertFalse($this->response->isInit);
+ $this->assertFalse($this->response->isResponseOutput);
+ $this->assertTrue($this->response->isExceptionOutput);
+ $this->assertFalse($this->response->isSend);
+ }
+
+ public function test_onException_ShouldPassExceptionToResponse()
+ {
+ $exception = $this->buildException();
+
+ $this->executeOnException($exception);
+
+ $this->assertSame($exception, $this->response->exception);
+ $this->assertSame(500, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldSendStatusCode400IfUnexpectedWebsite()
+ {
+ $this->executeOnException(new UnexpectedWebsiteFoundException('test'));
+ $this->assertSame(400, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldSendStatusCode400IfInvalidRequestParameterException()
+ {
+ $this->executeOnException(new InvalidRequestParameterException('test'));
+ $this->assertSame(400, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldNotRethrowAnException()
+ {
+ $exception = $this->buildException();
+
+ $this->handler->onException($this->tracker, $this->requestSet, $exception);
+ $this->assertTrue(true);
+ }
+
+ public function test_onAllRequestsTracked_ShouldNeverTriggerScheduledTasksEvenIfEnabled()
+ {
+ $runner = new ScheduledTasksRunner();
+ $runner->shouldRun = true;
+
+ $this->handler->setScheduledTasksRunner($runner);
+ $this->handler->onAllRequestsTracked($this->tracker, $this->requestSet);
+
+ $this->assertFalse($runner->ranScheduledTasks);
+ }
+
+ public function test_process_ShouldTrackAllSetRequests()
+ {
+ $this->assertSame(0, $this->tracker->getCountOfLoggedRequests());
+
+ $this->requestSet->setRequests(array(
+ array('idsite' => 1, 'url' => 'http://localhost/foo?bar'),
+ array('idsite' => 1, 'url' => 'http://localhost'),
+ ));
+
+ $this->handler->process($this->tracker, $this->requestSet);
+
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ private function buildException()
+ {
+ return new \Exception('MyMessage', 292);
+ }
+
+ private function executeOnException(Exception $exception)
+ {
+ try {
+ $this->handler->onException($this->tracker, $this->requestSet, $exception);
+ } catch (Exception $e) {
+ }
+ }
+}
diff --git a/plugins/BulkTracking/tests/Integration/RequestsTest.php b/plugins/BulkTracking/tests/Integration/RequestsTest.php
new file mode 100644
index 0000000000..ab45204303
--- /dev/null
+++ b/plugins/BulkTracking/tests/Integration/RequestsTest.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Integration;
+
+use Piwik\Plugins\BulkTracking\Tracker\Requests;
+use Piwik\Plugins\UsersManager\API;
+use Piwik\Plugins\UsersManager\UsersManager;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker\Request;
+use Piwik\Tracker\TrackerConfig;
+
+/**
+ * @group BulkTracking
+ * @group RequestsTest
+ * @group Plugins
+ * @group Tracker
+ */
+class RequestsTest extends IntegrationTestCase
+{
+ /**
+ * @var Requests
+ */
+ private $requests;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->requests = new Requests();
+ }
+
+ public function tearDown()
+ {
+ // clean up your test here if needed
+
+ parent::tearDown();
+ }
+
+ public function test_requiresAuthentication_shouldReturnTrue_IfEnabled()
+ {
+ $oldConfig = TrackerConfig::getConfigValue('bulk_requests_require_authentication');
+ TrackerConfig::setConfigValue('bulk_requests_require_authentication', 1);
+
+ $this->assertTrue($this->requests->requiresAuthentication());
+
+ TrackerConfig::setConfigValue('bulk_requests_require_authentication', $oldConfig);
+ }
+
+ public function test_requiresAuthentication_shouldReturnFalse_IfDisabled()
+ {
+ $oldConfig = TrackerConfig::getConfigValue('bulk_requests_require_authentication');
+ TrackerConfig::setConfigValue('bulk_requests_require_authentication', 0);
+
+ $this->assertFalse($this->requests->requiresAuthentication());
+
+ TrackerConfig::setConfigValue('bulk_requests_require_authentication', $oldConfig);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import
+ */
+ public function test_authenticateRequests_shouldThrowAnException_IfTokenAuthIsEmpty()
+ {
+ $requests = array($this->buildDummyRequest());
+ $this->requests->authenticateRequests($requests);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import
+ */
+ public function test_authenticateRequests_shouldThrowAnException_IfAnyTokenAuthIsEmpty()
+ {
+ $requests = array($this->buildDummyRequest($this->getSuperUserToken()), $this->buildDummyRequest());
+ $this->requests->authenticateRequests($requests);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage token_auth specified does not have Admin permission for idsite=1
+ */
+ public function test_authenticateRequests_shouldThrowAnException_IfTokenIsNotValid()
+ {
+ $dummyToken = API::getInstance()->getTokenAuth('test', UsersManager::getPasswordHash('2'));
+ $superUserToken = $this->getSuperUserToken();
+
+ $requests = array($this->buildDummyRequest($superUserToken), $this->buildDummyRequest($dummyToken));
+ $this->requests->authenticateRequests($requests);
+ }
+
+ public function test_authenticateRequests_shouldNotFail_IfAllTokensAreValid()
+ {
+ $superUserToken = $this->getSuperUserToken();
+
+ $requests = array($this->buildDummyRequest($superUserToken), $this->buildDummyRequest($superUserToken));
+ $this->requests->authenticateRequests($requests);
+
+ $this->assertTrue(true);
+ }
+
+ public function test_authenticateRequests_shouldNotFail_IfEmptyRequestSetGiven()
+ {
+ $this->requests->authenticateRequests(array());
+
+ $this->assertTrue(true);
+ }
+
+ private function getSuperUserToken()
+ {
+ Fixture::createSuperUser(false);
+ return Fixture::getTokenAuth();
+ }
+
+ private function buildDummyRequest($token = false)
+ {
+ return new Request(array('idsite' => '1'), $token);
+ }
+
+}
diff --git a/plugins/BulkTracking/tests/Integration/TrackerTest.php b/plugins/BulkTracking/tests/Integration/TrackerTest.php
new file mode 100644
index 0000000000..dd1e37ea70
--- /dev/null
+++ b/plugins/BulkTracking/tests/Integration/TrackerTest.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Integration;
+
+use Piwik\Common;
+use Piwik\Plugin;
+use Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker\Response;
+use Piwik\Plugins\BulkTracking\tests\Framework\TestCase\BulkTrackingTestCase;
+use Piwik\Plugins\BulkTracking\Tracker\Handler;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tracker;
+use Piwik\Translate;
+use Piwik\Tests\Framework\Mock\Tracker\RequestSet;
+
+class TestIntegrationTracker extends Tracker {
+
+ protected function loadTrackerPlugins()
+ {
+ // if we reload the plugins we would lose the injected data :(
+ }
+}
+
+/**
+ * @group TrackerTest
+ * @group Tracker
+ */
+class TrackerTest extends BulkTrackingTestCase
+{
+ /**
+ * @var TestIntegrationTracker
+ */
+ private $tracker;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->tracker = new TestIntegrationTracker();
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Fixture::createWebsite('2014-01-01 00:00:00');
+
+ $this->injectRawDataToBulk($this->getDummyRequest());
+ }
+
+ public function test_main_shouldIncreaseLoggedRequestsCounter()
+ {
+ $this->tracker->main($this->getHandler(), $this->getEmptyRequestSet());
+
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ public function test_main_shouldUseBulkHandler()
+ {
+ $handler = $this->getHandler();
+ $this->assertTrue($handler instanceof Handler);
+ }
+
+ public function test_main_shouldReturnBulkTrackingResponse()
+ {
+ $response = $this->tracker->main($this->getHandler(), $this->getEmptyRequestSet());
+
+ $this->assertSame('{"status":"success","tracked":2}', $response);
+ }
+
+ public function test_main_shouldReturnErrorResponse_InCaseOfAnyError()
+ {
+ $requestSet = new RequestSet();
+ $requestSet->enableThrowExceptionOnInit();
+
+ $handler = $this->getHandler();
+ $handler->setResponse(new Response());
+
+ $response = $this->tracker->main($handler, $requestSet);
+
+ $this->assertSame('{"status":"error","tracked":0}', $response);
+ }
+
+ public function test_main_shouldReturnErrorResponse_IfNotAuthorized()
+ {
+ $this->injectRawDataToBulk($this->getDummyRequest(), true);
+
+ $handler = $this->getHandler();
+ $handler->setResponse(new Response());
+
+ $response = $this->tracker->main($handler, $this->getEmptyRequestSet());
+
+ $this->assertSame('{"status":"error","tracked":0}', $response);
+ }
+
+ public function test_main_shouldActuallyTrack()
+ {
+ $this->assertEmpty($this->getIdVisit(1));
+ $this->assertEmpty($this->getIdVisit(2));
+
+ $requestSet = $this->getEmptyRequestSet();
+ $this->tracker->main($this->getHandler(), $requestSet);
+
+ $this->assertCount(2, $requestSet->getRequests(), 'Nothing tracked because it could not find 2 requests');
+
+ $visit1 = $this->getIdVisit(1);
+ $visit2 = $this->getIdVisit(2);
+
+ $this->assertNotEmpty($visit1);
+ $this->assertEquals(1, $visit1['idsite']);
+ $this->assertNotEmpty($visit2);
+ $this->assertEquals(2, $visit2['idsite']);
+
+ $this->assertEmpty($this->getIdVisit(3));
+ }
+
+ private function getHandler()
+ {
+ return Tracker\Handler\Factory::make();
+ }
+
+ private function getEmptyRequestSet()
+ {
+ return new Tracker\RequestSet();
+ }
+
+ private function getIdVisit($idVisit)
+ {
+ return Tracker::getDatabase()->fetchRow("SELECT * FROM " . Common::prefixTable('log_visit') . " WHERE idvisit = ?", array($idVisit));
+ }
+
+} \ No newline at end of file
diff --git a/plugins/BulkTracking/tests/System/TrackerTest.php b/plugins/BulkTracking/tests/System/TrackerTest.php
new file mode 100644
index 0000000000..5974a1acd5
--- /dev/null
+++ b/plugins/BulkTracking/tests/System/TrackerTest.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\System;
+
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group BulkTracking
+ * @group TrackerTest
+ * @group Tracker
+ * @group Plugins
+ */
+class TrackerTest extends SystemTestCase
+{
+ public static $fixture = null;
+
+ /**
+ * @var \PiwikTracker
+ */
+ private $tracker;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $idSite = 1;
+ $dateTime = '2014-01-01 00:00:01';
+
+ if (!Fixture::siteCreated($idSite)) {
+ Fixture::createWebsite($dateTime);
+ }
+
+ $this->tracker = Fixture::getTracker($idSite, $dateTime, $defaultInit = true);
+ $this->tracker->enableBulkTracking();
+ }
+
+ public function test_response_ShouldContainBulkTrackingApiResponse()
+ {
+ $this->tracker->doTrackPageView('Test');
+ $this->tracker->doTrackPageView('Test');
+
+ $response = $this->tracker->doBulkTrack();
+
+ $this->assertEquals('{"status":"success","tracked":2}', $response);
+ }
+} \ No newline at end of file
diff --git a/plugins/BulkTracking/tests/Unit/RequestsTest.php b/plugins/BulkTracking/tests/Unit/RequestsTest.php
new file mode 100644
index 0000000000..8a89e5c220
--- /dev/null
+++ b/plugins/BulkTracking/tests/Unit/RequestsTest.php
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Unit;
+
+use Piwik\Plugins\BulkTracking\Tracker\Requests;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Tracker\Request;
+
+/**
+ * @group BulkTracking
+ * @group RequestsTest
+ * @group Plugins
+ */
+class RequestsTest extends UnitTestCase
+{
+ /**
+ * @var Requests
+ */
+ private $requests;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->requests = new Requests();
+ }
+
+ public function test_isUsingBulkRequest_shouldReturnFalse_IfRequestIsEmpty()
+ {
+ $this->assertFalse($this->requests->isUsingBulkRequest(false));
+ $this->assertFalse($this->requests->isUsingBulkRequest(null));
+ $this->assertFalse($this->requests->isUsingBulkRequest(''));
+ $this->assertFalse($this->requests->isUsingBulkRequest(0));
+ }
+
+ public function test_isUsingBulkRequest_shouldReturnFalse_IfRequestIsNotABulkRequest()
+ {
+ $this->assertFalse($this->requests->isUsingBulkRequest(5));
+ $this->assertFalse($this->requests->isUsingBulkRequest('test'));
+ $this->assertFalse($this->requests->isUsingBulkRequest('requests'));
+ $this->assertFalse($this->requests->isUsingBulkRequest('{"test": "val", "request:" []}'));
+ $this->assertFalse($this->requests->isUsingBulkRequest('[5, 10, "request"]'));
+ }
+
+ public function test_isUsingBulkRequest_shouldReturnTrue_IfRequestIsABulkRequest()
+ {
+ $request = $this->buildRequestRawData(array(array('idsite' => '1')));
+ $this->assertTrue($this->requests->isUsingBulkRequest($request));
+
+ // don't know why this one is supposed to count as bulk request!
+ $this->assertTrue($this->requests->isUsingBulkRequest("{'requests'"));
+ }
+
+ public function test_getRequestsArrayFromBulkRequest_ShouldFindRequestsAndEmptyTokenAndItShouldTrimWhitespaceFromRawData()
+ {
+ $requests = array(array('idsite' => '1'), array('idsite' => '2'));
+ $request = $this->buildRequestRawData($requests);
+
+ $result = $this->requests->getRequestsArrayFromBulkRequest(' ' . $request . ' ');
+
+ $expected = array(array(array('idsite' => '1'), array('idsite' => '2')), '');
+ $this->assertEquals($expected, $result);
+ }
+
+ public function test_getRequestsArrayFromBulkRequest_shouldRecognize()
+ {
+ $token = md5('2');
+
+ $request = $this->buildRequestRawData(array(), $token);
+ $result = $this->requests->getRequestsArrayFromBulkRequest($request);
+
+ $expected = array(array(), $token);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function test_initRequestsAndTokenAuth_NoRequestsSetShouldStillFindToken()
+ {
+ $token = md5('2');
+
+ $request = json_encode(array('requests' => array(), 'token_auth' => $token));
+ $result = $this->requests->initRequestsAndTokenAuth($request);
+
+ $expected = array(array(), $token);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function test_initRequestsAndTokenAuth_ShouldFindRequestsAndEmptyToken()
+ {
+ $params = array(array('idsite' => '1'), array('idsite' => '2'));
+ $request = $this->buildRequestRawData($params);
+
+ $result = $this->requests->initRequestsAndTokenAuth($request);
+
+ /** @var Request[] $requests */
+ $requests = $result[0];
+ $tokenAuth = $result[1];
+
+ $this->assertEquals('', $tokenAuth); // none was set
+
+ $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams());
+ $this->assertEquals('', $requests[0]->getTokenAuth());
+ $this->assertEquals(array('idsite' => '2'), $requests[1]->getParams());
+ $this->assertEquals('', $requests[1]->getTokenAuth());
+ $this->assertCount(2, $requests);
+ }
+
+ public function test_initRequestsAndTokenAuth_ShouldFindRequestsAndASetTokenAndPassItToRequestInstances()
+ {
+ $token = md5(2);
+ $params = array(array('idsite' => '1'), array('idsite' => '2'));
+ $request = $this->buildRequestRawData($params, $token);
+
+ $result = $this->requests->initRequestsAndTokenAuth($request);
+
+ /** @var Request[] $requests */
+ $requests = $result[0];
+
+ $this->assertEquals($token, $result[1]);
+ $this->assertEquals($token, $requests[0]->getTokenAuth());
+ $this->assertEquals($token, $requests[1]->getTokenAuth());
+ }
+
+ public function test_initRequestsAndTokenAuth_ShouldIgnoreEmptyUrls()
+ {
+ $token = md5(2);
+ $params = array(array('idsite' => '1'), '', array('idsite' => '2'));
+ $request = $this->buildRequestRawData($params, $token);
+
+ $result = $this->requests->initRequestsAndTokenAuth($request);
+
+ /** @var Request[] $requests */
+ $requests = $result[0];
+
+ $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams());
+ $this->assertEquals(array('idsite' => '2'), $requests[1]->getParams());
+ $this->assertCount(2, $requests);
+ }
+
+ public function test_initRequestsAndTokenAuth_ShouldResolveUrls()
+ {
+ $token = md5(2);
+ $params = array('piwik.php?idsite=1', '', 'piwik.php?idsite=3&rec=0', array('idsite' => '2'));
+ $request = $this->buildRequestRawData($params, $token);
+
+ $result = $this->requests->initRequestsAndTokenAuth($request);
+
+ /** @var Request[] $requests */
+ $requests = $result[0];
+
+ $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams());
+ $this->assertEquals(array('idsite' => '3', 'rec' => '0'), $requests[1]->getParams());
+ $this->assertEquals(array('idsite' => '2'), $requests[2]->getParams());
+ $this->assertCount(3, $requests);
+ }
+
+ private function buildRequestRawData($requests, $token = null)
+ {
+ $params = array('requests' => $requests);
+
+ if (!empty($token)) {
+ $params['token_auth'] = $token;
+ }
+
+ return json_encode($params);
+ }
+
+}
diff --git a/plugins/BulkTracking/tests/Unit/ResponseTest.php b/plugins/BulkTracking/tests/Unit/ResponseTest.php
new file mode 100644
index 0000000000..9da1e9a6b3
--- /dev/null
+++ b/plugins/BulkTracking/tests/Unit/ResponseTest.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\BulkTracking\tests\Unit;
+
+use Piwik\Plugins\BulkTracking\Tracker\Response;
+use Piwik\Tests\Framework\Mock\Tracker;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Exception;
+
+class TestResponse extends Response {
+
+ protected function logExceptionToErrorLog(Exception $e)
+ {
+ // prevent console from outputting the error_log message
+ }
+}
+
+/**
+ * @group BulkTracking
+ * @group ResponseTest
+ * @group Plugins
+ */
+class ResponseTest extends UnitTestCase
+{
+ /**
+ * @var TestResponse
+ */
+ private $response;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->response = new TestResponse();
+ $this->response->init(new Tracker());
+ }
+
+ public function test_outputException_shouldOutputBulkResponse()
+ {
+ $tracker = $this->getTrackerWithCountedRequests();
+
+ $this->response->outputException($tracker, new Exception('My Custom Message'), 400);
+ $content = $this->response->getOutput();
+
+ $this->assertEquals('{"status":"error","tracked":5}', $content);
+ }
+
+ public function test_outputException_shouldOutputDebugMessageIfEnabled()
+ {
+ $tracker = $this->getTrackerWithCountedRequests();
+ $tracker->enableDebugMode();
+
+ $this->response->outputException($tracker, new Exception('My Custom Message'), 400);
+ $content = $this->response->getOutput();
+
+ $this->assertStringStartsWith('{"status":"error","tracked":5,"message":"My Custom Message\n', $content);
+ }
+
+ public function test_outputResponse_shouldOutputBulkResponse()
+ {
+ $tracker = $this->getTrackerWithCountedRequests();
+
+ $this->response->outputResponse($tracker);
+ $content = $this->response->getOutput();
+
+ $this->assertEquals('{"status":"success","tracked":5}', $content);
+ }
+
+ public function test_outputResponse_shouldNotOutputAnything_IfExceptionResponseAlreadySent()
+ {
+ $tracker = $this->getTrackerWithCountedRequests();
+
+ $this->response->outputException($tracker, new Exception('My Custom Message'), 400);
+ $this->response->outputResponse($tracker);
+ $content = $this->response->getOutput();
+
+ $this->assertEquals('{"status":"error","tracked":5}', $content);
+ }
+
+ private function getTrackerWithCountedRequests()
+ {
+ $tracker = new Tracker();
+ $tracker->setCountOfLoggedRequests(5);
+ return $tracker;
+ }
+
+}
diff --git a/plugins/CoreAdminHome/stylesheets/pluginSettings.less b/plugins/CoreAdminHome/stylesheets/pluginSettings.less
index 9633c0423d..0d8c8469cf 100644
--- a/plugins/CoreAdminHome/stylesheets/pluginSettings.less
+++ b/plugins/CoreAdminHome/stylesheets/pluginSettings.less
@@ -12,6 +12,10 @@
width:200px
}
+ .control_text, .control_password {
+ max-width: 220px;
+ }
+
.title {
font-weight: bold
}
diff --git a/plugins/CoreAdminHome/templates/pluginSettings.twig b/plugins/CoreAdminHome/templates/pluginSettings.twig
index 3dfe2873f4..fa1e032ea8 100644
--- a/plugins/CoreAdminHome/templates/pluginSettings.twig
+++ b/plugins/CoreAdminHome/templates/pluginSettings.twig
@@ -123,6 +123,7 @@
{% if setting.uiControlType == 'checkbox' and settingValue %}
checked="checked"
{% endif %}
+ class="control_{{ setting.uiControlType|e('html_attr') }}"
type="{{ setting.uiControlType|e('html_attr') }}"
name="{{ setting.getKey|e('html_attr') }}"
value="{{ settingValue|e('html_attr') }}"
diff --git a/plugins/CoreHome/Columns/ServerTime.php b/plugins/CoreHome/Columns/ServerTime.php
index 72cdcb1357..297c410058 100644
--- a/plugins/CoreHome/Columns/ServerTime.php
+++ b/plugins/CoreHome/Columns/ServerTime.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Date;
use Piwik\Db;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Tracker\Action;
@@ -32,6 +33,6 @@ class ServerTime extends ActionDimension
{
$timestamp = $request->getCurrentTimestamp();
- return Tracker::getDatetimeFromTimestamp($timestamp);
+ return Date::getDatetimeFromTimestamp($timestamp);
}
}
diff --git a/plugins/CoreHome/Columns/VisitFirstActionTime.php b/plugins/CoreHome/Columns/VisitFirstActionTime.php
index 92b6619fed..3985d53339 100644
--- a/plugins/CoreHome/Columns/VisitFirstActionTime.php
+++ b/plugins/CoreHome/Columns/VisitFirstActionTime.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Date;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -27,6 +28,6 @@ class VisitFirstActionTime extends VisitDimension
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
- return Tracker::getDatetimeFromTimestamp($request->getCurrentTimestamp());
+ return Date::getDatetimeFromTimestamp($request->getCurrentTimestamp());
}
} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/VisitLastActionTime.php b/plugins/CoreHome/Columns/VisitLastActionTime.php
index 988757301b..d25f09abab 100644
--- a/plugins/CoreHome/Columns/VisitLastActionTime.php
+++ b/plugins/CoreHome/Columns/VisitLastActionTime.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Date;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -31,7 +32,7 @@ class VisitLastActionTime extends VisitDimension
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
- return Tracker::getDatetimeFromTimestamp($request->getCurrentTimestamp());
+ return Date::getDatetimeFromTimestamp($request->getCurrentTimestamp());
}
/**
diff --git a/plugins/ExampleSettingsPlugin/Settings.php b/plugins/ExampleSettingsPlugin/Settings.php
index 1853873924..7529407b3f 100644
--- a/plugins/ExampleSettingsPlugin/Settings.php
+++ b/plugins/ExampleSettingsPlugin/Settings.php
@@ -53,7 +53,7 @@ class Settings extends \Piwik\Plugin\Settings
// User setting --> textbox converted to int defining a validator and filter
$this->createRefreshIntervalSetting();
- // User setting --> readio
+ // User setting --> radio
$this->createColorSetting();
// System setting --> allows selection of a single value
diff --git a/plugins/Provider/Columns/Provider.php b/plugins/Provider/Columns/Provider.php
index cba1b8b9f5..24ab6cecd2 100644
--- a/plugins/Provider/Columns/Provider.php
+++ b/plugins/Provider/Columns/Provider.php
@@ -42,6 +42,13 @@ class Provider extends VisitDimension
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
+ // Adding &dp=1 will disable the provider plugin, this is an "unofficial" parameter used to speed up log importer
+ $disableProvider = $request->getParam('dp');
+
+ if (!empty($disableProvider)) {
+ return false;
+ }
+
// if provider info has already been set, abort
$locationValue = $visitor->getVisitorColumn('location_provider');
if (!empty($locationValue)) {
diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking
new file mode 160000
+Subproject 7f33537161a81172bb00bbb5a4a3c338e7513ce
diff --git a/plugins/ScheduledReports/API.php b/plugins/ScheduledReports/API.php
index 54cbf41125..1eade02df0 100644
--- a/plugins/ScheduledReports/API.php
+++ b/plugins/ScheduledReports/API.php
@@ -21,6 +21,7 @@ use Piwik\Plugins\SegmentEditor\API as APISegmentEditor;
use Piwik\Plugins\SitesManager\API as SitesManagerApi;
use Piwik\ReportRenderer;
use Piwik\Site;
+use Piwik\Tracker;
use Piwik\Translate;
/**
@@ -554,7 +555,8 @@ class API extends \Piwik\Plugin\API
$this->getModel()->updateReport($report['idreport'], array('ts_last_sent' => $now));
// If running from piwik.php with debug, do not delete the PDF after sending the email
- if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ $tracker = new Tracker();
+ if (!$tracker->isDebugModeEnabled()) {
@chmod($outputFilename, 0600);
}
}
diff --git a/plugins/ScheduledReports/ScheduledReports.php b/plugins/ScheduledReports/ScheduledReports.php
index 4b8190fead..6d171768a7 100644
--- a/plugins/ScheduledReports/ScheduledReports.php
+++ b/plugins/ScheduledReports/ScheduledReports.php
@@ -20,6 +20,7 @@ use Piwik\Plugins\UsersManager\API as APIUsersManager;
use Piwik\Plugins\UsersManager\Model as UserModel;
use Piwik\ReportRenderer;
use Piwik\ScheduledTime;
+use Piwik\Tracker;
use Piwik\View;
use Zend_Mime;
use Piwik\Config;
@@ -406,7 +407,8 @@ class ScheduledReports extends \Piwik\Plugin
} catch (Exception $e) {
// If running from piwik.php with debug, we ignore the 'email not sent' error
- if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ $tracker = new Tracker();
+ if (!$tracker->isDebugModeEnabled()) {
throw new Exception("An error occured while sending '$filename' " .
" to " . implode(', ', $mail->getRecipients()) .
". Error was '" . $e->getMessage() . "'");
diff --git a/plugins/TestRunner/README.md b/plugins/TestRunner/README.md
index be10873613..d0c9c864d9 100644
--- a/plugins/TestRunner/README.md
+++ b/plugins/TestRunner/README.md
@@ -10,13 +10,17 @@ __I want to run the tests with different parameters on AWS, is it possible?__
Yes, at the time of writing this you have to edit the file `Runner/Remote.php`
+__Why am I getting an error "AWS was not able to validate the provided access credentials"?__
+
+It could be caused by an invalid set date. Execute `date` on the command line and make sure it is correct.
+
__How can I change the base image (AMI) that is used for AWS tests?__
* Log in to AWS
* Select `EC2 => AMI`
* Launch a new instance of the current AMI by selecting it and pressing `Launch`
* Select a `c3.large` instance type
-* Press `Review and Launch` and on next page `Launch` (in theory you have to select your keypair somewhere otherwise you will not be able to log in but I couldn't find where)
+* Press `Review and Launch` and on next page `Launch` (there you have to select your keypair otherwise you can't log in)
* Log in to the newly created instance. To get login information
* Go to `EC2 => Instances`
* Select the created instance
@@ -33,7 +37,7 @@ __How can I change the base image (AMI) that is used for AWS tests?__
or if you don't know Puppet at least add it in this shell script https://github.com/piwik/piwik-dev-environment/blob/master/puppet/files/setup.sh
For instance if you installed a new package you can simply add a new entry here https://github.com/piwik/piwik-dev-environment/blob/master/puppet/modules/piwik/manifests/base.pp
* In `EC2 => Instances` menu select the instance you are currently using.
-* Select `Actions => Create Image`
+* Select `Actions => Image => Create Image`
* Define the name `Piwik Testing vX.X` and a description like `Used to run Piwik tests via Piwik console`. Make sure to increase the box version in X.X (have a look in `EC2 => AMI` for current version)
* Press `Create Image`
* Go to `EC2 => AMIs` menu and while waiting for the image creation to complete add the following tags
@@ -54,6 +58,7 @@ __How do I create a new EC2 key/pair for a developer?__
1. Go to: https://console.aws.amazon.com/ec2/v2/home?region=us-east-1
2. Click `Create Key Pair`
3. Send PGP email
+
```
Here are info for running tests on Ec2
* Access Key ID:
diff --git a/plugins/TestRunner/TravisYmlView.php b/plugins/TestRunner/TravisYmlView.php
index 809de91144..01d41ac54a 100644
--- a/plugins/TestRunner/TravisYmlView.php
+++ b/plugins/TestRunner/TravisYmlView.php
@@ -25,6 +25,7 @@ class TravisYmlView extends View
*/
private static $travisYmlSectionNames = array(
'php',
+ 'services',
'language',
'script',
'before_install',
diff --git a/plugins/TestRunner/templates/travis.yml.twig b/plugins/TestRunner/templates/travis.yml.twig
index 8623bd8b92..1972feb6c8 100644
--- a/plugins/TestRunner/templates/travis.yml.twig
+++ b/plugins/TestRunner/templates/travis.yml.twig
@@ -22,6 +22,9 @@ php:
{% endfor %}
{% endif %}
+services:
+ - redis-server
+
# Separate different test suites
{% if existingEnv|default is empty -%}
env:
diff --git a/tests/LocalTracker.php b/tests/LocalTracker.php
index e0867a746d..a86d3a565c 100755
--- a/tests/LocalTracker.php
+++ b/tests/LocalTracker.php
@@ -5,9 +5,6 @@ use Piwik\Tracker;
use Piwik\Tracker\Cache;
$GLOBALS['PIWIK_TRACKER_DEBUG'] = false;
-if (!defined('PIWIK_ENABLE_TRACKING')) {
- define('PIWIK_ENABLE_TRACKING', true);
-}
require_once PIWIK_INCLUDE_PATH . '/core/Tracker.php';
require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db.php';
@@ -52,7 +49,7 @@ class Piwik_LocalTracker extends PiwikTracker
\Piwik\Plugin\Manager::getInstance()->unloadPlugins();
// modify config
- $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+ \Piwik\SettingsServer::setIsTrackerApiRequest();
$GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = true;
Tracker::$initTrackerMode = false;
Tracker::setTestEnvironment($testEnvironmentArgs, $method);
@@ -73,7 +70,16 @@ class Piwik_LocalTracker extends PiwikTracker
ob_start();
$localTracker = new Tracker();
- $localTracker->main($requests);
+ $request = new Tracker\RequestSet();
+ $request->setRequests($requests);
+
+ $handler = Tracker\Handler\Factory::make();
+
+ $response = $localTracker->main($handler, $request);
+
+ if (!is_null($response)) {
+ echo $response;
+ }
$output = ob_get_contents();
@@ -85,7 +91,7 @@ class Piwik_LocalTracker extends PiwikTracker
$_SERVER['HTTP_USER_AGENT'] = $oldUserAgent;
$_COOKIE = $oldCookie;
$GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = false;
- $GLOBALS['PIWIK_TRACKER_MODE'] = false;
+ \Piwik\SettingsServer::setIsNotTrackerApiRequest();
unset($_GET['bots']);
// reload plugins
diff --git a/tests/PHPUnit/Framework/Mock/Tracker.php b/tests/PHPUnit/Framework/Mock/Tracker.php
new file mode 100644
index 0000000000..64a22b5ee2
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker.php
@@ -0,0 +1,35 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock;
+
+class Tracker extends \Piwik\Tracker
+{
+ private $isDebugEnabled = false;
+ private $shouldRecord = true;
+
+ public function enableDebugMode()
+ {
+ $this->isDebugEnabled = true;
+ }
+
+ public function isDebugModeEnabled()
+ {
+ return $this->isDebugEnabled;
+ }
+
+ public function disableShouldRecordStatistics()
+ {
+ $this->shouldRecord = false;
+ }
+
+ public function shouldRecordStatistics()
+ {
+ return $this->shouldRecord;
+ }
+}
diff --git a/tests/PHPUnit/Framework/Mock/Tracker/Db.php b/tests/PHPUnit/Framework/Mock/Tracker/Db.php
new file mode 100644
index 0000000000..5e0baab51e
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker/Db.php
@@ -0,0 +1,53 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock\Tracker;
+
+use Piwik\Tracker\Db\Pdo\Mysql;
+
+class Db extends Mysql
+{
+ public $commitTransactionId = false;
+ public $rollbackTransactionId = false;
+ public $beganTransaction = false;
+ public $connectCalled = false;
+
+ public function __construct($dbInfo, $driverName = 'mysql')
+ {
+ $this->dsn = 'testdrivername';
+ $this->username = 'testuser';
+ $this->password = 'testpassword';
+ $this->charset = 'testcharset';
+ }
+
+ public function connect()
+ {
+ $this->connectCalled = true;
+ }
+
+ /**
+ * Start Transaction
+ * @return string TransactionID
+ */
+ public function beginTransaction()
+ {
+ $this->beganTransaction = true;
+ return 'my4929transactionid';
+ }
+
+ public function commit($xid)
+ {
+ $this->commitTransactionId = $xid;
+ }
+
+ public function rollBack($xid)
+ {
+ $this->rollbackTransactionId = $xid;
+ }
+
+}
diff --git a/tests/PHPUnit/Framework/Mock/Tracker/Handler.php b/tests/PHPUnit/Framework/Mock/Tracker/Handler.php
new file mode 100644
index 0000000000..b1f241deef
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker/Handler.php
@@ -0,0 +1,70 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock\Tracker;
+
+use Exception;
+use Piwik\Tracker;
+use Piwik\Tracker\RequestSet as TrackerRequestSet;
+
+class Handler extends \Piwik\Tracker\Handler
+{
+ public $isInit = false;
+ public $isInitTrackingRequests = false;
+ public $isOnStartTrackRequests = false;
+ public $isProcessed = false;
+ public $isOnAllRequestsTracked = false;
+ public $isOnException = false;
+ public $isFinished = false;
+ public $output = 'My Rendered Content';
+
+ private $doTriggerExceptionInProcess = false;
+
+ public function init(Tracker $tracker, TrackerRequestSet $TrackerRequestSet)
+ {
+ $this->isInit = true;
+ }
+
+ public function enableTriggerExceptionInProcess()
+ {
+ $this->doTriggerExceptionInProcess = true;
+ }
+
+ public function onStartTrackRequests(Tracker $tracker, TrackerRequestSet $TrackerRequestSet)
+ {
+ $this->isOnStartTrackRequests = true;
+ }
+
+ public function process(Tracker $tracker, TrackerRequestSet $TrackerRequestSet)
+ {
+ if ($this->doTriggerExceptionInProcess) {
+ throw new Exception('My Exception During Process');
+ }
+
+ $this->isProcessed = true;
+ }
+
+ public function onAllRequestsTracked(Tracker $tracker, TrackerRequestSet $TrackerRequestSet)
+ {
+ $this->isOnAllRequestsTracked = true;
+ }
+
+ public function onException(Tracker $tracker, TrackerRequestSet $TrackerRequestSet, Exception $e)
+ {
+ $this->isOnException = true;
+ $this->output = $e->getMessage();
+ }
+
+ public function finish(Tracker $tracker, TrackerRequestSet $TrackerRequestSet)
+ {
+ $this->isFinished = true;
+
+ return $this->output;
+ }
+
+}
diff --git a/tests/PHPUnit/Framework/Mock/Tracker/RequestSet.php b/tests/PHPUnit/Framework/Mock/Tracker/RequestSet.php
new file mode 100644
index 0000000000..bbaf933572
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker/RequestSet.php
@@ -0,0 +1,32 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock\Tracker;
+
+use Exception;
+use Piwik\Tracker;
+
+class RequestSet extends \Piwik\Tracker\RequestSet
+{
+ private $throwExceptionOnInit = false;
+
+ public function enableThrowExceptionOnInit()
+ {
+ $this->throwExceptionOnInit = true;
+ }
+
+ public function initRequestsAndTokenAuth()
+ {
+ if ($this->throwExceptionOnInit) {
+ throw new Exception('Init requests and token auth exception', 493);
+ }
+
+ parent::initRequestsAndTokenAuth();
+ }
+
+}
diff --git a/tests/PHPUnit/Framework/Mock/Tracker/Response.php b/tests/PHPUnit/Framework/Mock/Tracker/Response.php
new file mode 100644
index 0000000000..076d37c9f0
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker/Response.php
@@ -0,0 +1,51 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock\Tracker;
+
+use Exception;
+use Piwik\Tracker;
+
+class Response extends \Piwik\Tracker\Response
+{
+ public $statusCode = 200;
+ public $exception;
+ public $isInit = false;
+ public $isExceptionOutput = false;
+ public $isResponseOutput = false;
+ public $isSend = false;
+
+ private $output = 'My Dummy Content';
+
+ public function init(Tracker $tracker)
+ {
+ $this->isInit = true;
+ }
+
+ public function getOutput()
+ {
+ $this->isSend = true;
+
+ return $this->output;
+ }
+
+ public function outputException(Tracker $tracker, Exception $e, $statusCode)
+ {
+ $this->isExceptionOutput = true;
+ $this->statusCode = $statusCode;
+ $this->exception = $e;
+
+ $this->output = $e->getMessage();
+ }
+
+ public function outputResponse(Tracker $tracker)
+ {
+ $this->isResponseOutput = true;
+ }
+
+}
diff --git a/tests/PHPUnit/Framework/Mock/Tracker/ScheduledTasksRunner.php b/tests/PHPUnit/Framework/Mock/Tracker/ScheduledTasksRunner.php
new file mode 100644
index 0000000000..2f3d0fbb38
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Tracker/ScheduledTasksRunner.php
@@ -0,0 +1,28 @@
+<?php
+/**
+* Piwik - free/libre analytics platform
+*
+* @link http://piwik.org
+* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+*/
+
+namespace Piwik\Tests\Framework\Mock\Tracker;
+
+use Piwik\Tracker;
+
+class ScheduledTasksRunner extends Tracker\ScheduledTasksRunner
+{
+ public $shouldRun = true;
+ public $ranScheduledTasks = false;
+
+ public function shouldRun(Tracker $tracker)
+ {
+ return $this->shouldRun;
+ }
+
+ public function runScheduledTasks()
+ {
+ $this->ranScheduledTasks = true;
+ }
+
+}
diff --git a/tests/PHPUnit/Framework/TestCase/UnitTestCase.php b/tests/PHPUnit/Framework/TestCase/UnitTestCase.php
index e12bec330f..f08eaa1953 100755
--- a/tests/PHPUnit/Framework/TestCase/UnitTestCase.php
+++ b/tests/PHPUnit/Framework/TestCase/UnitTestCase.php
@@ -7,6 +7,7 @@
*/
namespace Piwik\Tests\Framework\TestCase;
+use Piwik\EventDispatcher;
use Piwik\Tests\Framework\Mock\File;
@@ -21,6 +22,7 @@ abstract class UnitTestCase extends \PHPUnit_Framework_TestCase
{
parent::setUp();
File::reset();
+ EventDispatcher::getInstance()->clearAllObservers();
}
public function tearDown()
diff --git a/tests/PHPUnit/Integration/Plugin/ManagerTest.php b/tests/PHPUnit/Integration/Plugin/ManagerTest.php
new file mode 100644
index 0000000000..3b6265b4a5
--- /dev/null
+++ b/tests/PHPUnit/Integration/Plugin/ManagerTest.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Plugin;
+
+use Piwik\Cache\PersistentCache;
+use Piwik\Db;
+use Piwik\Plugin;
+use Piwik\Settings\Storage;
+use Piwik\Tests\Integration\Settings\IntegrationTestCase;
+
+/**
+ * @group Plugin
+ * @group PluginManager
+ */
+class ManagerTest extends IntegrationTestCase
+{
+ /**
+ * @var Plugin\Manager
+ */
+ private $manager;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->manager = Plugin\Manager::getInstance();
+ }
+
+ public function test_loadTrackerPlugins_shouldDetectTrackerPlugins()
+ {
+ $this->assertGreaterThan(50, count($this->manager->getLoadedPlugins())); // make sure all plugins are loaded
+
+ $pluginsToLoad = $this->manager->loadTrackerPlugins();
+
+ $this->assertOnlyTrackerPluginsAreLoaded($pluginsToLoad);
+ }
+
+ public function test_loadTrackerPlugins_shouldCacheListOfPlugins()
+ {
+ $cache = $this->getCacheForTrackerPlugins();
+ $this->assertFalse($cache->has());
+
+ $pluginsToLoad = $this->manager->loadTrackerPlugins();
+
+ $this->assertTrue($cache->has());
+ $this->assertEquals($pluginsToLoad, $cache->get());
+ }
+
+ public function test_loadTrackerPlugins_shouldBeAbleToLoadPluginsCorrectWhenItIsCached()
+ {
+ $pluginsToLoad = array('CoreHome', 'UserSettings', 'Login', 'CoreAdminHome');
+ $this->getCacheForTrackerPlugins()->set($pluginsToLoad);
+
+ $pluginsToLoad = $this->manager->loadTrackerPlugins();
+
+ $this->assertCount(4, $this->manager->getLoadedPlugins());
+ $this->assertEquals($pluginsToLoad, array_keys($this->manager->getLoadedPlugins()));
+ }
+
+ public function test_loadTrackerPlugins_shouldUnloadAllPlugins_IfThereAreNoneToLoad()
+ {
+ $pluginsToLoad = array();
+ $this->getCacheForTrackerPlugins()->set($pluginsToLoad);
+
+ $pluginsToLoad = $this->manager->loadTrackerPlugins();
+
+ $this->assertEquals(array(), $pluginsToLoad);
+ $this->assertEquals(array(), $this->manager->getLoadedPlugins());
+ }
+
+ private function getCacheForTrackerPlugins()
+ {
+ return new PersistentCache('PluginsTracker');
+ }
+
+ private function assertOnlyTrackerPluginsAreLoaded($expectedPluginNamesLoaded)
+ {
+ // should currently load between 10 and 25 plugins
+ $this->assertLessThan(25, count($this->manager->getLoadedPlugins()));
+ $this->assertGreaterThan(10, count($this->manager->getLoadedPlugins()));
+
+ // we need to make sure it actually only loaded the correct ones
+ $this->assertEquals($expectedPluginNamesLoaded, array_keys($this->manager->getLoadedPlugins()));
+ }
+}
diff --git a/tests/PHPUnit/Integration/Plugin/SettingsTest.php b/tests/PHPUnit/Integration/Plugin/SettingsTest.php
index f8802674a4..f086b2b384 100644
--- a/tests/PHPUnit/Integration/Plugin/SettingsTest.php
+++ b/tests/PHPUnit/Integration/Plugin/SettingsTest.php
@@ -11,6 +11,7 @@ namespace Piwik\Tests\Integration\Plugin;
use Piwik\Db;
use Piwik\Plugin\Settings as PluginSettings;
use Piwik\Settings\Storage;
+use Piwik\SettingsServer;
use Piwik\Tests\Integration\Settings\CorePluginTestSettings;
use Piwik\Tests\Integration\Settings\IntegrationTestCase;
use Piwik\Tracker\Cache;
@@ -112,13 +113,13 @@ class SettingsTest extends IntegrationTestCase
{
$this->setSuperUser();
- $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+ SettingsServer::setIsTrackerApiRequest();
$settings = $this->createSettingsInstance();
$setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
$settings->addSetting($setting);
- unset($GLOBALS['PIWIK_TRACKER_MODE']);
+ SettingsServer::setIsNotTrackerApiRequest();
$storage = $setting->getStorage();
$this->assertTrue($storage instanceof SettingsStorage);
diff --git a/tests/PHPUnit/Integration/ReleaseCheckListTest.php b/tests/PHPUnit/Integration/ReleaseCheckListTest.php
index 2c4ab2a00d..f6bbc7a0bd 100644
--- a/tests/PHPUnit/Integration/ReleaseCheckListTest.php
+++ b/tests/PHPUnit/Integration/ReleaseCheckListTest.php
@@ -12,6 +12,7 @@ use Exception;
use Piwik\Config;
use Piwik\Filesystem;
use Piwik\Plugin\Manager;
+use Piwik\Tracker;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
@@ -142,6 +143,9 @@ class ReleaseCheckListTest extends \PHPUnit_Framework_TestCase
{
$this->assertTrue(!isset($GLOBALS['PIWIK_TRACKER_DEBUG']));
$this->assertEquals(0, $this->globalConfig['Tracker']['debug']);
+
+ $tracker = new Tracker();
+ $this->assertFalse($tracker->isDebugModeEnabled());
}
/**
diff --git a/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php
index 0c63124dfe..9c2de66667 100644
--- a/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php
+++ b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php
@@ -10,6 +10,7 @@ namespace Piwik\Tests\Integration\Settings\Storage;
use Piwik\Settings\Storage;
use Piwik\Settings\Storage\Factory;
+use Piwik\SettingsServer;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker\SettingsStorage;
@@ -51,11 +52,11 @@ class FactoryTest extends IntegrationTestCase
private function makeTrackerInstance()
{
- $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+ SettingsServer::setIsTrackerApiRequest();
$storage = Factory::make('PluginName');
- unset($GLOBALS['PIWIK_TRACKER_MODE']);
+ SettingsServer::setIsNotTrackerApiRequest();
return $storage;
}
diff --git a/tests/PHPUnit/Integration/Tracker/Handler/FactoryTest.php b/tests/PHPUnit/Integration/Tracker/Handler/FactoryTest.php
new file mode 100644
index 0000000000..fd77913bfa
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/Handler/FactoryTest.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker\Handler;
+
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker;
+use Piwik\Tracker\Handler;
+use Piwik\Tracker\Handler\Factory;
+
+/**
+ * @group Tracker
+ * @group Handler
+ * @group Factory
+ * @group FactoryTest
+ */
+class FactoryTest extends IntegrationTestCase
+{
+
+ public function tearDown()
+ {
+ EventDispatcher::getInstance()->clearObservers('Tracker.initRequestSet');
+ parent::tearDown();
+ }
+
+ public function test_make_shouldCreateDefaultInstance()
+ {
+ $handler = Factory::make();
+ $this->assertInstanceOf('Piwik\\Tracker\\Handler', $handler);
+ }
+
+ public function test_make_shouldTriggerEventOnce()
+ {
+ $called = 0;
+ $self = $this;
+ Piwik::addAction('Tracker.newHandler', function ($handler) use (&$called, $self) {
+ $called++;
+ $self->assertNull($handler);
+ });
+
+ Factory::make();
+ $this->assertSame(1, $called);
+ }
+
+ public function test_make_shouldPreferManuallyCreatedHandlerInstanceInEventOverDefaultHandler()
+ {
+ $handlerToUse = new Handler();
+ Piwik::addAction('Tracker.newHandler', function (&$handler) use ($handlerToUse) {
+ $handler = $handlerToUse;
+ });
+
+ $handler = Factory::make();
+ $this->assertSame($handlerToUse, $handler);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The Handler object set in the plugin
+ */
+ public function test_make_shouldTriggerExceptionInCaseWrongInstanceCreatedInHandler()
+ {
+ Piwik::addAction('Tracker.newHandler', function (&$handler) {
+ $handler = new Tracker();
+ });
+
+ Factory::make();
+ }
+}
diff --git a/tests/PHPUnit/Integration/Tracker/HandlerTest.php b/tests/PHPUnit/Integration/Tracker/HandlerTest.php
new file mode 100644
index 0000000000..812c341336
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/HandlerTest.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker;
+
+use Piwik\Exception\InvalidRequestParameterException;
+use Piwik\Exception\UnexpectedWebsiteFoundException;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\Mock\Tracker\Response;
+use Piwik\Tests\Framework\Mock\Tracker\ScheduledTasksRunner;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker;
+use Piwik\Tracker\Handler;
+use Piwik\Tests\Framework\Mock\Tracker\RequestSet;
+use Exception;
+
+/**
+ * @group HandlerTest
+ * @group Handler
+ * @group Tracker
+ */
+class HandlerTest extends IntegrationTestCase
+{
+ /**
+ * @var Handler
+ */
+ private $handler;
+
+ /**
+ * @var Response
+ */
+ private $response;
+
+ /**
+ * @var Tracker
+ */
+ private $tracker;
+
+ /**
+ * @var RequestSet
+ */
+ private $requestSet;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Tracker\Cache::deleteTrackerCache();
+
+ $this->response = new Response();
+ $this->handler = new Handler();
+ $this->handler->setResponse($this->response);
+ $this->tracker = new Tracker();
+ $this->requestSet = new RequestSet();
+ }
+
+ public function test_init_ShouldInitiateResponseInstance()
+ {
+ $this->handler->init($this->tracker, $this->requestSet);
+
+ $this->assertTrue($this->response->isInit);
+ $this->assertFalse($this->response->isResponseOutput);
+ $this->assertFalse($this->response->isSend);
+ }
+
+ public function test_finish_ShouldOutputAndSendResponse()
+ {
+ $response = $this->handler->finish($this->tracker, $this->requestSet);
+
+ $this->assertEquals('My Dummy Content', $response);
+ $this->assertFalse($this->response->isInit);
+ $this->assertFalse($this->response->isExceptionOutput);
+ $this->assertTrue($this->response->isResponseOutput);
+ $this->assertTrue($this->response->isSend);
+ }
+
+ public function test_finish_ShouldRedirectIfThereIsAValidUrl()
+ {
+ $_GET['redirecturl'] = 'http://localhost/test?foo=bar';
+
+ $this->requestSet->setRequests(array(array('idsite' => '1')));
+
+ try {
+ $this->handler->finish($this->tracker, $this->requestSet);
+ $this->fail('An expected exception was not thrown');
+ } catch (Exception $e) {
+ $this->assertContains('Piwik would redirect you to this URL: ' . $_GET['redirecturl'], $e->getMessage());
+ unset($_GET['redirecturl']);
+ }
+ }
+
+ public function test_finish_ShouldRedirectIfThereIsAValidBelongingToTheSite()
+ {
+ $_GET['redirecturl'] = 'http://piwik.net/';
+
+ $this->requestSet->setRequests(array(array('idsite' => '1')));
+
+ try {
+ $this->handler->finish($this->tracker, $this->requestSet);
+ $this->fail('An expected exception was not thrown');
+ } catch (Exception $e) {
+ $this->assertContains('Piwik would redirect you to this URL: http://piwik.net/', $e->getMessage());
+ unset($_GET['redirecturl']);
+ }
+ }
+
+ public function test_finish_ShouldNotRedirectIfThereIsAUrlThatDoesNotBelongToAnySite()
+ {
+ $_GET['redirecturl'] = 'http://random.piwik.org/test?foo=bar';
+
+ $this->requestSet->setRequests(array(array('idsite' => '1')));
+ $this->handler->finish($this->tracker, $this->requestSet);
+ unset($_GET['redirecturl']);
+
+ $this->assertTrue(true);
+ }
+
+ public function test_finish_ShouldNotRedirectIfUrlIsValidButNoRequests()
+ {
+ $_GET['redirecturl'] = 'http://localhost/test';
+
+ $this->requestSet->setRequests(array());
+ $this->handler->finish($this->tracker, $this->requestSet);
+ unset($_GET['redirecturl']);
+
+ $this->assertTrue(true);
+ }
+
+ public function test_finish_ShoulAlsoReturnAPossibleRenderedException()
+ {
+ $this->executeOnException($this->buildException());
+ $response = $this->handler->finish($this->tracker, $this->requestSet);
+
+ $this->assertEquals('MyMessage', $response);
+ }
+
+ public function test_onException_ShouldOutputResponse()
+ {
+ $this->executeOnException($this->buildException());
+
+ $this->assertFalse($this->response->isInit);
+ $this->assertFalse($this->response->isResponseOutput);
+ $this->assertTrue($this->response->isExceptionOutput);
+ $this->assertFalse($this->response->isSend);
+ }
+
+ public function test_onException_ShouldPassExceptionToResponse()
+ {
+ $exception = $this->buildException();
+
+ $this->executeOnException($exception);
+
+ $this->assertSame($exception, $this->response->exception);
+ $this->assertSame(500, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldSendStatusCode400IfUnexpectedWebsite()
+ {
+ $this->executeOnException(new UnexpectedWebsiteFoundException('test'));
+ $this->assertSame(400, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldSendStatusCode400IfInvalidRequestParameterException()
+ {
+ $this->executeOnException(new InvalidRequestParameterException('test'));
+ $this->assertSame(400, $this->response->statusCode);
+ }
+
+ public function test_onException_ShouldStilRedirectIfThereIsAnExceptionAndAValidRedirectUrl()
+ {
+ $_GET['redirecturl'] = 'http://localhost/test?foo=bar';
+
+ $this->requestSet->setRequests(array(array('idsite' => '1')));
+
+ try {
+ $this->handler->onException($this->tracker, $this->requestSet, $this->buildException());
+ $this->fail('An expected exception was not thrown');
+ } catch (Exception $e) {
+ $this->assertContains('Piwik would redirect you to this URL: ' . $_GET['redirecturl'], $e->getMessage());
+ unset($_GET['redirecturl']);
+ }
+ }
+
+ public function test_onException_ShouldNotRethrowExceptionToExitTrackerImmediately()
+ {
+ $exception = $this->buildException();
+
+ $this->handler->onException($this->tracker, $this->requestSet, $exception);
+ $this->assertTrue(true);
+ }
+
+ public function test_onAllRequestsTracked_ShouldTriggerScheduledTasksIfEnabled()
+ {
+ $runner = new ScheduledTasksRunner();
+ $runner->shouldRun = true;
+
+ $this->handler->setScheduledTasksRunner($runner);
+ $this->handler->onAllRequestsTracked($this->tracker, $this->requestSet);
+
+ $this->assertTrue($runner->ranScheduledTasks);
+ }
+
+ public function test_onAllRequestsTracked_ShouldNotTriggerScheduledTasksIfDisabled()
+ {
+ $runner = new ScheduledTasksRunner();
+ $runner->shouldRun = false;
+
+ $this->handler->setScheduledTasksRunner($runner);
+ $this->handler->onAllRequestsTracked($this->tracker, $this->requestSet);
+
+ $this->assertFalse($runner->ranScheduledTasks);
+ }
+
+ public function test_process_ShouldTrackAllSetRequests()
+ {
+ $this->assertSame(0, $this->tracker->getCountOfLoggedRequests());
+
+ $this->requestSet->setRequests(array(
+ array('idsite' => 1, 'url' => 'http://localhost/foo?bar'),
+ array('idsite' => 1, 'url' => 'http://localhost'),
+ ));
+
+ $this->handler->process($this->tracker, $this->requestSet);
+
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ private function buildException()
+ {
+ return new \Exception('MyMessage', 292);
+ }
+
+ private function executeOnException(Exception $exception)
+ {
+ try {
+ $this->handler->onException($this->tracker, $this->requestSet, $exception);
+ } catch (Exception $e) {
+ }
+ }
+}
diff --git a/tests/PHPUnit/Integration/Tracker/RequestSetTest.php b/tests/PHPUnit/Integration/Tracker/RequestSetTest.php
new file mode 100644
index 0000000000..a551149fa0
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/RequestSetTest.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker;
+
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tracker\RequestSet;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+
+class TestRequestSet extends RequestSet {
+
+ private $redirectUrl = '';
+
+ public function setRedirectUrl($url)
+ {
+ $this->redirectUrl = $url;
+ }
+
+ public function getRedirectUrl()
+ {
+ return $this->redirectUrl;
+ }
+}
+/**
+ * @group RequestSetTest
+ * @group RequestSet
+ * @group Tracker
+ */
+class RequestSetTest extends IntegrationTestCase
+{
+ /**
+ * @var TestRequestSet
+ */
+ private $requestSet;
+ private $get;
+ private $post;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Fixture::createWebsite('2014-01-01 00:00:00', 0, false, 'http://www.example.com');
+
+ $this->requestSet = $this->buildNewRequestSetThatIsNotInitializedYet();
+ $this->requestSet->setRequests(array(array('idsite' => 1), array('idsite' => 2)));
+
+ $this->get = $_GET;
+ $this->post = $_POST;
+
+ $_GET = array();
+ $_POST = array();
+ }
+
+ public function tearDown()
+ {
+ $_GET = $this->get;
+ $_POST = $this->post;
+
+ EventDispatcher::getInstance()->clearObservers('Tracker.initRequestSet');
+ parent::tearDown();
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldNotRedirect_IfNoUrlIsSet()
+ {
+ $this->assertFalse($this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldNotRedirect_IfUrlIsSetButNoRequests()
+ {
+ $this->requestSet->setRedirectUrl('http://localhost');
+ $this->assertEquals('http://localhost', $this->requestSet->getRedirectUrl());
+
+ $this->requestSet->setRequests(array());
+
+ $this->assertFalse($this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldNotRedirect_IfUrlHasNoHostOrIsNotUrl()
+ {
+ $this->requestSet->setRedirectUrl('abc');
+
+ $this->assertFalse($this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldNotRedirect_IfUrlIsNotWhitelistedInAnySiteId()
+ {
+ $this->requestSet->setRedirectUrl('http://example.org');
+
+ $this->assertFalse($this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldRedirect_IfUrlIsGivenAndWhitelistedInAnySiteId()
+ {
+ $this->requestSet->setRedirectUrl('http://www.example.com');
+
+ $this->assertEquals('http://www.example.com', $this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_shouldPerformRedirectToUrl_shouldRedirect_IfBaseDomainIsGivenAndWhitelistedInAnySiteId()
+ {
+ $this->requestSet->setRedirectUrl('http://example.com');
+
+ $this->assertEquals('http://example.com', $this->requestSet->shouldPerformRedirectToUrl());
+ }
+
+ public function test_initRequestsAndTokenAuth_shouldTriggerEventToInitRequestsButOnlyOnce()
+ {
+ $requestSet = $this->buildNewRequestSetThatIsNotInitializedYet();
+
+ $called = 0;
+ $passedRequest = null;
+ Piwik::addAction('Tracker.initRequestSet', function ($param) use (&$called, &$passedRequest) {
+ $called++;
+ $passedRequest = $param;
+ });
+
+ $requestSet->initRequestsAndTokenAuth();
+ $this->assertSame(1, $called);
+ $this->assertSame($requestSet, $passedRequest);
+
+ $requestSet->initRequestsAndTokenAuth(); // should not be called again
+ $this->assertSame(1, $called);
+ }
+
+ public function test_initRequestsAndTokednAuth_shouldInitializeRequestsWithEmptyArray()
+ {
+ $requestSet = $this->buildNewRequestSetThatIsNotInitializedYet();
+ $requestSet->initRequestsAndTokenAuth();
+ $this->assertEquals(array(), $requestSet->getRequests());
+ }
+
+ public function test_initRequestsAndTokednAuth_shouldInitializeFromGetAndPostIfEventDoesNotHandleRequests()
+ {
+ $_GET = array('idsite' => 1);
+ $_POST = array('c_i' => 'click');
+
+ Piwik::addAction('Tracker.initRequestSet', function (RequestSet $requestSet) {
+ $requestSet->setRequests(array(array('idsite' => '2'), array('idsite' => '3')));
+ });
+
+ $requestSet = $this->buildNewRequestSetThatIsNotInitializedYet();
+
+ $requestSet->initRequestsAndTokenAuth();
+
+ $requests = $requestSet->getRequests();
+ $this->assertCount(2, $requests);
+ $this->assertEquals(array('idsite' => '2'), $requests[0]->getParams());
+ $this->assertEquals(array('idsite' => '3'), $requests[1]->getParams());
+ }
+
+ public function test_initRequestsAndTokednAuth_shouldIgnoreGetAndPostIfInitializedByEvent()
+ {
+ $_GET = array('idsite' => '1');
+ $_POST = array('c_i' => 'click');
+
+ $requestSet = $this->buildNewRequestSetThatIsNotInitializedYet();
+ $requestSet->initRequestsAndTokenAuth();
+ $requests = $requestSet->getRequests();
+ $this->assertCount(1, $requests);
+ $this->assertEquals(array('idsite' => 1, 'c_i' => 'click'), $requests[0]->getParams());
+ }
+
+ private function buildNewRequestSetThatIsNotInitializedYet()
+ {
+ return new TestRequestSet();
+ }
+}
diff --git a/tests/PHPUnit/Integration/Tracker/RequestTest.php b/tests/PHPUnit/Integration/Tracker/RequestTest.php
new file mode 100644
index 0000000000..fe4ba830ca
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/RequestTest.php
@@ -0,0 +1,393 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker;
+
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Plugins\CustomVariables\CustomVariables;
+use Piwik\Plugins\UsersManager\API;
+use Piwik\Plugins\UsersManager\Model;
+use Piwik\Plugins\UsersManager\UsersManager;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tracker\Cache;
+use Piwik\Tracker\Request;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker\TrackerConfig;
+
+class TestRequest extends Request {
+
+ public function setIsAuthenticated()
+ {
+ $this->isAuthenticated = true;
+ }
+
+}
+
+/**
+ * @group RequestTest
+ * @group Request
+ * @group Tracker
+ */
+class RequestTest extends IntegrationTestCase
+{
+ /**
+ * @var TestRequest
+ */
+ private $request;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Fixture::createWebsite('2014-01-01 00:00:00');
+ Cache::deleteTrackerCache();
+
+ $this->request = $this->buildRequest(array('idsite' => '1'));
+ }
+
+ public function tearDown()
+ {
+ EventDispatcher::getInstance()->clearObservers('Request.initAuthenticationObject');
+ parent::tearDown();
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldReturnNoCustomVars_IfNoWerePassedInParams()
+ {
+ $this->assertEquals(array(), $this->request->getCustomVariablesInVisitScope());
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldReturnNoCustomVars_IfPassedParamIsNotAnArray()
+ {
+ $this->assertCustomVariablesInVisitScope(array(), '{"mykey":"myval"}');
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldReturnCustomVars_IfTheyAreValid()
+ {
+ $customVars = $this->buildCustomVars(array('mykey' => 'myval', 'test' => 'value'));
+ $expected = $this->buildExpectedCustomVars(array('mykey' => 'myval', 'test' => 'value'));
+
+ $this->assertCustomVariablesInVisitScope($expected, $customVars);
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldIgnoreIndexesLowerThan1()
+ {
+ $customVars = array(
+ array('mykey', 'myval'),
+ array('test', 'value'),
+ );
+ $expected = $this->buildExpectedCustomVars(array('test' => 'value'));
+
+ $this->assertCustomVariablesInVisitScope($expected, json_encode($customVars));
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldTruncateValuesIfTheyAreTooLong()
+ {
+ $maxLen = CustomVariables::getMaxLengthCustomVariables();
+
+ $customVars = $this->buildCustomVars(array(
+ 'mykey' => 'myval',
+ 'test' => str_pad('test', $maxLen + 5, 't'),
+ ));
+ $expected = $this->buildExpectedCustomVars(array(
+ 'mykey' => 'myval',
+ 'test' => str_pad('test', $maxLen, 't'),
+ ));
+
+ $this->assertCustomVariablesInVisitScope($expected, $customVars);
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldIgnoreVarsThatDoNotHaveKeyAndValue()
+ {
+ $customVars = array(
+ 1 => array('mykey', 'myval'),
+ 2 => array('test'),
+ );
+ $expected = $this->buildExpectedCustomVars(array('mykey' => 'myval'));
+
+ $this->assertCustomVariablesInVisitScope($expected, json_encode($customVars));
+ }
+
+ public function test_getCustomVariablesInVisitScope_ShouldSetDefaultValueToEmptyStringAndHandleOtherTypes()
+ {
+ $input = array(
+ 'myfloat' => 5.55,
+ 'myint' => 53,
+ 'mystring' => '',
+ );
+ $customVars = $this->buildCustomVars($input);
+ $expected = $this->buildExpectedCustomVars($input);
+
+ $this->assertCustomVariablesInVisitScope($expected, $customVars);
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldReturnNoCustomVars_IfNoWerePassedInParams()
+ {
+ $this->assertEquals(array(), $this->request->getCustomVariablesInPageScope());
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldReturnNoCustomVars_IfPassedParamIsNotAnArray()
+ {
+ $this->assertCustomVariablesInPageScope(array(), '{"mykey":"myval"}');
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldReturnCustomVars_IfTheyAreValid()
+ {
+ $customVars = $this->buildCustomVars(array('mykey' => 'myval', 'test' => 'value'));
+ $expected = $this->buildExpectedCustomVars(array('mykey' => 'myval', 'test' => 'value'));
+
+ $this->assertCustomVariablesInPageScope($expected, $customVars);
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldIgnoreIndexesLowerThan1()
+ {
+ $customVars = array(
+ array('mykey', 'myval'),
+ array('test', 'value'),
+ );
+ $expected = $this->buildExpectedCustomVars(array('test' => 'value'));
+
+ $this->assertCustomVariablesInPageScope($expected, json_encode($customVars));
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldTruncateValuesIfTheyAreTooLong()
+ {
+ $maxLen = CustomVariables::getMaxLengthCustomVariables();
+
+ $customVars = $this->buildCustomVars(array(
+ 'mykey' => 'myval',
+ 'test' => str_pad('test', $maxLen + 5, 't'),
+ ));
+ $expected = $this->buildExpectedCustomVars(array(
+ 'mykey' => 'myval',
+ 'test' => str_pad('test', $maxLen, 't'),
+ ));
+
+ $this->assertCustomVariablesInPageScope($expected, $customVars);
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldIgnoreVarsThatDoNotHaveKeyAndValue()
+ {
+ $customVars = array(
+ 1 => array('mykey', 'myval'),
+ 2 => array('test'),
+ );
+ $expected = $this->buildExpectedCustomVars(array('mykey' => 'myval'));
+
+ $this->assertCustomVariablesInPageScope($expected, json_encode($customVars));
+ }
+
+ public function test_getCustomVariablesInPageScope_ShouldSetDefaultValueToEmptyStringAndHandleOtherTypes()
+ {
+ $input = array(
+ 'myfloat' => 5.55,
+ 'myint' => 53,
+ 'mystring' => '',
+ );
+ $customVars = $this->buildCustomVars($input);
+ $expected = $this->buildExpectedCustomVars($input);
+
+ $this->assertCustomVariablesInPageScope($expected, $customVars);
+ }
+
+ public function test_isAuthenticated_ShouldBeNotAuthenticatedInTestsByDefault()
+ {
+ $this->assertFalse($this->request->isAuthenticated());
+ }
+
+ public function test_isAuthenticated_ShouldBeAuthenticatedIfCheckIsDisabledInConfig()
+ {
+ $oldConfig = TrackerConfig::getConfigValue('tracking_requests_require_authentication');
+ TrackerConfig::setConfigValue('tracking_requests_require_authentication', 0);
+
+ $this->assertTrue($this->request->isAuthenticated());
+
+ TrackerConfig::setConfigValue('tracking_requests_require_authentication', $oldConfig);
+ }
+
+ public function test_isAuthenticated_ShouldReadTheIsAuthenticatedPropertyAndIgnoreACheck()
+ {
+ $this->assertFalse($this->request->isAuthenticated());
+ $this->request->setIsAuthenticated();
+ $this->assertTrue($this->request->isAuthenticated());
+ }
+
+ public function test_isAuthenticated_ShouldWorkIfTokenIsCorrect()
+ {
+ $token = $this->createAdminUserForSite(2);
+
+ $request = $this->buildRequestWithToken(array('idsite' => '1'), $token);
+ $this->assertFalse($request->isAuthenticated());
+
+ $request = $this->buildRequestWithToken(array('idsite' => '2'), $token);
+ $this->assertTrue($request->isAuthenticated());
+ }
+
+ public function test_isAuthenticated_ShouldAlwaysWorkForSuperUser()
+ {
+ Fixture::createSuperUser(false);
+ $token = Fixture::getTokenAuth();
+
+ $request = $this->buildRequestWithToken(array('idsite' => '1'), $token);
+ $this->assertTrue($request->isAuthenticated());
+
+ $request = $this->buildRequestWithToken(array('idsite' => '2'), $token);
+ $this->assertTrue($request->isAuthenticated());
+ }
+
+ public function test_authenticateSuperUserOrAdmin_ShouldFailIfTokenIsEmpty()
+ {
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin('', 2);
+ $this->assertFalse($isAuthenticated);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin(null, 2);
+ $this->assertFalse($isAuthenticated);
+ }
+
+ public function test_authenticateSuperUserOrAdmin_ShouldPostAuthInitEvent_IfTokenIsGiven()
+ {
+ $called = 0;
+ Piwik::addAction('Request.initAuthenticationObject', function () use (&$called) {
+ $called++;
+ });
+
+ Request::authenticateSuperUserOrAdmin('', 2);
+ $this->assertSame(0, $called);
+
+ Request::authenticateSuperUserOrAdmin('atoken', 2);
+ $this->assertSame(1, $called);
+
+ Request::authenticateSuperUserOrAdmin('anothertoken', 2);
+ $this->assertSame(2, $called);
+
+ Request::authenticateSuperUserOrAdmin(null, 2);
+ $this->assertSame(2, $called);
+ }
+
+ public function test_authenticateSuperUserOrAdmin_ShouldNotBeAllowedToAccessSitesHavingInvalidId()
+ {
+ $token = $this->createAdminUserForSite(2);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, -2);
+ $this->assertFalse($isAuthenticated);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, 0);
+ $this->assertFalse($isAuthenticated);
+ }
+
+ public function test_authenticateSuperUserOrAdmin_ShouldWorkIfTokenIsCorrect()
+ {
+ $token = $this->createAdminUserForSite(2);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, 1);
+ $this->assertFalse($isAuthenticated);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, 2);
+ $this->assertTrue($isAuthenticated);
+ }
+
+ public function test_authenticateSuperUserOrAdmin_ShouldAlwaysWorkForSuperUser()
+ {
+ Fixture::createSuperUser(false);
+ $token = Fixture::getTokenAuth();
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, 1);
+ $this->assertTrue($isAuthenticated);
+
+ $isAuthenticated = Request::authenticateSuperUserOrAdmin($token, 2);
+ $this->assertTrue($isAuthenticated);
+ }
+
+ private function createAdminUserForSite($idSite)
+ {
+ $login = 'myadmin';
+ $passwordHash = UsersManager::getPasswordHash('password');
+
+ $token = API::getInstance()->getTokenAuth($login, $passwordHash);
+
+ $user = new Model();
+ $user->addUser($login, $passwordHash, 'admin@piwik', 'alias', $token, '2014-01-01 00:00:00');
+ $user->addUserAccess($login, 'admin', array($idSite));
+
+ return $token;
+ }
+
+ public function test_internalBuildExpectedCustomVars()
+ {
+ $this->assertEquals(array(), $this->buildExpectedCustomVars(array()));
+
+ $this->assertEquals(array('custom_var_k1' => 'key', 'custom_var_v1' => 'val'),
+ $this->buildExpectedCustomVars(array('key' => 'val')));
+
+ $this->assertEquals(array(
+ 'custom_var_k1' => 'key', 'custom_var_v1' => 'val',
+ 'custom_var_k2' => 'key2', 'custom_var_v2' => 'val2',
+ ), $this->buildExpectedCustomVars(array('key' => 'val', 'key2' => 'val2')));
+ }
+
+ public function test_internalBuildCustomVars()
+ {
+ $this->assertEquals('[]', $this->buildCustomVars(array()));
+
+ $this->assertEquals('{"1":["key","val"]}',
+ $this->buildCustomVars(array('key' => 'val')));
+
+ $this->assertEquals('{"1":["key","val"],"2":["key2","val2"]}',
+ $this->buildCustomVars(array('key' => 'val', 'key2' => 'val2')));
+ }
+
+ private function assertCustomVariablesInVisitScope($expectedCvars, $cvarsJsonEncoded)
+ {
+ $request = $this->buildRequest(array('_cvar' => $cvarsJsonEncoded));
+ $this->assertEquals($expectedCvars, $request->getCustomVariablesInVisitScope());
+ }
+
+ private function assertCustomVariablesInPageScope($expectedCvars, $cvarsJsonEncoded)
+ {
+ $request = $this->buildRequest(array('cvar' => $cvarsJsonEncoded));
+ $this->assertEquals($expectedCvars, $request->getCustomVariablesInPageScope());
+ }
+
+ private function buildExpectedCustomVars($customVars)
+ {
+ $vars = array();
+ $index = 1;
+
+ foreach ($customVars as $key => $value) {
+ $vars['custom_var_k' . $index] = $key;
+ $vars['custom_var_v' . $index] = $value;
+ $index++;
+ }
+
+ return $vars;
+ }
+
+ private function buildCustomVars($customVars)
+ {
+ $vars = array();
+ $index = 1;
+
+ foreach ($customVars as $key => $value) {
+ $vars[$index] = array($key, $value);
+ $index++;
+ }
+
+ return json_encode($vars);
+ }
+
+ private function buildRequest($params)
+ {
+ return new TestRequest($params);
+ }
+
+ private function buildRequestWithToken($params, $token)
+ {
+ return new TestRequest($params, $token);
+ }
+}
diff --git a/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php b/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php
index 1bda1105e6..0aa2a49627 100644
--- a/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php
+++ b/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php
@@ -8,10 +8,9 @@
namespace Piwik\Tests\Integration\Tracker;
+use Piwik\Cache\PersistentCache;
use Piwik\Option;
use Piwik\Settings\Storage;
-use Piwik\Settings\Setting;
-use Piwik\Tests\Integration\Settings\IntegrationTestCase;
use Piwik\Tests\Integration\Settings\StorageTest;
use Piwik\Tracker\Cache;
use Piwik\Tracker\SettingsStorage;
@@ -44,11 +43,11 @@ class SettingsStorageTest extends StorageTest
{
$this->setSettingValueInCache('my0815RandomName');
- $this->assertArrayHasKey('settingsStorage', Cache::getCacheGeneral());
+ $this->assertTrue($this->getCache()->has());
SettingsStorage::clearCache();
- $this->assertArrayNotHasKey('settingsStorage', Cache::getCacheGeneral());
+ $this->assertFalse($this->getCache()->has());
}
public function test_storageShouldNotCastAnyCachedValue()
@@ -63,10 +62,12 @@ class SettingsStorageTest extends StorageTest
$this->storage->setValue($this->setting, 5);
$this->storage->save();
+ $this->assertFalse($this->getCache()->has());
$this->assertNotFalse($this->getValueFromOptionTable()); // make sure saved in db
$storage = $this->buildStorage();
$this->assertEquals(5, $storage->getValue($this->setting));
+ $this->assertTrue($this->getCache()->has());
}
public function test_storageCreateACacheEntryIfNoCacheExistsYet()
@@ -76,53 +77,28 @@ class SettingsStorageTest extends StorageTest
$this->setSettingValueAndMakeSureCacheGetsCreated('myVal');
- $cache = Cache::getCacheGeneral();
+ $cache = $this->getCache()->get();
$this->assertEquals(array(
- $this->storage->getOptionKey() => array(
- $this->setting->getKey() => 'myVal'
- )
- ), $cache['settingsStorage']);
+ $this->setting->getKey() => 'myVal'
+ ), $cache);
}
- public function test_shouldAddACacheEntryToAnotherCacheEntryAndNotOverwriteAll()
+ protected function buildStorage()
{
- $dummyCacheEntry = array(
- 'Plugin_PluginNameOther_Settings' => array(
- 'anything' => 'anyval',
- 'any' => 'other'
- )
- );
- Cache::setCacheGeneral(array(
- 'settingsStorage' => $dummyCacheEntry
- ));
-
- Option::set($this->storage->getOptionKey(), serialize(array('mykey' => 'myVal')));
-
- $this->buildStorage()->getValue($this->setting); // force adding new cache entry
-
- $cache = Cache::getCacheGeneral();
-
- $dummyCacheEntry[$this->storage->getOptionKey()] = array(
- 'mykey' => 'myVal'
- );
-
- $this->assertEquals($dummyCacheEntry, $cache['settingsStorage']);
+ return new SettingsStorage('PluginName');
}
- protected function buildStorage()
+ private function getCache()
{
- return new SettingsStorage('PluginName');
+ return new PersistentCache($this->storage->getOptionKey());
}
private function setSettingValueInCache($value)
{
- Cache::setCacheGeneral(array(
- 'settingsStorage' => array(
- $this->storage->getOptionKey() => array(
- $this->setting->getKey() => $value
- )
- )
+ $cache = $this->getCache();
+ $cache->set(array(
+ $this->setting->getKey() => $value
));
}
diff --git a/tests/PHPUnit/Integration/Tracker/Visit/FactoryTest.php b/tests/PHPUnit/Integration/Tracker/Visit/FactoryTest.php
new file mode 100644
index 0000000000..2cefabc23b
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/Visit/FactoryTest.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker\Visit;
+
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker;
+use Piwik\Tracker\Visit;
+use Piwik\Tracker\Visit\Factory;
+
+/**
+ * @group Tracker
+ * @group Handler
+ * @group Visit
+ * @group Factory
+ * @group FactoryTest
+ */
+class FactoryTest extends IntegrationTestCase
+{
+
+ public function tearDown()
+ {
+ EventDispatcher::getInstance()->clearObservers('Tracker.makeNewVisitObject');
+ parent::tearDown();
+ }
+
+ public function test_make_shouldCreateDefaultInstance()
+ {
+ $visit = Factory::make();
+ $this->assertInstanceOf('Piwik\\Tracker\\Visit', $visit);
+ }
+
+ public function test_make_shouldTriggerEventOnce()
+ {
+ $called = 0;
+ $self = $this;
+ Piwik::addAction('Tracker.makeNewVisitObject', function ($visit) use (&$called, $self) {
+ $called++;
+ $self->assertNull($visit);
+ });
+
+ Factory::make();
+ $this->assertSame(1, $called);
+ }
+
+ public function test_make_shouldPreferManuallyCreatedHandlerInstanceInEventOverDefaultHandler()
+ {
+ $visitToUse = new Visit();
+ Piwik::addAction('Tracker.makeNewVisitObject', function (&$visit) use ($visitToUse) {
+ $visit = $visitToUse;
+ });
+
+ $visit = Factory::make();
+ $this->assertSame($visitToUse, $visit);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The Visit object set in the plugin
+ */
+ public function test_make_shouldTriggerExceptionInCaseWrongInstanceCreatedInHandler()
+ {
+ Piwik::addAction('Tracker.makeNewVisitObject', function (&$visit) {
+ $visit = new Tracker();
+ });
+
+ Factory::make();
+ }
+}
diff --git a/tests/PHPUnit/Integration/TrackerTest.php b/tests/PHPUnit/Integration/TrackerTest.php
index bc4c99090c..273acbbc5d 100644
--- a/tests/PHPUnit/Integration/TrackerTest.php
+++ b/tests/PHPUnit/Integration/TrackerTest.php
@@ -9,184 +9,313 @@
namespace Piwik\Tests\Integration;
use Piwik\Common;
-use Piwik\Db;
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Plugin;
+use Piwik\SettingsServer;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker;
+use Piwik\Tracker\RequestSet;
+use Piwik\Tracker\Request;
+use Piwik\Translate;
+
+class TestTracker extends Tracker
+{
+ public function __construct()
+ {
+ $this->isInstalled = true;
+ }
+
+ public function setIsNotInstalled()
+ {
+ $this->isInstalled = false;
+ }
+
+ public function disconnectDatabase()
+ {
+ parent::disconnectDatabase();
+ }
+}
/**
- * @group Core
+ * @group TrackerTest
+ * @group Tracker
*/
class TrackerTest extends IntegrationTestCase
{
+ /**
+ * @var TestTracker
+ */
+ private $tracker;
+
+ /**
+ * @var Request
+ */
+ private $request;
+
public function setUp()
{
parent::setUp();
- Fixture::createWebsite('2014-02-04');
+
+ Fixture::createWebsite('2014-01-01 00:00:00');
+
+ $this->tracker = new TestTracker();
+ $this->request = $this->buildRequest(array('idsite' => 1));
}
- protected static function configureFixture($fixture)
+ public function tearDown()
{
- $fixture->createSuperUser = true;
+ $this->tracker->disconnectDatabase();
+ EventDispatcher::getInstance()->clearObservers('Tracker.makeNewVisitObject');
+ if (array_key_exists('PIWIK_TRACKER_DEBUG', $GLOBALS)) {
+ unset($GLOBALS['PIWIK_TRACKER_DEBUG']);
+ }
+ parent::tearDown();
}
- /**
- * Test the Bulk tracking API as documented in: http://developer.piwik.org/api-reference/tracking-api#bulk-tracking
- *
- * With invalid token_auth the request would still work
- */
- public function test_trackingApiWithBulkRequests_viaCurl_withWrongTokenAuth()
+ public function test_isInstalled_shouldReturnTrue_AsPiwikIsInstalled()
{
- $token_auth = '33dc3f2536d3025974cccb4b4d2d98f4';
- $this->issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed = true);
+ $this->assertTrue($this->tracker->isInstalled());
}
- public function test_trackingApiWithBulkRequests_viaCurl_withCorrectTokenAuth()
+ public function test_shouldRecordStatistics_shouldReturnTrue_IfEnabled_WhichItIsByDefault()
{
- $token_auth = Fixture::getTokenAuth();
- \Piwik\Filesystem::deleteAllCacheOnUpdate();
- $this->issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed = true);
+ $this->assertTrue($this->tracker->shouldRecordStatistics());
}
- public function test_trackingEcommerceOrder_WithHtmlEscapedText_InsertsCorrectLogs()
+ public function test_shouldRecordStatistics_shouldReturnFalse_IfEnabledButNotInstalled()
{
- // item sku, item name, item category, item price, item quantity
- // NOTE: used to test with '&#x1D306;' character, however, mysql on travis fails with this when
- // inserting this character decoded.
- $ecItems = array(array('&quot;scarysku', 'superscarymovie&quot;', 'scary &amp; movies', 12.99, 1),
- array('&gt; scary', 'but &lt; &quot;super', 'scary&quot;', 14, 15),
- array("&#x27;Foo &#xA9;", " bar ", " baz &#x2603; qux", 16, 17));
+ $this->tracker->setIsNotInstalled();
+ $this->assertFalse($this->tracker->shouldRecordStatistics());
+ }
- $urlToTest = $this->getEcommerceItemsUrl($ecItems);
+ public function test_shouldRecordStatistics_shouldReturnFalse_IfDisabledButInstalled()
+ {
+ $oldConfig = Tracker\TrackerConfig::getConfigValue('record_statistics');
+ Tracker\TrackerConfig::setConfigValue('record_statistics', 0);
- $response = $this->sendTrackingRequestByCurl($urlToTest);
- Fixture::checkResponse($response);
+ $this->assertFalse($this->tracker->shouldRecordStatistics());
- $this->assertEquals(1, $this->getCountOfConversions());
+ Tracker\TrackerConfig::setConfigValue('record_statistics', $oldConfig); // reset
+ }
+
+ public function test_loadTrackerEnvironment_shouldSetGlobalsDebugVar_WhichShouldBeDisabledByDefault()
+ {
+ $this->assertTrue(!array_key_exists('PIWIK_TRACKER_DEBUG', $GLOBALS));
+
+ Tracker::loadTrackerEnvironment();
+
+ $this->assertFalse($GLOBALS['PIWIK_TRACKER_DEBUG']);
+ }
- $conversionItems = $this->getConversionItems();
- $this->assertEquals(3, count($conversionItems));
+ public function test_loadTrackerEnvironment_shouldSetGlobalsDebugVar()
+ {
+ $this->assertTrue(!array_key_exists('PIWIK_TRACKER_DEBUG', $GLOBALS));
+
+ $oldConfig = Tracker\TrackerConfig::getConfigValue('debug');
+ Tracker\TrackerConfig::setConfigValue('debug', 1);
- $this->assertActionEquals('"scarysku', $conversionItems[0]['idaction_sku']);
- $this->assertActionEquals('superscarymovie"', $conversionItems[0]['idaction_name']);
- $this->assertActionEquals('scary & movies', $conversionItems[0]['idaction_category']);
+ Tracker::loadTrackerEnvironment();
+ $this->assertTrue($this->tracker->isDebugModeEnabled());
- $this->assertActionEquals('> scary', $conversionItems[1]['idaction_sku']);
- $this->assertActionEquals('but < "super', $conversionItems[1]['idaction_name']);
- $this->assertActionEquals('scary"', $conversionItems[1]['idaction_category']);
+ Tracker\TrackerConfig::setConfigValue('debug', $oldConfig); // reset
- $this->assertActionEquals('\'Foo ©', $conversionItems[2]['idaction_sku']);
- $this->assertActionEquals('bar', $conversionItems[2]['idaction_name']);
- $this->assertActionEquals('baz ☃ qux', $conversionItems[2]['idaction_category']);
+ $this->assertTrue($GLOBALS['PIWIK_TRACKER_DEBUG']);
}
- public function test_trackingEcommerceOrder_WithAmpersandAndQuotes_InsertsCorrectLogs()
+ public function test_loadTrackerEnvironment_shouldEnableTrackerMode()
{
- // item sku, item name, item category, item price, item quantity
- $ecItems = array(array("\"scarysku&", "superscarymovie'", 'scary <> movies', 12.99, 1));
+ $this->assertFalse(SettingsServer::isTrackerApiRequest());
- $urlToTest = $this->getEcommerceItemsUrl($ecItems);
+ Tracker::loadTrackerEnvironment();
- $response = $this->sendTrackingRequestByCurl($urlToTest);
- Fixture::checkResponse($response);
+ $this->assertTrue(SettingsServer::isTrackerApiRequest());
+ }
- $this->assertEquals(1, $this->getCountOfConversions());
+ public function test_isDatabaseConnected_shouldReturnFalse_IfNotConnected()
+ {
+ $this->tracker->disconnectDatabase();
- $conversionItems = $this->getConversionItems();
- $this->assertEquals(1, count($conversionItems));
+ $this->assertFalse($this->tracker->isDatabaseConnected());
+ }
- $this->assertActionEquals('"scarysku&', $conversionItems[0]['idaction_sku']);
- $this->assertActionEquals('superscarymovie\'', $conversionItems[0]['idaction_name']);
- $this->assertActionEquals('scary <> movies', $conversionItems[0]['idaction_category']);
+ public function test_getDatabase_shouldReturnDbInstance()
+ {
+ $db = $this->tracker->getDatabase();
+
+ $this->assertInstanceOf('Piwik\\Tracker\\Db', $db);
}
- public function test_trackingEcommerceOrder_DoesNotFail_WhenEmptyEcommerceItemsParamUsed()
+ public function test_isDatabaseConnected_shouldReturnTrue_WhenDbIsConnected()
{
- // item sku, item name, item category, item price, item quantity
- $urlToTest = $this->getEcommerceItemsUrl("");
+ $db = $this->tracker->getDatabase(); // make sure connected
+ $this->assertNotEmpty($db);
- $response = $this->sendTrackingRequestByCurl($urlToTest);
- Fixture::checkResponse($response);
+ $this->assertTrue($this->tracker->isDatabaseConnected());
+ }
+
+ public function test_disconnectDatabase_shouldDisconnectDb()
+ {
+ $this->tracker->getDatabase(); // make sure connected
+ $this->assertTrue($this->tracker->isDatabaseConnected());
+
+ $this->tracker->disconnectDatabase();
+
+ $this->assertFalse($this->tracker->isDatabaseConnected());
+ }
+
+ public function test_trackRequest_shouldNotTrackAnything_IfRequestIsEmpty()
+ {
+ $called = false;
+ Piwik::addAction('Tracker.makeNewVisitObject', function () use (&$called) {
+ $called = true;
+ });
- $this->assertEquals(1, $this->getCountOfConversions());
- $this->assertEquals(0, count($this->getConversionItems()));
+ $this->tracker->trackRequest(new Request(array()));
+
+ $this->assertFalse($called);
+ }
+
+ public function test_trackRequest_shouldTrack_IfRequestIsNotEmpty()
+ {
+ $called = false;
+ Piwik::addAction('Tracker.makeNewVisitObject', function () use (&$called) {
+ $called = true;
+ });
+
+ $this->tracker->trackRequest($this->request);
+
+ $this->assertTrue($called);
+ }
+
+ public function test_trackRequest_shouldIncreaseLoggedRequestsCounter()
+ {
+ $this->tracker->trackRequest($this->request);
+ $this->assertSame(1, $this->tracker->getCountOfLoggedRequests());
+
+ $this->tracker->trackRequest($this->request);
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ public function test_trackRequest_shouldIncreaseLoggedRequestsCounter_EvenIfRequestIsEmpty()
+ {
+ $request = $this->buildRequest(array());
+ $this->assertTrue($request->isEmptyRequest());
+
+ $this->tracker->trackRequest($request);
+ $this->assertSame(1, $this->tracker->getCountOfLoggedRequests());
+
+ $this->tracker->trackRequest($request);
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ public function test_trackRequest_shouldActuallyTrack()
+ {
+ $request = $this->buildRequest(array('idsite' => 1, 'url' => 'http://www.example.com', 'action_name' => 'test', 'rec' => 1));
+ $this->tracker->trackRequest($request);
+
+ $this->assertActionEquals('test', 1);
+ $this->assertActionEquals('example.com', 2);
+ }
+
+ public function test_main_shouldReturnEmptyPiwikResponse_IfNoRequestsAreGiven()
+ {
+ $requestSet = $this->getEmptyRequestSet();
+ $requestSet->setRequests(array());
+
+ $response = $this->tracker->main($this->getDefaultHandler(), $requestSet);
+
+ $expected = "<a href='/'>Piwik</a> is a free/libre web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
+ $this->assertEquals($expected, $response);
}
- public function test_trackingEcommerceOrder_DoesNotFail_WhenNonArrayUsedWithEcommerceItemsParam()
+ public function test_main_shouldReturnApiResponse_IfRequestsAreGiven()
{
- // item sku, item name, item category, item price, item quantity
- $urlToTest = $this->getEcommerceItemsUrl("45");
+ $response = $this->tracker->main($this->getDefaultHandler(), $this->getRequestSetWithRequests());
- $response = $this->sendTrackingRequestByCurl($urlToTest);
Fixture::checkResponse($response);
+ }
+
+ public function test_main_shouldReturnNotReturnAnyApiResponse_IfImageIsDisabled()
+ {
+ $_GET['send_image'] = '0';
- $this->assertEquals(0, $this->getCountOfConversions());
- $this->assertEquals(0, count($this->getConversionItems()));
+ $response = $this->tracker->main($this->getDefaultHandler(), $this->getRequestSetWithRequests());
+
+ unset($_GET['send_image']);
+
+ $this->assertEquals('', $response);
}
- protected function issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed)
+ public function test_main_shouldActuallyTrackNumberOfTrackedRequests()
{
- $piwikHost = Fixture::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php';
+ $this->assertSame(0, $this->tracker->getCountOfLoggedRequests());
- $command = 'curl -s -X POST -d \'{"requests":["?idsite=1&url=http://example.org&action_name=Test bulk log Pageview&rec=1","?idsite=1&url=http://example.net/test.htm&action_name=Another bulk page view&rec=1"],"token_auth":"' . $token_auth . '"}\' ' . $piwikHost;
+ $this->tracker->main($this->getDefaultHandler(), $this->getRequestSetWithRequests());
- exec($command, $output, $result);
- if ($result !== 0) {
- throw new \Exception("tracking bulk failed: " . implode("\n", $output) . "\n\ncommand used: $command");
- }
- $output = implode("", $output);
- $this->assertStringStartsWith('{"status":', $output);
-
- if($expectTrackingToSucceed) {
- $this->assertNotContains('error', $output);
- $this->assertContains('success', $output);
- } else {
- $this->assertContains('error', $output);
- $this->assertNotContains('success', $output);
- }
+ $this->assertSame(2, $this->tracker->getCountOfLoggedRequests());
}
- private function sendTrackingRequestByCurl($url)
+ public function test_main_shouldNotTrackAnythingButStillReturnApiResponse_IfNotInstalledOrShouldNotRecordStats()
{
- if (!function_exists('curl_init')) {
- $this->markTestSkipped('Curl is not installed');
- }
+ $this->tracker->setIsNotInstalled();
+ $response = $this->tracker->main($this->getDefaultHandler(), $this->getRequestSetWithRequests());
+
+ Fixture::checkResponse($response);
+ $this->assertSame(0, $this->tracker->getCountOfLoggedRequests());
+ }
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, Fixture::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php' . $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ public function test_main_shouldReadValuesFromGETandPOSTifNoRequestSet()
+ {
+ $_GET = array('idsite' => '1');
+ $_POST = array('url' => 'http://localhost/post');
- $response = curl_exec($ch);
- $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
- $response = substr($response, $headerSize);
+ $requestSet = $this->getEmptyRequestSet();
+ $response = $this->tracker->main($this->getDefaultHandler(), $requestSet);
- curl_close($ch);
+ $_GET = array();
+ $_POST = array();
- return $response;
+ Fixture::checkResponse($response);
+ $this->assertSame(1, $this->tracker->getCountOfLoggedRequests());
+
+ $identifiedRequests = $requestSet->getRequests();
+ $this->assertCount(1, $identifiedRequests);
+ $this->assertEquals(array('idsite' => '1', 'url' => 'http://localhost/post'),
+ $identifiedRequests[0]->getParams());
}
- private function assertActionEquals($expected, $idaction)
+ private function getDefaultHandler()
{
- $actionName = Db::fetchOne("SELECT name FROM " . Common::prefixTable('log_action') . " WHERE idaction = ?", array($idaction));
- $this->assertEquals($expected, $actionName);
+ return new Tracker\Handler();
}
- private function getCountOfConversions()
+ private function getEmptyRequestSet()
{
- return Db::fetchOne("SELECT COUNT(*) FROM " . Common::prefixTable('log_conversion'));
+ return new RequestSet();
}
- private function getConversionItems()
+ private function getRequestSetWithRequests()
{
- return Db::fetchAll("SELECT * FROM " . Common::prefixTable('log_conversion_item'));
+ $requestSet = $this->getEmptyRequestSet();
+ $requestSet->setRequests(array(
+ $this->buildRequest(array('idsite' => '1', 'url' => 'http://localhost')),
+ $this->buildRequest(array('idsite' => '1', 'url' => 'http://localhost/test'))
+ ));
+
+ return $requestSet;
}
- private function getEcommerceItemsUrl($ecItems, $doJsonEncode = true)
+ private function assertActionEquals($expected, $idaction)
{
- $ecItemsStr = $doJsonEncode ? json_encode($ecItems) : $ecItems;
- return "?idsite=1&idgoal=0&rec=1&url=" . urlencode('http://quellehorreur.com/movies') . "&ec_items="
- . urlencode($ecItemsStr) . '&ec_id=myspecial-id-1234&revenue=16.99&ec_st=12.99&ec_tx=0&ec_sh=3';
+ $actionName = Tracker::getDatabase()->fetchOne("SELECT name FROM " . Common::prefixTable('log_action') . " WHERE idaction = ?", array($idaction));
+ $this->assertEquals($expected, $actionName);
}
+
+ private function buildRequest($params)
+ {
+ return new Request($params);
+ }
+
} \ No newline at end of file
diff --git a/tests/PHPUnit/System/TrackerResponseTest.php b/tests/PHPUnit/System/TrackerResponseTest.php
new file mode 100755
index 0000000000..7bd0f7aef7
--- /dev/null
+++ b/tests/PHPUnit/System/TrackerResponseTest.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Tests\System;
+
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group TrackerTest
+ * @group Plugins
+ */
+class TrackerResponseTest extends SystemTestCase
+{
+ public static $fixture = null;
+
+ /**
+ * @var \PiwikTracker
+ */
+ private $tracker;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $idSite = 1;
+ $dateTime = '2014-01-01 00:00:01';
+
+ if (!Fixture::siteCreated($idSite)) {
+ Fixture::createWebsite($dateTime);
+ }
+
+ $this->tracker = Fixture::getTracker($idSite, $dateTime, $defaultInit = true);
+ }
+
+ public function test_response_ShouldContainAnImage()
+ {
+ $response = $this->tracker->doTrackPageView('Test');
+
+ Fixture::checkResponse($response);
+ $this->assertNotEmpty($response);
+ }
+
+ public function test_response_ShouldBeEmpty_IfImageIsDisabled()
+ {
+ $this->tracker->disableSendImageResponse();
+
+ $response = $this->tracker->doTrackPageView('Test');
+
+ $this->assertSame('', $response);
+ }
+
+ public function test_response_ShouldSend200ResponseCode_IfImageIsEnabled()
+ {
+ $url = $this->tracker->getUrlTrackPageView('Test');
+
+ $this->assertResponseCode(200, $url);
+ }
+
+ public function test_response_ShouldSend204ResponseCode_IfImageIsDisabled()
+ {
+ $url = $this->tracker->getUrlTrackPageView('Test');
+ $url .= '&send_image=0';
+
+ $this->assertResponseCode(204, $url);
+ }
+
+ public function test_response_ShouldSend400ResponseCode_IfSiteIdIsInvalid()
+ {
+ $url = $this->tracker->getUrlTrackPageView('Test');
+ $url .= '&idsite=100';
+
+ $this->assertResponseCode(400, $url);
+ }
+
+ public function test_response_ShouldSend400ResponseCode_IfSiteIdIsZero()
+ {
+ $url = $this->tracker->getUrlTrackPageView('Test');
+ $url .= '&idsite=0';
+
+ $this->assertResponseCode(400, $url);
+ }
+
+ public function test_response_ShouldSend400ResponseCode_IfInvalidRequestParameterIsGiven()
+ {
+ $url = $this->tracker->getUrlTrackPageView('Test');
+ $url .= '&cid=' . str_pad('1', 16, '1');
+
+ $this->assertResponseCode(200, $url);
+ $this->assertResponseCode(400, $url . '1'); // has to be 16 char, but is 17 now
+ }
+
+ public function test_response_ShouldReturnPiwikMessage_InCaseOfEmptyRequest()
+ {
+ $url = Fixture::getTrackerUrl();
+ $response = file_get_contents($url);
+
+ $expected = "<a href='/'>Piwik</a> is a free/libre web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
+ $this->assertEquals($expected, $response);
+ }
+
+}
diff --git a/tests/PHPUnit/System/TrackerTest.php b/tests/PHPUnit/System/TrackerTest.php
index 84f5db3aac..65b16f6fea 100755..100644
--- a/tests/PHPUnit/System/TrackerTest.php
+++ b/tests/PHPUnit/System/TrackerTest.php
@@ -2,96 +2,192 @@
/**
* Piwik - free/libre analytics platform
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
+
namespace Piwik\Tests\System;
+use Piwik\Common;
+use Piwik\Db;
use Piwik\Tests\Framework\Fixture;
-use Piwik\Tests\Framework\TestCase\SystemTestCase;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker;
/**
- * @group TrackerTest
- * @group Plugins
+ * @group Core
+ * @group Tracker
*/
-class TrackerTest extends SystemTestCase
+class TrackerTest extends IntegrationTestCase
{
- public static $fixture = null;
+ public function setUp()
+ {
+ parent::setUp();
+ Fixture::createWebsite('2014-02-04');
+ }
+
+ protected static function configureFixture($fixture)
+ {
+ $fixture->createSuperUser = true;
+ }
/**
- * @var \PiwikTracker
+ * Test the Bulk tracking API as documented in: http://developer.piwik.org/api-reference/tracking-api#bulk-tracking
+ *
+ * With invalid token_auth the request would still work
*/
- private $tracker;
+ public function test_trackingApiWithBulkRequests_viaCurl_withWrongTokenAuth()
+ {
+ $token_auth = '33dc3f2536d3025974cccb4b4d2d98f4';
+ $this->issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed = true);
+ }
- public function setUp()
+ public function test_trackingApiWithBulkRequests_viaCurl_withCorrectTokenAuth()
{
- parent::setUp();
+ $token_auth = Fixture::getTokenAuth();
+ \Piwik\Filesystem::deleteAllCacheOnUpdate();
+ $this->issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed = true);
+ }
- $idSite = 1;
- $dateTime = '2014-01-01 00:00:01';
+ public function test_trackingEcommerceOrder_WithHtmlEscapedText_InsertsCorrectLogs()
+ {
+ // item sku, item name, item category, item price, item quantity
+ // NOTE: used to test with '&#x1D306;' character, however, mysql on travis fails with this when
+ // inserting this character decoded.
+ $ecItems = array(array('&quot;scarysku', 'superscarymovie&quot;', 'scary &amp; movies', 12.99, 1),
+ array('&gt; scary', 'but &lt; &quot;super', 'scary&quot;', 14, 15),
+ array("&#x27;Foo &#xA9;", " bar ", " baz &#x2603; qux", 16, 17));
- if (!Fixture::siteCreated($idSite)) {
- Fixture::createWebsite($dateTime);
- }
+ $urlToTest = $this->getEcommerceItemsUrl($ecItems);
+
+ $response = $this->sendTrackingRequestByCurl($urlToTest);
+ Fixture::checkResponse($response);
+
+ $this->assertEquals(1, $this->getCountOfConversions());
+
+ $conversionItems = $this->getConversionItems();
+ $this->assertEquals(3, count($conversionItems));
+
+ $this->assertActionEquals('"scarysku', $conversionItems[0]['idaction_sku']);
+ $this->assertActionEquals('superscarymovie"', $conversionItems[0]['idaction_name']);
+ $this->assertActionEquals('scary & movies', $conversionItems[0]['idaction_category']);
- $this->tracker = Fixture::getTracker($idSite, $dateTime, $defaultInit = true);
+ $this->assertActionEquals('> scary', $conversionItems[1]['idaction_sku']);
+ $this->assertActionEquals('but < "super', $conversionItems[1]['idaction_name']);
+ $this->assertActionEquals('scary"', $conversionItems[1]['idaction_category']);
+
+ $this->assertActionEquals('\'Foo ©', $conversionItems[2]['idaction_sku']);
+ $this->assertActionEquals('bar', $conversionItems[2]['idaction_name']);
+ $this->assertActionEquals('baz ☃ qux', $conversionItems[2]['idaction_category']);
}
- public function test_response_ShouldContainAnImage()
+ public function test_trackingEcommerceOrder_WithAmpersandAndQuotes_InsertsCorrectLogs()
{
- $response = $this->tracker->doTrackPageView('Test');
+ // item sku, item name, item category, item price, item quantity
+ $ecItems = array(array("\"scarysku&", "superscarymovie'", 'scary <> movies', 12.99, 1));
+
+ $urlToTest = $this->getEcommerceItemsUrl($ecItems);
+ $response = $this->sendTrackingRequestByCurl($urlToTest);
Fixture::checkResponse($response);
- $this->assertNotEmpty($response);
+
+ $this->assertEquals(1, $this->getCountOfConversions());
+
+ $conversionItems = $this->getConversionItems();
+ $this->assertEquals(1, count($conversionItems));
+
+ $this->assertActionEquals('"scarysku&', $conversionItems[0]['idaction_sku']);
+ $this->assertActionEquals('superscarymovie\'', $conversionItems[0]['idaction_name']);
+ $this->assertActionEquals('scary <> movies', $conversionItems[0]['idaction_category']);
}
- public function test_response_ShouldBeEmpty_IfImageIsDisabled()
+ public function test_trackingEcommerceOrder_DoesNotFail_WhenEmptyEcommerceItemsParamUsed()
{
- $this->tracker->disableSendImageResponse();
+ // item sku, item name, item category, item price, item quantity
+ $urlToTest = $this->getEcommerceItemsUrl("");
- $response = $this->tracker->doTrackPageView('Test');
+ $response = $this->sendTrackingRequestByCurl($urlToTest);
+ Fixture::checkResponse($response);
- $this->assertSame('', $response);
+ $this->assertEquals(1, $this->getCountOfConversions());
+ $this->assertEquals(0, count($this->getConversionItems()));
}
- public function test_response_ShouldSend200ResponseCode_IfImageIsEnabled()
+ public function test_trackingEcommerceOrder_DoesNotFail_WhenNonArrayUsedWithEcommerceItemsParam()
{
- $url = $this->tracker->getUrlTrackPageView('Test');
+ // item sku, item name, item category, item price, item quantity
+ $urlToTest = $this->getEcommerceItemsUrl("45");
+
+ $response = $this->sendTrackingRequestByCurl($urlToTest);
+ Fixture::checkResponse($response);
- $this->assertResponseCode(200, $url);
+ $this->assertEquals(0, $this->getCountOfConversions());
+ $this->assertEquals(0, count($this->getConversionItems()));
}
- public function test_response_ShouldSend204ResponseCode_IfImageIsDisabled()
+ protected function issueBulkTrackingRequest($token_auth, $expectTrackingToSucceed)
{
- $url = $this->tracker->getUrlTrackPageView('Test');
- $url .= '&send_image=0';
+ $piwikHost = Fixture::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php';
- $this->assertResponseCode(204, $url);
+ $command = 'curl -s -X POST -d \'{"requests":["?idsite=1&url=http://example.org&action_name=Test bulk log Pageview&rec=1","?idsite=1&url=http://example.net/test.htm&action_name=Another bulk page view&rec=1"],"token_auth":"' . $token_auth . '"}\' ' . $piwikHost;
+
+ exec($command, $output, $result);
+ if ($result !== 0) {
+ throw new \Exception("tracking bulk failed: " . implode("\n", $output) . "\n\ncommand used: $command");
+ }
+ $output = implode("", $output);
+ $this->assertStringStartsWith('{"status":', $output);
+
+ if($expectTrackingToSucceed) {
+ $this->assertNotContains('error', $output);
+ $this->assertContains('success', $output);
+ } else {
+ $this->assertContains('error', $output);
+ $this->assertNotContains('success', $output);
+ }
}
- public function test_response_ShouldSend400ResponseCode_IfSiteIdIsInvalid()
+ private function sendTrackingRequestByCurl($url)
{
- $url = $this->tracker->getUrlTrackPageView('Test');
- $url .= '&idsite=100';
+ if (!function_exists('curl_init')) {
+ $this->markTestSkipped('Curl is not installed');
+ }
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, Fixture::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php' . $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+
+ $response = curl_exec($ch);
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $response = substr($response, $headerSize);
+
+ curl_close($ch);
- $this->assertResponseCode(400, $url);
+ return $response;
}
- public function test_response_ShouldSend400ResponseCode_IfSiteIdIsZero()
+ private function assertActionEquals($expected, $idaction)
{
- $url = $this->tracker->getUrlTrackPageView('Test');
- $url .= '&idsite=0';
-
- $this->assertResponseCode(400, $url);
+ $actionName = Db::fetchOne("SELECT name FROM " . Common::prefixTable('log_action') . " WHERE idaction = ?", array($idaction));
+ $this->assertEquals($expected, $actionName);
}
- public function test_response_ShouldSend400ResponseCode_IfInvalidRequestParameterIsGiven()
+ private function getCountOfConversions()
{
- $url = $this->tracker->getUrlTrackPageView('Test');
- $url .= '&cid=' . str_pad('1', 16, '1');
+ return Db::fetchOne("SELECT COUNT(*) FROM " . Common::prefixTable('log_conversion'));
+ }
- $this->assertResponseCode(200, $url);
- $this->assertResponseCode(400, $url . '1'); // has to be 16 char, but is 17 now
+ private function getConversionItems()
+ {
+ return Db::fetchAll("SELECT * FROM " . Common::prefixTable('log_conversion_item'));
}
-}
+ private function getEcommerceItemsUrl($ecItems, $doJsonEncode = true)
+ {
+ $ecItemsStr = $doJsonEncode ? json_encode($ecItems) : $ecItems;
+ return "?idsite=1&idgoal=0&rec=1&url=" . urlencode('http://quellehorreur.com/movies') . "&ec_items="
+ . urlencode($ecItemsStr) . '&ec_id=myspecial-id-1234&revenue=16.99&ec_st=12.99&ec_tx=0&ec_sh=3';
+ }
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/Tracker/RequestSetTest.php b/tests/PHPUnit/Unit/Tracker/RequestSetTest.php
new file mode 100644
index 0000000000..4edb3f2310
--- /dev/null
+++ b/tests/PHPUnit/Unit/Tracker/RequestSetTest.php
@@ -0,0 +1,470 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Unit\Tracker;
+
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Tracker\Request;
+use Piwik\Tracker\RequestSet;
+
+class TestRequestSet extends RequestSet {
+
+ public function getRedirectUrl()
+ {
+ return parent::getRedirectUrl();
+ }
+
+ public function hasRedirectUrl()
+ {
+ return parent::hasRedirectUrl();
+ }
+
+ public function getAllSiteIdsWithinRequest()
+ {
+ return parent::getAllSiteIdsWithinRequest();
+ }
+
+ public function getEnvironment()
+ {
+ return parent::getEnvironment();
+ }
+}
+
+/**
+ * @group RequestSetTest
+ * @group RequestSet
+ * @group Tracker
+ */
+class RequestSetTest extends UnitTestCase
+{
+ /**
+ * @var TestRequestSet
+ */
+ private $requestSet;
+ private $time;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->requestSet = $this->createRequestSet();
+ $this->time = time();
+ }
+
+ private function createRequestSet()
+ {
+ return new TestRequestSet();
+ }
+
+ public function test_internalBuildRequest_ShoulBuildOneRequest()
+ {
+ $request = new Request(array('idsite' => '2'));
+ $request->setCurrentTimestamp($this->time);
+
+ $this->assertEquals($request, $this->buildRequest(2));
+ }
+
+ public function test_internalBuildRequests_ShoulBuildASetOfRequests()
+ {
+ $this->assertEquals(array(), $this->buildRequests(0));
+
+ $this->assertEquals(array($this->buildRequest(1)), $this->buildRequests(1));
+
+ $this->assertEquals(array(
+ $this->buildRequest(1),
+ $this->buildRequest(2),
+ $this->buildRequest(3)
+ ), $this->buildRequests(3));
+ }
+
+ public function test_getRequests_shouldReturnEmptyArray_IfThereAreNoRequestsInitializedYet()
+ {
+ $this->assertEquals(array(), $this->requestSet->getRequests());
+ }
+
+ public function test_setRequests_shouldNotFail_IfEmptyArrayGiven()
+ {
+ $this->requestSet->setRequests(array());
+ $this->assertEquals(array(), $this->requestSet->getRequests());
+ }
+
+ public function test_setRequests_shouldSetAndOverwriteRequests()
+ {
+ $this->requestSet->setRequests($this->buildRequests(3));
+ $this->assertEquals($this->buildRequests(3), $this->requestSet->getRequests());
+
+ // overwrite
+ $this->requestSet->setRequests($this->buildRequests(5));
+ $this->assertEquals($this->buildRequests(5), $this->requestSet->getRequests());
+
+ // overwrite
+ $this->requestSet->setRequests($this->buildRequests(1));
+ $this->assertEquals($this->buildRequests(1), $this->requestSet->getRequests());
+
+ // clear
+ $this->requestSet->setRequests(array());
+ $this->assertEquals(array(), $this->requestSet->getRequests());
+ }
+
+ public function test_setRequests_shouldConvertNonRequestInstancesToARequestInstance()
+ {
+ $requests = array(
+ $this->buildRequest(5),
+ array('idsite' => 9),
+ $this->buildRequest(2),
+ array('idsite' => 3),
+ $this->buildRequest(6)
+ );
+
+ $this->requestSet->setRequests($requests);
+
+ $setRequests = $this->requestSet->getRequests();
+ $this->assertEquals($this->buildRequest(5), $setRequests[0]);
+ $this->assertEquals($this->buildRequest(2), $setRequests[2]);
+ $this->assertEquals($this->buildRequest(6), $setRequests[4]);
+
+ $this->assertTrue($setRequests[1] instanceof Request);
+ $this->assertEquals(array('idsite' => 9), $setRequests[1]->getParams());
+
+ $this->assertTrue($setRequests[3] instanceof Request);
+ $this->assertEquals(array('idsite' => 3), $setRequests[3]->getParams());
+
+ $this->assertCount(5, $setRequests);
+ }
+
+ public function test_setRequests_shouldIgnoreEmptyRequestsButNotArrays()
+ {
+ $requests = array(
+ $this->buildRequest(5),
+ null,
+ $this->buildRequest(2),
+ 0,
+ $this->buildRequest(6),
+ array()
+ );
+
+ $this->requestSet->setRequests($requests);
+
+ $expected = array($this->buildRequest(5), $this->buildRequest(2), $this->buildRequest(6), new Request(array()));
+ $this->assertEquals($expected, $this->requestSet->getRequests());
+ }
+
+ public function test_getNumberOfRequests_shouldReturnZeroIfNothingSet()
+ {
+ $this->assertEquals(0, $this->requestSet->getNumberOfRequests());
+ }
+
+ public function test_getNumberOfRequests_shouldReturnNumberOfRequests()
+ {
+ $this->requestSet->setRequests($this->buildRequests(3));
+ $this->assertSame(3, $this->requestSet->getNumberOfRequests());
+
+ $this->requestSet->setRequests($this->buildRequests(5));
+ $this->assertSame(5, $this->requestSet->getNumberOfRequests());
+
+ $this->requestSet->setRequests($this->buildRequests(1));
+ $this->assertSame(1, $this->requestSet->getNumberOfRequests());
+ }
+
+ public function test_hasRequests_shouldReturnFalse_IfNotInitializedYetOrNoDataSet()
+ {
+ $this->assertFalse($this->requestSet->hasRequests());
+
+ $this->requestSet->setRequests(array());
+ $this->assertFalse($this->requestSet->hasRequests());
+ }
+
+ public function test_hasRequests_shouldReturnTrue_IfAtLeastOneRequestIsSet()
+ {
+ $this->assertFalse($this->requestSet->hasRequests());
+
+ $this->requestSet->setRequests($this->buildRequests(1));
+ $this->assertTrue($this->requestSet->hasRequests());
+
+ $this->requestSet->setRequests($this->buildRequests(5));
+ $this->assertTrue($this->requestSet->hasRequests());
+
+ $this->requestSet->setRequests(array(null, 0));
+ $this->assertFalse($this->requestSet->hasRequests());
+ }
+
+ public function test_getTokenAuth_ShouldReturnFalse_IfNoTokenIsSetAndNoRequestParam()
+ {
+ $this->assertFalse($this->requestSet->getTokenAuth());
+ }
+
+ public function test_getTokenAuth_setTokenAuth_shouldOverwriteTheToken()
+ {
+ $this->requestSet->setTokenAuth('MKyKTokenTestIn');
+
+ $this->assertEquals('MKyKTokenTestIn', $this->requestSet->getTokenAuth());
+ }
+
+ public function test_getTokenAuth_setTokenAuth_shouldBePossibleToClearASetToken()
+ {
+ $this->requestSet->setTokenAuth('MKyKTokenTestIn');
+ $this->assertNotEmpty($this->requestSet->getTokenAuth());
+
+ $this->requestSet->setTokenAuth(null);
+ $this->assertFalse($this->requestSet->getTokenAuth()); // does now fallback to get param
+ }
+
+ public function test_getTokenAuth_ShouldFallbackToRequestParam_IfNoTokenSet()
+ {
+ $_GET['token_auth'] = 'MyTokenAuthTest';
+
+ $this->assertSame('MyTokenAuthTest', $this->requestSet->getTokenAuth());
+
+ unset($_GET['token_auth']);
+ }
+
+ public function test_getEnvironment_shouldReturnCurrentServerVar()
+ {
+ $this->assertEquals(array(
+ 'server' => $_SERVER
+ ), $this->requestSet->getEnvironment());
+ }
+
+ public function test_intertnalFakeEnvironment_shouldActuallyReturnAValue()
+ {
+ $myEnv = $this->getFakeEnvironment();
+ $this->assertInternalType('array', $myEnv);
+ $this->assertNotEmpty($myEnv);
+ }
+
+ public function test_setEnvironment_shouldOverwriteAnEnvironment()
+ {
+ $this->requestSet->setEnvironment($this->getFakeEnvironment());
+
+ $this->assertEquals($this->getFakeEnvironment(), $this->requestSet->getEnvironment());
+ }
+
+ public function test_restoreEnvironment_shouldRestoreAPreviouslySetEnvironment()
+ {
+ $serverBackup = $_SERVER;
+
+ $this->requestSet->setEnvironment($this->getFakeEnvironment());
+ $this->requestSet->restoreEnvironment();
+
+ $this->assertEquals(array('mytest' => 'test'), $_SERVER);
+
+ $_SERVER = $serverBackup;
+ }
+
+ public function test_rememberEnvironment_shouldSaveCurrentEnvironment()
+ {
+ $expected = $_SERVER;
+
+ $this->requestSet->rememberEnvironment();
+
+ $this->assertEquals(array('server' => $expected), $this->requestSet->getEnvironment());
+
+ // should not change anything
+ $this->requestSet->restoreEnvironment();
+ $this->assertEquals($expected, $_SERVER);
+ }
+
+ public function test_getState_shouldReturnCurrentStateOfRequestSet()
+ {
+ $this->requestSet->setRequests($this->buildRequests(2));
+ $this->requestSet->setTokenAuth('mytoken');
+
+ $state = $this->requestSet->getState();
+
+ $expectedKeys = array('requests', 'env', 'tokenAuth', 'time');
+ $this->assertEquals($expectedKeys, array_keys($state));
+
+ $expectedRequests = array(
+ array('idsite' => 1),
+ array('idsite' => 2)
+ );
+
+ $this->assertEquals($expectedRequests, $state['requests']);
+ $this->assertEquals('mytoken', $state['tokenAuth']);
+ $this->assertTrue(is_numeric($state['time']));
+ $this->assertEquals(array('server' => $_SERVER), $state['env']);
+ }
+
+ public function test_getState_shouldRememberAnyAddedParamsFromRequestConstructor()
+ {
+ $_SERVER['HTTP_REFERER'] = 'test';
+
+ $requests = $this->buildRequests(1);
+
+ $this->requestSet->setRequests($requests);
+ $this->requestSet->setTokenAuth('mytoken');
+
+ $state = $this->requestSet->getState();
+
+ unset($_SERVER['HTTP_REFERER']);
+
+ $expectedRequests = array(
+ array('idsite' => 1)
+ );
+
+ $this->assertEquals($expectedRequests, $state['requests']);
+
+ // the actual params include an added urlref param which should NOT be in the state. otherwise we cannot detect empty requests etc
+ $this->assertEquals(array('idsite' => 1, 'url' => 'test'), $requests[0]->getParams());
+ }
+
+ public function test_restoreState_shouldRestoreRequestSet()
+ {
+ $serverBackup = $_SERVER;
+
+ $state = array(
+ 'requests' => array(array('idsite' => 1), array('idsite' => 2), array('idsite' => 3)),
+ 'time' => $this->time,
+ 'tokenAuth' => 'tokenAuthRestored',
+ 'env' => $this->getFakeEnvironment()
+ );
+
+ $this->requestSet->restoreState($state);
+
+ $this->assertEquals($this->getFakeEnvironment(), $this->requestSet->getEnvironment());
+ $this->assertEquals('tokenAuthRestored', $this->requestSet->getTokenAuth());
+
+ $expectedRequests = array(
+ new Request(array('idsite' => 1), 'tokenAuthRestored'),
+ new Request(array('idsite' => 2), 'tokenAuthRestored'),
+ new Request(array('idsite' => 3), 'tokenAuthRestored'),
+ );
+ $expectedRequests[0]->setCurrentTimestamp($this->time);
+ $expectedRequests[1]->setCurrentTimestamp($this->time);
+ $expectedRequests[2]->setCurrentTimestamp($this->time);
+
+ $requests = $this->requestSet->getRequests();
+ $this->assertEquals($expectedRequests, $requests);
+
+ // verify again just to be sure (only first one)
+ $this->assertEquals('tokenAuthRestored', $requests[0]->getTokenAuth());
+ $this->assertEquals($this->time, $requests[0]->getCurrentTimestamp());
+
+ // should not restoreEnvironment, only set the environment
+ $this->assertSame($serverBackup, $_SERVER);
+ }
+
+ public function test_restoreState_ifRequestWasEmpty_ShouldBeStillEmptyWhenRestored()
+ {
+ $_SERVER['HTTP_REFERER'] = 'test';
+
+ $this->requestSet->setRequests(array(new Request(array())));
+ $state = $this->requestSet->getState();
+
+ $requestSet = $this->createRequestSet();
+ $requestSet->restoreState($state);
+
+ unset($_SERVER['HTTP_REFERER']);
+
+ $requests = $requestSet->getRequests();
+ $this->assertTrue($requests[0]->isEmptyRequest());
+ }
+
+ public function test_restoreState_shouldResetTheStoredEnvironmentBeforeRestoringRequests()
+ {
+ $this->requestSet->setRequests(array(new Request(array())));
+ $state = $this->requestSet->getState();
+ $state['env']['server']['HTTP_REFERER'] = 'mytesturl';
+
+ $requestSet = $this->createRequestSet();
+ $requestSet->restoreState($state);
+
+ $requests = $requestSet->getRequests();
+ $this->assertTrue($requests[0]->isEmptyRequest());
+ $this->assertEquals(array('url' => 'mytesturl'), $requests[0]->getParams());
+ $this->assertTrue(empty($_SERVER['HTTP_REFERER']));
+ }
+
+ public function test_getRedirectUrl_ShouldReturnEmptyString_IfNoUrlSet()
+ {
+ $this->assertEquals('', $this->requestSet->getRedirectUrl());
+ }
+
+ public function test_getRedirectUrl_ShouldReturnTrue_IfAUrlSetIsSetViaGET()
+ {
+ $_GET['redirecturl'] = 'whatsoever';
+ $this->assertEquals('whatsoever', $this->requestSet->getRedirectUrl());
+ unset($_GET['redirecturl']);
+ }
+
+ public function test_getRedirectUrl_ShouldReturnTrue_IfAUrlSetIsSetViaPOST()
+ {
+ $_POST['redirecturl'] = 'whatsoeverPOST';
+ $this->assertEquals('whatsoeverPOST', $this->requestSet->getRedirectUrl());
+ unset($_POST['redirecturl']);
+ }
+
+ public function test_hasRedirectUrl_ShouldReturnFalse_IfNoUrlSet()
+ {
+ $this->assertFalse($this->requestSet->hasRedirectUrl());
+ }
+
+ public function test_hasRedirectUrl_ShouldReturnTrue_IfAUrlSetIsSetViaGET()
+ {
+ $_GET['redirecturl'] = 'whatsoever';
+ $this->assertTrue($this->requestSet->hasRedirectUrl());
+ unset($_GET['redirecturl']);
+ }
+
+ public function test_hasRedirectUrl_ShouldReturnTrue_IfAUrlSetIsSetViaPOST()
+ {
+ $_POST['redirecturl'] = 'whatsoever';
+ $this->assertTrue($this->requestSet->hasRedirectUrl());
+ unset($_POST['redirecturl']);
+ }
+
+ public function test_getAllSiteIdsWithinRequest_ShouldReturnEmptyArray_IfNoRequestsSet()
+ {
+ $this->assertEquals(array(), $this->requestSet->getAllSiteIdsWithinRequest());
+ }
+
+ public function test_getAllSiteIdsWithinRequest_ShouldReturnTheSiteIds_FromRequests()
+ {
+ $this->requestSet->setRequests($this->buildRequests(3));
+
+ $this->assertEquals(array(1, 2, 3), $this->requestSet->getAllSiteIdsWithinRequest());
+ }
+
+ public function test_getAllSiteIdsWithinRequest_ShouldReturnUniqueSiteIds_Unordered()
+ {
+ $this->requestSet->setRequests(array(
+ $this->buildRequest(1),
+ $this->buildRequest(5),
+ $this->buildRequest(1),
+ $this->buildRequest(2),
+ $this->buildRequest(2),
+ $this->buildRequest(9),
+ ));
+
+ $this->assertEquals(array(1, 5, 2, 9), $this->requestSet->getAllSiteIdsWithinRequest());
+ }
+
+ private function buildRequests($numRequests)
+ {
+ $requests = array();
+ for ($index = 1; $index <= $numRequests; $index++) {
+ $requests[] = $this->buildRequest($index);
+ }
+ return $requests;
+ }
+
+ private function buildRequest($idsite)
+ {
+ $request = new Request(array('idsite' => ('' . $idsite)));
+ $request->setCurrentTimestamp($this->time);
+
+ return $request;
+ }
+
+ private function getFakeEnvironment()
+ {
+ return array('server' => array('mytest' => 'test'));
+ }
+
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/Tracker/RequestTest.php b/tests/PHPUnit/Unit/Tracker/RequestTest.php
new file mode 100644
index 0000000000..e3ff037638
--- /dev/null
+++ b/tests/PHPUnit/Unit/Tracker/RequestTest.php
@@ -0,0 +1,595 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Unit\Tracker;
+
+use Piwik\Cookie;
+use Piwik\Network\IPUtils;
+use Piwik\Piwik;
+use Piwik\Plugins\CustomVariables\CustomVariables;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Tracker\Request;
+use Piwik\Tracker\TrackerConfig;
+
+class TestRequest extends Request {
+
+ public function getCookieName()
+ {
+ return parent::getCookieName();
+ }
+
+ public function getCookieExpire()
+ {
+ return parent::getCookieExpire();
+ }
+
+ public function getCookiePath()
+ {
+ return parent::getCookiePath();
+ }
+
+ public function makeThirdPartyCookie()
+ {
+ return parent::makeThirdPartyCookie();
+ }
+
+ public function setIsAuthenticated()
+ {
+ $this->isAuthenticated = true;
+ }
+
+}
+
+/**
+ * @group RequestSetTest
+ * @group RequestSet
+ * @group Tracker
+ */
+class RequestTest extends UnitTestCase
+{
+ /**
+ * @var TestRequest
+ */
+ private $request;
+ private $time;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->time = 1416795617;
+ $this->request = $this->buildRequest(array('idsite' => '1'));
+ }
+
+ public function test_getCurrentTimestamp_ShouldReturnTheSetTimestamp_IfNoCustomValueGiven()
+ {
+ $this->assertSame($this->time, $this->request->getCurrentTimestamp());
+ }
+
+ public function test_getCurrentTimestamp_ShouldReturnTheCurrentTimestamp_IfTimestampIsInvalid()
+ {
+ $request = $this->buildRequest(array('cdt' => '' . 5));
+ $request->setIsAuthenticated();
+ $this->assertSame($this->time, $request->getCurrentTimestamp());
+ }
+
+ public function test_cdt_ShouldReturnTheCurrentTimestamp_IfNotAuthenticatedAndTimestampIsNotRecent()
+ {
+ $request = $this->buildRequest(array('cdt' => '' . $this->time - 28800));
+ $this->assertSame($this->time, $request->getCurrentTimestamp());
+ }
+
+ public function test_cdt_ShouldReturnTheCustomTimestamp_IfNotAuthenticatedButTimestampIsRecent()
+ {
+ $request = $this->buildRequest(array('cdt' => '' . ($this->time - 5)));
+
+ $this->assertSame('' . ($this->time - 5), $request->getCurrentTimestamp());
+ }
+
+ public function test_cdt_ShouldReturnTheCustomTimestamp_IfAuthenticatedAndValid()
+ {
+ $request = $this->buildRequest(array('cdt' => '' . ($this->time - 28800)));
+ $request->setIsAuthenticated();
+ $this->assertSame('' . ($this->time - 28800), $request->getCurrentTimestamp());
+ }
+
+ public function test_cdt_ShouldReturnTheCustomTimestamp_IfTimestampIsInFuture()
+ {
+ $request = $this->buildRequest(array('cdt' => '' . ($this->time + 30800)));
+ $this->assertSame($this->time, $request->getCurrentTimestamp());
+ }
+
+ public function test_cdt_ShouldReturnTheCustomTimestamp_ShouldUseStrToTime_IfItIsNotATime()
+ {
+ $request = $this->buildRequest(array('cdt' => '5 years ago'));
+ $request->setIsAuthenticated();
+ $this->assertNotSame($this->time, $request->getCurrentTimestamp());
+ $this->assertNotEmpty($request->getCurrentTimestamp());
+ }
+
+ public function test_isEmptyRequest_ShouldReturnTrue_InCaseNoParamsSet()
+ {
+ $request = $this->buildRequest(array());
+ $this->assertTrue($request->isEmptyRequest());
+ }
+
+ public function test_isEmptyRequest_ShouldReturnTrue_InCaseNullIsSet()
+ {
+ $request = $this->buildRequest(null);
+ $this->assertTrue($request->isEmptyRequest());
+ }
+
+ public function test_isEmptyRequest_ShouldRecognizeEmptyRequest_EvenIfConstructorAddsAParam()
+ {
+ $_SERVER['HTTP_REFERER'] = 'http://www.example.com';
+
+ $request = $this->buildRequest(array());
+ $this->assertCount(1, $request->getParams());
+
+ $this->assertTrue($request->isEmptyRequest());
+ }
+
+ public function test_isEmptyRequest_ShouldReturnFalse_InCaseAtLEastOneParamIssSet()
+ {
+ $request = $this->buildRequest(array('idsite' => 1));
+ $this->assertFalse($request->isEmptyRequest());
+ }
+
+ public function test_getTokenAuth_shouldReturnDefaultValue_IfNoneSet()
+ {
+ $request = $this->buildRequest(array('idsite' => 1));
+ $this->assertFalse($request->getTokenAuth());
+ }
+
+ public function test_getTokenAuth_shouldReturnSetTokenAuth()
+ {
+ $request = $this->buildRequestWithToken(array('idsite' => 1), 'myToken');
+ $this->assertEquals('myToken', $request->getTokenAuth());
+ }
+
+ public function test_getForcedUserId_shouldReturnFalseByDefault()
+ {
+ $this->assertFalse($this->request->getForcedUserId());
+ }
+
+ public function test_getForcedUserId_shouldReturnCustomUserId_IfSet()
+ {
+ $request = $this->buildRequest(array('uid' => 'mytest'));
+ $this->assertEquals('mytest', $request->getForcedUserId());
+ }
+
+ public function test_getForcedUserId_shouldReturnFalse_IfCustomUserIdIsEmpty()
+ {
+ $request = $this->buildRequest(array('uid' => ''));
+ $this->assertFalse($request->getForcedUserId());
+ }
+
+ public function test_getDaysSinceFirstVisit_shouldReturnZeroIfNow()
+ {
+ $this->assertEquals(0.0, $this->request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceFirstVisit_ShouldNotReturnMinusValue()
+ {
+ $request = $this->buildRequest(array('_idts' => '' . ($this->time + 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(0.0, $request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceFirstVisit_TodayMinusHalfDay()
+ {
+ $request = $this->buildRequest(array('_idts' => '' . ($this->time - 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceFirstVisit_Yesterday()
+ {
+ $request = $this->buildRequest(array('_idts' => '' .($this->time - 86400)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceFirstVisit_12Days()
+ {
+ $request = $this->buildRequest(array('_idts' => '' . ($this->time - (86400 * 12))));
+ $request->setIsAuthenticated();
+ $this->assertEquals(12.0, $request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceFirstVisit_IfTimestampIsNotValidShouldIgnoreParam()
+ {
+ $request = $this->buildRequest(array('_idts' => '' . ($this->time - (86400 * 15 * 365))));
+ $this->assertEquals(0.0, $request->getDaysSinceFirstVisit());
+ }
+
+ public function test_getDaysSinceLastOrder_shouldReturnZeroIfNow()
+ {
+ $this->assertEquals(0.0, $this->request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastOrder_ShouldNotReturnMinusValue()
+ {
+ $request = $this->buildRequest(array('_ects' => '' . ($this->time + 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(0.0, $request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastOrder_TodayMinusHalfDay()
+ {
+ $request = $this->buildRequest(array('_ects' => '' . ($this->time - 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastOrder_Yesterday()
+ {
+ $request = $this->buildRequest(array('_ects' => '' . ($this->time - 86400)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastOrder_12Days()
+ {
+ $request = $this->buildRequest(array('_ects' => '' . ($this->time - (86400 * 12))));
+ $request->setIsAuthenticated();
+ $this->assertEquals(12.0, $request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastOrder_ShouldIgnoreParamIfInvalid()
+ {
+ $request = $this->buildRequest(array('_ects' => 5));
+ $this->assertFalse($request->getDaysSinceLastOrder());
+ }
+
+ public function test_getDaysSinceLastVisit_shouldReturnZeroIfNow()
+ {
+ $this->assertEquals(0.0, $this->request->getDaysSinceLastVisit());
+ }
+
+ public function test_getDaysSinceLastVisit_ShouldNotReturnMinusValue()
+ {
+ $request = $this->buildRequest(array('_viewts' => '' . ($this->time + 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(0.0, $request->getDaysSinceLastVisit());
+ }
+
+ public function test_getDaysSinceLastVisit_TodayMinusHalfDay()
+ {
+ $request = $this->buildRequest(array('_viewts' => '' . ($this->time - 43200)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceLastVisit());
+ }
+
+ public function test_getDaysSinceLastVisit_Yesterday()
+ {
+ $request = $this->buildRequest(array('_viewts' => '' . ($this->time - 86400)));
+ $request->setIsAuthenticated();
+ $this->assertEquals(1.0, $request->getDaysSinceLastVisit());
+ }
+
+ public function test_getDaysSinceLastVisit_12Days()
+ {
+ $request = $this->buildRequest(array('_viewts' => '' . ($this->time - (86400 * 12))));
+ $request->setIsAuthenticated();
+ $this->assertEquals(12.0, $request->getDaysSinceLastVisit());
+ }
+
+ public function test_getDaysSinceLastVisit_ShouldIgnoreParamIfInvalid()
+ {
+ $request = $this->buildRequest(array('_viewts' => '' . 5));
+ $this->assertSame(0, $request->getDaysSinceLastVisit());
+ }
+
+ public function test_getGoalRevenue_ShouldReturnDefaultValue_IfNothingSet()
+ {
+ $this->assertFalse($this->request->getGoalRevenue(false));
+ }
+
+ public function test_getGoalRevenue_ShouldReturnParam_IfSet()
+ {
+ $request = $this->buildRequest(array('revenue' => '5.51'));
+ $this->assertSame(5.51, $request->getGoalRevenue(false));
+ }
+
+ public function test_getUserIdHashed_shouldReturnSetTokenAuth()
+ {
+ $hash = $this->request->getUserIdHashed(1);
+
+ $this->assertEquals('356a192b7913b04c', $hash);
+ $this->assertSame(16, strlen($hash));
+ $this->assertTrue(ctype_alnum($hash));
+
+ $this->assertEquals('da4b9237bacccdf1', $this->request->getUserIdHashed(2));
+ }
+
+ public function test_getVisitCount_shouldReturnOne_IfNotSet()
+ {
+ $this->assertEquals(1, $this->request->getVisitCount());
+ }
+
+ public function test_getVisitCount_shouldReturnTheSetValue_IfHigherThanOne()
+ {
+ $request = $this->buildRequest(array('_idvc' => 13));
+ $this->assertEquals(13, $request->getVisitCount());
+ }
+
+ public function test_getVisitCount_shouldReturnAtLEastOneEvenIfLowerValueIsSet()
+ {
+ $request = $this->buildRequest(array('_idvc' => 0));
+ $this->assertEquals(1, $request->getVisitCount());
+
+ $request = $this->buildRequest(array('_idvc' => -1));
+ $this->assertEquals(1, $request->getVisitCount());
+ }
+
+ public function test_getLocalTime_shouldFallbackToCurrentDate_IfNoParamIsSet()
+ {
+ $this->assertEquals('02:20:17', $this->request->getLocalTime());
+ }
+
+ public function test_getLocalTime_shouldReturnAtLEastOneEvenIfLowerValueIsSet()
+ {
+ $request = $this->buildRequest(array('h' => 15, 'm' => 3, 's' => 4));
+ $this->assertEquals('15:03:04', $request->getLocalTime());
+ }
+
+ public function test_getLocalTime_shouldFallbackToPartsOfCurrentDate()
+ {
+ $request = $this->buildRequest(array('h' => 5));
+ $this->assertEquals('05:20:17', $request->getLocalTime());
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Requested parameter myCustomFaKeParaM is not a known Tracking API Parameter
+ */
+ public function test_getParam_shouldThrowException_IfTryingToAccessInvalidParam()
+ {
+ $this->request->getParam('myCustomFaKeParaM');
+ }
+
+ public function test_getParam_aString()
+ {
+ $request = $this->buildRequest(array('url' => 'test'));
+ $this->assertEquals('test', $request->getParam('url'));
+ }
+
+ public function test_getParam_aInt()
+ {
+ $request = $this->buildRequest(array('new_visit' => '12'));
+ $this->assertSame(12, $request->getParam('new_visit'));
+ }
+
+ public function test_getPlugins_shouldReturnZeroForAllIfNothingGiven()
+ {
+ $expected = array_fill(0, 10, 0);
+
+ $this->assertEquals($expected, $this->request->getPlugins());
+ }
+
+ public function test_getPlugins_shouldReturnAllOneIfAllGiven()
+ {
+ $plugins = array('fla', 'java', 'dir', 'qt', 'realp', 'pdf', 'wma', 'gears', 'ag', 'cookie');
+ $request = $this->buildRequest(array_fill_keys($plugins, '1'));
+
+ $this->assertEquals(array_fill(0, 10, 1), $request->getPlugins());
+ }
+
+ public function test_getPlugins_shouldDetectSome()
+ {
+ $plugins = array('fla' => 1, 'java', 'dir' => '1', 'qt' => '0', 'realp' => 0, 'gears', 'ag' => 1, 'cookie');
+ $request = $this->buildRequest($plugins);
+
+ $expected = array(1, 0, 1, 0, 0, 0, 0, 0, 1, 0);
+ $this->assertEquals($expected, $request->getPlugins());
+ }
+
+ public function test_getPageGenerationTime_shouldDefaultToFalse_IfNotGiven()
+ {
+ $this->assertFalse($this->request->getPageGenerationTime());
+ }
+
+ public function test_getPageGenerationTime_shouldIgnoreAnyValueLowerThan0()
+ {
+ $request = $this->buildRequest(array('gt_ms' => '0'));
+ $this->assertFalse($request->getPageGenerationTime());
+
+ $request = $this->buildRequest(array('gt_ms' => '-5'));
+ $this->assertFalse($request->getPageGenerationTime());
+ }
+
+ public function test_getPageGenerationTime_shouldIgnoreAnyValueThatIsTooHigh()
+ {
+ $request = $this->buildRequest(array('gt_ms' => '3600002'));
+ $this->assertFalse($request->getPageGenerationTime());
+ }
+
+ public function test_getPageGenerationTime_shouldReturnAValidValue()
+ {
+ $request = $this->buildRequest(array('gt_ms' => '1942'));
+ $this->assertSame(1942, $request->getPageGenerationTime());
+ }
+
+ public function test_truncateCustomVariable_shouldNotTruncateAnything_IfValueIsShortEnough()
+ {
+ $len = CustomVariables::getMaxLengthCustomVariables();
+ $input = str_pad('test', $len - 2, 't');
+
+ $result = Request::truncateCustomVariable($input);
+
+ $this->assertSame($result, $input);
+ }
+
+ public function test_truncateCustomVariable_shouldActuallyTruncateTheValue()
+ {
+ $len = CustomVariables::getMaxLengthCustomVariables();
+ $input = str_pad('test', $len + 2, 't');
+
+ $this->assertGreaterThan(100, $len);
+
+ $truncated = Request::truncateCustomVariable($input);
+
+ $this->assertEquals(str_pad('test', $len, 't'), $truncated);
+ }
+
+ public function test_getUserAgent_ShouldReturnEmptyString_IfNoneIsSet()
+ {
+ $this->assertEquals('', $this->request->getUserAgent());
+ }
+
+ public function test_getUserAgent_ShouldDefaultToServerUa_IfPossibleAndNoneIsSet()
+ {
+ $_SERVER['HTTP_USER_AGENT'] = 'MyUserAgent';
+ $this->assertSame('MyUserAgent', $this->request->getUserAgent());
+ unset($_SERVER['HTTP_USER_AGENT']);
+ }
+
+ public function test_getUserAgent_ShouldReturnTheUaFromParams_IfOneIsSet()
+ {
+ $request = $this->buildRequest(array('idsite' => '14', 'ua' => 'My Custom UA'));
+ $this->assertSame('My Custom UA', $request->getUserAgent());
+ }
+
+ public function test_getBrowserLanguage_ShouldReturnACustomSetLangParam_IfOneIsSet()
+ {
+ $request = $this->buildRequest(array('lang' => 'CusToMLang'));
+ $this->assertSame('CusToMLang', $request->getBrowserLanguage());
+ }
+
+ public function test_getBrowserLanguage_ShouldReturnADefaultLanguageInCaseNoneIsSet()
+ {
+ $lang = $this->request->getBrowserLanguage();
+ $this->assertNotEmpty($lang);
+ $this->assertTrue(2 <= strlen($lang) && strlen($lang) <= 10);
+ }
+
+ public function test_makeThirdPartyCookie_ShouldReturnAnInstanceOfCookie()
+ {
+ $cookie = $this->request->makeThirdPartyCookie();
+
+ $this->assertTrue($cookie instanceof Cookie);
+ }
+
+ public function test_makeThirdPartyCookie_ShouldPreconfigureTheCookieInstance()
+ {
+ $cookie = $this->request->makeThirdPartyCookie();
+
+ $this->assertCookieContains('COOKIE _pk_uid', $cookie);
+ $this->assertCookieContains('expire: 1450750817', $cookie);
+ $this->assertCookieContains('path: ,', $cookie);
+ }
+
+ private function assertCookieContains($needle, Cookie $cookie)
+ {
+ $this->assertContains($needle, $cookie . '');
+ }
+
+ public function test_getIdSite()
+ {
+ $request = $this->buildRequest(array('idsite' => '14'));
+ $this->assertSame(14, $request->getIdSite());
+ }
+
+ public function test_getIdSite_shouldTriggerEventAndReturnThatIdSite()
+ {
+ $self = $this;
+ Piwik::addAction('Tracker.Request.getIdSite', function (&$idSite, $params) use ($self) {
+ $self->assertSame(14, $idSite);
+ $self->assertEquals(array('idsite' => '14'), $params);
+ $idSite = 12;
+ });
+
+ $request = $this->buildRequest(array('idsite' => '14'));
+ $this->assertSame(12, $request->getIdSite());
+ }
+
+ /**
+ * @expectedException \Piwik\Exception\UnexpectedWebsiteFoundException
+ * @expectedExceptionMessage Invalid idSite: '0'
+ */
+ public function test_getIdSite_shouldThrowException_IfValueIsZero()
+ {
+ $request = $this->buildRequest(array('idsite' => '0'));
+ $request->getIdSite();
+ }
+
+ /**
+ * @expectedException \Piwik\Exception\UnexpectedWebsiteFoundException
+ * @expectedExceptionMessage Invalid idSite: '-1'
+ */
+ public function test_getIdSite_shouldThrowException_IfValueIsLowerThanZero()
+ {
+ $request = $this->buildRequest(array('idsite' => '-1'));
+ $request->getIdSite();
+ }
+
+ public function test_getIpString_ShouldDefaultToServerAddress()
+ {
+ $this->assertEquals($_SERVER['REMOTE_ADDR'], $this->request->getIpString());
+ }
+
+ public function test_getIpString_ShouldDefaultToServerAddress_IfCustomIpIsSetButNotAuthenticated()
+ {
+ $request = $this->buildRequest(array('cip' => '192.192.192.192'));
+ $this->assertEquals($_SERVER['REMOTE_ADDR'], $request->getIpString());
+ }
+
+ public function test_getIpString_ShouldReturnCustomIp_IfAuthenticated()
+ {
+ $request = $this->buildRequest(array('cip' => '192.192.192.192'));
+ $request->setIsAuthenticated();
+ $this->assertEquals('192.192.192.192', $request->getIpString());
+ }
+
+ public function test_getIp()
+ {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $this->assertEquals(IPUtils::stringToBinaryIP($ip), $this->request->getIp());
+ }
+
+ public function test_getCookieName_ShouldReturnConfigValue()
+ {
+ $this->assertEquals('_pk_uid', $this->request->getCookieName());
+ }
+
+ public function test_getCookieExpire_ShouldReturnConfigValue()
+ {
+ $this->assertEquals($this->time + (60 * 60 * 24 * 393), $this->request->getCookieExpire());
+ }
+
+ public function test_getCookiePath_ShouldBeEmptyByDefault()
+ {
+ $this->assertEquals('', $this->request->getCookiePath());
+ }
+
+ public function test_getCookiePath_ShouldReturnConfigValue()
+ {
+ $oldPath = TrackerConfig::getConfigValue('cookie_path');
+ TrackerConfig::setConfigValue('cookie_path', 'test');
+
+ $this->assertEquals('test', $this->request->getCookiePath());
+
+ TrackerConfig::setConfigValue('cookie_path', $oldPath);
+ }
+
+ private function buildRequest($params)
+ {
+ $request = new TestRequest($params);
+ $request->setCurrentTimestamp($this->time);
+
+ return $request;
+ }
+
+ private function buildRequestWithToken($params, $token)
+ {
+ return new TestRequest($params, $token);
+ }
+
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/Tracker/ResponseTest.php b/tests/PHPUnit/Unit/Tracker/ResponseTest.php
new file mode 100644
index 0000000000..9a1d1f28f7
--- /dev/null
+++ b/tests/PHPUnit/Unit/Tracker/ResponseTest.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Unit\Tracker;
+
+use Piwik\Common;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tracker\Response;
+use Piwik\Tests\Framework\Mock\Tracker;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Exception;
+
+class TestResponse extends Response {
+
+ protected function logExceptionToErrorLog(Exception $e)
+ {
+ // prevent console from outputting the error_log message
+ }
+
+ public function getMessageFromException($e)
+ {
+ return parent::getMessageFromException($e);
+ }
+}
+
+/**
+ * @group BulkTracking
+ * @group ResponseTest
+ * @group Plugins
+ * @group Tracker
+ */
+class ResponseTest extends UnitTestCase
+{
+ /**
+ * @var TestResponse
+ */
+ private $response;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->response = new TestResponse();
+ }
+
+ public function test_outputException_shouldAlwaysOutputApiResponse_IfDebugModeIsDisabled()
+ {
+ $this->response->init($this->getTracker());
+ $this->response->outputException($this->getTracker(), new Exception('My Custom Message'), 400);
+
+ Fixture::checkResponse($this->response->getOutput());
+ }
+
+ public function test_outputException_shouldOutputDebugMessageIfEnabled()
+ {
+ $tracker = $this->getTracker();
+ $this->response->init($tracker);
+
+ $tracker->enableDebugMode();
+
+ $this->response->outputException($tracker, new Exception('My Custom Message'), 400);
+
+ $content = $this->response->getOutput();
+
+ $this->assertContains('<title>Piwik &rsaquo; Error</title>', $content);
+ $this->assertContains('<p>My Custom Message', $content);
+ }
+
+ public function test_outputResponse_shouldOutputStandardApiResponse()
+ {
+ $this->response->init($this->getTracker());
+ $this->response->outputResponse($this->getTracker());
+
+ Fixture::checkResponse($this->response->getOutput());
+ }
+
+ public function test_outputResponse_shouldNotOutputApiResponse_IfDebugModeIsEnabled_AsWePrintOtherStuff()
+ {
+ $this->response->init($this->getTracker());
+
+ $tracker = $this->getTracker();
+ $tracker->enableDebugMode();
+ $this->response->outputResponse($tracker);
+
+ $this->assertEquals('', $this->response->getOutput());
+ }
+
+ public function test_outputResponse_shouldNotOutputApiResponse_IfSomethingWasPrintedUpfront()
+ {
+ $this->response->init($this->getTracker());
+
+ echo 5;
+ $this->response->outputResponse($this->getTracker());
+
+ $this->assertEquals('5', $this->response->getOutput());
+ }
+
+ public function test_outputResponse_shouldNotOutputResponseTwice_IfExceptionWasAlreadyOutput()
+ {
+ $this->response->init($this->getTracker());
+
+ $this->response->outputException($this->getTracker(), new Exception('My Custom Message'), 400);
+ $this->response->outputResponse($this->getTracker());
+
+ Fixture::checkResponse($this->response->getOutput());
+ }
+
+ public function test_outputResponse_shouldOutputNoResponse_If204HeaderIsRequested()
+ {
+ $this->response->init($this->getTracker());
+
+ $_GET['send_image'] = '0';
+ $this->response->outputResponse($this->getTracker());
+ unset($_GET['send_image']);
+
+ $this->assertEquals('', $this->response->getOutput());
+ }
+
+ public function test_outputResponse_shouldOutputPiwikMessage_InCaseNothingWasTracked()
+ {
+ $this->response->init($this->getTracker());
+
+ $tracker = $this->getTracker();
+ $tracker->setCountOfLoggedRequests(0);
+ $this->response->outputResponse($tracker);
+
+ $this->assertEquals("<a href='/'>Piwik</a> is a free/libre web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.",
+ $this->response->getOutput());
+ }
+
+ public function test_getMessageFromException_ShouldNotOutputAnyDetails_IfErrorContainsDbCredentials()
+ {
+ $message = $this->response->getMessageFromException(new Exception('Test Message', 1044));
+ $this->assertStringStartsWith("Error while connecting to the Piwik database", $message);
+
+ $message = $this->response->getMessageFromException(new Exception('Test Message', 42000));
+ $this->assertStringStartsWith("Error while connecting to the Piwik database", $message);
+ }
+
+ public function test_getMessageFromException_ShouldReturnMessageAndTrace_InCaseIsCli()
+ {
+ $message = $this->response->getMessageFromException(new Exception('Test Message', 8150));
+ $this->assertStringStartsWith("Test Message\n#0 [internal function]", $message);
+ }
+
+ public function test_getMessageFromException_ShouldOnlyReturnMessage_InCaseIsNotCli()
+ {
+ Common::$isCliMode = false;
+ $message = $this->response->getMessageFromException(new Exception('Test Message', 8150));
+ Common::$isCliMode = true;
+
+ $this->assertStringStartsWith("Test Message", $message);
+ }
+
+ public function test_outputResponse_shouldOutputApiResponse_IfTrackerIsDisabled()
+ {
+ $this->response->init($this->getTracker());
+
+ $tracker = $this->getTracker();
+ $tracker->setCountOfLoggedRequests(0);
+ $tracker->disableShouldRecordStatistics();
+ $this->response->outputResponse($tracker);
+
+ Fixture::checkResponse($this->response->getOutput());
+ }
+
+ private function getTracker()
+ {
+ $tracker = new Tracker();
+ $tracker->setCountOfLoggedRequests(5);
+ return $tracker;
+ }
+
+}
diff --git a/tests/PHPUnit/Unit/TrackerTest.php b/tests/PHPUnit/Unit/TrackerTest.php
new file mode 100644
index 0000000000..084ee627de
--- /dev/null
+++ b/tests/PHPUnit/Unit/TrackerTest.php
@@ -0,0 +1,274 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Unit;
+
+use Piwik\EventDispatcher;
+use Piwik\Piwik;
+use Piwik\Tracker\Request;
+use Piwik\Tests\Framework\Mock\Tracker\Handler;
+use Piwik\Tests\Framework\Mock\Tracker\RequestSet;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Tracker;
+use Piwik\Translate;
+
+class TestTracker extends Tracker
+{
+ public function __construct()
+ {
+ $this->record = true;
+ }
+
+ public function shouldRecordStatistics()
+ {
+ return $this->record;
+ }
+
+ public function disalbeRecordStatistics()
+ {
+ $this->record = false;
+ }
+}
+
+/**
+ * @group TrackerTest
+ * @group Tracker
+ */
+class TrackerTest extends UnitTestCase
+{
+ /**
+ * @var TestTracker
+ */
+ private $tracker;
+
+ /**
+ * @var Handler
+ */
+ private $handler;
+
+ /**
+ * @var RequestSet
+ */
+ private $requestSet;
+
+ private $time;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->time = time();
+ $this->tracker = new TestTracker();
+ $this->handler = new Handler();
+ $this->requestSet = new RequestSet();
+ $this->requestSet->setRequests(array($this->buildRequest(1), $this->buildRequest(1)));
+ }
+
+ public function tearDown()
+ {
+ EventDispatcher::getInstance()->clearObservers('Tracker.end');
+ parent::tearDown();
+ }
+
+ public function test_isDebugModeEnabled_shouldReturnFalse_ByDefault()
+ {
+ unset($GLOBALS['PIWIK_TRACKER_DEBUG']);
+ $this->assertFalse($this->tracker->isDebugModeEnabled());
+ }
+
+ public function test_isDebugModeEnabled_shouldReturnFalse_IfDisabled()
+ {
+ $GLOBALS['PIWIK_TRACKER_DEBUG'] = false;
+
+ $this->assertFalse($this->tracker->isDebugModeEnabled());
+
+ unset($GLOBALS['PIWIK_TRACKER_DEBUG']);
+ }
+
+ public function test_isDebugModeEnabled_shouldReturnTrue_IfEnabled()
+ {
+ $GLOBALS['PIWIK_TRACKER_DEBUG'] = true;
+
+ $this->assertTrue($this->tracker->isDebugModeEnabled());
+
+ unset($GLOBALS['PIWIK_TRACKER_DEBUG']);
+ }
+
+ public function test_main_shouldReturnFinishedResponse()
+ {
+ $response = $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertEquals('My Rendered Content', $response);
+ }
+
+ public function test_main_shouldReturnResponse_EvenWhenThereWasAnExceptionDuringProcess()
+ {
+ $this->handler->enableTriggerExceptionInProcess();
+ $response = $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertEquals('My Exception During Process', $response);
+ }
+
+ public function test_main_shouldReturnResponse_EvenWhenThereWasAnExceptionDuringInitRequests()
+ {
+ $this->requestSet->enableThrowExceptionOnInit();
+ $response = $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertEquals('Init requests and token auth exception', $response);
+ }
+
+ public function test_main_shouldTriggerHandlerInitAndFinishEvent()
+ {
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertTrue($this->handler->isInit);
+ $this->assertTrue($this->handler->isProcessed);
+ $this->assertTrue($this->handler->isFinished);
+ $this->assertFalse($this->handler->isOnException);
+ }
+
+ public function test_main_shouldTriggerHandlerInitAndFinishEvent_EvenIfShouldNotRecordStats()
+ {
+ $this->tracker->disalbeRecordStatistics();
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertTrue($this->handler->isInit);
+ $this->assertFalse($this->handler->isProcessed);
+ $this->assertTrue($this->handler->isFinished);
+ $this->assertFalse($this->handler->isOnException);
+ }
+
+ public function test_main_shouldTriggerHandlerInitAndFinishEvent_EvenIfThereIsAnException()
+ {
+ $this->handler->enableTriggerExceptionInProcess();
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertTrue($this->handler->isInit);
+ $this->assertTrue($this->handler->isFinished);
+ $this->assertTrue($this->handler->isOnException);
+ }
+
+ public function test_main_shouldPostEndEvent()
+ {
+ $called = false;
+ Piwik::addAction('Tracker.end', function () use (&$called) {
+ $called = true;
+ });
+
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertTrue($called);
+ }
+
+ public function test_main_shouldPostEndEvent_EvenIfShouldNotRecordStats()
+ {
+ $called = false;
+ Piwik::addAction('Tracker.end', function () use (&$called) {
+ $called = true;
+ });
+
+ $this->tracker->disalbeRecordStatistics();
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertFalse($this->handler->isProcessed);
+ $this->assertTrue($called);
+ }
+
+ public function test_main_shouldPostEndEvent_EvenIfThereIsAnException()
+ {
+ $called = false;
+ Piwik::addAction('Tracker.end', function () use (&$called) {
+ $called = true;
+ });
+
+ $this->handler->enableTriggerExceptionInProcess();
+ $this->tracker->main($this->handler, $this->requestSet);
+
+ $this->assertTrue($this->handler->isOnException);
+ $this->assertTrue($called);
+ }
+
+ public function test_track_shouldTrack_IfThereAreRequests()
+ {
+ $this->tracker->track($this->handler, $this->requestSet);
+
+ $this->assertTrue($this->handler->isOnStartTrackRequests);
+ $this->assertTrue($this->handler->isProcessed);
+ $this->assertTrue($this->handler->isOnAllRequestsTracked);
+ $this->assertFalse($this->handler->isOnException);
+ }
+
+ public function test_track_shouldNotTrackAnything_IfTrackingIsDisabled()
+ {
+ $this->tracker->disalbeRecordStatistics();
+ $this->tracker->track($this->handler, $this->requestSet);
+
+ $this->assertFalse($this->handler->isOnStartTrackRequests);
+ $this->assertFalse($this->handler->isProcessed);
+ $this->assertFalse($this->handler->isOnAllRequestsTracked);
+ $this->assertFalse($this->handler->isOnException);
+ }
+
+ public function test_track_shouldNotTrackAnything_IfNoRequestsAreSet()
+ {
+ $this->requestSet->setRequests(array());
+ $this->tracker->track($this->handler, $this->requestSet);
+
+ $this->assertFalse($this->handler->isOnStartTrackRequests);
+ $this->assertFalse($this->handler->isProcessed);
+ $this->assertFalse($this->handler->isOnAllRequestsTracked);
+ $this->assertFalse($this->handler->isOnException);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedException My Exception During Process
+ */
+ public function test_track_shouldNotCatchAnyException_IfExceptionWasThrown()
+ {
+ $this->handler->enableTriggerExceptionInProcess();
+ $this->tracker->track($this->handler, $this->requestSet);
+ }
+
+ public function test_getCountOfLoggedRequests_shouldReturnZero_WhenNothingTracked()
+ {
+ $this->assertEquals(0, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ public function test_hasLoggedRequests_shouldReturnFalse_WhenNothingTracked()
+ {
+ $this->assertFalse($this->tracker->hasLoggedRequests());
+ }
+
+ public function test_setCountOfLoggedRequests_shouldOverwriteNumberOfLoggedRequests()
+ {
+ $this->tracker->setCountOfLoggedRequests(5);
+ $this->assertEquals(5, $this->tracker->getCountOfLoggedRequests());
+ }
+
+ public function test_hasLoggedRequests_shouldReturnTrue_WhenSomeRequestsWereLogged()
+ {
+ $this->tracker->setCountOfLoggedRequests(1);
+ $this->assertTrue($this->tracker->hasLoggedRequests());
+
+ $this->tracker->setCountOfLoggedRequests(5);
+ $this->assertTrue($this->tracker->hasLoggedRequests());
+
+ $this->tracker->setCountOfLoggedRequests(0);
+ $this->assertFalse($this->tracker->hasLoggedRequests());
+ }
+
+ private function buildRequest($idsite)
+ {
+ $request = new Request(array('idsite' => $idsite));
+ $request->setCurrentTimestamp($this->time);
+
+ return $request;
+ }
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/proxy/piwik.php b/tests/PHPUnit/proxy/piwik.php
index 2acaea81bd..bc03155b25 100755
--- a/tests/PHPUnit/proxy/piwik.php
+++ b/tests/PHPUnit/proxy/piwik.php
@@ -11,7 +11,6 @@ use Piwik\DataTable\Manager;
use Piwik\Option;
use Piwik\Plugins\UserCountry\LocationProvider\GeoIp;
use Piwik\Site;
-use Piwik\Tracker\Cache;
use Piwik\Tracker;
require realpath(dirname(__FILE__)) . "/includes.php";
@@ -35,4 +34,7 @@ try {
echo "Unexpected error during tracking: " . $ex->getMessage() . "\n" . $ex->getTraceAsString() . "\n";
}
-ob_end_flush();
+if (ob_get_level() > 1) {
+ ob_end_flush();
+}
+
diff --git a/tests/travis/prepare.sh b/tests/travis/prepare.sh
index 93531210ce..a42932406d 100755
--- a/tests/travis/prepare.sh
+++ b/tests/travis/prepare.sh
@@ -43,3 +43,9 @@ mkdir ./tmp/tcpdf
mkdir ./tmp/climulti
chmod a+rw ./tests/lib/geoip-files
chmod a+rw ./plugins/*/tests/System/processed
+
+# install phpredis
+echo 'extension="redis.so"' > ./tmp/redis.ini
+phpenv config-add ./tmp/redis.ini
+
+php -i \ No newline at end of file