diff --git a/.travis.yml b/.travis.yml index 090a4291b3609aa4a214acbc64db8adf8e51356c..b313620187bb425e00375559ed176e68060f4b5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,12 +75,12 @@ matrix: env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL - php: hhvm env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL - # Only run Mysqli tests on PHP 5.3 - - php: 5.4 + # Only run Mysqli tests on PHP 5.4 + - php: 5.3.3 env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=MYSQLI - - php: 5.4 + - php: 5.3.3 env: TEST_SUITE=PluginTests MYSQL_ADAPTER=MYSQLI - - php: 5.4 + - php: 5.3.3 env: TEST_SUITE=CoreTests MYSQL_ADAPTER=MYSQLI - php: 5.5 env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=MYSQLI diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e6ad088e26a8581b6cf551b37a813f9aad4046d..93a02516f4e309ecd0a70c08c6d5db3b5391a2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,13 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API' * The following methods have been added: setPassword, setPasswordHash, getTokenAuthSecret and getLogin. * Clarifying semantics of each method and what they must support and can support. * **Read the documentation for the [Auth interface](http://developer.piwik.org/api-reference/Piwik/Auth) to learn more.** +* The `Piwik\Unzip\*` classes have been extracted out of the Piwik repository into a separate component named [Decompress](https://github.com/piwik/component-decompress). + * `Piwik\Unzip` has not moved, it is kept for backward compatibility. If you have been using that class, you don't need to change anything. + * The `Piwik\Unzip\*` classes (Tar, PclZip, Gzip, ZipArchive) have moved to the `Piwik\Decompress\*` namespace (inside the new repository). + * `Piwik\Unzip\UncompressInterface` has been moved and renamed to `Piwik\Decompress\DecompressInterface` (inside the new repository). + +### Deprecations +* The Piwik::setUserHasSuperUserAccess method is deprecated, instead use Access::doAsSuperUser. This method will ensure that super user access is properly rescinded after the callback finishes. ### New commands * `generate:angular-directive` Let's you easily generate a template for a new angular directive for any plugin. diff --git a/composer.json b/composer.json index be32b796bfc0f58d95a21e9be31f5e667460ac1a..270968a3ba475298d4468eefccbbd690203808ae 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ "symfony/console": ">=v2.3.5", "tedivm/jshrink": "v0.5.1", "mustangostang/spyc": "0.5.*", - "piwik/device-detector": "2.*" + "piwik/device-detector": "2.*", + "piwik/decompress": "~0.1.0" }, "require-dev": { "phpunit/phpunit": "~4.1", diff --git a/composer.lock b/composer.lock index 47fe2533ba01f89a0e900126c574d828ff5169c6..a8676c511ef5e216491b8257fbe84f3ec0ac97c7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "37a6e1fcbf0ec87f900f82bf83c0804f", + "hash": "d9542e577717b3fa1b0fa3d1fee3803b", "packages": [ { "name": "leafo/lessphp", @@ -94,18 +94,53 @@ ], "time": "2013-02-21 10:52:01" }, + { + "name": "piwik/decompress", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/piwik/component-decompress.git", + "reference": "f02c7f1d92b33955ce1b44a9623d8467fd2e2c49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/piwik/component-decompress/zipball/f02c7f1d92b33955ce1b44a9623d8467fd2e2c49", + "reference": "f02c7f1d92b33955ce1b44a9623d8467fd2e2c49", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Piwik\\Decompress\\": "src/" + }, + "classmap": [ + "libs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "time": "2014-10-02 22:01:11" + }, { "name": "piwik/device-detector", - "version": "dev-master", + "version": "2.4", "source": { "type": "git", "url": "https://github.com/piwik/device-detector.git", - "reference": "1c7334cd35052b9b7661e13396d4da7a7f47e323" + "reference": "64d91375aea2340b81e2cb46745dc3b2213d3231" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/piwik/device-detector/zipball/1c7334cd35052b9b7661e13396d4da7a7f47e323", - "reference": "1c7334cd35052b9b7661e13396d4da7a7f47e323", + "url": "https://api.github.com/repos/piwik/device-detector/zipball/64d91375aea2340b81e2cb46745dc3b2213d3231", + "reference": "64d91375aea2340b81e2cb46745dc3b2213d3231", "shasum": "" }, "require": { @@ -139,7 +174,7 @@ "parser", "useragent" ], - "time": "2014-09-24 16:59:50" + "time": "2014-08-29 18:24:04" }, { "name": "symfony/console", @@ -438,6 +473,56 @@ ], "time": "2013-11-09 22:30:54" }, + { + "name": "pear/archive_tar", + "version": "1.3.11", + "source": { + "type": "git", + "url": "https://github.com/pear/Archive_Tar.git", + "reference": "23341344e19bbab1056cf2d2773f28cfccf787a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/23341344e19bbab1056cf2d2773f28cfccf787a3", + "reference": "23341344e19bbab1056cf2d2773f28cfccf787a3", + "shasum": "" + }, + "require": { + "php": ">=4.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Archive_Tar": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Michiel Rook", + "email": "mrook@php.net", + "role": "Lead" + }, + { + "name": "Vincent Blavet", + "email": "vincent@phpconcept.net" + }, + { + "name": "Greg Beaver", + "email": "greg@chiaraquartet.net" + } + ], + "description": "Tar file management class", + "homepage": "https://github.com/pear/Archive_Tar", + "keywords": [ + "archive", + "tar" + ], + "time": "2013-02-09 11:44:32" + }, { "name": "phpunit/php-code-coverage", "version": "2.0.11", @@ -687,16 +772,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.2.6", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c28a790620fe30b049bb693be1ef9cd4e0fe906c" + "reference": "c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c28a790620fe30b049bb693be1ef9cd4e0fe906c", - "reference": "c28a790620fe30b049bb693be1ef9cd4e0fe906c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7", + "reference": "c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7", "shasum": "" }, "require": { @@ -757,7 +842,7 @@ "testing", "xunit" ], - "time": "2014-09-14 09:31:24" + "time": "2014-09-06 18:38:27" }, { "name": "phpunit/phpunit-mock-objects", @@ -1129,19 +1214,14 @@ "time": "2014-08-31 03:22:04" } ], - "aliases": [ - - ], + "aliases": [], "minimum-stability": "stable", "stability-flags": { - "piwik/device-detector": 20, "facebook/xhprof": 20 }, "prefer-stable": false, "platform": { "php": ">=5.3.2" }, - "platform-dev": [ - - ] + "platform-dev": [] } diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php index 69dc62063b3299773aef8a46e9ac891880ae7999..30513daaf3df76776bec40092a753f822903689a 100644 --- a/core/API/DataTableManipulator.php +++ b/core/API/DataTableManipulator.php @@ -141,6 +141,7 @@ abstract class DataTableManipulator /** * Extract the API method for loading subtables from the meta data * + * @throws Exception * @return string */ private function getApiMethodForSubtable() @@ -148,7 +149,7 @@ abstract class DataTableManipulator if (!$this->apiMethodForSubtable) { $meta = API::getInstance()->getMetadata('all', $this->apiModule, $this->apiMethod); - if(empty($meta)) { + if (empty($meta)) { throw new Exception(sprintf( "The DataTable cannot be manipulated: Metadata for report %s.%s could not be found. You can define the metadata in a hook, see example at: http://developer.piwik.org/api-reference/events#apigetreportmetadata", $this->apiModule, $this->apiMethod diff --git a/core/API/DataTableManipulator/Flattener.php b/core/API/DataTableManipulator/Flattener.php index 211af52d2bf5b76f22dd78c543a232620db99378..cfee16e6ba8207c73a0975ef0151fa64712cc5d0 100644 --- a/core/API/DataTableManipulator/Flattener.php +++ b/core/API/DataTableManipulator/Flattener.php @@ -127,6 +127,7 @@ class Flattener extends DataTableManipulator * Remove the flat parameter from the subtable request * * @param array $request + * @return array */ protected function manipulateSubtableRequest($request) { diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php index 1dc3bc85d91bdf85a3b5e13afff0f8490483db45..1cbfeeb3623cf0445a280a501faa0b836ed26bcf 100644 --- a/core/API/DataTableManipulator/ReportTotalsCalculator.php +++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php @@ -194,6 +194,7 @@ class ReportTotalsCalculator extends DataTableManipulator * Make sure to get all rows of the first level table. * * @param array $request + * @return array */ protected function manipulateSubtableRequest($request) { diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php index 668f97bb6064fea0561187bec506fad8cc7bce53..63ae943433c30b6defbf65cd270294df830d43b6 100644 --- a/core/API/DocumentationGenerator.php +++ b/core/API/DocumentationGenerator.php @@ -47,8 +47,8 @@ class DocumentationGenerator if (!empty($prefixUrls)) { $prefixUrls = 'http://demo.piwik.org/'; } + $str = $toc = ''; - $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth(); $parametersToSet = array( 'idSite' => Common::getRequestVar('idSite', 1, 'int'), 'period' => Common::getRequestVar('period', 'day', 'string'), @@ -57,52 +57,38 @@ class DocumentationGenerator foreach (Proxy::getInstance()->getMetadata() as $class => $info) { $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class); + if (in_array($moduleName, $this->modulesToHide)) { continue; } + $toc .= "<a href='#$moduleName'>$moduleName</a><br/>"; - $str .= "\n<a name='$moduleName' id='$moduleName'></a><h2>Module " . $moduleName . "</h2>"; - $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>"; - foreach ($info as $methodName => $infoMethod) { - if ($methodName == '__documentation') { - continue; - } - $params = $this->getParametersString($class, $methodName); - $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . ""; - $str .= '<small>'; - - if ($outputExampleUrls) { - // we prefix all URLs with $prefixUrls - // used when we include this output in the Piwik official documentation for example - $str .= "<span class=\"example\">"; - $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet); - if ($exampleUrl !== false) { - $lastNUrls = ''; - if (preg_match('/(&period)|(&date)/', $exampleUrl)) { - $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet); - $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>"; - } - $exampleUrl = $prefixUrls . $exampleUrl; - $str .= " [ Example in - <a target=_blank href='$exampleUrl&format=xml$token_auth'>XML</a>, - <a target=_blank href='$exampleUrl&format=JSON$token_auth'>Json</a>, - <a target=_blank href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a> - $lastNUrls - ]"; - } else { - $str .= " [ No example available ]"; - } - $str .= "</span>"; - } - $str .= '</small>'; - $str .= "</div>\n"; - } - $str .= '<div style="margin:15px;"><a href="#topApiRef">↑ Back to top</a></div>'; + $str .= $this->getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls); } $str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2> $toc $str"; + + return $str; + } + + private function getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls) + { + $str = ''; + + $str .= "\n<a name='$moduleName' id='$moduleName'></a><h2>Module " . $moduleName . "</h2>"; + $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>"; + foreach ($info as $methodName => $infoMethod) { + if ($methodName == '__documentation') { + continue; + } + + $str .= $this->getMethodString($moduleName, $class, $parametersToSet, $outputExampleUrls, $prefixUrls, $methodName, $str); + } + + $str .= '<div style="margin:15px;"><a href="#topApiRef">↑ Back to top</a></div>'; + return $str; } @@ -241,4 +227,43 @@ class DocumentationGenerator $sParameters = implode(", ", $asParameters); return "($sParameters)"; } + + private function getMethodString($moduleName, $class, $parametersToSet, $outputExampleUrls, $prefixUrls, $methodName) + { + $str = ''; + $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth(); + + $params = $this->getParametersString($class, $methodName); + $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . ""; + $str .= '<small>'; + + if ($outputExampleUrls) { + // we prefix all URLs with $prefixUrls + // used when we include this output in the Piwik official documentation for example + $str .= "<span class=\"example\">"; + $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet); + if ($exampleUrl !== false) { + $lastNUrls = ''; + if (preg_match('/(&period)|(&date)/', $exampleUrl)) { + $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet); + $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>"; + } + $exampleUrl = $prefixUrls . $exampleUrl; + $str .= " [ Example in + <a target=_blank href='$exampleUrl&format=xml$token_auth'>XML</a>, + <a target=_blank href='$exampleUrl&format=JSON$token_auth'>Json</a>, + <a target=_blank href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a> + $lastNUrls + ]"; + } else { + $str .= " [ No example available ]"; + } + $str .= "</span>"; + } + + $str .= '</small>'; + $str .= "</div>\n"; + + return $str; + } } diff --git a/core/API/Proxy.php b/core/API/Proxy.php index d3e1fbad3ff15cf26813d22c7d93d305a74cad24..daaa5c9f4232c247243a3c7a5668b9c278646b41 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -24,7 +24,7 @@ use ReflectionMethod; * * It will also log the performance of API calls (time spent, parameter values, etc.) if logger available * - * @method static \Piwik\API\Proxy getInstance() + * @method static Proxy getInstance() */ class Proxy extends Singleton { diff --git a/core/API/Request.php b/core/API/Request.php index 03b555ad188fe33c6c6dca324dcb5275815cee76..64e8e054fa3dd0349e541a878e17c2c46517a055 100644 --- a/core/API/Request.php +++ b/core/API/Request.php @@ -70,7 +70,7 @@ use Piwik\Log; */ class Request { - protected $request = null; + private $request = null; /** * Converts the supplied request string into an array of query paramater name/value diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php index 17d5d488f7ad8ae9c180230be8b6524dd612cf59..401dccf96c5231899e3a65b9b03e7a114580a3cf 100644 --- a/core/API/ResponseBuilder.php +++ b/core/API/ResponseBuilder.php @@ -18,7 +18,6 @@ use Piwik\DataTable\Filter\PivotByDimension; use Piwik\DataTable\Renderer; use Piwik\DataTable\DataTableInterface; use Piwik\DataTable\Filter\ColumnDelete; -use Piwik\Piwik; /** */ @@ -158,7 +157,7 @@ class ResponseBuilder return Renderer::formatValueXml($message); } - protected function handleDataTable(DataTableInterface $datatable) + private function handleDataTable(DataTableInterface $datatable) { $label = $this->getLabelFromRequest($this->request); @@ -224,7 +223,7 @@ class ResponseBuilder return $this->apiRenderer->renderDataTable($datatable); } - protected function handleArray($array) + private function handleArray($array) { $firstArray = null; $firstKey = null; diff --git a/core/Access.php b/core/Access.php index 00093e6e4f199c5a976bc9d397856c527fd0c3a6..578edaa04494828441b7c0bf0ff4ca9de47370f2 100644 --- a/core/Access.php +++ b/core/Access.php @@ -8,6 +8,7 @@ */ namespace Piwik; +use Exception; use Piwik\Db; /** @@ -33,29 +34,6 @@ use Piwik\Db; */ class Access { - private static $instance = null; - - /** - * Gets the singleton instance. Creates it if necessary. - */ - public static function getInstance() - { - if (self::$instance == null) { - self::$instance = new self; - - Piwik::postEvent('Access.createAccessSingleton', array(&self::$instance)); - } - return self::$instance; - } - - /** - * Sets the singleton instance. For testing purposes. - */ - public static function setSingletonInstance($instance) - { - self::$instance = $instance; - } - /** * Array of idsites available to the current user, indexed by permission level * @see getSitesIdWith*() @@ -100,6 +78,29 @@ class Access */ private $auth = null; + private static $instance = null; + + /** + * Gets the singleton instance. Creates it if necessary. + */ + public static function getInstance() + { + if (self::$instance == null) { + self::$instance = new self; + + Piwik::postEvent('Access.createAccessSingleton', array(&self::$instance)); + } + return self::$instance; + } + + /** + * Sets the singleton instance. For testing purposes. + */ + public static function setSingletonInstance($instance) + { + self::$instance = $instance; + } + /** * Returns the list of the existing Access level. * Useful when a given API method requests a given acccess Level. @@ -146,6 +147,14 @@ class Access if ($this->hasSuperUserAccess()) { return $this->reloadAccessSuperUser(); } + } + + if ($this->hasSuperUserAccess()) { + return $this->reloadAccessSuperUser(); + } + + // if the Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail TODO: docs + review + if ($this->auth === null) { return false; } @@ -155,6 +164,7 @@ class Access if (!$result->wasAuthenticationSuccessful()) { return false; } + $this->login = $result->getIdentity(); $this->token_auth = $result->getTokenAuth(); @@ -162,21 +172,26 @@ class Access if ($result->hasSuperUserAccess()) { return $this->reloadAccessSuperUser(); } + // in case multiple calls to API using different tokens, we ensure we reset it as not SU $this->setSuperUserAccess(false); // we join with site in case there are rows in access for an idsite that doesn't exist anymore // (backward compatibility ; before we deleted the site without deleting rows in _access table) $accessRaw = $this->getRawSitesWithSomeViewAccess($this->login); + foreach ($accessRaw as $access) { $this->idsitesByAccess[$access['access']][] = $access['idsite']; } + return true; } public function getRawSitesWithSomeViewAccess($login) { - return Db::fetchAll(self::getSqlAccessSite("access, t2.idsite"), $login); + $sql = self::getSqlAccessSite("access, t2.idsite"); + + return Db::fetchAll($sql, $login); } /** @@ -187,10 +202,11 @@ class Access */ public static function getSqlAccessSite($select) { - return "SELECT " . $select . " - FROM " . Common::prefixTable('access') . " as t1 - JOIN " . Common::prefixTable('site') . " as t2 USING (idsite) " . - " WHERE login = ?"; + $access = Common::prefixTable('access'); + $siteTable = Common::prefixTable('site'); + + return "SELECT " . $select . " FROM " . $access . " as t1 + JOIN " . $siteTable . " as t2 USING (idsite) WHERE login = ?"; } /** @@ -323,7 +339,9 @@ class Access if ($this->hasSuperUserAccess()) { return; } + $idSitesAccessible = $this->getSitesIdWithAdminAccess(); + if (count($idSitesAccessible) == 0) { throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin'))); } @@ -339,7 +357,9 @@ class Access if ($this->hasSuperUserAccess()) { return; } + $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess(); + if (count($idSitesAccessible) == 0) { throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('view'))); } @@ -357,8 +377,10 @@ class Access if ($this->hasSuperUserAccess()) { return; } + $idSites = $this->getIdSites($idSites); $idSitesAccessible = $this->getSitesIdWithAdminAccess(); + foreach ($idSites as $idsite) { if (!in_array($idsite, $idSitesAccessible)) { throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite))); @@ -378,8 +400,10 @@ class Access if ($this->hasSuperUserAccess()) { return; } + $idSites = $this->getIdSites($idSites); $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess(); + foreach ($idSites as $idsite) { if (!in_array($idsite, $idSitesAccessible)) { throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite))); @@ -399,11 +423,41 @@ class Access } $idSites = Site::getIdSitesFromIdSitesString($idSites); + if (empty($idSites)) { throw new NoAccessException("The parameter 'idSite=' is missing from the request."); } + return $idSites; } + + /** + * Executes a callback with superuser privileges, making sure those privileges are rescinded + * before this method exits. Privileges will be rescinded even if an exception is thrown. + * + * @param callback $function The callback to execute. Should accept no arguments. + * @return mixed The result of `$function`. + * @throws Exception rethrows any exceptions thrown by `$function`. + * @api + */ + public static function doAsSuperUser($function) + { + $isSuperUser = self::getInstance()->hasSuperUserAccess(); + + self::getInstance()->setSuperUserAccess(true); + + try { + $result = $function(); + } catch (Exception $ex) { + self::getInstance()->setSuperUserAccess($isSuperUser); + + throw $ex; + } + + self::getInstance()->setSuperUserAccess($isSuperUser); + + return $result; + } } /** diff --git a/core/Archive.php b/core/Archive.php index 65b5b915324871f3c8e212c261e798f38645bdff..7101850c1d90891b9a5b246dc7d98c8159709997 100644 --- a/core/Archive.php +++ b/core/Archive.php @@ -197,18 +197,23 @@ class Archive { $websiteIds = Site::getIdSitesFromIdSitesString($idSites, $_restrictSitesToLogin); - $timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false; + $timezone = false; + if (count($websiteIds) == 1) { + $timezone = Site::getTimezoneFor($websiteIds[0]); + } if (Period::isMultiplePeriod($strDate, $period)) { - $oPeriod = PeriodFactory::build($period, $strDate, $timezone); + $oPeriod = PeriodFactory::build($period, $strDate, $timezone); $allPeriods = $oPeriod->getSubperiods(); } else { - $oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate); + $oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate); $allPeriods = array($oPeriod); } - $segment = new Segment($segment, $websiteIds); - $idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG; + + $segment = new Segment($segment, $websiteIds); + $idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG; $isMultipleDate = Period::isMultiplePeriod($strDate, $period); + return Archive::factory($segment, $allPeriods, $websiteIds, $idSiteIsAll, $isMultipleDate, $skipAggregationOfSubTables); } @@ -239,9 +244,11 @@ class Archive { $forceIndexedBySite = false; $forceIndexedByDate = false; + if ($idSiteIsAll || count($idSites) > 1) { $forceIndexedBySite = true; } + if (count($periods) > 1 || $isMultipleDate) { $forceIndexedByDate = true; } @@ -265,7 +272,7 @@ class Archive * * @param string|array $names One or more archive names, eg, `'nb_visits'`, `'Referrers_distinctKeywords'`, * etc. - * @return false|numeric|array `false` if there is no data to return, a single numeric value if we're not querying + * @return false|integer|array `false` if there is no data to return, a single numeric value if we're not querying * for multiple sites/periods, or an array if multiple sites, periods or names are * queried for. */ @@ -406,9 +413,11 @@ class Archive private function getRequestedPlugins($archiveNames) { $result = array(); + foreach ($archiveNames as $name) { $result[] = self::getPluginForReport($name); } + return array_unique($result); } @@ -436,6 +445,7 @@ class Archive * @param int|null $idSubtable See {@link getDataTableExpanded()} * @param bool $skipAggregationOfSubTables Whether or not we should skip the aggregation of all sub-tables and only aggregate parent DataTable. * @param int|null $depth See {@link getDataTableExpanded()} + * @throws \Exception * @return DataTable|DataTable\Map See {@link getDataTable()} and * {@link getDataTableExpanded()} for more * information @@ -445,9 +455,10 @@ class Archive { Piwik::checkUserHasViewAccess($idSite); - if($skipAggregationOfSubTables && ($expanded || $idSubtable)) { + if ($skipAggregationOfSubTables && ($expanded || $idSubtable)) { throw new \Exception("Not expected to skipAggregationOfSubTables when expanded=1 or idSubtable is set."); } + $archive = Archive::build($idSite, $period, $date, $segment, $_restrictSitesToLogin = false, $skipAggregationOfSubTables); if ($idSubtable === false) { $idSubtable = null; @@ -495,6 +506,7 @@ class Archive $archiveNames, $archiveDataType, $this->params->getIdSites(), $this->params->getPeriods(), $defaultRow = null); $archiveIds = $this->getArchiveIds($archiveNames); + if (empty($archiveIds)) { return $result; } @@ -532,7 +544,7 @@ class Archive // figure out which archives haven't been processed (if an archive has been processed, // then we have the archive IDs in $this->idarchives) - $doneFlags = array(); + $doneFlags = array(); $archiveGroups = array(); foreach ($plugins as $plugin) { $doneFlag = $this->getDoneStringForPlugin($plugin); @@ -541,7 +553,7 @@ class Archive if (!isset($this->idarchives[$doneFlag])) { $archiveGroup = $this->getArchiveGroupOfPlugin($plugin); - if($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) { + if ($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) { $archiveGroup = reset($plugins); } $archiveGroups[] = $archiveGroup; @@ -559,19 +571,7 @@ class Archive } } - // order idarchives by the table month they belong to - $idArchivesByMonth = array(); - foreach (array_keys($doneFlags) as $doneFlag) { - if (empty($this->idarchives[$doneFlag])) { - continue; - } - - foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) { - foreach ($idarchives as $id) { - $idArchivesByMonth[$dateRange][] = $id; - } - } - } + $idArchivesByMonth = $this->getIdArchivesByMonth($doneFlags); return $idArchivesByMonth; } @@ -800,9 +800,29 @@ class Archive $idArchive = $archiveLoader->prepareArchive($plugin); - if($idArchive) { + if ($idArchive) { $this->idarchives[$doneFlag][$periodString][] = $idArchive; } } } + + private function getIdArchivesByMonth($doneFlags) + { + // order idarchives by the table month they belong to + $idArchivesByMonth = array(); + + foreach (array_keys($doneFlags) as $doneFlag) { + if (empty($this->idarchives[$doneFlag])) { + continue; + } + + foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) { + foreach ($idarchives as $id) { + $idArchivesByMonth[$dateRange][] = $id; + } + } + } + + return $idArchivesByMonth; + } } diff --git a/core/Archive/DataCollection.php b/core/Archive/DataCollection.php index 89b435f9dcd96f0c89d552d81893a36faf70cd02..efd63cd925b3fa4cc6e3a0c110da2a3ba2551bbf 100644 --- a/core/Archive/DataCollection.php +++ b/core/Archive/DataCollection.php @@ -188,6 +188,7 @@ class DataCollection $this->putRowInIndex($result, $indexKeys, $row, $idSite, $period); } } + return $result; } @@ -208,6 +209,7 @@ class DataCollection $this->dataNames, $this->dataType, $this->sitesId, $this->periods, $this->defaultRow); $index = $this->getIndexedArray($resultIndices); + return $dataTableFactory->make($index, $resultIndices); } @@ -249,6 +251,7 @@ class DataCollection $dataTableFactory->useSubtable($idSubTable); $index = $this->getIndexedArray($resultIndices); + return $dataTableFactory->make($index, $resultIndices); } diff --git a/core/Archive/DataTableFactory.php b/core/Archive/DataTableFactory.php index 41806c62f967d0bbba56e56fb358e4330a0b5645..71eaa7a8c1b8db33f11be4dde542617a69c4ebab 100644 --- a/core/Archive/DataTableFactory.php +++ b/core/Archive/DataTableFactory.php @@ -154,6 +154,7 @@ class DataTableFactory } $this->transformMetadata($dataTable); + return $dataTable; } diff --git a/core/ArchiveProcessor.php b/core/ArchiveProcessor.php index 2df309d69c50bbeb536725ce3953f712a13f25c3..71e90cde23aa3a93f107d98523d4e6b3a8b096f0 100644 --- a/core/ArchiveProcessor.php +++ b/core/ArchiveProcessor.php @@ -79,12 +79,12 @@ class ArchiveProcessor /** * @var \Piwik\DataAccess\ArchiveWriter */ - protected $archiveWriter; + private $archiveWriter; /** * @var \Piwik\DataAccess\LogAggregator */ - protected $logAggregator; + private $logAggregator; /** * @var Archive @@ -94,14 +94,14 @@ class ArchiveProcessor /** * @var Parameters */ - protected $params; + private $params; /** * @var int */ - protected $numberOfVisits = false; + private $numberOfVisits = false; - protected $numberOfVisitsConverted = false; + private $numberOfVisitsConverted = false; /** * If true, unique visitors are not calculated when we are aggregating data for multiple sites. @@ -125,11 +125,12 @@ class ArchiveProcessor protected function getArchive() { - if(empty($this->archive)) { + if (empty($this->archive)) { $subPeriods = $this->params->getSubPeriods(); - $idSites = $this->params->getIdSites(); + $idSites = $this->params->getIdSites(); $this->archive = Archive::factory($this->params->getSegment(), $subPeriods, $idSites); } + return $this->archive; } @@ -208,6 +209,7 @@ class ArchiveProcessor if (!is_array($recordNames)) { $recordNames = array($recordNames); } + $nameToCount = array(); foreach ($recordNames as $recordName) { $latestUsedTableId = Manager::getInstance()->getMostRecentTableId(); @@ -218,7 +220,7 @@ class ArchiveProcessor $nameToCount[$recordName]['level0'] = $rowsCount; $rowsCountRecursive = $rowsCount; - if($this->isAggregateSubTables()) { + if ($this->isAggregateSubTables()) { $rowsCountRecursive = $table->getRowsCountRecursive(); } $nameToCount[$recordName]['recursive'] = $rowsCountRecursive; @@ -271,7 +273,7 @@ class ArchiveProcessor public function getNumberOfVisits() { - if($this->numberOfVisits === false) { + if ($this->numberOfVisits === false) { throw new Exception("visits should have been set here"); } return $this->numberOfVisits; @@ -343,7 +345,7 @@ class ArchiveProcessor */ protected function aggregateDataTableRecord($name, $columnsAggregationOperation = null, $columnsToRenameAfterAggregation = null) { - if($this->isAggregateSubTables()) { + if ($this->isAggregateSubTables()) { // By default we shall aggregate all sub-tables. $dataTable = $this->getArchive()->getDataTableExpanded($name, $idSubTable = null, $depth = null, $addMetadataSubtableId = false); } else { @@ -440,15 +442,18 @@ class ArchiveProcessor protected function getAggregatedDataTableMap($data, $columnsAggregationOperation) { $table = new DataTable(); + if (!empty($columnsAggregationOperation)) { $table->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, $columnsAggregationOperation); } + if ($data instanceof DataTable\Map) { // as $date => $tableToSum $this->aggregatedDataTableMapsAsOne($data, $table); } else { $table->addDataTable($data, $this->isAggregateSubTables()); } + return $table; } @@ -460,7 +465,7 @@ class ArchiveProcessor protected function aggregatedDataTableMapsAsOne(Map $map, DataTable $aggregated) { foreach ($map->getDataTables() as $tableToAggregate) { - if($tableToAggregate instanceof Map) { + if ($tableToAggregate instanceof Map) { $this->aggregatedDataTableMapsAsOne($tableToAggregate, $aggregated); } else { $aggregated->addDataTable($tableToAggregate, $this->isAggregateSubTables()); @@ -477,6 +482,7 @@ class ArchiveProcessor if (is_null($columnsToRenameAfterAggregation)) { $columnsToRenameAfterAggregation = self::$columnsToRenameAfterAggregation; } + foreach ($columnsToRenameAfterAggregation as $oldName => $newName) { $table->renameColumn($oldName, $newName, $this->isAggregateSubTables()); } @@ -487,6 +493,7 @@ class ArchiveProcessor if (!is_array($columns)) { $columns = array($columns); } + $operationForColumn = $this->getOperationForColumns($columns, $operationToApply); $dataTable = $this->getArchive()->getDataTableFromNumeric($columns); @@ -497,7 +504,7 @@ class ArchiveProcessor } $rowMetrics = $results->getFirstRow(); - if($rowMetrics === false) { + if ($rowMetrics === false) { $rowMetrics = new Row; } $this->enrichWithUniqueVisitorsMetric($rowMetrics); @@ -510,6 +517,7 @@ class ArchiveProcessor $metrics[$name] = 0; } } + return $metrics; } diff --git a/core/ArchiveProcessor/Loader.php b/core/ArchiveProcessor/Loader.php index a4ffa4d12eb4a52f20113a18abbe87e460d9e802..b684d8fee1aa9540c174cc8db1d5997d959a0be1 100644 --- a/core/ArchiveProcessor/Loader.php +++ b/core/ArchiveProcessor/Loader.php @@ -10,7 +10,6 @@ namespace Piwik\ArchiveProcessor; use Piwik\Archive; use Piwik\ArchiveProcessor; use Piwik\Config; -use Piwik\DataAccess\ArchivePurger; use Piwik\DataAccess\ArchiveSelector; use Piwik\Date; use Piwik\Period; @@ -82,6 +81,7 @@ class Loader * Prepares the core metrics if needed. * * @param $visits + * @return array */ protected function prepareCoreMetricsArchive($visits, $visitsConverted) { @@ -102,12 +102,14 @@ class Loader $visits = $metrics['nb_visits']; $visitsConverted = $metrics['nb_visits_converted']; } + return array($visits, $visitsConverted); } protected function prepareAllPluginsArchive($visits, $visitsConverted) { $pluginsArchiver = new PluginsArchiver($this->params, $this->isArchiveTemporary()); + if ($this->mustProcessVisitCount($visits) || $this->doesRequestedPluginIncludeVisitsSummary() ) { @@ -115,9 +117,11 @@ class Loader $visits = $metrics['nb_visits']; $visitsConverted = $metrics['nb_visits_converted']; } + if ($this->isThereSomeVisits($visits)) { $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted); } + $idArchive = $pluginsArchiver->finalizeArchive(); return array($idArchive, $visits); @@ -136,11 +140,13 @@ class Loader { $period = $this->params->getPeriod()->getLabel(); $debugSetting = 'always_archive_data_period'; // default + if ($period == 'day') { $debugSetting = 'always_archive_data_day'; } elseif ($period == 'range') { $debugSetting = 'always_archive_data_range'; } + return (bool) Config::getInstance()->Debug[$debugSetting]; } @@ -162,9 +168,11 @@ class Loader } $idAndVisits = ArchiveSelector::getArchiveIdAndVisits($this->params, $minDatetimeArchiveProcessedUTC); + if (!$idAndVisits) { return $noArchiveFound; } + return $idAndVisits; } @@ -183,19 +191,27 @@ class Loader // Permanent archive return $endDateTimestamp; } + + $dateStart = $this->params->getDateStart(); + $period = $this->params->getPeriod(); + $segment = $this->params->getSegment(); + $site = $this->params->getSite(); + // Temporary archive - return Rules::getMinTimeProcessedForTemporaryArchive($this->params->getDateStart(), $this->params->getPeriod(), $this->params->getSegment(), $this->params->getSite()); + return Rules::getMinTimeProcessedForTemporaryArchive($dateStart, $period, $segment, $site); } protected static function determineIfArchivePermanent(Date $dateEnd) { $now = time(); $endTimestampUTC = strtotime($dateEnd->getDateEndUTC()); + if ($endTimestampUTC <= $now) { // - if the period we are looking for is finished, we look for a ts_archived that // is greater than the last day of the archive return $endTimestampUTC; } + return false; } @@ -204,6 +220,7 @@ class Loader if (is_null($this->temporaryArchive)) { throw new \Exception("getMinTimeArchiveProcessed() should be called prior to isArchiveTemporary()"); } + return $this->temporaryArchive; } diff --git a/core/ArchiveProcessor/Parameters.php b/core/ArchiveProcessor/Parameters.php index 79cb1c719685fafe90ae19e3f3b7f2a06c684905..1528d8210c7964ac7683ed51cc06ef39a237a896 100644 --- a/core/ArchiveProcessor/Parameters.php +++ b/core/ArchiveProcessor/Parameters.php @@ -91,7 +91,7 @@ class Parameters */ public function getSubPeriods() { - if($this->getPeriod()->getLabel() == 'day') { + if ($this->getPeriod()->getLabel() == 'day') { return array( $this->getPeriod() ); } return $this->getPeriod()->getSubperiods(); diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php index 10f61287ed737bc84389d0768b0d34cc04b2c05e..2986d05dd3d34bdad76d113895b9105a8cc1d1bd 100644 --- a/core/ArchiveProcessor/PluginsArchiver.php +++ b/core/ArchiveProcessor/PluginsArchiver.php @@ -56,7 +56,7 @@ class PluginsArchiver */ public function callAggregateCoreMetrics() { - if($this->isSingleSiteDayArchive) { + if ($this->isSingleSiteDayArchive) { $metrics = $this->aggregateDayVisitsMetrics(); } else { $metrics = $this->aggregateMultipleVisitsMetrics(); @@ -92,11 +92,11 @@ class PluginsArchiver /** @var Archiver $archiver */ $archiver = new $archiverClass($this->archiveProcessor); - if(!$archiver->isEnabled()) { + if (!$archiver->isEnabled()) { continue; } - if($this->shouldProcessReportsForPlugin($pluginName)) { - if($this->isSingleSiteDayArchive) { + if ($this->shouldProcessReportsForPlugin($pluginName)) { + if ($this->isSingleSiteDayArchive) { $archiver->aggregateDayReport(); } else { $archiver->aggregateMultipleReports(); diff --git a/core/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php index a64a4f81ad2ce4a9f2d9a6b9c344db9ac854515b..31b2fc380aa8cbcb0fd6053eaa870eea18e8e828 100644 --- a/core/ArchiveProcessor/Rules.php +++ b/core/ArchiveProcessor/Rules.php @@ -33,8 +33,6 @@ class Rules const FLAG_TABLE_PURGED = 'lastPurge_'; - public static $purgeOutdatedArchivesIsDisabled = false; - /** Flag that will forcefully disable the archiving process (used in tests only) */ public static $archivingDisabledByTests = false; @@ -129,16 +127,6 @@ class Rules return $doneFlags; } - public static function disablePurgeOutdatedArchives() - { - self::$purgeOutdatedArchivesIsDisabled = true; - } - - public static function enablePurgeOutdatedArchives() - { - self::$purgeOutdatedArchivesIsDisabled = false; - } - /** * Given a monthly archive table, will delete all reports that are now outdated, * or reports that ended with an error @@ -148,9 +136,6 @@ class Rules */ public static function shouldPurgeOutdatedArchives(Date $date) { - if (self::$purgeOutdatedArchivesIsDisabled) { - return false; - } $key = self::FLAG_TABLE_PURGED . "blob_" . $date->toString('Y_m'); $timestamp = Option::get($key); @@ -222,7 +207,7 @@ class Rules { $uiSettingIsEnabled = Controller::isGeneralSettingsAdminEnabled(); - if($uiSettingIsEnabled) { + if ($uiSettingIsEnabled) { $timeToLive = Option::get(self::OPTION_TODAY_ARCHIVE_TTL); if ($timeToLive !== false) { return $timeToLive; @@ -266,7 +251,7 @@ class Rules { $uiSettingIsEnabled = Controller::isGeneralSettingsAdminEnabled(); - if($uiSettingIsEnabled) { + if ($uiSettingIsEnabled) { $browserArchivingEnabled = Option::get(self::OPTION_BROWSER_TRIGGER_ARCHIVING); if ($browserArchivingEnabled !== false) { return (bool)$browserArchivingEnabled; diff --git a/core/AssetManager.php b/core/AssetManager.php index 1badba4d9e676f84b2dd9bcf2cf16519f51e38a0..6759facb2b573f1ed0c66210d7cb1d7f24ba488e 100644 --- a/core/AssetManager.php +++ b/core/AssetManager.php @@ -37,7 +37,7 @@ use Piwik\Translate; * the global option 'disable_merged_assets'. See the documentation in the global * config for more information. * - * @method static \Piwik\AssetManager getInstance() + * @method static AssetManager getInstance() */ class AssetManager extends Singleton { @@ -72,7 +72,7 @@ class AssetManager extends Singleton $this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array('plugins/Morpheus/stylesheets/base.less', 'plugins/Morpheus/stylesheets/general/_forms.less'), array(), $this->theme); $theme = Manager::getInstance()->getThemeEnabled(); - if(!empty($theme)) { + if (!empty($theme)) { $this->theme = new Theme(); } } @@ -206,7 +206,7 @@ class AssetManager extends Singleton $pluginName = $plugin->getPluginName(); $pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName); - if(($pluginIsCore && $core) || (!$pluginIsCore && !$core)) + if (($pluginIsCore && $core) || (!$pluginIsCore && !$core)) $loadedPlugins[] = $pluginName; } @@ -220,12 +220,12 @@ class AssetManager extends Singleton { $assetsToRemove = array($this->getMergedStylesheetAsset()); - if($pluginName) { + if ($pluginName) { - if($this->pluginContainsJScriptAssets($pluginName)) { + if ($this->pluginContainsJScriptAssets($pluginName)) { PiwikConfig::getInstance()->init(); - if(Manager::getInstance()->isPluginBundledWithCore($pluginName)) { + if (Manager::getInstance()->isPluginBundledWithCore($pluginName)) { $assetsToRemove[] = $this->getMergedCoreJSAsset(); @@ -347,13 +347,13 @@ class AssetManager extends Singleton $plugin = Manager::getInstance()->getLoadedPlugin($pluginName); - if($plugin->isTheme()) { + if ($plugin->isTheme()) { $theme = Manager::getInstance()->getTheme($pluginName); $javaScriptFiles = $theme->getJavaScriptFiles(); - if(!empty($javaScriptFiles)) + if (!empty($javaScriptFiles)) $assets = array_merge($assets, $javaScriptFiles); } diff --git a/core/AssetManager/UIAssetCatalog.php b/core/AssetManager/UIAssetCatalog.php index d8a45f89646d927cabbf93dd60b189aed506b370..826259ecb3725cee3f147d776133946abcbb7b45 100644 --- a/core/AssetManager/UIAssetCatalog.php +++ b/core/AssetManager/UIAssetCatalog.php @@ -40,7 +40,7 @@ class UIAssetCatalog { $location = $uiAsset->getAbsoluteLocation(); - if(!$this->assetAlreadyInCatalog($location)) { + if (!$this->assetAlreadyInCatalog($location)) { $this->existingAssetLocations[] = $location; $this->uiAssets[] = $uiAsset; } diff --git a/core/AssetManager/UIAssetFetcher.php b/core/AssetManager/UIAssetFetcher.php index 955ced51a75b3ef99fbab9c859896024266d8972..3bd34c118e60f9f127164d82365ca01e41b97814 100644 --- a/core/AssetManager/UIAssetFetcher.php +++ b/core/AssetManager/UIAssetFetcher.php @@ -56,7 +56,7 @@ abstract class UIAssetFetcher */ public function getCatalog() { - if($this->catalog == null) + if ($this->catalog == null) $this->createCatalog(); return $this->catalog; diff --git a/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php index a5ab3d096d5a745c2824647c241aba6c290a8498..5600068e44d79d2416e2cd366b53376b8fbaf7f4 100644 --- a/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php +++ b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php @@ -18,7 +18,7 @@ class JScriptUIAssetFetcher extends UIAssetFetcher protected function retrieveFileLocations() { - if(!empty($this->plugins)) { + if (!empty($this->plugins)) { /** * Triggered when gathering the list of all JavaScript files needed by Piwik @@ -53,14 +53,14 @@ class JScriptUIAssetFetcher extends UIAssetFetcher protected function addThemeFiles() { $theme = $this->getTheme(); - if(!$theme) { + if (!$theme) { return; } - if(in_array($theme->getThemeName(), $this->plugins)) { + if (in_array($theme->getThemeName(), $this->plugins)) { $jsInThemes = $this->getTheme()->getJavaScriptFiles(); - if(!empty($jsInThemes)) { + if (!empty($jsInThemes)) { foreach($jsInThemes as $jsFile) { diff --git a/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php index 47a78a47cae02138a4cedb18d5ecf74b8991cf89..47ac94d04b27f6d094878365e99df87307d29246 100644 --- a/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php +++ b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php @@ -18,7 +18,6 @@ class StylesheetUIAssetFetcher extends UIAssetFetcher $theme = $this->getTheme(); $themeName = $theme->getThemeName(); - $themeName = $this->getTheme()->getThemeName(); $order = array( 'libs/', 'plugins/CoreHome/stylesheets/color_manager.css', // must be before other Piwik stylesheets @@ -72,7 +71,7 @@ class StylesheetUIAssetFetcher extends UIAssetFetcher protected function addThemeFiles() { $theme = $this->getTheme(); - if(!$theme) { + if (!$theme) { return; } $themeStylesheet = $this->getTheme()->getStylesheet(); diff --git a/core/AssetManager/UIAssetMerger.php b/core/AssetManager/UIAssetMerger.php index 8850de512b45939fd33a6888f661820bd4b8233b..42b5ce0cfee25954d9fe6301801eada6c8ef8d51 100644 --- a/core/AssetManager/UIAssetMerger.php +++ b/core/AssetManager/UIAssetMerger.php @@ -48,7 +48,7 @@ abstract class UIAssetMerger public function generateFile() { - if(!$this->shouldGenerate()) + if (!$this->shouldGenerate()) return; $this->mergedContent = $this->getMergedAssets(); @@ -138,8 +138,9 @@ abstract class UIAssetMerger */ private function shouldGenerate() { - if(!$this->mergedAsset->exists()) + if (!$this->mergedAsset->exists()) { return true; + } return !$this->isFileUpToDate(); } @@ -162,19 +163,11 @@ abstract class UIAssetMerger return false; } - /** - * @return boolean - */ - private function isMergedAssetsDisabled() - { - return AssetManager::getInstance()->isMergedAssetsDisabled(); - } - private function adjustPaths() { $theme = $this->assetFetcher->getTheme(); // During installation theme is not yet ready - if($theme) { + if ($theme) { $this->mergedContent = $this->assetFetcher->getTheme()->rewriteAssetsPathToTheme($this->mergedContent); } } @@ -189,7 +182,7 @@ abstract class UIAssetMerger */ protected function getCacheBusterValue() { - if(empty($this->cacheBusterValue)) + if (empty($this->cacheBusterValue)) $this->cacheBusterValue = $this->generateCacheBuster(); return $this->cacheBusterValue; @@ -199,12 +192,4 @@ abstract class UIAssetMerger { $this->mergedContent = $this->getPreamble() . $this->mergedContent; } - - /** - * @return boolean - */ - private function shouldCompareExistingVersion() - { - return $this->isMergedAssetsDisabled(); - } } diff --git a/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php index bfb2442279f30e73a35c0637df79ae857be5b7ed..935018c580b94c9fe0ad88d9c0ca407660d18b4e 100644 --- a/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php +++ b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php @@ -57,7 +57,7 @@ class JScriptUIAssetMerger extends UIAssetMerger { $plugins = $this->getPlugins(); - if(!empty($plugins)) { + if (!empty($plugins)) { /** * Triggered after all the JavaScript files Piwik uses are minified and merged into a diff --git a/core/AssetManager/UIAssetMinifier.php b/core/AssetManager/UIAssetMinifier.php index 5f738092cc486eeba4faafcdd903bfefa9835f85..07dd3c769dcfa70b22966f6fab5e38d0e9a1c0c3 100644 --- a/core/AssetManager/UIAssetMinifier.php +++ b/core/AssetManager/UIAssetMinifier.php @@ -36,6 +36,7 @@ class UIAssetMinifier extends Singleton public function isMinifiedJs($content) { $lineCount = substr_count($content, "\n"); + if ($lineCount == 0) { return true; } diff --git a/core/Auth.php b/core/Auth.php index 5c858010c46df411434b77d6a8cccfa93f171fa4..8e3a1e28e98646bd0aaa44620e9b53536d050a94 100644 --- a/core/Auth.php +++ b/core/Auth.php @@ -12,20 +12,39 @@ namespace Piwik; use Exception; /** - * Base for authentication implementations. Plugins that provide Auth implementations - * must provide a class that implements this interface. Additionally, an instance - * of that class must be set in the {@link \Piwik\Registry} class with the 'auth' - * key during the {@link Request.initAuthenticationObject} event. + * Base interface for authentication implementations. + * + * Plugins that provide Auth implementations must provide a class that implements + * this interface. Additionally, an instance of that class must be set in the + * {@link \Piwik\Registry} class with the 'auth' key during the + * [Request.initAuthenticationObject](http://developer.piwik.org/api-reference/events#requestinitauthenticationobject) + * event. * * Authentication implementations must support authentication via username and * clear-text password and authentication via username and token auth. They can * additionally support authentication via username and an MD5 hash of a password. If - * they don't support it, then formless authentication will fail. + * they don't support it, then [formless authentication](http://piwik.org/faq/how-to/faq_30/) will fail. * * Derived implementations should favor authenticating by password over authenticating * by token auth. That is to say, if a token auth and a password are set, password * authentication should be used. * + * ### Examples + * + * **How an Auth implementation will be used** + * + * // authenticating by password + * $auth = \Piwik\Registry::get('auth'); + * $auth->setLogin('user'); + * $auth->setPassword('password'); + * $result = $auth->authenticate(); + * + * // authenticating by token auth + * $auth = \Piwik\Registry::get('auth'); + * $auth->setLogin('user'); + * $auth->setTokenAuth('...'); + * $result = $auth->authenticate(); + * * @api */ interface Auth @@ -59,7 +78,7 @@ interface Auth * uses an MD5 hash of a user's password. * * @return string - * @throws Exception if the token auth cannot be calculated at the current time. + * @throws Exception if the token auth secret does not exist or cannot be obtained. */ public function getTokenAuthSecret(); @@ -95,6 +114,9 @@ interface Auth * {@link Piwik\Plugins\Login\SessionInitializer::getHashTokenAuth()} method. * * @return AuthResult + * @throws Exception if the Auth implementation has an invalid state (ie, no login + * was specified). Note: implementations are not **required** to throw + * exceptions for invalid state, but they are allowed to. */ public function authenticate(); } diff --git a/core/BaseFactory.php b/core/BaseFactory.php index 6f09f952efdbe74225dce4aade4f5864d5844a51..0bc8716eb2c03552018a830fb6f7a3efba90a79f 100644 --- a/core/BaseFactory.php +++ b/core/BaseFactory.php @@ -9,7 +9,6 @@ namespace Piwik; use Exception; -use Piwik\Common; /** * Base class for all factory types. diff --git a/core/CacheFile.php b/core/CacheFile.php index 9eb0587595219483a264d52d55275e264ec14677..48a23004bf3521a93264f7c2ca6883ac8f3ad41d 100644 --- a/core/CacheFile.php +++ b/core/CacheFile.php @@ -25,11 +25,7 @@ class CacheFile /** * @var string */ - protected $cachePath; - /** - * @var - */ - protected $cachePrefix; + private $cachePath; /** * Minimum enforced TTL in seconds @@ -67,11 +63,12 @@ class CacheFile if (empty($id)) { return false; } + $id = $this->cleanupId($id); $cache_complete = false; - $content = ''; - $expires_on = false; + $content = ''; + $expires_on = false; // We are assuming that most of the time cache will exists $cacheFilePath = $this->cachePath . $id . '.php'; @@ -88,6 +85,7 @@ class CacheFile ) { return false; } + return $content; } @@ -104,6 +102,7 @@ class CacheFile if (!Filesystem::isValidFilename($id)) { throw new Exception("Invalid cache ID request $id"); } + return $id; } @@ -120,25 +119,23 @@ class CacheFile if (empty($id)) { return false; } + if (!is_dir($this->cachePath)) { Filesystem::mkdir($this->cachePath); } + if (!is_writable($this->cachePath)) { return false; } - $id = $this->cleanupId($id); + $id = $this->cleanupId($id); $id = $this->cachePath . $id . '.php'; if (is_object($content)) { throw new \Exception('You cannot use the CacheFile to cache an object, only arrays, strings and numbers.'); } - $cache_literal = "<" . "?php\n"; - $cache_literal .= "$" . "content = " . var_export($content, true) . ";\n"; - $cache_literal .= "$" . "expires_on = " . $this->getExpiresTime() . ";\n"; - $cache_literal .= "$" . "cache_complete = true;\n"; - $cache_literal .= "?" . ">"; + $cache_literal = $this->buildCacheLiteral($content); // Write cache to a temp file, then rename it, overwriting the old cache // On *nix systems this should guarantee atomicity @@ -162,6 +159,7 @@ class CacheFile return true; } + return false; } @@ -176,14 +174,17 @@ class CacheFile if (empty($id)) { return false; } + $id = $this->cleanupId($id); $filename = $this->cachePath . $id . '.php'; + if (file_exists($filename)) { $this->opCacheInvalidate($filename); @unlink($filename); return true; } + return false; } @@ -218,8 +219,19 @@ class CacheFile @opcache_invalidate($filepath, $force = true); } if (function_exists('apc_delete_file')) { - apc_delete_file($filepath); + @apc_delete_file($filepath); } } } + + private function buildCacheLiteral($content) + { + $cache_literal = "<" . "?php\n"; + $cache_literal .= "$" . "content = " . var_export($content, true) . ";\n"; + $cache_literal .= "$" . "expires_on = " . $this->getExpiresTime() . ";\n"; + $cache_literal .= "$" . "cache_complete = true;\n"; + $cache_literal .= "?" . ">"; + + return $cache_literal; + } } diff --git a/core/CliMulti.php b/core/CliMulti.php index 21e0ee0b0dd773b29cbe5b479653ad2e103e004a..6de96af5b01e90b6598e2c99736db779ca3be91c 100644 --- a/core/CliMulti.php +++ b/core/CliMulti.php @@ -58,13 +58,15 @@ class CliMulti { public function request(array $piwikUrls) { $chunks = array($piwikUrls); - if($this->concurrentProcessesLimit) { + if ($this->concurrentProcessesLimit) { $chunks = array_chunk( $piwikUrls, $this->concurrentProcessesLimit); } + $results = array(); foreach($chunks as $urlsChunk) { $results = array_merge($results, $this->requestUrls($urlsChunk)); } + return $results; } @@ -89,17 +91,22 @@ class CliMulti { private function start($piwikUrls) { foreach ($piwikUrls as $index => $url) { - $cmdId = $this->generateCommandId($url) . $index; - $output = new Output($cmdId); + $cmdId = $this->generateCommandId($url) . $index; + $this->executeUrlCommand($cmdId, $url); + } + } - if ($this->supportsAsync) { - $this->executeAsyncCli($url, $output, $cmdId); - } else { - $this->executeNotAsyncHttp($url, $output); - } + private function executeUrlCommand($cmdId, $url) + { + $output = new Output($cmdId); - $this->outputs[] = $output; + if ($this->supportsAsync) { + $this->executeAsyncCli($url, $output, $cmdId); + } else { + $this->executeNotAsyncHttp($url, $output); } + + $this->outputs[] = $output; } private function buildCommand($hostname, $query, $outputFile) @@ -192,7 +199,7 @@ class CliMulti { $timeOneWeekAgo = strtotime('-1 week'); $files = _glob(self::getTmpPath() . '/*'); - if(empty($files)) { + if (empty($files)) { return; } diff --git a/core/CliMulti/CliPhp.php b/core/CliMulti/CliPhp.php index df82e05376787703b51b4f3c9dd31c219a455eae..9ee5b191b1b7c50af37bcfb852d795c9b77cd1d1 100644 --- a/core/CliMulti/CliPhp.php +++ b/core/CliMulti/CliPhp.php @@ -18,12 +18,12 @@ class CliPhp { if (defined('PHP_BINARY')) { - if($this->isValidPhpType(PHP_BINARY)) { - return PHP_BINARY . ' -q'; + if ($this->isHhvmBinary(PHP_BINARY)) { + return PHP_BINARY . ' --php'; } - if($this->isHhvmBinary(PHP_BINARY)) { - return PHP_BINARY . ' --php'; + if ($this->isValidPhpType(PHP_BINARY)) { + return PHP_BINARY . ' -q'; } } diff --git a/core/CliMulti/Process.php b/core/CliMulti/Process.php index e8dfb662a4ab4261e27ffa2234c88bfd38d5650e..15dc539e59c7b67c315fc243e66575c20d268cef 100644 --- a/core/CliMulti/Process.php +++ b/core/CliMulti/Process.php @@ -154,7 +154,7 @@ class Process return false; } - if(!self::isProcFSMounted()) { + if (!self::isProcFSMounted()) { return false; } @@ -169,11 +169,11 @@ class Process { $uname = @shell_exec('uname -a'); - if(empty($uname)) { + if (empty($uname)) { $uname = php_uname(); } - if(strpos($uname, 'synology') !== false) { + if (strpos($uname, 'synology') !== false) { return true; } return false; @@ -208,12 +208,12 @@ class Process */ private static function isProcFSMounted() { - if(is_resource(@fopen('/proc', 'r'))) { + if (is_resource(@fopen('/proc', 'r'))) { return true; } // Testing if /proc is a resource with @fopen fails on systems with open_basedir set. // by using stat we not only test the existance of /proc but also confirm it's a 'proc' filesystem - $type = shell_exec('stat -f -c "%T" /proc 2>/dev/null'); + $type = @shell_exec('stat -f -c "%T" /proc 2>/dev/null'); return strpos($type, 'proc') === 0; } diff --git a/core/Columns/Updater.php b/core/Columns/Updater.php index 2dd6e4040e8e55ea5552092c580803f04eab0167..1bc53607ca27a9983d2ced5b487f2dcea9e82f8b 100644 --- a/core/Columns/Updater.php +++ b/core/Columns/Updater.php @@ -87,17 +87,17 @@ class Updater extends \Piwik\Updates $changingColumns = array(); - foreach (VisitDimension::getAllDimensions() as $dimension) { + foreach (self::getVisitDimensions() as $dimension) { $updates = self::getUpdatesForDimension($dimension, 'log_visit.', $visitColumns, $conversionColumns); $changingColumns = self::mixinUpdates($changingColumns, $updates); } - foreach (ActionDimension::getAllDimensions() as $dimension) { + foreach (self::getActionDimensions() as $dimension) { $updates = self::getUpdatesForDimension($dimension, 'log_link_visit_action.', $actionColumns); $changingColumns = self::mixinUpdates($changingColumns, $updates); } - foreach (ConversionDimension::getAllDimensions() as $dimension) { + foreach (self::getConversionDimensions() as $dimension) { $updates = self::getUpdatesForDimension($dimension, 'log_conversion.', $conversionColumns); $changingColumns = self::mixinUpdates($changingColumns, $updates); } @@ -167,15 +167,15 @@ class Updater extends \Piwik\Updates $actionColumns = DbHelper::getTableColumns(Common::prefixTable('log_link_visit_action')); $conversionColumns = DbHelper::getTableColumns(Common::prefixTable('log_conversion')); - foreach (VisitDimension::getAllDimensions() as $dimension) { + foreach (self::getVisitDimensions() as $dimension) { $versions = self::mixinVersions($dimension, 'log_visit.', $visitColumns, $versions); } - foreach (ActionDimension::getAllDimensions() as $dimension) { + foreach (self::getActionDimensions() as $dimension) { $versions = self::mixinVersions($dimension, 'log_link_visit_action.', $actionColumns, $versions); } - foreach (ConversionDimension::getAllDimensions() as $dimension) { + foreach (self::getConversionDimensions() as $dimension) { $versions = self::mixinVersions($dimension, 'log_conversion.', $conversionColumns, $versions); } @@ -327,4 +327,25 @@ class Updater extends \Piwik\Updates return array(); } + + private static function getVisitDimensions() + { + return VisitDimension::getAllDimensions(); + } + + /** + * @return mixed|Dimension[] + */ + private static function getActionDimensions() + { + return ActionDimension::getAllDimensions(); + } + + /** + * @return mixed|Dimension[] + */ + private static function getConversionDimensions() + { + return ConversionDimension::getAllDimensions(); + } } diff --git a/core/Common.php b/core/Common.php index d9ddf0be25609755ba881902a74043d835571f6b..5c1391bfdb65414abf1f8949e58c1e65a39f91b7 100644 --- a/core/Common.php +++ b/core/Common.php @@ -362,10 +362,13 @@ class Common */ private static function undoMagicQuotes($value) { - return version_compare(PHP_VERSION, '5.4', '<') - && get_magic_quotes_gpc() - ? stripslashes($value) - : $value; + if (version_compare(PHP_VERSION, '5.4', '<') && + get_magic_quotes_gpc()) { + + $value = stripslashes($value); + } + + return $value; } /** @@ -375,8 +378,7 @@ class Common */ public static function sanitizeLineBreaks($value) { - $value = str_replace(array("\n", "\r", "\0"), '', $value); - return $value; + return str_replace(array("\n", "\r", "\0"), '', $value); } /** @@ -406,6 +408,7 @@ class Common if (is_null($requestArrayToUse)) { $requestArrayToUse = $_GET + $_POST; } + $varDefault = self::sanitizeInputValues($varDefault); if ($varType === 'int') { // settype accepts only integer @@ -469,6 +472,7 @@ class Common } settype($value, $varType); } + return $value; } @@ -496,14 +500,17 @@ class Common public static function hash($str, $raw_output = false) { static $hashAlgorithm = null; + if (is_null($hashAlgorithm)) { $hashAlgorithm = @Config::getInstance()->General['hash_algorithm']; } if ($hashAlgorithm) { $hash = @hash($hashAlgorithm, $str, $raw_output); - if ($hash !== false) + if ($hash !== false) { + return $hash; + } } return md5($str, $raw_output); @@ -520,7 +527,7 @@ class Common public static function getRandomString($length = 16, $alphabet = "abcdefghijklmnoprstuvwxyz0123456789") { $chars = $alphabet; - $str = ''; + $str = ''; list($usec, $sec) = explode(" ", microtime()); $seed = ((float)$sec + (float)$usec) * 100000; @@ -530,6 +537,7 @@ class Common $rand_key = mt_rand(0, strlen($chars) - 1); $str .= substr($chars, $rand_key, 1); } + return str_shuffle($str); } @@ -571,6 +579,7 @@ class Common ) { throw new Exception("visitorId is expected to be a " . Tracker::LENGTH_HEX_ID_STRING . " hex char string"); } + return self::hex2bin($id); } @@ -584,6 +593,7 @@ class Common { require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php'; $userIdHashed = \PiwikTracker::getUserIdHashed($userId); + return self::convertVisitorIdToBin($userIdHashed); } @@ -730,6 +740,7 @@ class Common if ($includeInternalCodes) { return array_merge($countriesList, $extras); } + return $countriesList; } @@ -1054,7 +1065,7 @@ class Common public static function sendHeader($header, $replace = true) { // don't send header in CLI mode - if(!Common::isPhpCliMode() and !headers_sent()) { + if (!Common::isPhpCliMode() and !headers_sent()) { header($header, $replace); } } diff --git a/core/Config.php b/core/Config.php index 9d6326bb2e76dea8cbb6e291d86cde6adfd30c0a..749c585647c96bf65b1bd90d328fc22a5071f380 100644 --- a/core/Config.php +++ b/core/Config.php @@ -36,7 +36,7 @@ use Exception; * Config::getInstance()->MySection = array('myoption' => 1); * Config::getInstance()->forceSave(); * - * @method static \Piwik\Config getInstance() + * @method static Config getInstance() */ class Config extends Singleton { @@ -416,9 +416,9 @@ class Config extends Singleton $section = $this->getFromGlobalConfig($name); $sectionCommon = $this->getFromCommonConfig($name); - if(empty($section) && !empty($sectionCommon)) { + if (empty($section) && !empty($sectionCommon)) { $section = $sectionCommon; - } elseif(!empty($section) && !empty($sectionCommon)) { + } elseif (!empty($section) && !empty($sectionCommon)) { $section = $this->array_merge_recursive_distinct($section, $sectionCommon); } @@ -459,6 +459,7 @@ class Config extends Singleton $user['bridge'] = 1; return $user; } + } catch (Exception $e) {} return array(); @@ -559,7 +560,7 @@ class Config extends Singleton } // If there is a common.config.ini.php, this will ensure config.ini.php does not duplicate its values - if(!empty($configCommon)) { + if (!empty($configCommon)) { $configGlobal = $this->array_merge_recursive_distinct($configGlobal, $configCommon); } diff --git a/core/Console.php b/core/Console.php index 03f9df438dc0c5d38ead6f91bcc2eb3b49e8b439..423c06d7713efddaa670a3a1addce4d4c3d28105 100644 --- a/core/Console.php +++ b/core/Console.php @@ -40,6 +40,7 @@ class Console extends Application { $this->initPiwikHost($input); $this->initConfig($output); + try { self::initPlugins(); } catch(\Exception $e) { @@ -51,16 +52,24 @@ class Console extends Application $commands = $this->getAvailableCommands(); foreach ($commands as $command) { - if (!class_exists($command)) { - Log::warning(sprintf('Cannot add command %s, class does not exist', $command)); - } elseif (!is_subclass_of($command, 'Piwik\Plugin\ConsoleCommand')) { - Log::warning(sprintf('Cannot add command %s, class does not extend Piwik\Plugin\ConsoleCommand', $command)); - } else { - $this->add(new $command); - } + $this->addCommandIfExists($command); } - return parent::doRun($input, $output); + $self = $this; + return Access::doAsSuperUser(function () use ($input, $output, $self) { + return call_user_func(array($self, 'Symfony\Component\Console\Application::doRun'), $input, $output); + }); + } + + private function addCommandIfExists($command) + { + if (!class_exists($command)) { + Log::warning(sprintf('Cannot add command %s, class does not exist', $command)); + } elseif (!is_subclass_of($command, 'Piwik\Plugin\ConsoleCommand')) { + Log::warning(sprintf('Cannot add command %s, class does not extend Piwik\Plugin\ConsoleCommand', $command)); + } else { + $this->add(new $command); + } } /** @@ -128,9 +137,11 @@ class Console extends Application protected function initConfig(OutputInterface $output) { $config = Config::getInstance(); + try { $config->checkLocalConfigFound(); return $config; + } catch (\Exception $e) { $output->writeln($e->getMessage() . "\n"); } @@ -151,6 +162,8 @@ class Console extends Application $extra = new \Piwik\Plugins\EnterpriseAdmin\EnterpriseAdmin(); $extra->addConsoleCommands($commands); } + return $commands; } + } diff --git a/core/Cookie.php b/core/Cookie.php index d081ec944e79e3a3ba01c2cc4108792f4a8f1cb9..993d97f1e29170ab171c49c2dd2cc8bd34d47f28 100644 --- a/core/Cookie.php +++ b/core/Cookie.php @@ -200,12 +200,14 @@ class Cookie private function extractSignedContent($content) { $signature = substr($content, -40); + if (substr($content, -43, 3) == self::VALUE_SEPARATOR . '_=' && $signature == sha1(substr($content, 0, -40) . SettingsPiwik::getSalt()) ) { // strip trailing: VALUE_SEPARATOR '_=' signature" return substr($content, 0, -43); } + return false; } @@ -218,6 +220,7 @@ class Cookie protected function loadContentFromCookie() { $cookieStr = $this->extractSignedContent($_COOKIE[$this->name]); + if ($cookieStr === false) { return; } @@ -255,6 +258,7 @@ class Cookie protected function generateContentString() { $cookieStr = ''; + foreach ($this->value as $name => $value) { if (!is_numeric($value)) { $value = base64_encode(safe_serialize($value)); @@ -335,6 +339,7 @@ class Cookie $this->value[$name] = $value; return; } + $this->value[$this->keyStore][$name] = $value; } @@ -347,14 +352,19 @@ class Cookie public function get($name) { $name = self::escapeValue($name); - if ($this->keyStore === false) { - return isset($this->value[$name]) - ? self::escapeValue($this->value[$name]) - : false; + if (false === $this->keyStore) { + if (isset($this->value[$name])) { + return self::escapeValue($this->value[$name]); + } + + return false; + } + + if (isset($this->value[$this->keyStore][$name])) { + return self::escapeValue($this->value[$this->keyStore][$name]); } - return isset($this->value[$this->keyStore][$name]) - ? self::escapeValue($this->value[$this->keyStore][$name]) - : false; + + return false; } /** @@ -364,8 +374,9 @@ 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\n"; $str .= var_export($this->value, $return = true); + return $str; } diff --git a/core/CronArchive.php b/core/CronArchive.php index 442480051ba9161a28e3c2b22278a99f5c5c0131..47fbd7d3dfaa839b53d8c3c6730b46c5db061a9c 100644 --- a/core/CronArchive.php +++ b/core/CronArchive.php @@ -181,6 +181,13 @@ class CronArchive */ public $concurrentRequestsPerWebsite = false; + private $websitesWithVisitsSinceLastRun = 0; + private $skippedPeriodsArchivesWebsite = 0; + private $skippedDayArchivesWebsites = 0; + private $skipped = 0; + private $processed = 0; + private $archivedPeriodsArchivesWebsite = 0; + /** * Returns the option name of the option that stores the time core:archive was last executed. * @@ -212,10 +219,13 @@ class CronArchive */ public function main() { - $this->init(); - $this->run(); - $this->runScheduledTasks(); - $this->end(); + $self = $this; + Access::doAsSuperUser(function () use ($self) { + $self->init(); + $self->run(); + $self->runScheduledTasks(); + $self->end(); + }); } public function init() @@ -225,7 +235,6 @@ class CronArchive $this->initTokenAuth(); $this->initCheckCli(); $this->initStateFromParameters(); - Piwik::setUserHasSuperUserAccess(true); $this->logInitInfo(); $this->checkPiwikUrlIsValid(); @@ -234,10 +243,10 @@ class CronArchive // record archiving start time Option::set(self::OPTION_ARCHIVING_STARTED_TS, time()); - $this->segments = $this->initSegmentsToArchive(); + $this->segments = $this->initSegmentsToArchive(); $this->allWebsites = APISitesManager::getInstance()->getAllSitesId(); - if(!empty($this->shouldArchiveOnlySpecificPeriods)) { + if (!empty($this->shouldArchiveOnlySpecificPeriods)) { $this->log("- Will process the following periods: " . implode(", ", $this->shouldArchiveOnlySpecificPeriods) . " (--force-periods)"); } @@ -279,13 +288,6 @@ class CronArchive $this->runScheduledTasks(); } - private $websitesWithVisitsSinceLastRun = 0; - private $skippedPeriodsArchivesWebsite = 0; - private $skippedDayArchivesWebsites = 0; - private $skipped = 0; - private $processed = 0; - private $archivedPeriodsArchivesWebsite = 0; - /** * Main function, runs archiving on all websites with new activity */ @@ -310,7 +312,7 @@ class CronArchive } $skipWebsiteForced = in_array($idSite, $this->shouldSkipSpecifiedSites); - if($skipWebsiteForced) { + if ($skipWebsiteForced) { $this->log("Skipped website id $idSite, found in --skip-idsites "); $this->skipped++; continue; @@ -380,6 +382,7 @@ class CronArchive // do not logError since errors are already in stderr $this->log("Error: " . $error); } + $summary = count($this->errors) . " total errors during this script execution, please investigate and try and fix these errors."; $this->logFatalError($summary); } @@ -402,9 +405,11 @@ class CronArchive $this->log("Starting Scheduled tasks... "); $tasksOutput = $this->request("?module=API&method=CoreAdminHome.runScheduledTasks&format=csv&convertToUnicode=0&token_auth=" . $this->token_auth); + if ($tasksOutput == \Piwik\DataTable\Renderer\Csv::NO_DATA_AVAILABLE) { $tasksOutput = " No task to run"; } + $this->log($tasksOutput); $this->log("done"); $this->logSection(""); @@ -415,6 +420,7 @@ class CronArchive $timerWebsite = new Timer; $lastTimestampWebsiteProcessedPeriods = $lastTimestampWebsiteProcessedDay = false; + if ($this->archiveAndRespectTTL) { Option::clearCachedOption($this->lastRunKey($idSite, "periods")); $lastTimestampWebsiteProcessedPeriods = Option::get($this->lastRunKey($idSite, "periods")); @@ -433,6 +439,7 @@ class CronArchive if ($this->processPeriodsMaximumEverySeconds > 10 * 60) { $secondsSinceLastExecution += 5 * 60; } + $shouldArchivePeriods = $secondsSinceLastExecution > $this->processPeriodsMaximumEverySeconds; if (empty($lastTimestampWebsiteProcessedPeriods)) { // 2) OR always if script never executed for this website before @@ -455,7 +462,7 @@ class CronArchive } $websiteIdIsForced = in_array($idSite, $this->shouldArchiveSpecifiedSites); - if($websiteIdIsForced) { + if ($websiteIdIsForced) { $shouldArchivePeriods = true; } @@ -492,7 +499,7 @@ class CronArchive } $shouldProceed = $this->processArchiveDays($idSite, $lastTimestampWebsiteProcessedDay, $shouldArchivePeriods, $timerWebsite); - if(!$shouldProceed) { + if (!$shouldProceed) { return false; } @@ -508,7 +515,7 @@ class CronArchive $success = true; foreach (array('week', 'month', 'year') as $period) { - if(!$this->shouldProcessPeriod($period)) { + if (!$this->shouldProcessPeriod($period)) { // if any period was skipped, we do not mark the Periods archiving as successful $success = false; continue; @@ -521,6 +528,7 @@ class CronArchive if ($success) { Option::set($this->lastRunKey($idSite, "periods"), time()); } + $this->archivedPeriodsArchivesWebsite++; $requestsWebsite = $this->requests - $requestsBefore; @@ -568,9 +576,11 @@ class CronArchive private function initSegmentsToArchive() { $segments = \Piwik\SettingsPiwik::getKnownSegmentsToArchive(); + if (empty($segments)) { return array(); } + $this->log("- Will pre-process " . count($segments) . " Segments for each website and each period: " . implode(", ", $segments)); return $segments; } @@ -603,7 +613,7 @@ class CronArchive // when some data was purged from this website // we make sure we query all previous days/weeks/months $processDaysSince = $lastTimestampWebsiteProcessedDay; - if($this->isOldReportInvalidatedForWebsite($idSite) + if ($this->isOldReportInvalidatedForWebsite($idSite) // when --force-all-websites option, // also forces to archive last52 days to be safe || $this->shouldArchiveAllSites) { @@ -634,7 +644,7 @@ class CronArchive $this->processed++; // If there is no visit today and we don't need to process this website, we can skip remaining archives - if ($visitsToday == 0 + if (0 == $visitsToday && !$shouldArchivePeriods ) { $this->log("Skipped website id $idSite, no visit today, " . $timerWebsite->__toString()); @@ -642,7 +652,7 @@ class CronArchive return false; } - if ($visitsLastDays == 0 + if (0 == $visitsLastDays && !$shouldArchivePeriods && $this->shouldArchiveAllSites ) { @@ -662,7 +672,7 @@ class CronArchive private function getSegmentsForSite($idSite) { $segmentsAllSites = $this->segments; - $segmentsThisSite = \Piwik\SettingsPiwik::getKnownSegmentsToArchiveForSite($idSite); + $segmentsThisSite = SettingsPiwik::getKnownSegmentsToArchiveForSite($idSite); if (!empty($segmentsThisSite)) { $this->log("Will pre-process the following " . count($segmentsThisSite) . " Segments for this website (id = $idSite): " . implode(", ", $segmentsThisSite)); } @@ -731,7 +741,7 @@ class CronArchive } // we have already logged the daily archive above - if($period != "day") { + if ($period != "day") { $this->logArchivedWebsite($idSite, $period, $date, $visitsInLastPeriods, $visitsLastPeriod, $timer); } @@ -744,7 +754,7 @@ class CronArchive private function logSection($title = "") { $this->log("---------------------------"); - if(!empty($title)) { + if (!empty($title)) { $this->log($title); } } @@ -788,7 +798,7 @@ class CronArchive { $url = $this->piwikUrl . $url . self::APPEND_TO_API_REQUEST; - if($this->shouldStartProfiler) { + if ($this->shouldStartProfiler) { $url .= "&xhprof=2"; } @@ -851,6 +861,7 @@ class CronArchive if (Common::isPhpCliMode()) { return; } + $token_auth = Common::getRequestVar('token_auth', '', 'string'); if ($token_auth !== $this->token_auth || strlen($token_auth) != 32 @@ -892,7 +903,7 @@ class CronArchive $this->shouldArchiveOnlySitesWithTrafficSince = $this->isShouldArchiveAllSitesWithTrafficSince(); $this->shouldArchiveOnlySpecificPeriods = $this->getPeriodsToProcess(); - if($this->shouldArchiveOnlySitesWithTrafficSince === false) { + if ($this->shouldArchiveOnlySitesWithTrafficSince === false) { // force-all-periods is not set here if (empty($this->lastSuccessRunTimestamp)) { // First time we run the script @@ -905,7 +916,7 @@ class CronArchive // force-all-periods is set here $this->archiveAndRespectTTL = false; - if($this->shouldArchiveOnlySitesWithTrafficSince === true) { + if ($this->shouldArchiveOnlySitesWithTrafficSince === true) { // force-all-periods without value $this->shouldArchiveOnlySitesWithTrafficSince = self::ARCHIVE_SITES_WITH_TRAFFIC_SINCE; } @@ -935,7 +946,7 @@ class CronArchive */ public function initWebsiteIds() { - if(count($this->shouldArchiveSpecifiedSites) > 0) { + if (count($this->shouldArchiveSpecifiedSites) > 0) { $this->log("- Will process " . count($this->shouldArchiveSpecifiedSites) . " websites (--force-idsites)"); return $this->shouldArchiveSpecifiedSites; @@ -955,11 +966,14 @@ class CronArchive private function initTokenAuth() { - $superUser = Db::get()->fetchRow("SELECT login, token_auth - FROM " . Common::prefixTable("user") . " - WHERE superuser_access = 1 - ORDER BY date_registered ASC"); - $this->token_auth = $superUser['token_auth']; + $token = ''; + + /** + * @ignore + */ + Piwik::postEvent('CronArchive.getTokenAuth', array(&$token)); + + $this->token_auth = $token; } private function initPiwikHost($piwikUrl = false) @@ -978,12 +992,12 @@ class CronArchive $this->logFatalErrorUrlExpected(); } - if(!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) { + if (!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) { // try adding http:// in case it's missing $piwikUrl = "http://" . $piwikUrl; } - if(!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) { + if (!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) { $this->logFatalErrorUrlExpected(); } @@ -1099,12 +1113,14 @@ class CronArchive $websiteDayHasFinishedSinceLastRun = APISitesManager::getInstance()->getSitesIdFromTimezones($timezones); $websiteDayHasFinishedSinceLastRun = array_diff($websiteDayHasFinishedSinceLastRun, $websiteIds); $this->websiteDayHasFinishedSinceLastRun = $websiteDayHasFinishedSinceLastRun; + if (count($websiteDayHasFinishedSinceLastRun) > 0) { $ids = !empty($websiteDayHasFinishedSinceLastRun) ? ", IDs: " . implode(", ", $websiteDayHasFinishedSinceLastRun) : ""; $this->log("- Will process " . count($websiteDayHasFinishedSinceLastRun) . " other websites because the last time they were archived was on a different day (in the website's timezone) " . $ids); } + return $websiteDayHasFinishedSinceLastRun; } @@ -1170,6 +1186,7 @@ class CronArchive $this->log("WARNING: Automatically increasing --force-timeout-for-periods from {$this->forceTimeoutPeriod} to " . $this->todayArchiveTimeToLive . " to match the cache timeout for Today's report specified in Piwik UI > Settings > General Settings"); + return $this->todayArchiveTimeToLive; } @@ -1178,11 +1195,13 @@ class CronArchive if (empty($this->shouldArchiveAllPeriodsSince)) { return false; } + if (is_numeric($this->shouldArchiveAllPeriodsSince) && $this->shouldArchiveAllPeriodsSince > 1 ) { return (int)$this->shouldArchiveAllPeriodsSince; } + return true; } @@ -1192,6 +1211,7 @@ class CronArchive protected function removeWebsiteFromInvalidatedWebsites($idSite) { $websiteIdsInvalidated = APICoreAdminHome::getWebsiteIdsToInvalidate(); + if (count($websiteIdsInvalidated)) { $found = array_search($idSite, $websiteIdsInvalidated); if ($found !== false) { @@ -1209,25 +1229,29 @@ class CronArchive private function getVisitsLastPeriodFromApiResponse($stats) { - if(empty($stats)) { + if (empty($stats)) { return 0; } + $today = end($stats); + return $today['nb_visits']; } private function getVisitsFromApiResponse($stats) { - if(empty($stats)) { + if (empty($stats)) { return 0; } + $visits = 0; foreach($stats as $metrics) { - if(empty($metrics['nb_visits'])) { + if (empty($metrics['nb_visits'])) { continue; } $visits += $metrics['nb_visits']; } + return $visits; } @@ -1240,9 +1264,11 @@ class CronArchive private function getApiDateParameter($idSite, $period, $lastTimestampWebsiteProcessed = false) { $dateRangeForced = $this->getDateRangeToProcess(); - if(!empty($dateRangeForced)) { + + if (!empty($dateRangeForced)) { return $dateRangeForced; } + return $this->getDateLastN($idSite, $period, $lastTimestampWebsiteProcessed); } @@ -1256,7 +1282,7 @@ class CronArchive */ private function logArchivedWebsite($idSite, $period, $date, $visitsInLastPeriods, $visitsToday, Timer $timer) { - if(substr($date, 0, 4) === 'last') { + if (substr($date, 0, 4) === 'last') { $visitsInLastPeriods = (int)$visitsInLastPeriods . " visits in last " . $date . " " . $period . "s, "; $thisPeriod = $period == "day" ? "today" : "this " . $period; $visitsInLastPeriod = (int)$visitsToday . " visits " . $thisPeriod . ", "; @@ -1276,9 +1302,11 @@ class CronArchive if (empty($this->restrictToDateRange)) { return false; } + if (strpos($this->restrictToDateRange, ',') === false) { throw new Exception("--force-date-range expects a date range ie. YYYY-MM-DD,YYYY-MM-DD"); } + return $this->restrictToDateRange; } @@ -1289,6 +1317,7 @@ class CronArchive { $this->restrictToPeriods = array_intersect($this->restrictToPeriods, $this->getDefaultPeriodsToProcess()); $this->restrictToPeriods = array_intersect($this->restrictToPeriods, PeriodFactory::getPeriodsEnabledForAPI()); + return $this->restrictToPeriods; } @@ -1311,9 +1340,10 @@ class CronArchive private function shouldProcessPeriod($period) { - if(empty($this->shouldArchiveOnlySpecificPeriods)) { + if (empty($this->shouldArchiveOnlySpecificPeriods)) { return true; } + return in_array($period, $this->shouldArchiveOnlySpecificPeriods); } @@ -1344,6 +1374,7 @@ class CronArchive if (!empty($this->dateLastForced)) { $dateLast = $this->dateLastForced; } + return "last" . $dateLast; } @@ -1352,9 +1383,10 @@ class CronArchive */ private function getConcurrentRequestsPerWebsite() { - if ($this->concurrentRequestsPerWebsite !== false) { + if (false !== $this->concurrentRequestsPerWebsite) { return $this->concurrentRequestsPerWebsite; } + return self::MAX_CONCURRENT_API_REQUESTS; } } diff --git a/core/DataAccess/ArchivePurger.php b/core/DataAccess/ArchivePurger.php index 94e1622376c983234b3865819662a37eb83f3377..d795ee6fba16c7c4986597ae699ff8162b676dfc 100644 --- a/core/DataAccess/ArchivePurger.php +++ b/core/DataAccess/ArchivePurger.php @@ -31,16 +31,7 @@ class ArchivePurger * Select the archives that have already been invalidated and have been since re-processed. * It purges records for each distinct { archive name (includes segment hash) , idsite, date, period } tuple. */ - $query = ' - SELECT t1.idarchive FROM `' . $archiveTable . '` t1 - INNER JOIN `' . $archiveTable . '` t2 - ON t1.name = t2.name AND t1.idsite=t2.idsite - AND t1.date1=t2.date1 AND t1.date2=t2.date2 AND t1.period=t2.period - WHERE t1.value = ' . ArchiveWriter::DONE_INVALIDATED . ' - AND t2.value IN(' . ArchiveWriter::DONE_OK . ', ' . ArchiveWriter::DONE_OK_TEMPORARY . ') - AND t1.ts_archived < t2.ts_archived AND t1.name LIKE \'done%\''; - - $result = Db::fetchAll($query); + $result = self::getModel()->purgeInvalidatedArchiveTable($archiveTable); if (count($result) > 0) { $archiveIds = array_map( @@ -59,36 +50,39 @@ class ArchivePurger } } + private static function getModel() + { + return new Model(); + } public static function purgeOutdatedArchives(Date $dateStart) { $purgeArchivesOlderThan = Rules::shouldPurgeOutdatedArchives($dateStart); + if (!$purgeArchivesOlderThan) { return; } $idArchivesToDelete = self::getTemporaryArchiveIdsOlderThan($dateStart, $purgeArchivesOlderThan); + if (!empty($idArchivesToDelete)) { self::deleteArchiveIds($dateStart, $idArchivesToDelete); } + self::deleteArchivesWithPeriodRange($dateStart); Log::debug("Purging temporary archives: done [ purged archives older than %s in %s ] [Deleted IDs: %s]", - $purgeArchivesOlderThan, - $dateStart->toString("Y-m"), - implode(',', $idArchivesToDelete)); + $purgeArchivesOlderThan, + $dateStart->toString("Y-m"), + implode(',', $idArchivesToDelete)); } protected static function getTemporaryArchiveIdsOlderThan(Date $date, $purgeArchivesOlderThan) { - $query = "SELECT idarchive - FROM " . ArchiveTableCreator::getNumericTable($date) . " - WHERE name LIKE 'done%' - AND (( value = " . ArchiveWriter::DONE_OK_TEMPORARY . " - AND ts_archived < ?) - OR value = " . ArchiveWriter::DONE_ERROR . ")"; - - $result = Db::fetchAll($query, array($purgeArchivesOlderThan)); + $archiveTable = ArchiveTableCreator::getNumericTable($date); + + $result = self::getModel()->getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan); + $idArchivesToDelete = array(); if (!empty($result)) { foreach ($result as $row) { @@ -104,36 +98,25 @@ class ArchivePurger */ protected static function deleteArchivesWithPeriodRange(Date $date) { - $query = "DELETE FROM %s WHERE period = ? AND ts_archived < ?"; - - $yesterday = Date::factory('yesterday')->getDateTime(); - $bind = array(Piwik::$idPeriods['range'], $yesterday); $numericTable = ArchiveTableCreator::getNumericTable($date); - Db::query(sprintf($query, $numericTable), $bind); + $blobTable = ArchiveTableCreator::getBlobTable($date); + $yesterday = Date::factory('yesterday')->getDateTime(); + Log::debug("Purging Custom Range archives: done [ purged archives older than %s from %s / blob ]", - $yesterday, - $numericTable); - try { - Db::query(sprintf($query, ArchiveTableCreator::getBlobTable($date)), $bind); - } catch (Exception $e) { - // Individual blob tables could be missing - } + $yesterday, $numericTable); + + self::getModel()->deleteArchivesWithPeriodRange($numericTable, $blobTable, Piwik::$idPeriods['range'], $yesterday); } protected static function deleteArchiveIds(Date $date, $idArchivesToDelete) { - $batches = array_chunk($idArchivesToDelete, 1000); - foreach ($batches as $idsToDelete) { - $query = "DELETE FROM %s WHERE idarchive IN (" . implode(',', $idsToDelete) . ")"; + $batches = array_chunk($idArchivesToDelete, 1000); + $numericTable = ArchiveTableCreator::getNumericTable($date); + $blobTable = ArchiveTableCreator::getBlobTable($date); - Db::query(sprintf($query, ArchiveTableCreator::getNumericTable($date))); - try { - Db::query(sprintf($query, ArchiveTableCreator::getBlobTable($date))); - } catch (Exception $e) { - // Individual blob tables could be missing - } + foreach ($batches as $idsToDelete) { + self::getModel()->deleteArchiveIds($numericTable, $blobTable, $idsToDelete); } - } } diff --git a/core/DataAccess/ArchiveSelector.php b/core/DataAccess/ArchiveSelector.php index 5a715e69396e8b7d3e0887a44ed97c769baaa808..a342257eca4eb1714643489357890a2e2efc58ad 100644 --- a/core/DataAccess/ArchiveSelector.php +++ b/core/DataAccess/ArchiveSelector.php @@ -14,10 +14,8 @@ use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Date; use Piwik\Db; -use Piwik\Log; use Piwik\Period; use Piwik\Period\Range; -use Piwik\Piwik; use Piwik\Segment; /** @@ -40,40 +38,36 @@ class ArchiveSelector const NB_VISITS_CONVERTED_RECORD_LOOKED_UP = "nb_visits_converted"; + private static function getModel() + { + return new Model(); + } + public static function getArchiveIdAndVisits(ArchiveProcessor\Parameters $params, $minDatetimeArchiveProcessedUTC) { - $dateStart = $params->getPeriod()->getDateStart(); - $bindSQL = array($params->getSite()->getId(), - $dateStart->toString('Y-m-d'), - $params->getPeriod()->getDateEnd()->toString('Y-m-d'), - $params->getPeriod()->getId(), - ); - - $timeStampWhere = ''; + $idSite = $params->getSite()->getId(); + $period = $params->getPeriod()->getId(); + $dateStart = $params->getPeriod()->getDateStart(); + $dateStartIso = $dateStart->toString('Y-m-d'); + $dateEndIso = $params->getPeriod()->getDateEnd()->toString('Y-m-d'); + + $numericTable = ArchiveTableCreator::getNumericTable($dateStart); + + $minDatetimeIsoArchiveProcessedUTC = null; if ($minDatetimeArchiveProcessedUTC) { - $timeStampWhere = " AND ts_archived >= ? "; - $bindSQL[] = Date::factory($minDatetimeArchiveProcessedUTC)->getDatetime(); + $minDatetimeIsoArchiveProcessedUTC = Date::factory($minDatetimeArchiveProcessedUTC)->getDatetime(); } $requestedPlugin = $params->getRequestedPlugin(); - $segment = $params->getSegment(); + $segment = $params->getSegment(); $isSkipAggregationOfSubTables = $params->isSkipAggregationOfSubTables(); - $plugins = array("VisitsSummary", $requestedPlugin); - $sqlWhereArchiveName = self::getNameCondition($plugins, $segment, $isSkipAggregationOfSubTables); - - $sqlQuery = " SELECT idarchive, value, name, date1 as startDate - FROM " . ArchiveTableCreator::getNumericTable($dateStart) . "`` - WHERE idsite = ? - AND date1 = ? - AND date2 = ? - AND period = ? - AND ( ($sqlWhereArchiveName) - OR name = '" . self::NB_VISITS_RECORD_LOOKED_UP . "' - OR name = '" . self::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "') - $timeStampWhere - ORDER BY idarchive DESC"; - $results = Db::fetchAll($sqlQuery, $bindSQL); + + $doneFlags = self::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables); + $possibleValues = self::getPossibleValues(); + + $results = self::getModel()->getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC, $doneFlags, $possibleValues); + if (empty($results)) { return false; } @@ -83,9 +77,8 @@ class ArchiveSelector list($visits, $visitsConverted) = self::getVisitsMetricsFromResults($idArchive, $idArchiveVisitsSummary, $results); - if ($visits === false - && $idArchive === false - ) { + if (false === $visits && false === $idArchive) { + return false; } @@ -96,9 +89,11 @@ class ArchiveSelector { $visits = $visitsConverted = false; $archiveWithVisitsMetricsWasFound = ($idArchiveVisitsSummary !== false); + if ($archiveWithVisitsMetricsWasFound) { $visits = $visitsConverted = 0; } + foreach ($results as $result) { if (in_array($result['idarchive'], array($idArchive, $idArchiveVisitsSummary))) { $value = (int)$result['value']; @@ -114,6 +109,7 @@ class ArchiveSelector } } } + return array($visits, $visitsConverted); } @@ -121,6 +117,7 @@ class ArchiveSelector { $idArchive = false; $namesRequestedPlugin = Rules::getDoneFlags(array($requestedPlugin), $segment, $isSkipAggregationOfSubTables); + foreach ($results as $result) { if ($idArchive === false && in_array($result['name'], $namesRequestedPlugin) @@ -129,6 +126,7 @@ class ArchiveSelector break; } } + return $idArchive; } @@ -150,7 +148,7 @@ class ArchiveSelector */ public static function getArchiveIds($siteIds, $periods, $segment, $plugins, $isSkipAggregationOfSubTables = false) { - if(empty($siteIds)) { + if (empty($siteIds)) { throw new \Exception("Website IDs could not be read from the request, ie. idSite="); } @@ -200,8 +198,10 @@ class ArchiveSelector $sql = sprintf($getArchiveIdsSql, $table, $dateCondition); + $archiveIds = Db::fetchAll($sql, $bind); + // get the archive IDs - foreach (Db::fetchAll($sql, $bind) as $row) { + foreach ($archiveIds as $row) { $archiveName = $row['name']; //FIXMEA duplicate with Archive.php @@ -251,18 +251,23 @@ class ArchiveSelector // get data from every table we're querying $rows = array(); foreach ($archiveIds as $period => $ids) { + if (empty($ids)) { throw new Exception("Unexpected: id archive not found for period '$period' '"); } + // $period = "2009-01-04,2009-01-04", $date = Date::factory(substr($period, 0, 10)); + if ($archiveDataType == 'numeric') { $table = ArchiveTableCreator::getNumericTable($date); } else { $table = ArchiveTableCreator::getBlobTable($date); } - $sql = sprintf($getValuesSql, $table, implode(',', $ids)); + + $sql = sprintf($getValuesSql, $table, implode(',', $ids)); $dataRows = Db::fetchAll($sql, $bind); + foreach ($dataRows as $row) { $rows[] = $row; } @@ -281,13 +286,44 @@ class ArchiveSelector * @return string */ private static function getNameCondition(array $plugins, Segment $segment, $isSkipAggregationOfSubTables) + { + // the flags used to tell how the archiving process for a specific archive was completed, + // if it was completed + $doneFlags = self::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables); + $allDoneFlags = "'" . implode("','", $doneFlags) . "'"; + + $possibleValues = self::getPossibleValues(); + + // create the SQL to find archives that are DONE + return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))"; + } + + /** + * Returns the SQL condition used to find successfully completed archives that + * this instance is querying for. + * + * @param array $plugins + * @param Segment $segment + * @param bool $isSkipAggregationOfSubTables + * @return string + */ + private static function getDoneFlags(array $plugins, Segment $segment, $isSkipAggregationOfSubTables) { // the flags used to tell how the archiving process for a specific archive was completed, // if it was completed $doneFlags = Rules::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables); - $allDoneFlags = "'" . implode("','", $doneFlags) . "'"; + return $doneFlags; + } + /** + * Returns the SQL condition used to find successfully completed archives that + * this instance is querying for. + * + * @return string + */ + private static function getPossibleValues() + { $possibleValues = array(ArchiveWriter::DONE_OK, ArchiveWriter::DONE_OK_TEMPORARY); if (!Rules::isRequestAuthorizedToArchive()) { @@ -295,9 +331,7 @@ class ArchiveSelector $possibleValues[] = ArchiveWriter::DONE_INVALIDATED; } - // create the SQL to find archives that are DONE - return "((name IN ($allDoneFlags)) AND " . - " (value IN (" . implode(',', $possibleValues) . ")))"; + return $possibleValues; } } diff --git a/core/DataAccess/ArchiveTableCreator.php b/core/DataAccess/ArchiveTableCreator.php index 67abd2b1237eca4fa39c749d5795f81a5a0f47fa..230c7da05a08a9ac7862448300fb8ed3936cb5fb 100644 --- a/core/DataAccess/ArchiveTableCreator.php +++ b/core/DataAccess/ArchiveTableCreator.php @@ -9,7 +9,6 @@ namespace Piwik\DataAccess; -use Exception; use Piwik\Common; use Piwik\Date; use Piwik\Db; @@ -18,8 +17,7 @@ use Piwik\DbHelper; class ArchiveTableCreator { const NUMERIC_TABLE = "numeric"; - - const BLOB_TABLE = "blob"; + const BLOB_TABLE = "blob"; public static $tablesAlreadyInstalled = null; @@ -38,7 +36,9 @@ class ArchiveTableCreator $tableNamePrefix = "archive_" . $type; $tableName = $tableNamePrefix . "_" . $date->toString('Y_m'); $tableName = Common::prefixTable($tableName); + self::createArchiveTablesIfAbsent($tableName, $tableNamePrefix); + return $tableName; } @@ -49,24 +49,16 @@ class ArchiveTableCreator } if (!in_array($tableName, self::$tablesAlreadyInstalled)) { - $db = Db::get(); - $sql = DbHelper::getTableCreateSql($tableNamePrefix); - - // replace table name template by real name - $tableNamePrefix = Common::prefixTable($tableNamePrefix); - $sql = str_replace($tableNamePrefix, $tableName, $sql); - try { - $db->query($sql); - } catch (Exception $e) { - // accept mysql error 1050: table already exists, throw otherwise - if (!$db->isErrNo($e, '1050')) { - throw $e; - } - } + self::getModel()->createArchiveTable($tableName, $tableNamePrefix); self::$tablesAlreadyInstalled[] = $tableName; } } + private static function getModel() + { + return new Model(); + } + public static function clear() { self::$tablesAlreadyInstalled = null; @@ -89,6 +81,7 @@ class ArchiveTableCreator } $archiveTables = array(); + foreach (self::$tablesAlreadyInstalled as $table) { if (strpos($table, 'archive_numeric_') !== false || strpos($table, 'archive_blob_') !== false @@ -96,13 +89,15 @@ class ArchiveTableCreator $archiveTables[] = $table; } } + return $archiveTables; } public static function getDateFromTableName($tableName) { $tableName = Common::unprefixTable($tableName); - $date = str_replace(array('archive_numeric_', 'archive_blob_'), '', $tableName); + $date = str_replace(array('archive_numeric_', 'archive_blob_'), '', $tableName); + return $date; } @@ -111,9 +106,11 @@ class ArchiveTableCreator if (strpos($tableName, 'archive_numeric_') !== false) { return self::NUMERIC_TABLE; } + if (strpos($tableName, 'archive_blob_') !== false) { return self::BLOB_TABLE; } + return false; } } diff --git a/core/DataAccess/ArchiveWriter.php b/core/DataAccess/ArchiveWriter.php index 70f2159b67dd5a4e058ec0e47c218e5fe9c969e2..1ea6b0d08c26a5622eaa4a29f62f75f28de7441c 100644 --- a/core/DataAccess/ArchiveWriter.php +++ b/core/DataAccess/ArchiveWriter.php @@ -23,7 +23,6 @@ use Piwik\Period; */ class ArchiveWriter { - const PREFIX_SQL_LOCK = "locked_"; /** * Flag stored at the end of the archiving * @@ -64,9 +63,10 @@ class ArchiveWriter public function __construct(ArchiveProcessor\Parameters $params, $isArchiveTemporary) { $this->idArchive = false; - $this->idSite = $params->getSite()->getId(); - $this->segment = $params->getSegment(); - $this->period = $params->getPeriod(); + $this->idSite = $params->getSite()->getId(); + $this->segment = $params->getSegment(); + $this->period = $params->getPeriod(); + $idSites = array($this->idSite); $this->doneFlag = Rules::getDoneStringFlagFor($idSites, $this->segment, $this->period->getLabel(), $params->getRequestedPlugin(), $params->isSkipAggregationOfSubTables()); $this->isArchiveTemporary = $isArchiveTemporary; @@ -92,7 +92,7 @@ class ArchiveWriter $newName = $name . '_' . $id; } - $value = $this->compress($value); + $value = $this->compress($value); $clean[] = array($newName, $value); } $this->insertBulkRecords($clean); @@ -108,6 +108,7 @@ class ArchiveWriter if ($this->idArchive === false) { throw new Exception("Must call allocateNewArchiveId() first"); } + return $this->idArchive; } @@ -119,7 +120,11 @@ class ArchiveWriter public function finalizeArchive() { - $this->deletePreviousArchiveStatus(); + $numericTable = $this->getTableNumeric(); + $idArchive = $this->getIdArchive(); + + $this->getModel()->deletePreviousArchiveStatus($numericTable, $idArchive, $this->doneFlag); + $this->logArchiveStatusAsFinal(); } @@ -128,28 +133,8 @@ class ArchiveWriter if (Db::get()->hasBlobDataType()) { return gzcompress($data); } - return $data; - } - - protected function getArchiveLockName() - { - $numericTable = $this->getTableNumeric(); - $dbLockName = "allocateNewArchiveId.$numericTable"; - return $dbLockName; - } - - protected function acquireArchiveTableLock() - { - $dbLockName = $this->getArchiveLockName(); - if (Db::getDbLock($dbLockName, $maxRetries = 30) === false) { - throw new Exception("allocateNewArchiveId: Cannot get named lock $dbLockName."); - } - } - protected function releaseArchiveTableLock() - { - $dbLockName = $this->getArchiveLockName(); - Db::releaseDbLock($dbLockName); + return $data; } protected function allocateNewArchiveId() @@ -171,56 +156,31 @@ class ArchiveWriter { $numericTable = $this->getTableNumeric(); $idSite = $this->idSite; + $date = date("Y-m-d H:i:s"); - $this->acquireArchiveTableLock(); + $id = $this->getModel()->insertNewArchiveId($numericTable, $idSite, $date); - $locked = self::PREFIX_SQL_LOCK . Common::generateUniqId(); - $date = date("Y-m-d H:i:s"); - $insertSql = "INSERT INTO $numericTable " - . " SELECT IFNULL( MAX(idarchive), 0 ) + 1, - '" . $locked . "', - " . (int)$idSite . ", - '" . $date . "', - '" . $date . "', - 0, - '" . $date . "', - 0 " - . " FROM $numericTable as tb1"; - Db::get()->exec($insertSql); - - $this->releaseArchiveTableLock(); - - $selectIdSql = "SELECT idarchive FROM $numericTable WHERE name = ? LIMIT 1"; - $id = Db::get()->fetchOne($selectIdSql, $locked); return $id; } - protected function logArchiveStatusAsIncomplete() + private function getModel() { - $statusWhileProcessing = self::DONE_ERROR; - $this->insertRecord($this->doneFlag, $statusWhileProcessing); + return new Model(); } - protected function deletePreviousArchiveStatus() + protected function logArchiveStatusAsIncomplete() { - // without advisory lock here, the DELETE would acquire Exclusive Lock - $this->acquireArchiveTableLock(); - - Db::query("DELETE FROM " . $this->getTableNumeric() . " - WHERE idarchive = ? AND (name = '" . $this->doneFlag - . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')", - array($this->getIdArchive()) - ); - - $this->releaseArchiveTableLock(); + $this->insertRecord($this->doneFlag, self::DONE_ERROR); } protected function logArchiveStatusAsFinal() { $status = self::DONE_OK; + if ($this->isArchiveTemporary) { $status = self::DONE_OK_TEMPORARY; } + $this->insertRecord($this->doneFlag, $status); } @@ -233,27 +193,37 @@ class ArchiveWriter foreach ($records as $record) { $this->insertRecord($record[0], $record[1]); } + return true; } + $bindSql = $this->getInsertRecordBind(); - $values = array(); + $values = array(); $valueSeen = false; foreach ($records as $record) { // don't record zero - if (empty($record[1])) continue; + if (empty($record[1])) { + continue; + } - $bind = $bindSql; - $bind[] = $record[0]; // name - $bind[] = $record[1]; // value + $bind = $bindSql; + $bind[] = $record[0]; // name + $bind[] = $record[1]; // value $values[] = $bind; $valueSeen = $record[1]; } - if (empty($values)) return true; + + if (empty($values)) { + return true; + } $tableName = $this->getTableNameToInsert($valueSeen); - BatchInsert::tableInsertBatch($tableName, $this->getInsertFields(), $values); + $fields = $this->getInsertFields(); + + BatchInsert::tableInsertBatch($tableName, $fields, $values); + return true; } @@ -272,15 +242,11 @@ class ArchiveWriter } $tableName = $this->getTableNameToInsert($value); + $fields = $this->getInsertFields(); + $record = $this->getInsertRecordBind(); + + $this->getModel()->insertRecord($tableName, $fields, $record, $name, $value); - // duplicate idarchives are Ignored, see https://github.com/piwik/piwik/issues/987 - $query = "INSERT IGNORE INTO " . $tableName . " - (" . implode(", ", $this->getInsertFields()) . ") - VALUES (?,?,?,?,?,?,?,?)"; - $bindSql = $this->getInsertRecordBind(); - $bindSql[] = $name; - $bindSql[] = $value; - Db::query($query, $bindSql); return true; } @@ -299,6 +265,7 @@ class ArchiveWriter if (is_numeric($value)) { return $this->getTableNumeric(); } + return ArchiveTableCreator::getBlobTable($this->dateStart); } diff --git a/core/DataAccess/LogAggregator.php b/core/DataAccess/LogAggregator.php index 95a7603176a7e3b4ad05fa0931ee3d396a3e37ee..83946b1f6fad37a58d7424a799d00b065cd32aad 100644 --- a/core/DataAccess/LogAggregator.php +++ b/core/DataAccess/LogAggregator.php @@ -292,52 +292,61 @@ class LogAggregator $tableName = self::LOG_VISIT_TABLE; $availableMetrics = $this->getVisitsMetricFields(); - $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics); - $from = array($tableName); - $where = $this->getWhereStatement($tableName, self::VISIT_DATETIME_FIELD, $where); + $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics); + $from = array($tableName); + $where = $this->getWhereStatement($tableName, self::VISIT_DATETIME_FIELD, $where); $groupBy = $this->getGroupByStatement($dimensions, $tableName); $orderBy = false; if ($rankingQuery) { $orderBy = '`' . Metrics::INDEX_NB_VISITS . '` DESC'; } + $query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy); if ($rankingQuery) { unset($availableMetrics[Metrics::INDEX_MAX_ACTIONS]); $sumColumns = array_keys($availableMetrics); + if ($metrics) { $sumColumns = array_intersect($sumColumns, $metrics); } + $rankingQuery->addColumn($sumColumns, 'sum'); if ($this->isMetricRequested(Metrics::INDEX_MAX_ACTIONS, $metrics)) { $rankingQuery->addColumn(Metrics::INDEX_MAX_ACTIONS, 'max'); } + return $rankingQuery->execute($query['sql'], $query['bind']); } + return $this->getDb()->query($query['sql'], $query['bind']); } protected function getSelectsMetrics($metricsAvailable, $metricsRequested = false) { $selects = array(); + foreach ($metricsAvailable as $metricId => $statement) { if ($this->isMetricRequested($metricId, $metricsRequested)) { - $aliasAs = $this->getSelectAliasAs($metricId); + $aliasAs = $this->getSelectAliasAs($metricId); $selects[] = $statement . $aliasAs; } } + return $selects; } protected function getSelectStatement($dimensions, $tableName, $additionalSelects, array $availableMetrics, $requestedMetrics = false) { $dimensionsToSelect = $this->getDimensionsToSelect($dimensions, $additionalSelects); + $selects = array_merge( $this->getSelectDimensions($dimensionsToSelect, $tableName), $this->getSelectsMetrics($availableMetrics, $requestedMetrics), !empty($additionalSelects) ? $additionalSelects : array() ); + $select = implode(self::FIELDS_SEPARATOR, $selects); return $select; } @@ -354,6 +363,7 @@ class LogAggregator if (empty($additionalSelects)) { return $dimensions; } + $dimensionsToSelect = array(); foreach ($dimensions as $selectAs => $dimension) { $asAlias = $this->getSelectAliasAs($dimension); @@ -363,6 +373,7 @@ class LogAggregator } } } + $dimensionsToSelect = array_unique($dimensionsToSelect); return $dimensionsToSelect; } @@ -381,6 +392,7 @@ class LogAggregator { foreach ($dimensions as $selectAs => &$field) { $selectAsString = $field; + if (!is_numeric($selectAs)) { $selectAsString = $selectAs; } else { @@ -389,16 +401,18 @@ class LogAggregator $selectAsString = $appendSelectAs = false; } } + $isKnownField = !in_array($field, array('referrer_data')); - if ($selectAsString == $field - && $isKnownField - ) { + + if ($selectAsString == $field && $isKnownField) { $field = $this->prefixColumn($field, $tableName); } + if ($appendSelectAs && $selectAsString) { $field = $this->prefixColumn($field, $tableName) . $this->getSelectAliasAs($selectAsString); } } + return $dimensions; } @@ -421,7 +435,7 @@ class LogAggregator protected function isFieldFunctionOrComplexExpression($field) { return strpos($field, "(") !== false - || strpos($field, "CASE") !== false; + || strpos($field, "CASE") !== false; } protected function getSelectAliasAs($metricId) @@ -432,7 +446,7 @@ class LogAggregator protected function isMetricRequested($metricId, $metricsRequested) { return $metricsRequested === false - || in_array($metricId, $metricsRequested); + || in_array($metricId, $metricsRequested); } protected function getWhereStatement($tableName, $datetimeField, $extraWhere = false) @@ -440,17 +454,20 @@ class LogAggregator $where = "$tableName.$datetimeField >= ? AND $tableName.$datetimeField <= ? AND $tableName.idsite IN (". Common::getSqlStringFieldsArray($this->sites) . ")"; + if (!empty($extraWhere)) { $extraWhere = sprintf($extraWhere, $tableName, $tableName); - $where .= ' AND ' . $extraWhere; + $where .= ' AND ' . $extraWhere; } + return $where; } protected function getGroupByStatement($dimensions, $tableName) { $dimensions = $this->getSelectDimensions($dimensions, $tableName, $appendSelectAs = false); - $groupBy = implode(", ", $dimensions); + $groupBy = implode(", ", $dimensions); + return $groupBy; } @@ -464,6 +481,7 @@ class LogAggregator { $bind = array($this->dateStart->getDateStartUTC(), $this->dateEnd->getDateEndUTC()); $bind = array_merge($bind, $this->sites); + return $bind; } @@ -494,7 +512,7 @@ class LogAggregator * * @param string $dimension One or more **log\_conversion\_item** columns to group aggregated data by. * Eg, `'idaction_sku'` or `'idaction_sku, idaction_category'`. - * @return Zend_Db_Statement A statement object that can be used to iterate through the query's + * @return \Zend_Db_Statement A statement object that can be used to iterate through the query's * result set. See [above](#queryEcommerceItems-result-set) to learn more * about what this query selects. * @api @@ -624,9 +642,9 @@ class LogAggregator $tableName = self::LOG_ACTIONS_TABLE; $availableMetrics = $this->getActionsMetricFields(); - $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics); - $from = array($tableName); - $where = $this->getWhereStatement($tableName, self::ACTION_DATETIME_FIELD, $where); + $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics); + $from = array($tableName); + $where = $this->getWhereStatement($tableName, self::ACTION_DATETIME_FIELD, $where); $groupBy = $this->getGroupByStatement($dimensions, $tableName); $orderBy = false; @@ -638,12 +656,14 @@ class LogAggregator foreach ($joinLogActionOnColumn as $i => $joinColumn) { $tableAlias = 'log_action' . ($multiJoin ? $i + 1 : ''); + if (strpos($joinColumn, ' ') === false) { $joinOn = $tableAlias . '.idaction = ' . $tableName . '.' . $joinColumn; } else { - // more complex join column like IF(...) + // more complex join column like if (...) $joinOn = $tableAlias . '.idaction = ' . $joinColumn; } + $from[] = array( 'table' => 'log_action', 'tableAlias' => $tableAlias, @@ -663,7 +683,9 @@ class LogAggregator if ($metrics) { $sumColumns = array_intersect($sumColumns, $metrics); } + $rankingQuery->addColumn($sumColumns, 'sum'); + return $rankingQuery->execute($query['sql'], $query['bind']); } @@ -733,21 +755,22 @@ class LogAggregator * @param bool|string $where An optional SQL expression used in the SQL's **WHERE** clause. * @param array $additionalSelects Additional SELECT fields that are not included in the group by * clause. These can be aggregate expressions, eg, `SUM(somecol)`. - * @return Zend_Db_Statement + * @return \Zend_Db_Statement */ public function queryConversionsByDimension($dimensions = array(), $where = false, $additionalSelects = array()) { $dimensions = array_merge(array(self::IDGOAL_FIELD), $dimensions); + $tableName = self::LOG_CONVERSION_TABLE; $availableMetrics = $this->getConversionsMetricFields(); - $tableName = self::LOG_CONVERSION_TABLE; $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics); - $from = array($tableName); - $where = $this->getWhereStatement($tableName, self::CONVERSION_DATETIME_FIELD, $where); + $from = array($tableName); + $where = $this->getWhereStatement($tableName, self::CONVERSION_DATETIME_FIELD, $where); $groupBy = $this->getGroupByStatement($dimensions, $tableName); $orderBy = false; - $query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy); + $query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy); + return $this->getDb()->query($query['sql'], $query['bind']); } @@ -824,14 +847,16 @@ class LogAggregator { $selects = array(); $extraCondition = ''; + if ($restrictToReturningVisitors) { // extra condition for the SQL SELECT that makes sure only returning visits are counted // when creating the 'days since last visit' report $extraCondition = 'and log_visit.visitor_returning = 1'; - $extraSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) " - . " as `" . $selectColumnPrefix . 'General_NewVisits' . "`"; + $extraSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) " + . " as `" . $selectColumnPrefix . 'General_NewVisits' . "`"; $selects[] = $extraSelect; } + foreach ($ranges as $gap) { if (count($gap) == 2) { $lowerBound = $gap[0]; @@ -840,12 +865,11 @@ class LogAggregator $selectAs = "$selectColumnPrefix$lowerBound-$upperBound"; $selects[] = "sum(case when $table.$column between $lowerBound and $upperBound $extraCondition" . - " then 1 else 0 end) as `$selectAs`"; + " then 1 else 0 end) as `$selectAs`"; } else { $lowerBound = $gap[0]; - $selectAs = $selectColumnPrefix . ($lowerBound + 1) . urlencode('+'); - + $selectAs = $selectColumnPrefix . ($lowerBound + 1) . urlencode('+'); $selects[] = "sum(case when $table.$column > $lowerBound $extraCondition then 1 else 0 end) as `$selectAs`"; } } @@ -869,6 +893,7 @@ class LogAggregator public static function makeArrayOneColumn($row, $columnName, $lookForThisPrefix = false) { $cleanRow = array(); + foreach ($row as $label => $count) { if (empty($lookForThisPrefix) || strpos($label, $lookForThisPrefix) === 0 @@ -877,6 +902,7 @@ class LogAggregator $cleanRow[$cleanLabel] = array($columnName => $count); } } + return $cleanRow; } diff --git a/core/DataAccess/Model.php b/core/DataAccess/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..facaa5dd9741ab9d4b18f821a5966e6d8f95575c --- /dev/null +++ b/core/DataAccess/Model.php @@ -0,0 +1,229 @@ +<?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\DataAccess; + +use Exception; +use Piwik\Common; +use Piwik\Db; +use Piwik\DbHelper; + +/** + * Cleans up outdated archives + * + * @package Piwik\DataAccess + */ +class Model +{ + const PREFIX_SQL_LOCK = "locked_"; + + public function purgeInvalidatedArchiveTable($archiveTable) + { + /** + * Select the archives that have already been invalidated and have been since re-processed. + * It purges records for each distinct { archive name (includes segment hash) , idsite, date, period } tuple. + */ + $query = 'SELECT t1.idarchive FROM `' . $archiveTable . '` t1 + INNER JOIN `' . $archiveTable . '` t2 + ON t1.name = t2.name AND t1.idsite=t2.idsite + AND t1.date1=t2.date1 AND t1.date2=t2.date2 AND t1.period=t2.period + WHERE t1.value = ' . ArchiveWriter::DONE_INVALIDATED . ' + AND t2.value IN(' . ArchiveWriter::DONE_OK . ', ' . ArchiveWriter::DONE_OK_TEMPORARY . ') + AND t1.ts_archived < t2.ts_archived AND t1.name LIKE \'done%\''; + + $result = Db::fetchAll($query); + + return $result; + } + + public function getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan) + { + $query = "SELECT idarchive FROM " . $archiveTable . " + WHERE name LIKE 'done%' + AND (( value = " . ArchiveWriter::DONE_OK_TEMPORARY . " + AND ts_archived < ?) + OR value = " . ArchiveWriter::DONE_ERROR . ")"; + + return Db::fetchAll($query, array($purgeArchivesOlderThan)); + } + + /* + * Deleting "Custom Date Range" reports, since they can be re-processed and would take up un-necessary space + */ + public function deleteArchivesWithPeriodRange($numericTable, $blobTable, $range, $date) + { + $query = "DELETE FROM %s WHERE period = ? AND ts_archived < ?"; + $bind = array($range, $date); + + Db::query(sprintf($query, $numericTable), $bind); + + try { + Db::query(sprintf($query, $blobTable), $bind); + } catch (Exception $e) { + // Individual blob tables could be missing + } + } + + public function deleteArchiveIds($numericTable, $blobTable, $idsToDelete) + { + $query = "DELETE FROM %s WHERE idarchive IN (" . implode(',', $idsToDelete) . ")"; + + Db::query(sprintf($query, $numericTable)); + + try { + Db::query(sprintf($query, $blobTable)); + } catch (Exception $e) { + // Individual blob tables could be missing + } + } + + public function getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC, $doneFlags, $possibleValues) + { + $bindSQL = array($idSite, + $dateStartIso, + $dateEndIso, + $period, + ); + + $timeStampWhere = ''; + if ($minDatetimeIsoArchiveProcessedUTC) { + $timeStampWhere = " AND ts_archived >= ? "; + $bindSQL[] = $minDatetimeIsoArchiveProcessedUTC; + } + + $sqlWhereArchiveName = self::getNameCondition($doneFlags, $possibleValues); + + $sqlQuery = "SELECT idarchive, value, name, date1 as startDate FROM $numericTable + WHERE idsite = ? + AND date1 = ? + AND date2 = ? + AND period = ? + AND ( ($sqlWhereArchiveName) + OR name = '" . ArchiveSelector::NB_VISITS_RECORD_LOOKED_UP . "' + OR name = '" . ArchiveSelector::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "') + $timeStampWhere + ORDER BY idarchive DESC"; + $results = Db::fetchAll($sqlQuery, $bindSQL); + + return $results; + } + + public function createArchiveTable($tableName, $tableNamePrefix) + { + $db = Db::get(); + $sql = DbHelper::getTableCreateSql($tableNamePrefix); + + // replace table name template by real name + $tableNamePrefix = Common::prefixTable($tableNamePrefix); + $sql = str_replace($tableNamePrefix, $tableName, $sql); + + try { + $db->query($sql); + } catch (Exception $e) { + // accept mysql error 1050: table already exists, throw otherwise + if (!$db->isErrNo($e, '1050')) { + throw $e; + } + } + } + + /** + * Locks the archive table to generate a new archive ID. + * + * We lock to make sure that + * if several archiving processes are running at the same time (for different websites and/or periods) + * then they will each use a unique archive ID. + * + * @return int + */ + public function insertNewArchiveId($numericTable, $idSite, $date) + { + $this->acquireArchiveTableLock($numericTable); + + $locked = self::PREFIX_SQL_LOCK . Common::generateUniqId(); + + $insertSql = "INSERT INTO $numericTable " + . " SELECT IFNULL( MAX(idarchive), 0 ) + 1, + '" . $locked . "', + " . (int)$idSite . ", + '" . $date . "', + '" . $date . "', + 0, + '" . $date . "', + 0 " + . " FROM $numericTable as tb1"; + Db::get()->exec($insertSql); + + $this->releaseArchiveTableLock($numericTable); + + $selectIdSql = "SELECT idarchive FROM $numericTable WHERE name = ? LIMIT 1"; + $id = Db::get()->fetchOne($selectIdSql, $locked); + return $id; + } + + public function deletePreviousArchiveStatus($numericTable, $archiveId, $doneFlag) + { + // without advisory lock here, the DELETE would acquire Exclusive Lock + $this->acquireArchiveTableLock($numericTable); + + Db::query("DELETE FROM $numericTable WHERE idarchive = ? AND (name = '" . $doneFlag + . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')", + array($archiveId) + ); + + $this->releaseArchiveTableLock($numericTable); + } + + public function insertRecord($tableName, $fields, $record, $name, $value) + { + // duplicate idarchives are Ignored, see https://github.com/piwik/piwik/issues/987 + $query = "INSERT IGNORE INTO " . $tableName . " (" . implode(", ", $fields) . ") + VALUES (?,?,?,?,?,?,?,?)"; + + $bindSql = $record; + $bindSql[] = $name; + $bindSql[] = $value; + + Db::query($query, $bindSql); + + return true; + } + + /** + * Returns the SQL condition used to find successfully completed archives that + * this instance is querying for. + */ + private static function getNameCondition($doneFlags, $possibleValues) + { + $allDoneFlags = "'" . implode("','", $doneFlags) . "'"; + + // create the SQL to find archives that are DONE + return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))"; + } + + protected function acquireArchiveTableLock($numericTable) + { + $dbLockName = $this->getArchiveLockName($numericTable); + + if (Db::getDbLock($dbLockName, $maxRetries = 30) === false) { + throw new Exception("allocateNewArchiveId: Cannot get named lock $dbLockName."); + } + } + + protected function releaseArchiveTableLock($numericTable) + { + $dbLockName = $this->getArchiveLockName($numericTable); + Db::releaseDbLock($dbLockName); + } + + protected function getArchiveLockName($numericTable) + { + return "allocateNewArchiveId.$numericTable"; + } + +} diff --git a/core/DataArray.php b/core/DataArray.php index 042d5ae9619d13324a7f7e328077bd8a75484e90..04c98971259e73c17da451657c547d704e64c137 100644 --- a/core/DataArray.php +++ b/core/DataArray.php @@ -108,7 +108,7 @@ class DataArray // In case the existing Row had no action metrics (eg. Custom Variable XYZ with "visit" scope) // but the new Row has action metrics (eg. same Custom Variable XYZ this time with a "page" scope) - if(!isset($oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS])) { + if (!isset($oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS])) { $toZero = array(Metrics::INDEX_MAX_ACTIONS, Metrics::INDEX_SUM_VISIT_LENGTH, Metrics::INDEX_BOUNCE_COUNT, @@ -247,8 +247,8 @@ class DataArray $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), self::EVENT_VALUE_PRECISION); // Update minimum only if it is set - if($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) { - if($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) { + if ($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) { + if ($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) { $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], self::EVENT_VALUE_PRECISION); } else { $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), self::EVENT_VALUE_PRECISION); @@ -360,7 +360,7 @@ class DataArray // if there are no "visit" column, we force one to prevent future complications // eg. This helps the setDefaultColumnsToDisplay() call - if(!isset($values[Metrics::INDEX_NB_VISITS])) { + if (!isset($values[Metrics::INDEX_NB_VISITS])) { $values[Metrics::INDEX_NB_VISITS] = 0; } } diff --git a/core/DataTable.php b/core/DataTable.php index bc5a57cff664988a623f4b8dac84f609b690f4a2..4d308f7d21c914bfeca5168babd5a933468320c0 100644 --- a/core/DataTable.php +++ b/core/DataTable.php @@ -469,11 +469,13 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess * metadata can be used to specify a different type of operation. * * @param \Piwik\DataTable $tableToSum + * @param bool $doAggregateSubTables + * @throws Exception */ public function addDataTable(DataTable $tableToSum, $doAggregateSubTables = true) { - if($tableToSum instanceof Simple) { - if($tableToSum->getRowsCount() > 1) { + if ($tableToSum instanceof Simple) { + if ($tableToSum->getRowsCount() > 1) { throw new Exception("Did not expect a Simple table with more than one row in addDataTable()"); } $row = $tableToSum->getFirstRow(); @@ -897,7 +899,7 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess foreach ($this->getRows() as $row) { $row->renameColumn($oldName, $newName); - if($doRenameColumnsOfSubTables) { + if ($doRenameColumnsOfSubTables) { if (($idSubDataTable = $row->getIdSubDataTable()) !== null) { Manager::getInstance()->getTable($idSubDataTable)->renameColumn($oldName, $newName); } @@ -1602,7 +1604,7 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess } else { $rowFound->sumRow($row, $copyMeta = true, $this->getMetadata(self::COLUMN_AGGREGATION_OPS_METADATA_NAME)); - if($doAggregateSubTables) { + if ($doAggregateSubTables) { // if the row to add has a subtable whereas the current row doesn't // we simply add it (cloning the subtable) // if the row has the subtable already diff --git a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php index 742f1aef6ddb5f357fa3b0cb0bc76714aa2f0f2b..963ac9acbde6321bb693c7fe639781bead51e660 100644 --- a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php +++ b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php @@ -8,12 +8,10 @@ */ namespace Piwik\DataTable\Filter; -use Exception; use Piwik\DataTable; use Piwik\DataTable\Row; use Piwik\Metrics; use Piwik\Piwik; -use Piwik\Tracker\GoalManager; /** * Adds goal related metrics to a {@link DataTable} using metrics that already exist. diff --git a/core/DataTable/Filter/BeautifyRangeLabels.php b/core/DataTable/Filter/BeautifyRangeLabels.php index 534f484bff1aebca0160eea7ef816df4fa001a3a..bf5e8d65d9ff28dbb8d6f435686adeb28a2670e3 100644 --- a/core/DataTable/Filter/BeautifyRangeLabels.php +++ b/core/DataTable/Filter/BeautifyRangeLabels.php @@ -65,7 +65,7 @@ class BeautifyRangeLabels extends ColumnCallbackReplace parent::__construct($table, 'label', array($this, 'beautify'), array()); $this->labelSingular = $labelSingular; - $this->labelPlural = $labelPlural; + $this->labelPlural = $labelPlural; } /** diff --git a/core/DataTable/Filter/CalculateEvolutionFilter.php b/core/DataTable/Filter/CalculateEvolutionFilter.php index 80f789919e6d65b1d46d7abd4e2cc8f5859be4a9..0ef6a30a2fadceff2f14e2ee831008c4df234d8a 100755 --- a/core/DataTable/Filter/CalculateEvolutionFilter.php +++ b/core/DataTable/Filter/CalculateEvolutionFilter.php @@ -121,6 +121,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage { $value = self::getPercentageValue($value, $divisor, $this->quotientPrecision); $value = self::appendPercentSign($value); + return $value; } @@ -152,6 +153,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage if ($appendPercentSign) { $number = self::appendPercentSign($number); } + return $number; } @@ -165,6 +167,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage if ($number > 0) { $number = '+' . $number; } + return $number; } diff --git a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php index 5cc83d8e3d2c3ec9e20ae4bf325179ea7cc72204..81d8eadf047e4b2aa22711049d2301a01ea8c9f3 100644 --- a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php +++ b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php @@ -109,6 +109,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter if ($divisor > 0 && $value > 0) { $quotient = round($value / $divisor, $this->quotientPrecision); } + return $quotient; } diff --git a/core/DataTable/Filter/ColumnCallbackAddMetadata.php b/core/DataTable/Filter/ColumnCallbackAddMetadata.php index e89000f8fdc6b99b893a0a1c3734078ac0191524..c7945839cb3283f04c2df44e783b1f454ba06308 100644 --- a/core/DataTable/Filter/ColumnCallbackAddMetadata.php +++ b/core/DataTable/Filter/ColumnCallbackAddMetadata.php @@ -48,12 +48,12 @@ class ColumnCallbackAddMetadata extends BaseFilter if (!is_array($columnsToRead)) { $columnsToRead = array($columnsToRead); } - $this->columnsToRead = $columnsToRead; - $this->functionToApply = $functionToApply; + $this->columnsToRead = $columnsToRead; + $this->functionToApply = $functionToApply; $this->functionParameters = $functionParameters; - $this->metadataToAdd = $metadataToAdd; - $this->applyToSummaryRow = $applyToSummaryRow; + $this->metadataToAdd = $metadataToAdd; + $this->applyToSummaryRow = $applyToSummaryRow; } /** diff --git a/core/DataTable/Filter/ColumnCallbackDeleteRow.php b/core/DataTable/Filter/ColumnCallbackDeleteRow.php index 189fbd3b814de902674913fe572d2d75f2adf5b2..414bf71e8a8bb519d17a2adf9fb893c96813cb1a 100644 --- a/core/DataTable/Filter/ColumnCallbackDeleteRow.php +++ b/core/DataTable/Filter/ColumnCallbackDeleteRow.php @@ -25,7 +25,6 @@ use Piwik\DataTable\BaseFilter; */ class ColumnCallbackDeleteRow extends BaseFilter { - private $columnToFilter; private $function; private $functionParams; diff --git a/core/DataTable/Filter/ColumnCallbackReplace.php b/core/DataTable/Filter/ColumnCallbackReplace.php index e33dfa9a147b6070de57b60e29b684a268851011..ca53405d7e80ca9d9941f1eecbb36c6440a7f929 100644 --- a/core/DataTable/Filter/ColumnCallbackReplace.php +++ b/core/DataTable/Filter/ColumnCallbackReplace.php @@ -54,14 +54,14 @@ class ColumnCallbackReplace extends BaseFilter $extraColumnParameters = array()) { parent::__construct($table); - $this->functionToApply = $functionToApply; + $this->functionToApply = $functionToApply; $this->functionParameters = $functionParameters; if (!is_array($columnsToFilter)) { $columnsToFilter = array($columnsToFilter); } - $this->columnsToFilter = $columnsToFilter; + $this->columnsToFilter = $columnsToFilter; $this->extraColumnParameters = $extraColumnParameters; } @@ -73,6 +73,7 @@ class ColumnCallbackReplace extends BaseFilter public function filter($table) { foreach ($table->getRows() as $row) { + $extraColumnParameters = array(); foreach ($this->extraColumnParameters as $columnName) { $extraColumnParameters[] = $row->getColumn($columnName); @@ -86,9 +87,11 @@ class ColumnCallbackReplace extends BaseFilter } $parameters = array_merge(array($value), $extraColumnParameters); + if (!is_null($this->functionParameters)) { $parameters = array_merge($parameters, $this->functionParameters); } + $newValue = call_user_func_array($this->functionToApply, $parameters); $this->setElementToReplace($row, $column, $newValue); $this->filterSubTable($row); diff --git a/core/DataTable/Filter/ColumnDelete.php b/core/DataTable/Filter/ColumnDelete.php index 5aac6d44a63e54ebbca7797c3457cdc391ace6f1..743f5afd8632bc642056bf8206e79f1399f46c98 100644 --- a/core/DataTable/Filter/ColumnDelete.php +++ b/core/DataTable/Filter/ColumnDelete.php @@ -91,6 +91,7 @@ class ColumnDelete extends BaseFilter * See {@link ColumnDelete}. * * @param DataTable $table + * @return DataTable */ public function filter($table) { diff --git a/core/DataTable/Filter/GroupBy.php b/core/DataTable/Filter/GroupBy.php index 8633fb2e9d54df4ddf5e2557bbd41a25b4fc58b3..b899bb9059b8d7610f0854b15267bff7cfe322fe 100755 --- a/core/DataTable/Filter/GroupBy.php +++ b/core/DataTable/Filter/GroupBy.php @@ -59,9 +59,9 @@ class GroupBy extends BaseFilter { parent::__construct($table); - $this->groupByColumn = $groupByColumn; + $this->groupByColumn = $groupByColumn; $this->reduceFunction = $reduceFunction; - $this->parameters = $parameters; + $this->parameters = $parameters; } /** @@ -82,7 +82,7 @@ class GroupBy extends BaseFilter // reduce the group by column of this row $groupByColumnValue = $row->getColumn($this->groupByColumn); - $parameters = array_merge(array($groupByColumnValue), $this->parameters); + $parameters = array_merge(array($groupByColumnValue), $this->parameters); $groupByValue = call_user_func_array($this->reduceFunction, $parameters); if (!isset($groupByRows[$groupByValue])) { diff --git a/core/DataTable/Filter/Limit.php b/core/DataTable/Filter/Limit.php index 5cf848a50eef018579831464ef5fdf2dabe5bbd4..415be296020ec1464f311826ed15b945fdff8dff 100644 --- a/core/DataTable/Filter/Limit.php +++ b/core/DataTable/Filter/Limit.php @@ -34,9 +34,9 @@ class Limit extends BaseFilter public function __construct($table, $offset, $limit = -1, $keepSummaryRow = false) { parent::__construct($table); - $this->offset = $offset; - $this->limit = $limit; + $this->offset = $offset; + $this->limit = $limit; $this->keepSummaryRow = $keepSummaryRow; } diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php index 683e93cffe9f766f7d5ab919a9fdbd89630d67cf..9df22502887258ba8ed4ab17026a04e9a8b7151f 100644 --- a/core/DataTable/Filter/Sort.php +++ b/core/DataTable/Filter/Sort.php @@ -38,11 +38,13 @@ class Sort extends BaseFilter public function __construct($table, $columnToSort, $order = 'desc', $naturalSort = true, $recursiveSort = false) { parent::__construct($table); + if ($recursiveSort) { $table->enableRecursiveSort(); } + $this->columnToSort = $columnToSort; - $this->naturalSort = $naturalSort; + $this->naturalSort = $naturalSort; $this->setOrder($order); } @@ -55,10 +57,10 @@ class Sort extends BaseFilter { if ($order == 'asc') { $this->order = 'asc'; - $this->sign = 1; + $this->sign = 1; } else { $this->order = 'desc'; - $this->sign = -1; + $this->sign = -1; } } @@ -225,17 +227,21 @@ class Sort extends BaseFilter if ($table instanceof Simple) { return; } + if (empty($this->columnToSort)) { return; } + $rows = $table->getRows(); if (count($rows) == 0) { return; } + $row = current($rows); if ($row === false) { return; } + $this->columnToSort = $this->selectColumnToSort($row); $value = $row->getColumn($this->columnToSort); @@ -248,6 +254,7 @@ class Sort extends BaseFilter $methodToUse = "sortString"; } } + $table->sort(array($this, $methodToUse), $this->columnToSort); } } diff --git a/core/DataTable/Filter/Truncate.php b/core/DataTable/Filter/Truncate.php index 192d85b82fdf38bcac8154c7c5b3e76c4d6daa52..120fc377601f770d896dfc9b8811131be4343e85 100644 --- a/core/DataTable/Filter/Truncate.php +++ b/core/DataTable/Filter/Truncate.php @@ -89,9 +89,10 @@ class Truncate extends BaseFilter return; } - $rows = $table->getRows(); - $count = $table->getRowsCount(); + $rows = $table->getRows(); + $count = $table->getRowsCount(); $newRow = new Row(array(Row::COLUMNS => array('label' => DataTable::LABEL_SUMMARY_ROW))); + for ($i = $this->truncateAfter; $i < $count; $i++) { if (!isset($rows[$i])) { // case when the last row is a summary row, it is not indexed by $cout but by DataTable::ID_SUMMARY_ROW diff --git a/core/DataTable/Manager.php b/core/DataTable/Manager.php index c19060b803175f65192a13e88a5d4e020a9e8c8d..d225b8fb87a3ad6e3e0899ec4065ae39defc18aa 100644 --- a/core/DataTable/Manager.php +++ b/core/DataTable/Manager.php @@ -63,6 +63,7 @@ class Manager extends Singleton if (!isset($this->tables[$idTable])) { throw new TableNotFoundException(sprintf("This report has been reprocessed since your last click. To see this error less often, please increase the timeout value in seconds in Settings > General Settings. (error: id %s not found).", $idTable)); } + return $this->tables[$idTable]; } @@ -86,6 +87,7 @@ class Manager extends Singleton $this->deleteTable($id); } } + if ($deleteWhenIdTableGreaterThan == 0) { $this->tables = array(); $this->nextTableId = 1; diff --git a/core/DataTable/Map.php b/core/DataTable/Map.php index dd8bec7ca5b6d9dbed57556375ac857762d31608..8795eac13423b6507fb2ab47f050a0e2ff64223b 100644 --- a/core/DataTable/Map.php +++ b/core/DataTable/Map.php @@ -246,12 +246,14 @@ class Map implements DataTableInterface public function getColumn($name) { $values = array(); + foreach ($this->getDataTables() as $table) { $moreValues = $table->getColumn($name); foreach ($moreValues as &$value) { $values[] = $value; } } + return $values; } diff --git a/core/DataTable/Renderer.php b/core/DataTable/Renderer.php index 5705b3e205289dd9aade20705f39c6cb94f098c8..f825739611ce1887c831dad84aec619cb2f330a5 100644 --- a/core/DataTable/Renderer.php +++ b/core/DataTable/Renderer.php @@ -161,13 +161,16 @@ abstract class Renderer extends BaseFactory { $className = ucfirst(strtolower($id)); $className = 'Piwik\DataTable\Renderer\\' . $className; + return $className; } protected static function getInvalidClassIdExceptionMessage($id) { $availableRenderers = implode(', ', self::getRenderers()); - return Piwik::translate('General_ExceptionInvalidRendererFormat', array(self::getClassNameFromClassId($id), $availableRenderers)); + $klassName = self::getClassNameFromClassId($id); + + return Piwik::translate('General_ExceptionInvalidRendererFormat', array($klassName, $availableRenderers)); } /** @@ -187,12 +190,15 @@ abstract class Renderer extends BaseFactory $value = @mb_convert_encoding($value, 'UTF-8', 'UTF-8'); } $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8'); + $htmlentities = array(" ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "€"); - $xmlentities = array("¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "€"); - $value = str_replace($htmlentities, $xmlentities, $value); + $xmlentities = array("¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "€"); + $value = str_replace($htmlentities, $xmlentities, $value); + } elseif ($value === false) { $value = 0; } + return $value; } diff --git a/core/DataTable/Renderer/Console.php b/core/DataTable/Renderer/Console.php index 88161165fc6ef45c7baf42bd64a62fe9a630c77f..0e1c127fb140f3a6186f4ffb0fca9243d9882d98 100644 --- a/core/DataTable/Renderer/Console.php +++ b/core/DataTable/Renderer/Console.php @@ -142,7 +142,7 @@ class Console extends Renderer foreach ($metadata as $id => $metadataIn) { $output .= "<br />"; $output .= $prefix . " <b>$id</b><br />"; - if(is_array($metadataIn)) { + if (is_array($metadataIn)) { foreach ($metadataIn as $name => $value) { $output .= $prefix . $prefix . "$name => $value"; } diff --git a/core/DataTable/Renderer/Json.php b/core/DataTable/Renderer/Json.php index 8fea38a6586575c489bbbcceefc41e3754f40a65..f53ea60e67f52e250e422f79f54fe51fc730032b 100644 --- a/core/DataTable/Renderer/Json.php +++ b/core/DataTable/Renderer/Json.php @@ -11,7 +11,6 @@ namespace Piwik\DataTable\Renderer; use Piwik\Common; use Piwik\DataTable\Renderer; use Piwik\DataTable; -use Piwik\ProxyHttp; /** * JSON export. diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php index 72e6d67b6c52cb5bb6359b1bdc62b2d82af01ed6..07946adfd10baa9f912f649ff856c6a2384ddb39 100644 --- a/core/DataTable/Row.php +++ b/core/DataTable/Row.php @@ -183,7 +183,7 @@ class Row implements \ArrayAccess, \IteratorAggregate if (isset($this->c[self::COLUMNS][$oldName])) { $this->c[self::COLUMNS][$newName] = $this->c[self::COLUMNS][$oldName]; } - // outside the if() since we want to delete nulled columns + // outside the if () since we want to delete nulled columns unset($this->c[self::COLUMNS][$oldName]); } @@ -500,9 +500,10 @@ class Row implements \ArrayAccess, \IteratorAggregate * * @param \Piwik\DataTable\Row $rowToSum The row to sum to this row. * @param bool $enableCopyMetadata Whether metadata should be copied or not. - * @param array $aggregationOperations for columns that should not be summed, determine which + * @param array|bool $aggregationOperations for columns that should not be summed, determine which * aggregation should be used (min, max). format: * `array('column name' => 'function name')` + * @throws Exception */ public function sumRow(Row $rowToSum, $enableCopyMetadata = true, $aggregationOperations = false) { @@ -528,7 +529,7 @@ class Row implements \ArrayAccess, \IteratorAggregate if ($columnToSumName == Metrics::INDEX_MAX_ACTIONS) { $operation = 'max'; } - if(empty($operation)) { + if (empty($operation)) { throw new Exception("Unknown aggregation operation for column $columnToSumName."); } diff --git a/core/Date.php b/core/Date.php index c5b4cd7af6766a3b1a8e6d70eb1fbb4df2b968a6..5b8e0fab6e01504348d2885c92fdc1a35fcaa6f9 100644 --- a/core/Date.php +++ b/core/Date.php @@ -273,15 +273,16 @@ class Date // Unit tests pass (@see Date.test.php) but I'm pretty sure this is not the right way to do it date_default_timezone_set($this->timezone); $dtzone = timezone_open('UTC'); - $time = date('r', $this->timestamp); - $dtime = date_create($time); + $time = date('r', $this->timestamp); + $dtime = date_create($time); + date_timezone_set($dtime, $dtzone); - $dateWithTimezone = date_format($dtime, 'r'); + $dateWithTimezone = date_format($dtime, 'r'); $dateWithoutTimezone = substr($dateWithTimezone, 0, -6); - $timestamp = strtotime($dateWithoutTimezone); + $timestamp = strtotime($dateWithoutTimezone); date_default_timezone_set('UTC'); - return (int)$timestamp; + return (int) $timestamp; } /** diff --git a/core/Db.php b/core/Db.php index e845a5ab6e5b4c70162cc99ace8639818d0d0d04..52c454e3333ed84dadc287bafb7da4811d9c3fe7 100644 --- a/core/Db.php +++ b/core/Db.php @@ -141,6 +141,7 @@ class Db } $profiler->queryEnd($q); + return $return; } @@ -271,7 +272,7 @@ class Db * * @param string $table The name of the table to delete from. Must be prefixed (see {@link Piwik\Common::prefixTable()}). * @param string $where The where clause of the query. Must include the WHERE keyword. - * @param $orderBy The column to order by and the order by direction, eg, `idvisit ASC`. + * @param string $orderBy The column to order by and the order by direction, eg, `idvisit ASC`. * @param int $maxRowsPerQuery The maximum number of rows to delete per `DELETE` query. * @param array $parameters Parameters to bind for each query. * @return int The total number of rows deleted. @@ -279,13 +280,13 @@ class Db public static function deleteAllRows($table, $where, $orderBy, $maxRowsPerQuery = 100000, $parameters = array()) { $orderByClause = $orderBy ? "ORDER BY $orderBy" : ""; - $sql = "DELETE FROM $table - $where - $orderByClause + + $sql = "DELETE FROM $table $where $orderByClause LIMIT " . (int)$maxRowsPerQuery; // delete rows w/ a limit $totalRowsDeleted = 0; + do { $rowsDeleted = self::query($sql, $parameters)->rowCount(); @@ -308,6 +309,7 @@ class Db public static function optimizeTables($tables) { $optimize = Config::getInstance()->General['enable_sql_optimize_queries']; + if (empty($optimize)) { return; } @@ -315,13 +317,14 @@ class Db if (empty($tables)) { return false; } + if (!is_array($tables)) { $tables = array($tables); } // filter out all InnoDB tables $myisamDbTables = array(); - foreach (Db::fetchAll("SHOW TABLE STATUS") as $row) { + foreach (self::getTableStatus() as $row) { if (strtolower($row['Engine']) == 'myisam' && in_array($row['Name'], $tables) ) { @@ -337,6 +340,11 @@ class Db return self::query("OPTIMIZE TABLE " . implode(',', $myisamDbTables)); } + private static function getTableStatus() + { + return Db::fetchAll("SHOW TABLE STATUS"); + } + /** * Drops the supplied table or tables. * @@ -397,6 +405,7 @@ class Db if (!is_array($tablesToRead)) { $tablesToRead = array($tablesToRead); } + if (!is_array($tablesToWrite)) { $tablesToWrite = array($tablesToWrite); } @@ -405,6 +414,7 @@ class Db foreach ($tablesToWrite as $table) { $lockExprs[] = $table . " WRITE"; } + foreach ($tablesToRead as $table) { $lockExprs[] = $table . " READ"; } @@ -466,6 +476,7 @@ class Db public static function segmentedFetchFirst($sql, $first, $last, $step, $params = array()) { $result = false; + if ($step > 0) { for ($i = $first; $result === false && $i <= $last; $i += $step) { $result = self::fetchOne($sql, array_merge($params, array($i, $i + $step))); @@ -475,6 +486,7 @@ class Db $result = self::fetchOne($sql, array_merge($params, array($i, $i + $step))); } } + return $result; } @@ -502,6 +514,7 @@ class Db public static function segmentedFetchOne($sql, $first, $last, $step, $params = array()) { $result = array(); + if ($step > 0) { for ($i = $first; $i <= $last; $i += $step) { $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step))); @@ -511,6 +524,7 @@ class Db $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step))); } } + return $result; } @@ -539,17 +553,19 @@ class Db public static function segmentedFetchAll($sql, $first, $last, $step, $params = array()) { $result = array(); + if ($step > 0) { for ($i = $first; $i <= $last; $i += $step) { $currentParams = array_merge($params, array($i, $i + $step)); - $result = array_merge($result, self::fetchAll($sql, $currentParams)); + $result = array_merge($result, self::fetchAll($sql, $currentParams)); } } else { for ($i = $first; $i >= $last; $i += $step) { $currentParams = array_merge($params, array($i, $i + $step)); - $result = array_merge($result, self::fetchAll($sql, $currentParams)); + $result = array_merge($result, self::fetchAll($sql, $currentParams)); } } + return $result; } @@ -623,6 +639,7 @@ class Db } $maxRetries--; } + return false; } diff --git a/core/Db/Adapter.php b/core/Db/Adapter.php index 342a320d723c38d58b29d7940521d9e92e7af7a2..5ddd529caa00d8d18a8c3672541ec91ba6135111 100644 --- a/core/Db/Adapter.php +++ b/core/Db/Adapter.php @@ -38,7 +38,7 @@ class Adapter } $className = self::getAdapterClassName($adapterName); - $adapter = new $className($dbInfos); + $adapter = new $className($dbInfos); if ($connect) { $adapter->getConnection(); diff --git a/core/Db/Adapter/Mysqli.php b/core/Db/Adapter/Mysqli.php index c864d057b01736e3efd626500d3ce560a3e13dd1..fa74f35c9d23b03b3ee01acc6b9f260eabd7a73b 100644 --- a/core/Db/Adapter/Mysqli.php +++ b/core/Db/Adapter/Mysqli.php @@ -56,8 +56,9 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface */ public function checkServerVersion() { - $serverVersion = $this->getServerVersion(); + $serverVersion = $this->getServerVersion(); $requiredVersion = Config::getInstance()->General['minimum_mysql_version']; + if (version_compare($serverVersion, $requiredVersion) === -1) { throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion))); } @@ -72,6 +73,7 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface { $serverVersion = $this->getServerVersion(); $clientVersion = $this->getClientVersion(); + // incompatible change to DECIMAL implementation in 5.0.3 if (version_compare($serverVersion, '5.0.3') >= 0 && version_compare($clientVersion, '5.0.3') < 0 @@ -168,10 +170,12 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface public function getClientVersion() { $this->_connect(); - $version = $this->_connection->server_version; - $major = (int)($version / 10000); - $minor = (int)($version % 10000 / 100); + + $version = $this->_connection->server_version; + $major = (int)($version / 10000); + $minor = (int)($version % 10000 / 100); $revision = (int)($version % 100); + return $major . '.' . $minor . '.' . $revision; } } diff --git a/core/Db/Adapter/Pdo/Mssql.php b/core/Db/Adapter/Pdo/Mssql.php index fd0248958e568f2ff0c9cfaff676819ecd7ad2bb..ab032e145d06f837928d5cf05000e4e34d97b72b 100644 --- a/core/Db/Adapter/Pdo/Mssql.php +++ b/core/Db/Adapter/Pdo/Mssql.php @@ -66,7 +66,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface try { $serverName = $this->_config["host"]; - $database = $this->_config["dbname"]; + $database = $this->_config["dbname"]; if (is_null($database)) { $database = 'master'; } @@ -134,8 +134,9 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface */ public function checkServerVersion() { - $serverVersion = $this->getServerVersion(); + $serverVersion = $this->getServerVersion(); $requiredVersion = Config::getInstance()->General['minimum_mssql_version']; + if (version_compare($serverVersion, $requiredVersion) === -1) { throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion))); } @@ -149,7 +150,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface public function getServerVersion() { try { - $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion"); + $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion"); $result = $stmt->fetchAll(Zend_Db::FETCH_NUM); if (count($result)) { return $result[0][0]; @@ -169,6 +170,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface { $serverVersion = $this->getServerVersion(); $clientVersion = $this->getClientVersion(); + if (version_compare($serverVersion, '10') >= 0 && version_compare($clientVersion, '10') < 0 ) { @@ -224,6 +226,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) { return $match[1] == $errno; } + return false; } diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php index 59c3880bc9a577306f0d1b9d32fd343db62d374c..3ed5ae9c0f65b062879925d926ef1e703b5a0b98 100644 --- a/core/Db/Adapter/Pdo/Mysql.php +++ b/core/Db/Adapter/Pdo/Mysql.php @@ -92,8 +92,9 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface */ public function checkServerVersion() { - $serverVersion = $this->getServerVersion(); + $serverVersion = $this->getServerVersion(); $requiredVersion = Config::getInstance()->General['minimum_mysql_version']; + if (version_compare($serverVersion, $requiredVersion) === -1) { throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion))); } @@ -108,6 +109,7 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface { $serverVersion = $this->getServerVersion(); $clientVersion = $this->getClientVersion(); + // incompatible change to DECIMAL implementation in 5.0.3 if (version_compare($serverVersion, '5.0.3') >= 0 && version_compare($clientVersion, '5.0.3') < 0 @@ -159,6 +161,7 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) { return $match[1] == $errno; } + return false; } @@ -170,9 +173,11 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface public function isConnectionUTF8() { $charsetInfo = $this->fetchAll('SHOW VARIABLES LIKE ?', array('character_set_connection')); + if (empty($charsetInfo)) { return false; } + $charset = $charsetInfo[0]['Value']; return $charset === 'utf8'; } diff --git a/core/Db/Adapter/Pdo/Pgsql.php b/core/Db/Adapter/Pdo/Pgsql.php index c68280fe28227f73cdff3f20d89c124d99a23e69..0a37ab2dc92103978dff34394baaaba2cd2db984 100644 --- a/core/Db/Adapter/Pdo/Pgsql.php +++ b/core/Db/Adapter/Pdo/Pgsql.php @@ -47,6 +47,7 @@ class Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements AdapterInterface { $databaseVersion = $this->getServerVersion(); $requiredVersion = Config::getInstance()->General['minimum_pgsql_version']; + if (version_compare($databaseVersion, $requiredVersion) === -1) { throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion))); } @@ -146,6 +147,7 @@ class Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements AdapterInterface if (preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match)) { return $match[1] == $map[$errno]; } + return false; } diff --git a/core/Db/BatchInsert.php b/core/Db/BatchInsert.php index 858dad4e38e8c8f0604ce80497405c020f7cee2d..f6e7a223c78c9f5d001a096a35db250871715a77 100644 --- a/core/Db/BatchInsert.php +++ b/core/Db/BatchInsert.php @@ -33,13 +33,12 @@ class BatchInsert public static function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true) { $fieldList = '(' . join(',', $fields) . ')'; - $ignore = $ignoreWhenDuplicate ? 'IGNORE' : ''; + $ignore = $ignoreWhenDuplicate ? 'IGNORE' : ''; foreach ($values as $row) { - $query = "INSERT $ignore - INTO " . $tableName . " - $fieldList - VALUES (" . Common::getSqlStringFieldsArray($row) . ")"; + $query = "INSERT $ignore INTO " . $tableName . " + $fieldList + VALUES (" . Common::getSqlStringFieldsArray($row) . ")"; Db::query($query, $row); } } @@ -123,7 +122,7 @@ class BatchInsert { // Chroot environment: prefix the path with the absolute chroot path $chrootPath = Config::getInstance()->General['absolute_chroot_path']; - if(!empty($chrootPath)) { + if (!empty($chrootPath)) { $filePath = $chrootPath . $filePath; } @@ -172,7 +171,8 @@ class BatchInsert * @see http://bugs.php.net/bug.php?id=54158 */ $openBaseDir = ini_get('open_basedir'); - $safeMode = ini_get('safe_mode'); + $safeMode = ini_get('safe_mode'); + if (empty($openBaseDir) && empty($safeMode)) { // php 5.x - LOAD DATA LOCAL INFILE is disabled if open_basedir restrictions or safe_mode enabled $keywords[] = 'LOCAL '; @@ -199,9 +199,11 @@ class BatchInsert $exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message; } } + if (count($exceptions)) { throw new Exception(implode(",", $exceptions)); } + return false; } @@ -218,8 +220,8 @@ class BatchInsert // Set up CSV delimiters, quotes, etc $delim = $fileSpec['delim']; $quote = $fileSpec['quote']; - $eol = $fileSpec['eol']; - $null = $fileSpec['null']; + $eol = $fileSpec['eol']; + $null = $fileSpec['null']; $escapespecial_cb = $fileSpec['escapespecial_cb']; $fp = @fopen($filePath, 'wb'); @@ -246,6 +248,7 @@ class BatchInsert throw new Exception('Error writing to the tmp file ' . $filePath); } } + fclose($fp); @chmod($filePath, 0777); diff --git a/core/Db/Schema.php b/core/Db/Schema.php index e210de5835258e2c1411d15f2abaf66e3ac29d8a..796e71dca5b384175572ac445afb75e421779a32 100644 --- a/core/Db/Schema.php +++ b/core/Db/Schema.php @@ -38,7 +38,7 @@ class Schema extends Singleton private static function getSchemaClassName($schemaName) { // Upgrade from pre 2.0.4 - if(strtolower($schemaName) == 'myisam' + if (strtolower($schemaName) == 'myisam' || empty($schemaName)) { $schemaName = self::DEFAULT_SCHEMA; } @@ -116,11 +116,11 @@ class Schema extends Singleton */ private function loadSchema() { - $config = Config::getInstance(); - $dbInfos = $config->database; + $config = Config::getInstance(); + $dbInfos = $config->database; $schemaName = trim($dbInfos['schema']); - $className = self::getSchemaClassName($schemaName); + $className = self::getSchemaClassName($schemaName); $this->schema = new $className(); } @@ -134,6 +134,7 @@ class Schema extends Singleton if ($this->schema === null) { $this->loadSchema(); } + return $this->schema; } diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 43a42f167d1adc256224d030ab5b244ca37567bf..eb8c1f71f13cc82b720ff43a7e5241f47e3de8fe 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -20,6 +20,8 @@ use Piwik\DbHelper; */ class Mysql implements SchemaInterface { + private $tablesInstalled = null; + /** * Is this MySQL storage engine available? * @@ -58,216 +60,213 @@ class Mysql implements SchemaInterface $prefixTables = $this->getTablePrefix(); $tables = array( - 'user' => "CREATE TABLE {$prefixTables}user ( - login VARCHAR(100) NOT NULL, - password CHAR(32) NOT NULL, - alias VARCHAR(45) NOT NULL, - email VARCHAR(100) NOT NULL, - token_auth CHAR(32) NOT NULL, - superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0', - date_registered TIMESTAMP NULL, - PRIMARY KEY(login), - UNIQUE KEY uniq_keytoken(token_auth) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'access' => "CREATE TABLE {$prefixTables}access ( - login VARCHAR(100) NOT NULL, - idsite INTEGER UNSIGNED NOT NULL, - access VARCHAR(10) NULL, - PRIMARY KEY(login, idsite) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'site' => "CREATE TABLE {$prefixTables}site ( - idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - name VARCHAR(90) NOT NULL, - main_url VARCHAR(255) NOT NULL, - ts_created TIMESTAMP NULL, - ecommerce TINYINT DEFAULT 0, - sitesearch TINYINT DEFAULT 1, - sitesearch_keyword_parameters TEXT NOT NULL, - sitesearch_category_parameters TEXT NOT NULL, - timezone VARCHAR( 50 ) NOT NULL, - currency CHAR( 3 ) NOT NULL, - excluded_ips TEXT NOT NULL, - excluded_parameters TEXT NOT NULL, - excluded_user_agents TEXT NOT NULL, - `group` VARCHAR(250) NOT NULL, - `type` VARCHAR(255) NOT NULL, - keep_url_fragment TINYINT NOT NULL DEFAULT 0, - PRIMARY KEY(idsite) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'site_url' => "CREATE TABLE {$prefixTables}site_url ( - idsite INTEGER(10) UNSIGNED NOT NULL, - url VARCHAR(255) NOT NULL, - PRIMARY KEY(idsite, url) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'goal' => " CREATE TABLE `{$prefixTables}goal` ( - `idsite` int(11) NOT NULL, - `idgoal` int(11) NOT NULL, - `name` varchar(50) NOT NULL, - `match_attribute` varchar(20) NOT NULL, - `pattern` varchar(255) NOT NULL, - `pattern_type` varchar(10) NOT NULL, - `case_sensitive` tinyint(4) NOT NULL, - `allow_multiple` tinyint(4) NOT NULL, - `revenue` float NOT NULL, - `deleted` tinyint(4) NOT NULL default '0', - PRIMARY KEY (`idsite`,`idgoal`) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'logger_message' => "CREATE TABLE {$prefixTables}logger_message ( - idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + 'user' => "CREATE TABLE {$prefixTables}user ( + login VARCHAR(100) NOT NULL, + password CHAR(32) NOT NULL, + alias VARCHAR(45) NOT NULL, + email VARCHAR(100) NOT NULL, + token_auth CHAR(32) NOT NULL, + superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0', + date_registered TIMESTAMP NULL, + PRIMARY KEY(login), + UNIQUE KEY uniq_keytoken(token_auth) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'access' => "CREATE TABLE {$prefixTables}access ( + login VARCHAR(100) NOT NULL, + idsite INTEGER UNSIGNED NOT NULL, + access VARCHAR(10) NULL, + PRIMARY KEY(login, idsite) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'site' => "CREATE TABLE {$prefixTables}site ( + idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(90) NOT NULL, + main_url VARCHAR(255) NOT NULL, + ts_created TIMESTAMP NULL, + ecommerce TINYINT DEFAULT 0, + sitesearch TINYINT DEFAULT 1, + sitesearch_keyword_parameters TEXT NOT NULL, + sitesearch_category_parameters TEXT NOT NULL, + timezone VARCHAR( 50 ) NOT NULL, + currency CHAR( 3 ) NOT NULL, + excluded_ips TEXT NOT NULL, + excluded_parameters TEXT NOT NULL, + excluded_user_agents TEXT NOT NULL, + `group` VARCHAR(250) NOT NULL, + `type` VARCHAR(255) NOT NULL, + keep_url_fragment TINYINT NOT NULL DEFAULT 0, + PRIMARY KEY(idsite) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'site_url' => "CREATE TABLE {$prefixTables}site_url ( + idsite INTEGER(10) UNSIGNED NOT NULL, + url VARCHAR(255) NOT NULL, + PRIMARY KEY(idsite, url) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'goal' => "CREATE TABLE `{$prefixTables}goal` ( + `idsite` int(11) NOT NULL, + `idgoal` int(11) NOT NULL, + `name` varchar(50) NOT NULL, + `match_attribute` varchar(20) NOT NULL, + `pattern` varchar(255) NOT NULL, + `pattern_type` varchar(10) NOT NULL, + `case_sensitive` tinyint(4) NOT NULL, + `allow_multiple` tinyint(4) NOT NULL, + `revenue` float NOT NULL, + `deleted` tinyint(4) NOT NULL default '0', + PRIMARY KEY (`idsite`,`idgoal`) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'logger_message' => "CREATE TABLE {$prefixTables}logger_message ( + idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, tag VARCHAR(50) NULL, - timestamp TIMESTAMP NULL, + timestamp TIMESTAMP NULL, level VARCHAR(16) NULL, - message TEXT NULL, - PRIMARY KEY(idlogger_message) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'log_action' => "CREATE TABLE {$prefixTables}log_action ( - idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - name TEXT, - hash INTEGER(10) UNSIGNED NOT NULL, - type TINYINT UNSIGNED NULL, - url_prefix TINYINT(2) NULL, - PRIMARY KEY(idaction), - INDEX index_type_hash (type, hash) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'log_visit' => "CREATE TABLE {$prefixTables}log_visit ( - idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - idsite INTEGER(10) UNSIGNED NOT NULL, - idvisitor BINARY(8) NOT NULL, - visit_last_action_time DATETIME NOT NULL, - config_id BINARY(8) NOT NULL, - location_ip VARBINARY(16) NOT NULL, - PRIMARY KEY(idvisit), - INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time), - INDEX index_idsite_datetime (idsite, visit_last_action_time), - INDEX index_idsite_idvisitor (idsite, idvisitor) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", + message TEXT NULL, + PRIMARY KEY(idlogger_message) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'log_action' => "CREATE TABLE {$prefixTables}log_action ( + idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name TEXT, + hash INTEGER(10) UNSIGNED NOT NULL, + type TINYINT UNSIGNED NULL, + url_prefix TINYINT(2) NULL, + PRIMARY KEY(idaction), + INDEX index_type_hash (type, hash) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'log_visit' => "CREATE TABLE {$prefixTables}log_visit ( + idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + idsite INTEGER(10) UNSIGNED NOT NULL, + idvisitor BINARY(8) NOT NULL, + visit_last_action_time DATETIME NOT NULL, + config_id BINARY(8) NOT NULL, + location_ip VARBINARY(16) NOT NULL, + PRIMARY KEY(idvisit), + INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time), + INDEX index_idsite_datetime (idsite, visit_last_action_time), + INDEX index_idsite_idvisitor (idsite, idvisitor) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", 'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` ( - idsite int(10) UNSIGNED NOT NULL, - idvisitor BINARY(8) NOT NULL, - server_time DATETIME NOT NULL, - idvisit INTEGER(10) UNSIGNED NOT NULL, - idorder varchar(100) NOT NULL, - - idaction_sku INTEGER(10) UNSIGNED NOT NULL, - idaction_name INTEGER(10) UNSIGNED NOT NULL, - idaction_category INTEGER(10) UNSIGNED NOT NULL, - idaction_category2 INTEGER(10) UNSIGNED NOT NULL, - idaction_category3 INTEGER(10) UNSIGNED NOT NULL, - idaction_category4 INTEGER(10) UNSIGNED NOT NULL, - idaction_category5 INTEGER(10) UNSIGNED NOT NULL, - price FLOAT NOT NULL, - quantity INTEGER(10) UNSIGNED NOT NULL, - deleted TINYINT(1) UNSIGNED NOT NULL, - - PRIMARY KEY(idvisit, idorder, idaction_sku), - INDEX index_idsite_servertime ( idsite, server_time ) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` ( - idvisit int(10) unsigned NOT NULL, - idsite int(10) unsigned NOT NULL, - idvisitor BINARY(8) NOT NULL, - server_time datetime NOT NULL, - idaction_url int(11) default NULL, - idlink_va int(11) default NULL, - idgoal int(10) NOT NULL, - buster int unsigned NOT NULL, - idorder varchar(100) default NULL, - items SMALLINT UNSIGNED DEFAULT NULL, - url text NOT NULL, - - PRIMARY KEY (idvisit, idgoal, buster), - UNIQUE KEY unique_idsite_idorder (idsite, idorder), - INDEX index_idsite_datetime ( idsite, server_time ) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", + idsite int(10) UNSIGNED NOT NULL, + idvisitor BINARY(8) NOT NULL, + server_time DATETIME NOT NULL, + idvisit INTEGER(10) UNSIGNED NOT NULL, + idorder varchar(100) NOT NULL, + idaction_sku INTEGER(10) UNSIGNED NOT NULL, + idaction_name INTEGER(10) UNSIGNED NOT NULL, + idaction_category INTEGER(10) UNSIGNED NOT NULL, + idaction_category2 INTEGER(10) UNSIGNED NOT NULL, + idaction_category3 INTEGER(10) UNSIGNED NOT NULL, + idaction_category4 INTEGER(10) UNSIGNED NOT NULL, + idaction_category5 INTEGER(10) UNSIGNED NOT NULL, + price FLOAT NOT NULL, + quantity INTEGER(10) UNSIGNED NOT NULL, + deleted TINYINT(1) UNSIGNED NOT NULL, + PRIMARY KEY(idvisit, idorder, idaction_sku), + INDEX index_idsite_servertime ( idsite, server_time ) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` ( + idvisit int(10) unsigned NOT NULL, + idsite int(10) unsigned NOT NULL, + idvisitor BINARY(8) NOT NULL, + server_time datetime NOT NULL, + idaction_url int(11) default NULL, + idlink_va int(11) default NULL, + idgoal int(10) NOT NULL, + buster int unsigned NOT NULL, + idorder varchar(100) default NULL, + items SMALLINT UNSIGNED DEFAULT NULL, + url text NOT NULL, + PRIMARY KEY (idvisit, idgoal, buster), + UNIQUE KEY unique_idsite_idorder (idsite, idorder), + INDEX index_idsite_datetime ( idsite, server_time ) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action ( - idlink_va INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT, - idsite int(10) UNSIGNED NOT NULL, - idvisitor BINARY(8) NOT NULL, - idvisit INTEGER(10) UNSIGNED NOT NULL, - idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0, - idaction_name_ref INTEGER(10) UNSIGNED NOT NULL, - - custom_float FLOAT NULL DEFAULT NULL, - PRIMARY KEY(idlink_va), - INDEX index_idvisit(idvisit) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling ( - query TEXT NOT NULL, - count INTEGER UNSIGNED NULL, - sum_time_ms FLOAT NULL, - UNIQUE KEY query(query(100)) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'option' => "CREATE TABLE `{$prefixTables}option` ( - option_name VARCHAR( 255 ) NOT NULL, - option_value LONGTEXT NOT NULL, - autoload TINYINT NOT NULL DEFAULT '1', - PRIMARY KEY ( option_name ), - INDEX autoload( autoload ) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'session' => "CREATE TABLE {$prefixTables}session ( - id VARCHAR( 255 ) NOT NULL, - modified INTEGER, - lifetime INTEGER, - data TEXT, - PRIMARY KEY ( id ) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric ( - idarchive INTEGER UNSIGNED NOT NULL, - name VARCHAR(255) NOT NULL, - idsite INTEGER UNSIGNED NULL, - date1 DATE NULL, - date2 DATE NULL, - period TINYINT UNSIGNED NULL, - ts_archived DATETIME NULL, - value DOUBLE NULL, - PRIMARY KEY(idarchive, name), - INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived), - INDEX index_period_archived(period, ts_archived) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", - - 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob ( - idarchive INTEGER UNSIGNED NOT NULL, - name VARCHAR(255) NOT NULL, - idsite INTEGER UNSIGNED NULL, - date1 DATE NULL, - date2 DATE NULL, - period TINYINT UNSIGNED NULL, - ts_archived DATETIME NULL, - value MEDIUMBLOB NULL, - PRIMARY KEY(idarchive, name), - INDEX index_period_archived(period, ts_archived) - ) ENGINE=$engine DEFAULT CHARSET=utf8 - ", + idlink_va INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT, + idsite int(10) UNSIGNED NOT NULL, + idvisitor BINARY(8) NOT NULL, + idvisit INTEGER(10) UNSIGNED NOT NULL, + idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0, + idaction_name_ref INTEGER(10) UNSIGNED NOT NULL, + custom_float FLOAT NULL DEFAULT NULL, + PRIMARY KEY(idlink_va), + INDEX index_idvisit(idvisit) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling ( + query TEXT NOT NULL, + count INTEGER UNSIGNED NULL, + sum_time_ms FLOAT NULL, + UNIQUE KEY query(query(100)) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'option' => "CREATE TABLE `{$prefixTables}option` ( + option_name VARCHAR( 255 ) NOT NULL, + option_value LONGTEXT NOT NULL, + autoload TINYINT NOT NULL DEFAULT '1', + PRIMARY KEY ( option_name ), + INDEX autoload( autoload ) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'session' => "CREATE TABLE {$prefixTables}session ( + id VARCHAR( 255 ) NOT NULL, + modified INTEGER, + lifetime INTEGER, + data TEXT, + PRIMARY KEY ( id ) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric ( + idarchive INTEGER UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + idsite INTEGER UNSIGNED NULL, + date1 DATE NULL, + date2 DATE NULL, + period TINYINT UNSIGNED NULL, + ts_archived DATETIME NULL, + value DOUBLE NULL, + PRIMARY KEY(idarchive, name), + INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived), + INDEX index_period_archived(period, ts_archived) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + + 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob ( + idarchive INTEGER UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + idsite INTEGER UNSIGNED NULL, + date1 DATE NULL, + date2 DATE NULL, + period TINYINT UNSIGNED NULL, + ts_archived DATETIME NULL, + value MEDIUMBLOB NULL, + PRIMARY KEY(idarchive, name), + INDEX index_period_archived(period, ts_archived) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", ); + return $tables; } @@ -297,17 +296,17 @@ class Mysql implements SchemaInterface */ public function getTablesNames() { - $aTables = array_keys($this->getTablesCreateSql()); + $aTables = array_keys($this->getTablesCreateSql()); $prefixTables = $this->getTablePrefix(); + $return = array(); foreach ($aTables as $table) { $return[] = $prefixTables . $table; } + return $return; } - private $tablesInstalled = null; - /** * Get list of installed columns in a table * @@ -317,7 +316,7 @@ class Mysql implements SchemaInterface */ public function getTableColumns($tableName) { - $db = Db::get(); + $db = $this->getDb(); $allColumns = $db->fetchAll("SHOW COLUMNS FROM . $tableName"); @@ -340,13 +339,11 @@ class Mysql implements SchemaInterface if (is_null($this->tablesInstalled) || $forceReload === true ) { - $db = Db::get(); - $prefixTables = $this->getTablePrefix(); - // '_' matches any character; force it to be literal - $prefixTables = str_replace('_', '\_', $prefixTables); + $db = $this->getDb(); + $prefixTables = $this->getTablePrefixEscaped(); - $allTables = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'"); + $allTables = $this->getAllExistingTables($prefixTables); // all the tables to be installed $allMyTables = $this->getTablesNames(); @@ -356,12 +353,13 @@ class Mysql implements SchemaInterface // at this point we have the static list of core tables, but let's add the monthly archive tables $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'"); - $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'"); + $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'"); $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob); $this->tablesInstalled = $allTablesReallyInstalled; } + return $this->tablesInstalled; } @@ -385,6 +383,7 @@ class Mysql implements SchemaInterface if (is_null($dbName)) { $dbName = $this->getDbName(); } + Db::exec("CREATE DATABASE IF NOT EXISTS " . $dbName . " DEFAULT CHARACTER SET utf8"); } @@ -408,7 +407,7 @@ class Mysql implements SchemaInterface } catch (Exception $e) { // mysql code error 1050:table already exists // see bug #153 https://github.com/piwik/piwik/issues/153 - if (!Db::get()->isErrNo($e, '1050')) { + if (!$this->getDb()->isErrNo($e, '1050')) { throw $e; } } @@ -428,7 +427,7 @@ class Mysql implements SchemaInterface */ public function createTables() { - $db = Db::get(); + $db = $this->getDb(); $prefixTables = $this->getTablePrefix(); $tablesAlreadyInstalled = $this->getTablesInstalled(); @@ -451,9 +450,9 @@ class Mysql implements SchemaInterface { // The anonymous user is the user that is assigned by default // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin - $db = Db::get(); + $db = $this->getDb(); $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . " - VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );"); + VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );"); } /** @@ -461,15 +460,15 @@ class Mysql implements SchemaInterface */ public function truncateAllTables() { - $tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true); - foreach ($tablesAlreadyInstalled as $table) { + $tables = $this->getAllExistingTables(); + foreach ($tables as $table) { Db::query("TRUNCATE `$table`"); } } private function getTablePrefix() { - $dbInfos = Db::getDatabaseConfig(); + $dbInfos = Db::getDatabaseConfig(); $prefixTables = $dbInfos['tables_prefix']; return $prefixTables; @@ -478,10 +477,15 @@ class Mysql implements SchemaInterface private function getTableEngine() { $dbInfos = Db::getDatabaseConfig(); - $engine = $dbInfos['type']; + $engine = $dbInfos['type']; + return $engine; } + private function getDb(){ + return Db::get(); + } + private function getDbName() { $dbInfos = Db::getDatabaseConfig(); @@ -489,4 +493,21 @@ class Mysql implements SchemaInterface return $dbName; } + + private function getAllExistingTables($prefixTables = false) + { + if (empty($prefixTables)) { + $prefixTables = $this->getTablePrefixEscaped(); + } + + return Db::get()->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'"); + } + + private function getTablePrefixEscaped() + { + $prefixTables = $this->getTablePrefix(); + // '_' matches any character; force it to be literal + $prefixTables = str_replace('_', '\_', $prefixTables); + return $prefixTables; + } } diff --git a/core/DeviceDetectorCache.php b/core/DeviceDetectorCache.php index 2c637aa95ec36017a864d245fb6a93f717022b30..ba65b82c9f639be3f99b44d4c31d61596d925182 100644 --- a/core/DeviceDetectorCache.php +++ b/core/DeviceDetectorCache.php @@ -32,6 +32,7 @@ class DeviceDetectorCache extends CacheFile implements \DeviceDetector\Cache\Cac if (empty($id)) { return false; } + $id = $this->cleanupId($id); if (array_key_exists($id, self::$staticCache)) { diff --git a/core/Error.php b/core/Error.php index f7decbbc163529966c622cd24e71937045f2497e..c56e3301a7705f23685d570cf208b6437bb00c51 100644 --- a/core/Error.php +++ b/core/Error.php @@ -8,11 +8,6 @@ */ namespace Piwik; -use Piwik\Common; -use Piwik\Log; -use Piwik\Piwik; -use Piwik\Version; - require_once PIWIK_INCLUDE_PATH . '/core/Log.php'; /** diff --git a/core/EventDispatcher.php b/core/EventDispatcher.php index d9d26b437763b494eeed05480c4e18e64e05c187..d343da8bebe2f261dad95756bc9d1a2809ecd8fe 100644 --- a/core/EventDispatcher.php +++ b/core/EventDispatcher.php @@ -48,7 +48,7 @@ class EventDispatcher extends Singleton /** * Plugin\Manager instance used to get list of loaded plugins. * - * @var Piwik\Plugin\Manager + * @var \Piwik\Plugin\Manager */ private $pluginManager; diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php index 0e526225d59c3e1b6307f798a863c3a293e0c6e5..8566385070453de3f741dc1d361f375f44a7af87 100644 --- a/core/ExceptionHandler.php +++ b/core/ExceptionHandler.php @@ -9,7 +9,6 @@ namespace Piwik; use Piwik\API\ResponseBuilder; -use Piwik\Common; use Piwik\Plugin; /** diff --git a/core/Filechecks.php b/core/Filechecks.php index 33d0ed06964834d1516b096c1ac64c2093b2d6ee..3dc6d53195b677316029a2fd5e04c34564ffa120 100644 --- a/core/Filechecks.php +++ b/core/Filechecks.php @@ -43,7 +43,7 @@ class Filechecks $directoryToCheck = PIWIK_USER_PATH . $directoryToCheck; } - if(strpos($directoryToCheck, '/tmp/') !== false) { + if (strpos($directoryToCheck, '/tmp/') !== false) { $directoryToCheck = SettingsPiwik::rewriteTmpPathWithInstanceId($directoryToCheck); } @@ -87,9 +87,9 @@ class Filechecks $directoryList = "<code>chown -R ". self::getUserAndGroup() ." " . $realpath . "</code><br />" . $directoryList; } - if(function_exists('shell_exec')) { + if (function_exists('shell_exec')) { $currentUser = self::getUser(); - if(!empty($currentUser)) { + if (!empty($currentUser)) { $optionalUserInfo = " (running as user '" . $currentUser . "')"; } } @@ -211,13 +211,13 @@ class Filechecks private static function getUserAndGroup() { $user = self::getUser(); - if(!function_exists('shell_exec')) { + if (!function_exists('shell_exec')) { return $user . ':' . $user; } $group = trim(shell_exec('groups '. $user .' | cut -f3 -d" "')); - if(empty($group)) { + if (empty($group)) { $group = 'www-data'; } return $user . ':' . $group; @@ -225,7 +225,7 @@ class Filechecks private static function getUser() { - if(!function_exists('shell_exec')) { + if (!function_exists('shell_exec')) { return 'www-data'; } return trim(shell_exec('whoami')); diff --git a/core/FrontController.php b/core/FrontController.php index a50fb152ca3f98c558cb04f626d58974a107fab1..ce39922a2ee3b08f98d158caa2ed689df55c750b 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -57,6 +57,7 @@ use Piwik\Plugins\CoreHome\Controller as CoreHomeController; class FrontController extends Singleton { const DEFAULT_MODULE = 'CoreHome'; + /** * Set to false and the Front Controller will not dispatch the request * @@ -153,7 +154,8 @@ class FrontController extends Singleton return array(new CoreHomeController(), 'renderReportWidget'); } - if (!empty($action) && 'menu' === substr($action, 0, 4)) { + if (!empty($action) && Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report + ::PREFIX_ACTION_IN_MENU))) { $reportAction = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls $report = Report::factory($module, $reportAction); @@ -508,22 +510,22 @@ class FrontController extends Singleton protected function handleSSLRedirection() { // Specifically disable for the opt out iframe - if(Piwik::getModule() == 'CoreAdminHome' && Piwik::getAction() == 'optOut') { + if (Piwik::getModule() == 'CoreAdminHome' && Piwik::getAction() == 'optOut') { return; } // Disable Https for VisitorGenerator - if(Piwik::getModule() == 'VisitorGenerator') { + if (Piwik::getModule() == 'VisitorGenerator') { return; } - if(Common::isPhpCliMode()) { + if (Common::isPhpCliMode()) { return; } // Only enable this feature after Piwik is already installed - if(!SettingsPiwik::isPiwikInstalled()) { + if (!SettingsPiwik::isPiwikInstalled()) { return; } // proceed only when force_ssl = 1 - if(!SettingsPiwik::isHttpsForced()) { + if (!SettingsPiwik::isHttpsForced()) { return; } Url::redirectToHttps(); diff --git a/core/Http.php b/core/Http.php index 537330716ab6fcb113b793a9f90a89fa862cb8f3..3643fc8553765cc07dbd50985aac51af6df290e9 100644 --- a/core/Http.php +++ b/core/Http.php @@ -165,7 +165,7 @@ class Http $aUrl = trim($aUrl); // other result data - $status = null; + $status = null; $headers = array(); if ($method == 'socket') { @@ -183,7 +183,7 @@ class Http throw new Exception('Invalid protocol/scheme: ' . $url['scheme']); } $host = $url['host']; - $port = isset($url['port)']) ? $url['port'] : 80; + $port = isset($url['port']) ? $url['port'] : 80; $path = isset($url['path']) ? $url['path'] : '/'; if (isset($url['query'])) { $path .= '?' . $url['query']; diff --git a/core/IP.php b/core/IP.php index 52f5f7c23ff88c70cd67557042fc76db0c2b8478..691bc7442c19762df0a61e3a4793ae570d202139 100644 --- a/core/IP.php +++ b/core/IP.php @@ -370,7 +370,7 @@ class IP { $proxyIps = array(); $config = Config::getInstance()->General; - if(isset($config['proxy_ips'])) { + if (isset($config['proxy_ips'])) { $proxyIps = $config['proxy_ips']; } if (!is_array($proxyIps)) { diff --git a/core/Log.php b/core/Log.php index a860dd19893ad23772d4860d66fda5ddd352220a..b3421d13e1ae44f6bfc799a77409129a55b1875f 100644 --- a/core/Log.php +++ b/core/Log.php @@ -452,7 +452,7 @@ class Log extends Singleton if ($level == self::ERROR) { $message = $this->getMessageFormattedScreen($level, $tag, $datetime, $message); $this->writeErrorToStandardErrorOutput($message); - if(!isset($this->writers['screen'])) { + if (!isset($this->writers['screen'])) { echo $message; } } @@ -599,7 +599,7 @@ class Log extends Singleton */ private function writeErrorToStandardErrorOutput($message) { - if(defined('PIWIK_TEST_MODE')) { + if (defined('PIWIK_TEST_MODE')) { // do not log on stderr during tests (prevent display of errors in CI output) return; } diff --git a/core/Mail.php b/core/Mail.php index d26b08b1544f738cb2f37f1e559bb71f45d7ba45..179b1221ab31a946b61ba1c165be28fafa92bdd6 100644 --- a/core/Mail.php +++ b/core/Mail.php @@ -34,9 +34,13 @@ class Mail extends Zend_Mail public function setDefaultFromPiwik() { $customLogo = new CustomLogo(); - $fromEmailName = $customLogo->isEnabled() - ? Piwik::translate('CoreHome_WebAnalyticsReports') - : Piwik::translate('ScheduledReports_PiwikReports'); + + if ($customLogo->isEnabled()) { + $fromEmailName = Piwik::translate('CoreHome_WebAnalyticsReports'); + } else { + $fromEmailName = Piwik::translate('ScheduledReports_PiwikReports'); + } + $fromEmailAddress = Config::getInstance()->General['noreply_email_address']; $this->setFrom($fromEmailAddress, $fromEmailName); } @@ -77,20 +81,29 @@ class Mail extends Zend_Mail private function initSmtpTransport() { $mailConfig = Config::getInstance()->mail; + if (empty($mailConfig['host']) || $mailConfig['transport'] != 'smtp' ) { return; } + $smtpConfig = array(); - if (!empty($mailConfig['type'])) + if (!empty($mailConfig['type'])) { $smtpConfig['auth'] = strtolower($mailConfig['type']); - if (!empty($mailConfig['username'])) + } + + if (!empty($mailConfig['username'])) { $smtpConfig['username'] = $mailConfig['username']; - if (!empty($mailConfig['password'])) + } + + if (!empty($mailConfig['password'])) { $smtpConfig['password'] = $mailConfig['password']; - if (!empty($mailConfig['encryption'])) + } + + if (!empty($mailConfig['encryption'])) { $smtpConfig['ssl'] = $mailConfig['encryption']; + } $tr = new \Zend_Mail_Transport_Smtp($mailConfig['host'], $smtpConfig); Mail::setDefaultTransport($tr); @@ -112,12 +125,12 @@ class Mail extends Zend_Mail */ protected function parseDomainPlaceholderAsPiwikHostName($email) { - $hostname = Config::getInstance()->mail['defaultHostnameIfEmpty']; + $hostname = Config::getInstance()->mail['defaultHostnameIfEmpty']; $piwikHost = Url::getCurrentHost($hostname); // If known Piwik URL, use it instead of "localhost" $piwikUrl = SettingsPiwik::getPiwikUrl(); - $url = parse_url($piwikUrl); + $url = parse_url($piwikUrl); if ($this->isHostDefinedAndNotLocal($url)) { $piwikHost = $url['host']; } diff --git a/core/Menu/Group.php b/core/Menu/Group.php index 17e7c9bc5ca7ca2c52d35a42e91baa51ef9b8a51..90e063e2648efe9691ffea87413ea1a5acdcc61e 100644 --- a/core/Menu/Group.php +++ b/core/Menu/Group.php @@ -18,8 +18,8 @@ class Group public function add($subTitleMenu, $url, $tooltip = false) { $this->items[] = array( - 'name' => $subTitleMenu, - 'url' => $url, + 'name' => $subTitleMenu, + 'url' => $url, 'tooltip' => $tooltip );; } diff --git a/core/Menu/MenuAbstract.php b/core/Menu/MenuAbstract.php index 1fe1152c98e38ffca391eee2601919fcb1299cfd..0d77ff21d0efc6e11182f51715fa3b5cced047a1 100644 --- a/core/Menu/MenuAbstract.php +++ b/core/Menu/MenuAbstract.php @@ -9,7 +9,6 @@ namespace Piwik\Menu; use Piwik\Common; -use Piwik\Log; use Piwik\Plugins\SitesManager\API; use Piwik\Singleton; use Piwik\Plugin\Manager as PluginManager; @@ -104,7 +103,7 @@ abstract class MenuAbstract extends Singleton public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false) { // make sure the idSite value used is numeric (hack-y fix for #3426) - if (!is_numeric(Common::getRequestVar('idSite', false))) { + if (isset($url['idSite']) && !is_numeric($url['idSite'])) { $idSites = API::getInstance()->getSitesIdWithAtLeastViewAccess(); $url['idSite'] = reset($idSites); } @@ -215,8 +214,8 @@ abstract class MenuAbstract extends Singleton { foreach ($this->edits as $edit) { $mainMenuToEdit = $edit[0]; - $subMenuToEdit = $edit[1]; - $newUrl = $edit[2]; + $subMenuToEdit = $edit[1]; + $newUrl = $edit[2]; if ($subMenuToEdit === null) { $menuDataToEdit = @$this->menu[$mainMenuToEdit]; @@ -236,14 +235,14 @@ abstract class MenuAbstract extends Singleton { foreach($this->menuEntriesToRemove as $menuToDelete) { - if(empty($menuToDelete[1])) { + if (empty($menuToDelete[1])) { // Delete Main Menu - if(isset($this->menu[$menuToDelete[0]])) { + if (isset($this->menu[$menuToDelete[0]])) { unset($this->menu[$menuToDelete[0]]); } } else { // Delete Sub Menu - if(isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) { + if (isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) { unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]); } } @@ -256,9 +255,10 @@ abstract class MenuAbstract extends Singleton { foreach ($this->renames as $rename) { $mainMenuOriginal = $rename[0]; - $subMenuOriginal = $rename[1]; - $mainMenuRenamed = $rename[2]; - $subMenuRenamed = $rename[3]; + $subMenuOriginal = $rename[1]; + $mainMenuRenamed = $rename[2]; + $subMenuRenamed = $rename[3]; + // Are we changing a submenu? if (!empty($subMenuOriginal)) { if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) { diff --git a/core/Menu/MenuAdmin.php b/core/Menu/MenuAdmin.php index 5c814b6706014781c5cf5ba1e89ff1aeb38146f4..13e84805967c0eed3b95c608614e15e98f99462b 100644 --- a/core/Menu/MenuAdmin.php +++ b/core/Menu/MenuAdmin.php @@ -45,7 +45,9 @@ class MenuAdmin extends MenuAbstract */ public static function addEntry($adminMenuName, $url, $displayedForCurrentUser = true, $order = 20) { - self::getInstance()->add('General_Settings', $adminMenuName, $url, $displayedForCurrentUser, $order); + if ($displayedForCurrentUser) { + self::getInstance()->addItem('General_Settings', $adminMenuName, $url, $order); + } } /** @@ -59,7 +61,7 @@ class MenuAdmin extends MenuAbstract */ public function addDevelopmentItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CoreAdminHome_MenuDevelopment', $menuName, $url, true, $order, $tooltip); + $this->addItem('CoreAdminHome_MenuDevelopment', $menuName, $url, $order, $tooltip); } /** @@ -73,7 +75,7 @@ class MenuAdmin extends MenuAbstract */ public function addDiagnosticItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CoreAdminHome_MenuDiagnostic', $menuName, $url, true, $order, $tooltip); + $this->addItem('CoreAdminHome_MenuDiagnostic', $menuName, $url, $order, $tooltip); } /** @@ -87,7 +89,7 @@ class MenuAdmin extends MenuAbstract */ public function addPlatformItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CorePluginsAdmin_MenuPlatform', $menuName, $url, true, $order, $tooltip); + $this->addItem('CorePluginsAdmin_MenuPlatform', $menuName, $url, $order, $tooltip); } /** @@ -101,7 +103,7 @@ class MenuAdmin extends MenuAbstract */ public function addSettingsItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('General_Settings', $menuName, $url, true, $order, $tooltip); + $this->addItem('General_Settings', $menuName, $url, $order, $tooltip); } /** @@ -115,7 +117,7 @@ class MenuAdmin extends MenuAbstract */ public function addManageItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CoreAdminHome_MenuManage', $menuName, $url, true, $order, $tooltip); + $this->addItem('CoreAdminHome_MenuManage', $menuName, $url, $order, $tooltip); } /** diff --git a/core/Menu/MenuMain.php b/core/Menu/MenuMain.php index 5f27f3229e2d28af05c90417bbf6cabafa790b96..adb6b538e992e52d529ce66c350d68300564a410 100644 --- a/core/Menu/MenuMain.php +++ b/core/Menu/MenuMain.php @@ -11,7 +11,7 @@ namespace Piwik\Menu; /** * @deprecated since 2.4.0 * @see MenuReporting - * @method static \Piwik\Menu\MenuMain getInstance() + * @method static MenuMain getInstance() * @ignore */ class MenuMain extends MenuReporting diff --git a/core/Menu/MenuReporting.php b/core/Menu/MenuReporting.php index 28fddd8313a69c1be4e7814f3d7d96521b88288b..ac35151bcb773b89d03978763973794642c0e1d4 100644 --- a/core/Menu/MenuReporting.php +++ b/core/Menu/MenuReporting.php @@ -45,7 +45,7 @@ class MenuReporting extends MenuAbstract */ public function addVisitorsItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('General_Visitors', $menuName, $url, true, $order, $tooltip); + $this->addItem('General_Visitors', $menuName, $url, $order, $tooltip); } /** @@ -59,7 +59,7 @@ class MenuReporting extends MenuAbstract */ public function addActionsItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('General_Actions', $menuName, $url, true, $order, $tooltip); + $this->addItem('General_Actions', $menuName, $url, $order, $tooltip); } /** @@ -88,7 +88,7 @@ class MenuReporting extends MenuAbstract */ public function addReferrersItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('Referrers_Referrers', $menuName, $url, true, $order, $tooltip); + $this->addItem('Referrers_Referrers', $menuName, $url, $order, $tooltip); } /** diff --git a/core/Menu/MenuUser.php b/core/Menu/MenuUser.php index ad9b195d46b402c652b162988a880f67cadf36a1..758ac3d578c5877050b8d421ad600ff0052621ad 100755 --- a/core/Menu/MenuUser.php +++ b/core/Menu/MenuUser.php @@ -26,7 +26,7 @@ namespace Piwik\Menu; * ); * } * - * @method static \Piwik\Menu\MenuUser getInstance() + * @method static MenuUser getInstance() */ class MenuUser extends MenuAbstract { @@ -42,7 +42,7 @@ class MenuUser extends MenuAbstract */ public function addManageItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CoreAdminHome_MenuManage', $menuName, $url, true, $order, $tooltip); + $this->addItem('CoreAdminHome_MenuManage', $menuName, $url, $order, $tooltip); } /** @@ -56,7 +56,7 @@ class MenuUser extends MenuAbstract */ public function addPlatformItem($menuName, $url, $order = 50, $tooltip = false) { - $this->add('CorePluginsAdmin_MenuPlatform', $menuName, $url, true, $order, $tooltip); + $this->addItem('CorePluginsAdmin_MenuPlatform', $menuName, $url, $order, $tooltip); } /** diff --git a/core/Metrics.php b/core/Metrics.php index 03b9e2e0946cabc5f909bfa49f5a805610909108..0f8baf5a10f7403eb6aac173cba32d6f530c07c1 100644 --- a/core/Metrics.php +++ b/core/Metrics.php @@ -175,9 +175,11 @@ class Metrics public static function getVisitsMetricNames() { $names = array(); + foreach (self::$metricsAggregatedFromLogs as $metricId) { $names[$metricId] = self::$mappingFromIdToName[$metricId]; } + return $names; } diff --git a/core/Metrics/Processed.php b/core/Metrics/Processed.php index bbc638828a90d9bd7a74658eb158ace5c199e400..0e6b7c969be13bcbed478d8486ff89264906ac15 100644 --- a/core/Metrics/Processed.php +++ b/core/Metrics/Processed.php @@ -11,8 +11,6 @@ namespace Piwik\Metrics; use Piwik\Metrics; use Piwik\DataTable\Row; use Piwik\DataTable; -use Piwik\Piwik; -use Piwik\Tracker\GoalManager; class Processed extends Base { diff --git a/core/MetricsFormatter.php b/core/MetricsFormatter.php index 8c58215e301c4f54102f68e59c3453fa6ffa53b1..855466352586da20400308252405090630363b78 100644 --- a/core/MetricsFormatter.php +++ b/core/MetricsFormatter.php @@ -61,10 +61,10 @@ class MetricsFormatter // Display 01:45:17 time format if ($displayTimeAsSentence === false) { - $hours = floor($numberOfSeconds / 3600); + $hours = floor($numberOfSeconds / 3600); $minutes = floor(($reminder = ($numberOfSeconds - $hours * 3600)) / 60); $seconds = floor($reminder - $minutes * 60); - $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds); + $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds); $centiSeconds = ($numberOfSeconds * 100) % 100; if ($centiSeconds) { $time .= '.' . sprintf("%02s", $centiSeconds); @@ -76,19 +76,19 @@ class MetricsFormatter } $secondsInYear = 86400 * 365.25; - $years = floor($numberOfSeconds / $secondsInYear); + $years = floor($numberOfSeconds / $secondsInYear); $minusYears = $numberOfSeconds - $years * $secondsInYear; - $days = floor($minusYears / 86400); + $days = floor($minusYears / 86400); $minusDays = $numberOfSeconds - $days * 86400; - $hours = floor($minusDays / 3600); + $hours = floor($minusDays / 3600); $minusDaysAndHours = $minusDays - $hours * 3600; $minutes = floor($minusDaysAndHours / 60); - $seconds = $minusDaysAndHours - $minutes * 60; + $seconds = $minusDaysAndHours - $minutes * 60; $precision = ($seconds > 0 && $seconds < 0.01 ? 3 : 2); - $seconds = round($seconds, $precision); + $seconds = round($seconds, $precision); if ($years > 0) { $return = sprintf(Piwik::translate('General_YearsDays'), $years, $days); @@ -109,6 +109,7 @@ class MetricsFormatter if ($isHtml) { return str_replace(' ', ' ', $return); } + return $return; } @@ -134,6 +135,7 @@ class MetricsFormatter break; } } + return round($size, $precision) . " " . $currentUnit; } @@ -175,6 +177,7 @@ class MetricsFormatter $value = sprintf("%01." . $precision . "f", $value); } } + $prettyMoney = $currencyBefore . $space . $value . $currencyAfter; return $prettyMoney; } @@ -196,16 +199,19 @@ class MetricsFormatter $timeAsSentence = (substr($columnName, -16) == '_time_generation'); return self::getPrettyTimeFromSeconds($value, $timeAsSentence); } + // Add revenue symbol to revenues if (strpos($columnName, 'revenue') !== false && strpos($columnName, 'evolution') === false) { return self::getPrettyMoney($value, $idSite, $isHtml); } + // Add % symbol to rates if (strpos($columnName, '_rate') !== false) { if (strpos($value, "%") === false) { return $value . "%"; } } + return $value; } @@ -217,12 +223,14 @@ class MetricsFormatter */ public static function getCurrencySymbol($idSite) { - $symbols = MetricsFormatter::getCurrencyList(); - $site = new Site($idSite); + $symbols = MetricsFormatter::getCurrencyList(); + $site = new Site($idSite); $currency = $site->getCurrency(); + if (isset($symbols[$currency])) { return $symbols[$currency][0]; } + return ''; } @@ -235,10 +243,12 @@ class MetricsFormatter public static function getCurrencyList() { static $currenciesList = null; + if (is_null($currenciesList)) { require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Currencies.php'; $currenciesList = $GLOBALS['Piwik_CurrencyList']; } + return $currenciesList; } } diff --git a/core/Nonce.php b/core/Nonce.php index 3937c4f94b030c3d338714314fa07da793bfe5af..1b64b87933ffdf5d467310bb06be2bdf53dbdfb9 100644 --- a/core/Nonce.php +++ b/core/Nonce.php @@ -160,7 +160,7 @@ class Nonce * @param string $nonceName The nonce's unique ID. See {@link getNonce()}. * @param string|null $nonce The nonce from the client. If `null`, the value from the * **nonce** query parameter is used. - * @throws Exception if the nonce is invalid. See {@link verifyNonce()}. + * @throws \Exception if the nonce is invalid. See {@link verifyNonce()}. */ public static function checkNonce($nonceName, $nonce = null) { diff --git a/core/Option.php b/core/Option.php index c2c7816bbab33e00f63389bc064a13ee03aa7e03..c4410520eab49e07614cb5fc1d93a284b2c36cb3 100644 --- a/core/Option.php +++ b/core/Option.php @@ -68,7 +68,7 @@ class Option */ public static function set($name, $value, $autoload = 0) { - return self::getInstance()->setValue($name, $value, $autoload); + self::getInstance()->setValue($name, $value, $autoload); } /** @@ -79,7 +79,7 @@ class Option */ public static function delete($name, $value = null) { - return self::getInstance()->deleteValue($name, $value); + self::getInstance()->deleteValue($name, $value); } /** @@ -91,7 +91,7 @@ class Option */ public static function deleteLike($namePattern, $value = null) { - return self::getInstance()->deleteNameLike($namePattern, $value); + self::getInstance()->deleteNameLike($namePattern, $value); } public static function clearCachedOption($name) @@ -162,12 +162,13 @@ class Option if (isset($this->all[$name])) { return $this->all[$name]; } - $value = Db::fetchOne('SELECT option_value ' . - 'FROM `' . Common::prefixTable('option') . '` ' . - 'WHERE option_name = ?', $name); + $value = Db::fetchOne('SELECT option_value FROM `' . Common::prefixTable('option') . '` ' . + 'WHERE option_name = ?', $name); + if ($value === false) { return false; } + $this->all[$name] = $value; return $value; } @@ -184,11 +185,11 @@ class Option protected function deleteValue($name, $value) { - $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name = ?'; + $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name = ?'; $bind[] = $name; if (isset($value)) { - $sql .= ' AND option_value = ?'; + $sql .= ' AND option_value = ?'; $bind[] = $value; } @@ -199,11 +200,11 @@ class Option protected function deleteNameLike($name, $value = null) { - $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?'; + $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?'; $bind[] = $name; if (isset($value)) { - $sql .= ' AND option_value = ?'; + $sql .= ' AND option_value = ?'; $bind[] = $value; } @@ -214,7 +215,7 @@ class Option protected function getNameLike($name) { - $sql = 'SELECT option_name, option_value FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?'; + $sql = 'SELECT option_name, option_value FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?'; $bind = array($name); $result = array(); @@ -235,9 +236,8 @@ class Option return; } - $all = Db::fetchAll('SELECT option_value, option_name - FROM `' . Common::prefixTable('option') . '` - WHERE autoload = 1'); + $all = Db::fetchAll('SELECT option_value, option_name FROM `' . Common::prefixTable('option') . '` + WHERE autoload = 1'); foreach ($all as $option) { $this->all[$option['option_name']] = $option['option_value']; } diff --git a/core/Period.php b/core/Period.php index 6898020ce704ca4447a79d9dc01105e41f67aba8..1e3f188b5ed5bd901107decacf00eb781b7e4fde 100644 --- a/core/Period.php +++ b/core/Period.php @@ -100,16 +100,20 @@ abstract class Period public function getDateStart() { $this->generate(); + if (count($this->subperiods) == 0) { return $this->getDate(); } + $periods = $this->getSubperiods(); + /** @var $currentPeriod Period */ $currentPeriod = $periods[0]; while ($currentPeriod->getNumberOfSubperiods() > 0) { - $periods = $currentPeriod->getSubperiods(); + $periods = $currentPeriod->getSubperiods(); $currentPeriod = $periods[0]; } + return $currentPeriod->getDate(); } @@ -121,16 +125,20 @@ abstract class Period public function getDateEnd() { $this->generate(); + if (count($this->subperiods) == 0) { return $this->getDate(); } + $periods = $this->getSubperiods(); + /** @var $currentPeriod Period */ $currentPeriod = $periods[count($periods) - 1]; while ($currentPeriod->getNumberOfSubperiods() > 0) { - $periods = $currentPeriod->getSubperiods(); + $periods = $currentPeriod->getSubperiods(); $currentPeriod = $periods[count($periods) - 1]; } + return $currentPeriod->getDate(); } @@ -212,10 +220,12 @@ abstract class Period public function toString($format = "Y-m-d") { $this->generate(); + $dateString = array(); foreach ($this->subperiods as $period) { $dateString[] = $period->toString($format); } + return $dateString; } @@ -259,6 +269,9 @@ abstract class Period */ public function getRangeString() { - return $this->getDateStart()->toString("Y-m-d") . "," . $this->getDateEnd()->toString("Y-m-d"); + $dateStart = $this->getDateStart(); + $dateEnd = $this->getDateEnd(); + + return $dateStart->toString("Y-m-d") . "," . $dateEnd->toString("Y-m-d"); } } diff --git a/core/Period/Day.php b/core/Period/Day.php index 5f64627e45a6c1c758052140a09f135fd32380f7..b8212844cde18a498a08e7b17bcfd1ab0a8a52fb 100644 --- a/core/Period/Day.php +++ b/core/Period/Day.php @@ -37,8 +37,10 @@ class Day extends Period public function getLocalizedShortString() { //"Mon 15 Aug" - $date = $this->getDateStart(); - $out = $date->getLocalized(Piwik::translate('CoreHome_ShortDateFormat')); + $date = $this->getDateStart(); + $template = Piwik::translate('CoreHome_ShortDateFormat'); + + $out = $date->getLocalized($template); return $out; } @@ -50,8 +52,9 @@ class Day extends Period public function getLocalizedLongString() { //"Mon 15 Aug" - $date = $this->getDateStart(); + $date = $this->getDateStart(); $template = Piwik::translate('CoreHome_DateFormat'); + $out = $date->getLocalized($template); return $out; } diff --git a/core/Period/Factory.php b/core/Period/Factory.php index b49095653119d34b0f6c57352d9ce5f8b064dee6..05303f40df787d0dd19a95a5d617b39cabff9672 100644 --- a/core/Period/Factory.php +++ b/core/Period/Factory.php @@ -60,7 +60,7 @@ class Factory public static function checkPeriodIsEnabled($period) { - if(!self::isPeriodEnabledForAPI($period)) { + if (!self::isPeriodEnabledForAPI($period)) { self::throwExceptionInvalidPeriod($period); } } diff --git a/core/Period/Month.php b/core/Period/Month.php index 167280f15b99ea2bfe0a9db0e5ab0d23b035a46a..46091809804958d54ed29f05f98874730cc60e2d 100644 --- a/core/Period/Month.php +++ b/core/Period/Month.php @@ -60,6 +60,7 @@ class Month extends Period if ($this->subperiodsProcessed) { return; } + parent::generate(); $date = $this->date; diff --git a/core/Period/Range.php b/core/Period/Range.php index 2ea6ab1792ba365f51ec54e1b8046669df35d859..36692dc0de2ba102809b5f17a3cf8f9fedb1fa47 100644 --- a/core/Period/Range.php +++ b/core/Period/Range.php @@ -45,12 +45,14 @@ class Range extends Period public function __construct($strPeriod, $strDate, $timezone = 'UTC', $today = false) { $this->strPeriod = $strPeriod; - $this->strDate = $strDate; + $this->strDate = $strDate; + $this->timezone = $timezone; $this->defaultEndDate = null; - $this->timezone = $timezone; + if ($today === false) { $today = Date::factory('now', $this->timezone); } + $this->today = $today; } @@ -63,10 +65,12 @@ class Range extends Period { //"30 Dec 08 - 26 Feb 09" $dateStart = $this->getDateStart(); - $dateEnd = $this->getDateEnd(); - $template = Piwik::translate('CoreHome_ShortDateFormatWithYear'); + $dateEnd = $this->getDateEnd(); + $template = Piwik::translate('CoreHome_ShortDateFormatWithYear'); + $shortDateStart = $dateStart->getLocalized($template); - $shortDateEnd = $dateEnd->getLocalized($template); + $shortDateEnd = $dateEnd->getLocalized($template); + $out = "$shortDateStart - $shortDateEnd"; return $out; } @@ -90,9 +94,11 @@ class Range extends Period public function getDateStart() { $dateStart = parent::getDateStart(); + if (empty($dateStart)) { throw new Exception("Specified date range is invalid."); } + return $dateStart; } @@ -203,10 +209,12 @@ class Range extends Period } else { throw new Exception(Piwik::translate('General_ExceptionInvalidDateRange', array($this->strDate, ' \'lastN\', \'previousN\', \'YYYY-MM-DD,YYYY-MM-DD\''))); } + if ($this->strPeriod != 'range') { $this->fillArraySubPeriods($startDate, $endDate, $this->strPeriod); return; } + $this->processOptimalSubperiods($startDate, $endDate); // When period=range, we want End Date to be the actual specified end date, // rather than the end of the month / week / whatever is used for processing this range @@ -223,9 +231,11 @@ class Range extends Period public static function parseDateRange($dateString) { $matched = preg_match('/^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),(([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})|today|now|yesterday)$/D', trim($dateString), $regs); + if (empty($matched)) { return false; } + return $regs; } @@ -241,6 +251,7 @@ class Range extends Period if (!is_null($this->endDate)) { return $this->endDate; } + return parent::getDateEnd(); } @@ -425,7 +436,7 @@ class Range extends Period * @param int $lastN The number of periods of type `$period` that the result range should * span. * @param string $endDate The desired end date of the range. - * @param Site $site The site whose timezone should be used. + * @param \Piwik\Site $site The site whose timezone should be used. * @return string The date range string, eg, `'2012-01-02,2013-01-02'`. * @api */ @@ -434,6 +445,7 @@ class Range extends Period $last30Relative = new Range($period, $lastN, $site->getTimezone()); $last30Relative->setDefaultEndDate(Date::factory($endDate)); $date = $last30Relative->getDateStart()->toString() . "," . $last30Relative->getDateEnd()->toString(); + return $date; } diff --git a/core/Period/Week.php b/core/Period/Week.php index f7b72649432b5b9d1af374a2ff5378c9c00e785e..23f7ab32b669817549453dcf198064c67f285e50 100644 --- a/core/Period/Week.php +++ b/core/Period/Week.php @@ -26,7 +26,7 @@ class Week extends Period { //"30 Dec - 6 Jan 09" $dateStart = $this->getDateStart(); - $dateEnd = $this->getDateEnd(); + $dateEnd = $this->getDateEnd(); $string = Piwik::translate('CoreHome_ShortWeekFormat'); $string = self::getTranslatedRange($string, $dateStart, $dateEnd); @@ -42,6 +42,7 @@ class Week extends Period { $format = Piwik::translate('CoreHome_LongWeekFormat'); $string = self::getTranslatedRange($format, $this->getDateStart(), $this->getDateEnd()); + return Piwik::translate('CoreHome_PeriodWeek') . " " . $string; } @@ -58,6 +59,7 @@ class Week extends Period $string = $dateStart->getLocalized($string); $string = str_replace('To%', '%', $string); $string = $dateEnd->getLocalized($string); + return $string; } @@ -68,10 +70,11 @@ class Week extends Period */ public function getPrettyString() { - $out = Piwik::translate('General_DateRangeFromTo', - array($this->getDateStart()->toString(), - $this->getDateEnd()->toString()) - ); + $dateStart = $this->getDateStart(); + $dateEnd = $this->getDateEnd(); + + $out = Piwik::translate('General_DateRangeFromTo', array($dateStart->toString(), $dateEnd->toString())); + return $out; } @@ -83,6 +86,7 @@ class Week extends Period if ($this->subperiodsProcessed) { return; } + parent::generate(); $date = $this->date; diff --git a/core/Period/Year.php b/core/Period/Year.php index 3582161e3a17d16e9ec97424b71ffaf0cf6df491..cb2d202ff7f9dbc01fab7211966e2e233982b65a 100644 --- a/core/Period/Year.php +++ b/core/Period/Year.php @@ -58,6 +58,7 @@ class Year extends Period if ($this->subperiodsProcessed) { return; } + parent::generate(); $year = $this->date->toString("Y"); @@ -78,10 +79,12 @@ class Year extends Period function toString($format = 'ignored') { $this->generate(); + $stringMonth = array(); foreach ($this->subperiods as $month) { $stringMonth[] = $month->getDateStart()->toString("Y") . "-" . $month->getDateStart()->toString("m") . "-01"; } + return $stringMonth; } } diff --git a/core/Piwik.php b/core/Piwik.php index c00d5e42e9b63594feb77c96cdc6381859b13676..16f50f387faee4f6336241eec2a8d52953a971f0 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -9,7 +9,6 @@ namespace Piwik; use Exception; -use Piwik\Common; use Piwik\Db\Adapter; use Piwik\Db\Schema; use Piwik\Db; @@ -135,7 +134,7 @@ class Piwik // changes made to this code should be mirrored in plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js var generateJsCode $jsCode = file_get_contents(PIWIK_INCLUDE_PATH . "/plugins/Morpheus/templates/javascriptCode.tpl"); $jsCode = htmlentities($jsCode); - if(substr($piwikUrl, 0, 4) !== 'http') { + if (substr($piwikUrl, 0, 4) !== 'http') { $piwikUrl = 'http://' . $piwikUrl; } preg_match('~^(http|https)://(.*)$~D', $piwikUrl, $matches); @@ -428,7 +427,10 @@ class Piwik * Helper method user to set the current as superuser. * This should be used with great care as this gives the user all permissions. * + * This method is deprecated, use {@link Access::doAsSuperUser()} instead. + * * @param bool $bool true to set current user as Super User + * @deprecated */ public static function setUserHasSuperUserAccess($bool = true) { @@ -889,4 +891,4 @@ class Piwik return $result; } -} \ No newline at end of file +} diff --git a/core/Plugin.php b/core/Plugin.php index 8839a962122c382215c2f16fcbaed70223158f30..55a70ab501bc83df99286fa0a22a4ce72c9954ab 100644 --- a/core/Plugin.php +++ b/core/Plugin.php @@ -354,7 +354,7 @@ class Plugin if ($this->cache->has()) { $components = $this->cache->get(); - if($this->includeComponents($components)) { + if ($this->includeComponents($components)) { return $components; } else { // problem including one cached file, refresh cache diff --git a/core/Plugin/ComponentFactory.php b/core/Plugin/ComponentFactory.php index 68415e939762840b7231d07bc203f8869f58da33..9cb9cd1c1b31ec4027f3bcfd9cb488502777d083 100644 --- a/core/Plugin/ComponentFactory.php +++ b/core/Plugin/ComponentFactory.php @@ -74,7 +74,7 @@ class ComponentFactory * @param callback $predicate * @return mixed The component that satisfies $predicate or null if not found. */ - public static function getComponentIf($componentTypeClass, $pluginName, $predicate) + public static function getComponentif ($componentTypeClass, $pluginName, $predicate) { $pluginManager = PluginManager::getInstance(); diff --git a/core/Plugin/ConsoleCommand.php b/core/Plugin/ConsoleCommand.php index 884b6f60ee2e3cab63b7f3c84a04596c641e32cf..29e2fc65f6c6fb422c7fa54d93e2d283bdf0871d 100644 --- a/core/Plugin/ConsoleCommand.php +++ b/core/Plugin/ConsoleCommand.php @@ -8,7 +8,6 @@ */ namespace Piwik\Plugin; -use Piwik\Common; use Symfony\Component\Console\Command\Command as SymfonyCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; diff --git a/core/Plugin/Controller.php b/core/Plugin/Controller.php index 3ebad0f1eda42ee437889b0a9f702b61e7c4d796..0342e34724246897c6e7072f440285a30f2e5284 100644 --- a/core/Plugin/Controller.php +++ b/core/Plugin/Controller.php @@ -834,32 +834,10 @@ abstract class Controller public function redirectToIndex($moduleToRedirect, $actionToRedirect, $websiteId = null, $defaultPeriod = null, $defaultDate = null, $parameters = array()) { - - $userPreferences = new UserPreferences(); - - if (empty($websiteId)) { - $websiteId = $userPreferences->getDefaultWebsiteId(); - } - if (empty($defaultDate)) { - $defaultDate = $userPreferences->getDefaultDate(); - } - if (empty($defaultPeriod)) { - $defaultPeriod = $userPreferences->getDefaultPeriod(); - } - $parametersString = ''; - if (!empty($parameters)) { - $parametersString = '&' . Url::getQueryStringFromParameters($parameters); - } - - if ($websiteId) { - $url = "index.php?module=" . $moduleToRedirect - . "&action=" . $actionToRedirect - . "&idSite=" . $websiteId - . "&period=" . $defaultPeriod - . "&date=" . $defaultDate - . $parametersString; - Url::redirectToUrl($url); - exit; + try { + $this->doRedirectToUrl($moduleToRedirect, $actionToRedirect, $websiteId, $defaultPeriod, $defaultDate, $parameters); + } catch(Exception $e) { + // no website ID to default to, so could not redirect } if (Piwik::hasUserSuperUserAccess()) { @@ -881,6 +859,7 @@ abstract class Controller exit; } + /** * Checks that the token_auth in the URL matches the currently logged-in user's token_auth. * @@ -897,7 +876,7 @@ abstract class Controller $tokenRequest = Common::getRequestVar('token_auth', false); $tokenUser = Piwik::getCurrentUserTokenAuth(); - if(empty($tokenRequest) && empty($tokenUser)) { + if (empty($tokenRequest) && empty($tokenUser)) { return; // UI tests } @@ -1000,4 +979,28 @@ abstract class Controller Please check that you are logged in Piwik and have permission to access the specified website."); } } + + /** + * @param $moduleToRedirect + * @param $actionToRedirect + * @param $websiteId + * @param $defaultPeriod + * @param $defaultDate + * @param $parameters + * @throws Exception + */ + private function doRedirectToUrl($moduleToRedirect, $actionToRedirect, $websiteId, $defaultPeriod, $defaultDate, $parameters) + { + $menu = new Menu(); + + $parameters = array_merge( + $menu->urlForDefaultUserParams($websiteId, $defaultPeriod, $defaultDate), + $parameters + ); + $queryParams = !empty($parameters) ? '&' . Url::getQueryStringFromParameters($parameters) : ''; + $url = "index.php?module=%s&action=%s"; + $url = sprintf($url, $moduleToRedirect, $actionToRedirect); + $url = $url . $queryParams; + Url::redirectToUrl($url); + } } diff --git a/core/Plugin/ControllerAdmin.php b/core/Plugin/ControllerAdmin.php index b18fe67de67787a5317e78700844a2730490e00e..e563e759f96eb6532847e95fef6fb03a297bcb72 100644 --- a/core/Plugin/ControllerAdmin.php +++ b/core/Plugin/ControllerAdmin.php @@ -43,6 +43,7 @@ abstract class ControllerAdmin extends Controller private static function notifyAnyInvalidPlugin() { $missingPlugins = \Piwik\Plugin\Manager::getInstance()->getMissingPlugins(); + if (empty($missingPlugins)) { return; } @@ -50,9 +51,11 @@ abstract class ControllerAdmin extends Controller if (!Piwik::hasUserSuperUserAccess()) { return; } + $pluginsLink = Url::getCurrentQueryStringWithParametersModified(array( 'module' => 'CorePluginsAdmin', 'action' => 'plugins' )); + $invalidPluginsWarning = Piwik::translate('CoreAdminHome_InvalidPluginsWarning', array( self::getPiwikVersion(), '<strong>' . implode('</strong>, <strong>', $missingPlugins) . '</strong>')) @@ -167,8 +170,10 @@ abstract class ControllerAdmin extends Controller $view->currentAdminMenuName = MenuAdmin::getInstance()->getCurrentAdminMenuName(); $view->isDataPurgeSettingsEnabled = self::isDataPurgeSettingsEnabled(); - $view->enableFrames = PiwikConfig::getInstance()->General['enable_framed_settings']; - if (!$view->enableFrames) { + $enableFrames = PiwikConfig::getInstance()->General['enable_framed_settings']; + $view->enableFrames = $enableFrames; + + if (!$enableFrames) { $view->setXFrameOptions('sameorigin'); } @@ -192,6 +197,7 @@ abstract class ControllerAdmin extends Controller $view->adminMenu = $adminMenu; $notifications = $view->notifications; + if (empty($notifications)) { $view->notifications = NotificationManager::getAllNotificationsToDisplay(); NotificationManager::cancelAllNonPersistent(); diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index eb234c1b6cdc79614f0181701a6d270d010691e4..da2ba679df34b24c0b9fee03ba475b37e945150b 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -10,8 +10,6 @@ namespace Piwik\Plugin; use Piwik\Cache\PersistentCache; -use Piwik\Cache\PluginAwareStaticCache; -use Piwik\Cache\StaticCache; use Piwik\CacheFile; use Piwik\Common; use Piwik\Config as PiwikConfig; @@ -38,7 +36,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php'; /** * The singleton that manages plugin loading/unloading and installation/uninstallation. * - * @method static \Piwik\Plugin\Manager getInstance() + * @method static Manager getInstance() */ class Manager extends Singleton { @@ -166,7 +164,7 @@ class Manager extends Singleton public function isPluginOfficialAndNotBundledWithCore($pluginName) { static $gitModules; - if(empty($gitModules)) { + if (empty($gitModules)) { $gitModules = file_get_contents(PIWIK_INCLUDE_PATH . '/.gitmodules'); } // All submodules are officially maintained plugins @@ -541,7 +539,7 @@ class Manager extends Singleton $listPlugins = array_unique($listPlugins); foreach ($listPlugins as $pluginName) { // Hide plugins that are never going to be used - if($this->isPluginBogus($pluginName)) { + if ($this->isPluginBogus($pluginName)) { continue; } @@ -609,15 +607,15 @@ class Manager extends Singleton protected function isPluginThirdPartyAndBogus($pluginName) { - if($this->isPluginBundledWithCore($pluginName)) { + if ($this->isPluginBundledWithCore($pluginName)) { return false; } - if($this->isPluginBogus($pluginName)) { + if ($this->isPluginBogus($pluginName)) { return true; } $path = $this->getPluginsDirectory() . $pluginName; - if(!$this->isManifestFileFound($path)) { + if (!$this->isManifestFileFound($path)) { return true; } return false; @@ -773,7 +771,7 @@ class Manager extends Singleton $plugins = $this->getLoadedPlugins(); $enabled = $this->getActivatedPlugins(); - if(empty($enabled)) { + if (empty($enabled)) { return array(); } $enabled = array_combine($enabled, $enabled); diff --git a/core/Plugin/Menu.php b/core/Plugin/Menu.php index 6d2bdefccd28908042c9da8ed2c7a72b82a37deb..0cdc1878df6f93ccf52dcb98a4dd55609cabef58 100644 --- a/core/Plugin/Menu.php +++ b/core/Plugin/Menu.php @@ -14,6 +14,7 @@ use Piwik\Menu\MenuReporting; use Piwik\Menu\MenuTop; use Piwik\Menu\MenuUser; use Piwik\Plugin\Manager as PluginManager; +use Piwik\Plugins\UsersManager\UserPreferences; /** * Base class of all plugin menu providers. Plugins that define their own menu items can extend this class to easily @@ -105,8 +106,8 @@ class Menu * @param string $module The name of the module/plugin the action belongs to. The module name is case sensitive. * @param string $controllerAction The name of the action that should be executed within your controller * @param array $additionalParams Optional URL parameters that will be appended to the URL - * @return array|null Returns null if the given module is either not installed or not activated. Returns the URL - * to the given module action otherwise. + * @return array|null Returns null if the given module is either not installed or not activated. Returns the array + * of query parameter names and values to the given module action otherwise. * * @since 2.7.0 * // not API for now @@ -129,6 +130,80 @@ class Menu return $params; } + /** + * Generates a URL to the given action of the current module, and it will also append some URL query parameters from the + * User preferences: idSite, period, date. If you do not need the parameters idSite, period and date to be generated + * use {@link urlForAction()} instead. + * + * @param string $controllerAction The name of the action that should be executed within your controller + * @param array $additionalParams Optional URL parameters that will be appended to the URL + * @return array Returns the array of query parameter names and values to the given module action and idSite date and period. + * + */ + protected function urlForActionWithDefaultUserParams($controllerAction, $additionalParams = array()) + { + $urlModuleAction = $this->urlForAction($controllerAction); + return array_merge( + $urlModuleAction, + $this->urlForDefaultUserParams(), + $additionalParams + ); + } + + /** + * Generates a URL to the given action of the given module, and it will also append some URL query parameters from the + * User preferences: idSite, period, date. If you do not need the parameters idSite, period and date to be generated + * use {@link urlForModuleAction()} instead. + * + * @param string $module The name of the module/plugin the action belongs to. The module name is case sensitive. + * @param string $controllerAction The name of the action that should be executed within your controller + * @param array $additionalParams Optional URL parameters that will be appended to the URL + * @return array|null Returns the array of query parameter names and values to the given module action and idSite date and period. + * Returns null if the module or action is invalid. + * + */ + protected function urlForModuleActionWithDefaultUserParams($module, $controllerAction, $additionalParams = array()) + { + $urlModuleAction = $this->urlForModuleAction($module, $controllerAction); + return array_merge( + $urlModuleAction, + $this->urlForDefaultUserParams(), + $additionalParams + ); + } + + /** + * Returns the &idSite=X&period=Y&date=Z query string fragment, + * fetched from current logged-in user's preferences. + * + * @param bool $websiteId + * @param bool $defaultPeriod + * @param bool $defaultDate + * @return string eg '&idSite=1&period=week&date=today' + * @throws \Exception in case a website was not specified and a default website id could not be found + */ + public function urlForDefaultUserParams($websiteId = false, $defaultPeriod = false, $defaultDate = false) + { + $userPreferences = new UserPreferences(); + if (empty($websiteId)) { + $websiteId = $userPreferences->getDefaultWebsiteId(); + } + if (empty($websiteId)) { + throw new \Exception("A website ID was not specified and a website to default to could not be found."); + } + if (empty($defaultDate)) { + $defaultDate = $userPreferences->getDefaultDate(); + } + if (empty($defaultPeriod)) { + $defaultPeriod = $userPreferences->getDefaultPeriod(); + } + return array( + 'idSite' => $websiteId, + 'period' => $defaultPeriod, + 'date' => $defaultDate, + ); + } + /** * Configures the reporting menu which should only contain links to reports of a specific site such as * "Search Engines", "Page Titles" or "Locations & Provider". diff --git a/core/Plugin/MetadataLoader.php b/core/Plugin/MetadataLoader.php index 25f48ce0a095d78d713db1af3b6213a0b03b8da9..34bb90dcdcf021a3dc85721939d3e0949d89bcbc 100644 --- a/core/Plugin/MetadataLoader.php +++ b/core/Plugin/MetadataLoader.php @@ -101,6 +101,7 @@ class MetadataLoader ) { throw new Exception("Invalid JSON file: $path"); } + return $info; } } diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php index 6d950babd2b5482b440b6093b138247b23e88f8e..c30e731d4627a205024b8416e868431cb1a1ac2c 100644 --- a/core/Plugin/Report.php +++ b/core/Plugin/Report.php @@ -41,6 +41,12 @@ class Report */ const COMPONENT_SUBNAMESPACE = 'Reports'; + /** + * When added to the menu, a given report eg 'getCampaigns' + * will be routed as &action=menuGetCampaigns + */ + const PREFIX_ACTION_IN_MENU = 'menu'; + /** * The name of the module which is supposed to be equal to the name of the plugin. The module is detected * automatically. @@ -328,11 +334,12 @@ class Report { if ($this->menuTitle) { $action = $this->getMenuControllerAction(); - $menu->add($this->category, - $this->menuTitle, - array('module' => $this->module, 'action' => $action), - $this->isEnabled(), - $this->order); + if ($this->isEnabled()) { + $menu->addItem($this->category, + $this->menuTitle, + array('module' => $this->module, 'action' => $action), + $this->order); + } } } @@ -710,7 +717,7 @@ class Report private function getMenuControllerAction() { - return 'menu' . ucfirst($this->action); + return self::PREFIX_ACTION_IN_MENU . ucfirst($this->action); } private function getSubtableApiMethod() @@ -731,7 +738,7 @@ class Report */ public static function getForDimension(Dimension $dimension) { - return ComponentFactory::getComponentIf(__CLASS__, $dimension->getModule(), function (Report $report) use ($dimension) { + return ComponentFactory::getComponentif (__CLASS__, $dimension->getModule(), function (Report $report) use ($dimension) { return !$report->isSubtableReport() && $report->getDimension() && $report->getDimension()->getId() == $dimension->getId(); diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php index d7f72028cf5d404273b0cf1d194923491ef9ec05..4f3b854127f2fac99547933a45dad33a87cb265a 100644 --- a/core/Plugin/ViewDataTable.php +++ b/core/Plugin/ViewDataTable.php @@ -275,11 +275,11 @@ abstract class ViewDataTable implements ViewInterface protected function assignRelatedReportsTitle() { - if(!empty($this->config->related_reports_title)) { + if (!empty($this->config->related_reports_title)) { // title already assigned by a plugin return; } - if(count($this->config->related_reports) == 1) { + if (count($this->config->related_reports) == 1) { $this->config->related_reports_title = Piwik::translate('General_RelatedReport') . ':'; } else { $this->config->related_reports_title = Piwik::translate('General_RelatedReports') . ':'; diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php index e82a8d7f7397f3c9d1d33474c027b7bad09adf17..e7e79c62e2beef67bf3b9efd89a221379a9b66c8 100644 --- a/core/Plugin/Visualization.php +++ b/core/Plugin/Visualization.php @@ -498,6 +498,7 @@ class Visualization extends ViewDataTable 'filter_excludelowpop', 'filter_excludelowpop_value', ); + foreach ($deleteFromJavascriptVariables as $name) { if (isset($javascriptVariablesToSet[$name])) { unset($javascriptVariablesToSet[$name]); diff --git a/core/Profiler.php b/core/Profiler.php index 7468b1f5d669e0fc7ffca43602fcca1a23450b59..2b92583d89d5ff07218f9ff42f53a44c2ce30240 100644 --- a/core/Profiler.php +++ b/core/Profiler.php @@ -143,7 +143,7 @@ class Profiler { $totalTime = self::getDbElapsedSecs(); $queryCount = Profiler::getQueryCount(); - if($queryCount > 0) { + if ($queryCount > 0) { Log::debug(sprintf("Total queries = %d (total sql time = %.2fs)", $queryCount, $totalTime)); } } @@ -234,7 +234,7 @@ class Profiler $currentGitBranch = SettingsPiwik::getCurrentGitBranch(); $profilerNamespace = "piwik"; - if($currentGitBranch != 'master') { + if ($currentGitBranch != 'master') { $profilerNamespace .= "." . $currentGitBranch; } @@ -326,7 +326,7 @@ class Profiler { $runIds = file_get_contents( self::getPathToXHProfRunIds() ); $array = json_decode($runIds, $assoc = true); - if(!is_array($array)) { + if (!is_array($array)) { $array = array(); } return $array; diff --git a/core/ProxyHttp.php b/core/ProxyHttp.php index a1e7fda32c41c5aea78f2d1c29fbd65ff01b0207..69f7144b44b4b315b2b6cc8dfa40934e62df60cf 100644 --- a/core/ProxyHttp.php +++ b/core/ProxyHttp.php @@ -233,6 +233,7 @@ class ProxyHttp // FastCGI $key = 'Status:'; } + Common::sendHeader($key . ' ' . $status); } diff --git a/core/RankingQuery.php b/core/RankingQuery.php index f40ae1273e39aeb191e59bedfa4911b28d1a4842..f44845f07535435f69dfc2646b0f11b89da849b6 100644 --- a/core/RankingQuery.php +++ b/core/RankingQuery.php @@ -215,7 +215,7 @@ class RankingQuery public function execute($innerQuery, $bind = array()) { $query = $this->generateQuery($innerQuery); - $data = Db::fetchAll($query, $bind); + $data = Db::fetchAll($query, $bind); if ($this->columnToMarkExcludedRows !== false) { // split the result into the regular result and the rows with special treatment diff --git a/core/Registry.php b/core/Registry.php index a3383a84c0789b2b033ceb17fda59c00afb26e66..5022bf09e24a42410c5a5431f2566a70bd10a452 100644 --- a/core/Registry.php +++ b/core/Registry.php @@ -11,7 +11,8 @@ namespace Piwik; /** * Registry class. * - * @method static \Piwik\Registry getInstance() + * @method static Registry getInstance() + * @api */ class Registry extends Singleton { diff --git a/core/ReportRenderer/Csv.php b/core/ReportRenderer/Csv.php index 23ace2ac4d4b219c8c3ce754b89cbcb5bade8699..94eca5172a605f3cd5a1c599d9610f7780ede8a8 100644 --- a/core/ReportRenderer/Csv.php +++ b/core/ReportRenderer/Csv.php @@ -105,7 +105,7 @@ class Csv extends ReportRenderer ); $reportData = $csvRenderer->render($processedReport); - if(empty($reportData)) { + if (empty($reportData)) { $reportData = Piwik::translate('CoreHome_ThereIsNoDataForThisReport'); } diff --git a/core/ScheduledTask.php b/core/ScheduledTask.php index 15b702dab862333e5d4f348f2fd19142fdd07c4e..d570f5bea2e65b600c2f1a2e2602b2afd823e17a 100644 --- a/core/ScheduledTask.php +++ b/core/ScheduledTask.php @@ -77,6 +77,7 @@ class ScheduledTask * should be executed and how long before the next execution. * @param int $priority The priority of the task. Tasks with a higher priority will be executed first. * Tasks with low priority will be executed last. + * @throws Exception */ public function __construct($objectInstance, $methodName, $methodParameter, $scheduledTime, $priority = self::NORMAL_PRIORITY) diff --git a/core/ScheduledTime.php b/core/ScheduledTime.php index f221a82a43e528596f248d9dedf8d7a9e83f393b..3da4afe98baae790a10b5bd9f5998786eb09612b 100644 --- a/core/ScheduledTime.php +++ b/core/ScheduledTime.php @@ -183,7 +183,7 @@ abstract class ScheduledTime * and a string description of the day within the period to execute the task on. * * @param string $periodType The scheduled period type. Can be `'hourly'`, `'daily'`, `'weekly'`, or `'monthly'`. - * @param string|int|false $periodDay A string describing the day within the scheduled period to execute + * @param bool|false|int|string $periodDay A string describing the day within the scheduled period to execute * the task on. Only valid for week and month periods. * * If `'weekly'` is supplied for `$periodType`, this should be a day @@ -192,6 +192,7 @@ abstract class ScheduledTime * If `'monthly'` is supplied for `$periodType`, this can be a numeric * day in the month or a day in one week of the month. For example, * `12`, `23`, `'first sunday'` or `'fourth tuesday'`. + * @throws Exception * @api */ public static function factory($periodType, $periodDay = false) @@ -203,13 +204,13 @@ abstract class ScheduledTime return new Daily(); case 'weekly': $result = new Weekly(); - if($periodDay !== false) { + if ($periodDay !== false) { $result->setDay($periodDay); } return $result; case 'monthly': $result = new Monthly($periodDay); - if($periodDay !== false) { + if ($periodDay !== false) { if (is_int($periodDay)) { $result->setDay($periodDay); } else { diff --git a/core/ScheduledTime/Monthly.php b/core/ScheduledTime/Monthly.php index d4900530e8d397d5e91e564f2a842880437c78fb..84189f9badc345bf4d6154e52243c11c6d28006c 100644 --- a/core/ScheduledTime/Monthly.php +++ b/core/ScheduledTime/Monthly.php @@ -46,7 +46,6 @@ class Monthly extends ScheduledTime $day = Weekly::getDayIntFromString($dayNumberString) % 7; // get week number - $week = false; $weekNumberString = strtolower($weekNumberString); if (isset(self::$weekNumberStringToInt[$weekNumberString])) { $week = self::$weekNumberStringToInt[$weekNumberString]; diff --git a/core/Segment.php b/core/Segment.php index e12b165028c1290bb0679243119f23cb5ade9d42..420946794da028e8890c7906058a82399dd74d75 100644 --- a/core/Segment.php +++ b/core/Segment.php @@ -70,6 +70,7 @@ class Segment * @param string $segmentCondition The segment condition, eg, `'browserCode=ff;countryCode=CA'`. * @param array $idSites The list of sites the segment will be used with. Some segments are * dependent on the site, such as goal segments. + * @throws Exception */ public function __construct($segmentCondition, $idSites) { @@ -99,7 +100,7 @@ class Segment // As a preventive measure, we restrict the filter size to a safe limit $string = substr($string, 0, self::SEGMENT_TRUNCATE_LIMIT); - $this->string = $string; + $this->string = $string; $this->idSites = $idSites; $segment = new SegmentExpression($string); $this->segment = $segment; @@ -117,6 +118,7 @@ class Segment $expression[SegmentExpression::INDEX_OPERAND] = $cleanedExpression; $cleanedExpressions[] = $expression; } + $segment->setSubExpressionsAfterCleanup($cleanedExpressions); } @@ -155,10 +157,10 @@ class Segment throw new Exception("You do not have enough permission to access the segment " . $name); } - if($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY + if ($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY && $matchType != SegmentExpression::MATCH_IS_NULL_OR_EMPTY) { - if(isset($segment['sqlFilterValue'])) { + if (isset($segment['sqlFilterValue'])) { $value = call_user_func($segment['sqlFilterValue'], $value); } diff --git a/core/SegmentExpression.php b/core/SegmentExpression.php index 682aa201d0189d7dcb47938de796c4a9d8fac38f..cbd2a3eb9e1ff2e8acdf8a7e24c49380e91b5204 100644 --- a/core/SegmentExpression.php +++ b/core/SegmentExpression.php @@ -84,7 +84,7 @@ class SegmentExpression } $leftMember = $matches[1]; - $operation = $matches[2]; + $operation = $matches[2]; $valueRightMember = urldecode($matches[3]); // is null / is not null @@ -138,6 +138,7 @@ class SegmentExpression if ($operand[1] !== null) { $this->valuesBind[] = $operand[1]; } + $operand = $operand[0]; $sqlSubExpressions[] = array( self::INDEX_BOOL_OPERATOR => $operator, @@ -161,9 +162,9 @@ class SegmentExpression */ protected function getSqlMatchFromDefinition($def, &$availableTables) { - $field = $def[0]; + $field = $def[0]; $matchType = $def[1]; - $value = $def[2]; + $value = $def[2]; $alsoMatchNULLValues = false; switch ($matchType) { @@ -188,22 +189,22 @@ class SegmentExpression break; case self::MATCH_CONTAINS: $sqlMatch = 'LIKE'; - $value = '%' . $this->escapeLikeString($value) . '%'; + $value = '%' . $this->escapeLikeString($value) . '%'; break; case self::MATCH_DOES_NOT_CONTAIN: $sqlMatch = 'NOT LIKE'; - $value = '%' . $this->escapeLikeString($value) . '%'; + $value = '%' . $this->escapeLikeString($value) . '%'; $alsoMatchNULLValues = true; break; case self::MATCH_IS_NOT_NULL_NOR_EMPTY: $sqlMatch = 'IS NOT NULL AND (' . $field . ' <> \'\' OR ' . $field . ' = 0)'; - $value = null; + $value = null; break; case self::MATCH_IS_NULL_OR_EMPTY: $sqlMatch = 'IS NULL OR ' . $field . ' = \'\' '; - $value = null; + $value = null; break; case self::MATCH_ACTIONS_CONTAINS: @@ -212,7 +213,7 @@ class SegmentExpression // it can be used internally to inject sub-expressions into the query. // see Segment::getCleanedExpression() $sqlMatch = 'IN (' . $value['SQL'] . ')'; - $value = $this->escapeLikeString($value['bind']); + $value = $this->escapeLikeString($value['bind']); break; default: throw new Exception("Filter contains the match type '" . $matchType . "' which is not supported"); diff --git a/core/Session/SaveHandler/DbTable.php b/core/Session/SaveHandler/DbTable.php index 249faca6e1af9df1108d9c01180e7b762a6f0d1c..74fc5e388b6c641ed993ad609ee5437451a97222 100644 --- a/core/Session/SaveHandler/DbTable.php +++ b/core/Session/SaveHandler/DbTable.php @@ -119,8 +119,7 @@ class DbTable implements Zend_Session_SaveHandler_Interface */ public function destroy($id) { - $sql = 'DELETE FROM ' . $this->config['name'] - . ' WHERE ' . $this->config['primary'] . ' = ?'; + $sql = 'DELETE FROM ' . $this->config['name'] . ' WHERE ' . $this->config['primary'] . ' = ?'; Db::get()->query($sql, array($id)); diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php index 4e83190e703da42a1d6753d0a0638088d53f8a09..26949c560a2d5c59b22caf72fbee613067b782a9 100644 --- a/core/SettingsPiwik.php +++ b/core/SettingsPiwik.php @@ -179,7 +179,7 @@ class SettingsPiwik $url = $currentUrl; } - if(ProxyHttp::isHttps()) { + if (ProxyHttp::isHttps()) { $url = str_replace("http://", "https://", $url); } return $url; @@ -195,7 +195,7 @@ class SettingsPiwik $exists = file_exists($config); // Piwik is installed if the config file is found - if(!$exists) { + if (!$exists) { return false; } @@ -205,12 +205,12 @@ class SettingsPiwik if (array_key_exists('installation_in_progress', $general)) { $isInstallationInProgress = (bool) $general['installation_in_progress']; } - if($isInstallationInProgress) { + if ($isInstallationInProgress) { return false; } // Check that the database section is really set, ie. file is not empty - if(empty(Config::getInstance()->database['username'])) { + if (empty(Config::getInstance()->database['username'])) { return false; } return true; @@ -297,6 +297,8 @@ class SettingsPiwik * this will return false.. * * @param $piwikServerUrl + * @param bool $acceptInvalidSSLCertificates + * @throws Exception * @return bool */ public static function checkPiwikServerWorking($piwikServerUrl, $acceptInvalidSSLCertificates = false) @@ -337,7 +339,7 @@ class SettingsPiwik public static function getCurrentGitBranch() { $file = PIWIK_INCLUDE_PATH . '/.git/HEAD'; - if(!file_exists($file)) { + if (!file_exists($file)) { return ''; } $firstLineOfGitHead = file($file); @@ -385,19 +387,19 @@ class SettingsPiwik protected static function getPiwikInstanceId() { // until Piwik is installed, we use hostname as instance_id - if(!self::isPiwikInstalled() + if (!self::isPiwikInstalled() && Common::isPhpCliMode()) { // enterprise:install use case return Config::getHostname(); } // config.ini.php not ready yet, instance_id will not be set - if(!Config::getInstance()->existsLocalConfig()) { + if (!Config::getInstance()->existsLocalConfig()) { return false; } $instanceId = @Config::getInstance()->General['instance_id']; - if(!empty($instanceId)) { + if (!empty($instanceId)) { return $instanceId; } diff --git a/core/Site.php b/core/Site.php index 7efeee77ef1ba2f72c8329d61d20d9897f36a575..96b7316b76e9ce7cfadae02a67b857d126d56da4 100644 --- a/core/Site.php +++ b/core/Site.php @@ -94,7 +94,7 @@ class Site */ protected static function setSite($idSite, $infoSite) { - if(empty($idSite) || empty($infoSite)) { + if (empty($idSite) || empty($infoSite)) { throw new Exception("An unexpected website was found, check idSite in the request."); } @@ -361,7 +361,7 @@ class Site return API::getInstance()->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin); } - if(is_bool($ids)) { + if (is_bool($ids)) { return array(); } if (!is_array($ids)) { @@ -406,7 +406,7 @@ class Site $site = API::getInstance()->getSiteFromId($idsite); self::setSite($idsite, $site); } - if($field) { + if ($field) { return self::$infoSites[$idsite][$field]; } return self::$infoSites[$idsite]; diff --git a/core/Theme.php b/core/Theme.php index b0fe5abecba9864b0253997b81bb5a0251f843b0..f7f85a52919ae5a4dce3321ab1d9b8402487c713 100644 --- a/core/Theme.php +++ b/core/Theme.php @@ -64,7 +64,7 @@ class Theme return false; } $jsFiles = $info['javascript']; - if(!is_array($jsFiles)) { + if (!is_array($jsFiles)) { $jsFiles = array($jsFiles); } foreach($jsFiles as &$jsFile) { @@ -107,18 +107,18 @@ class Theme // Basic health check, we dont replace if not starting with plugins/ $posPluginsInPath = strpos($pathAsset, 'plugins'); - if( $posPluginsInPath !== 0) { + if ( $posPluginsInPath !== 0) { return $source; } // or if it's already rewritten - if(strpos($pathAsset, $this->themeName) !== false) { + if (strpos($pathAsset, $this->themeName) !== false) { return $source; } $pathPluginName = substr($pathAsset, strlen('plugins/')); $nextSlash = strpos($pathPluginName, '/'); - if($nextSlash === false) { + if ($nextSlash === false) { return $source; } $pathPluginName = substr($pathPluginName, 0, $nextSlash); @@ -133,11 +133,11 @@ class Theme // Strip trailing query string $fileToCheck = $overridingAsset; $queryStringPos = strpos($fileToCheck, '?'); - if( $queryStringPos !== false) { + if ( $queryStringPos !== false) { $fileToCheck = substr($fileToCheck, 0, $queryStringPos); } - if(file_exists($fileToCheck)) { + if (file_exists($fileToCheck)) { return str_replace($pathAsset, $overridingAsset, $source); } return $source; diff --git a/core/Tracker.php b/core/Tracker.php index 733f23d8a32e93552e6c0e3b3980d5a1ef3ecea8..3aa27237a12e193d89076aa81f9b6a640b8cae25 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -221,7 +221,7 @@ class Tracker */ public function main($args = null) { - if(!SettingsPiwik::isPiwikInstalled()) { + if (!SettingsPiwik::isPiwikInstalled()) { return $this->handleEmptyRequest(); } try { @@ -468,7 +468,7 @@ class Tracker Common::sendHeader('Content-Type: text/html; charset=utf-8'); echo $this->getMessageFromException($e); } else { - $this->outputTransparentGif(); + $this->outputTransparentGif (); } exit; } @@ -516,7 +516,7 @@ class Tracker } switch ($this->getState()) { case self::STATE_LOGGING_DISABLE: - $this->outputTransparentGif(); + $this->outputTransparentGif (); Common::printDebug("Logging disabled, display transparent logo"); break; @@ -528,7 +528,7 @@ class Tracker case self::STATE_NOSCRIPT_REQUEST: case self::STATE_NOTHING_TO_NOTICE: default: - $this->outputTransparentGif(); + $this->outputTransparentGif (); Common::printDebug("Nothing to notice => default behaviour"); break; } @@ -663,7 +663,7 @@ class Tracker return $visit; } - protected function outputTransparentGif() + protected function outputTransparentGif () { if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'] @@ -717,7 +717,7 @@ class Tracker protected function handleEmptyRequest(Request $request = null) { - if(is_null($request)) { + if (is_null($request)) { $request = new Request($_GET + $_POST); } $countParameters = $request->getParamsCount(); diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php index 6175d86e59a41bbf318931077197b78d39225b88..6df6f64cfcc3f0c570488686e4e147afc05b6a4b 100644 --- a/core/Tracker/Action.php +++ b/core/Tracker/Action.php @@ -22,32 +22,50 @@ use Piwik\Tracker; */ abstract class Action { - const TYPE_PAGE_URL = 1; - const TYPE_OUTLINK = 2; - const TYPE_DOWNLOAD = 3; + const TYPE_PAGE_URL = 1; + const TYPE_OUTLINK = 2; + const TYPE_DOWNLOAD = 3; const TYPE_PAGE_TITLE = 4; - const TYPE_ECOMMERCE_ITEM_SKU = 5; + const TYPE_ECOMMERCE_ITEM_SKU = 5; const TYPE_ECOMMERCE_ITEM_NAME = 6; const TYPE_ECOMMERCE_ITEM_CATEGORY = 7; const TYPE_SITE_SEARCH = 8; - const TYPE_EVENT = 10; // Alias TYPE_EVENT_CATEGORY + const TYPE_EVENT = 10; // Alias TYPE_EVENT_CATEGORY const TYPE_EVENT_CATEGORY = 10; - const TYPE_EVENT_ACTION = 11; - const TYPE_EVENT_NAME = 12; + const TYPE_EVENT_ACTION = 11; + const TYPE_EVENT_NAME = 12; - const TYPE_CONTENT = 13; // Alias TYPE_CONTENT_NAME - const TYPE_CONTENT_NAME = 13; - const TYPE_CONTENT_PIECE = 14; - const TYPE_CONTENT_TARGET = 15; + const TYPE_CONTENT = 13; // Alias TYPE_CONTENT_NAME + const TYPE_CONTENT_NAME = 13; + const TYPE_CONTENT_PIECE = 14; + const TYPE_CONTENT_TARGET = 15; const TYPE_CONTENT_INTERACTION = 16; const DB_COLUMN_CUSTOM_FLOAT = 'custom_float'; private static $factoryPriority = array( - self::TYPE_PAGE_URL, self::TYPE_CONTENT, self::TYPE_SITE_SEARCH, self::TYPE_EVENT, self::TYPE_OUTLINK, self::TYPE_DOWNLOAD + self::TYPE_PAGE_URL, + self::TYPE_CONTENT, + self::TYPE_SITE_SEARCH, + self::TYPE_EVENT, + self::TYPE_OUTLINK, + self::TYPE_DOWNLOAD ); + /** + * Public so that events listener can access it + * + * @var Request + */ + public $request; + + private $idLinkVisitAction; + private $actionIdsCached = array(); + private $actionName; + private $actionType; + private $actionUrl; + /** * Makes the correct Action object based on the request. * @@ -102,7 +120,7 @@ abstract class Action $instances = array(); foreach ($actions as $action) { - /** @var \Piwik\Tracker\Action $instance */ + /** @var \Piwik\Tracker\Action $action */ if ($action::shouldHandle($request)) { $instances[] = new $action($request); } @@ -111,19 +129,6 @@ abstract class Action return $instances; } - /** - * Public so that events listener can access it - * - * @var Request - */ - public $request; - - private $idLinkVisitAction; - private $actionIdsCached = array(); - private $actionName; - private $actionType; - private $actionUrl; - public function __construct($type, Request $request) { $this->actionType = $type; @@ -152,8 +157,7 @@ abstract class Action public function getCustomVariables() { - $customVariables = $this->request->getCustomVariables($scope = 'page'); - return $customVariables; + return $this->request->getCustomVariables($scope = 'page'); } // custom_float column @@ -164,8 +168,7 @@ abstract class Action protected function setActionName($name) { - $name = PageUrl::cleanupString((string)$name); - $this->actionName = $name; + $this->actionName = PageUrl::cleanupString((string)$name); } protected function setActionUrl($url) @@ -178,8 +181,7 @@ abstract class Action Common::printDebug(' After is "' . $url . '"'); } - $url = PageUrl::getUrlIfLookValid($url); - $this->actionUrl = $url; + $this->actionUrl = PageUrl::getUrlIfLookValid($url); } abstract protected function getActionsToLookup(); @@ -187,11 +189,13 @@ abstract class Action protected function getUrlAndType() { $url = $this->getActionUrl(); + if (!empty($url)) { // normalize urls by stripping protocol and www $url = PageUrl::normalizeUrl($url); return array($url['url'], self::TYPE_PAGE_URL, $url['prefixId']); } + return false; } @@ -214,9 +218,11 @@ abstract class Action public function getIdActionName() { - if(!isset($this->actionIdsCached['idaction_name'])) { + if (!isset($this->actionIdsCached['idaction_name'])) { + return false; } + return $this->actionIdsCached['idaction_name']; } @@ -230,24 +236,17 @@ abstract class Action return $this->idLinkVisitAction; } - public function writeDebugInfo() - { - $type = self::getTypeAsString($this->getActionType()); - Common::printDebug("Action is a $type, - Action name = " . $this->getActionName() . ", - Action URL = " . $this->getActionUrl()); - return true; - } - public static function getTypeAsString($type) { - $class = new \ReflectionClass("\\Piwik\\Tracker\\Action"); + $class = new \ReflectionClass("\\Piwik\\Tracker\\Action"); $constants = $class->getConstants(); $typeId = array_search($type, $constants); - if($typeId === false) { + + if (false === $typeId) { throw new Exception("Unexpected action type " . $type); } + return str_replace('TYPE_', '', $typeId); } @@ -262,24 +261,27 @@ abstract class Action */ public function loadIdsFromLogActionTable() { - if(!empty($this->actionIdsCached)) { + if (!empty($this->actionIdsCached)) { return; } - $actions = $this->getActionsToLookup(); + /** @var ActionDimension[] $dimensions */ $dimensions = ActionDimension::getAllDimensions(); + $actions = $this->getActionsToLookup(); foreach ($dimensions as $dimension) { $value = $dimension->onLookupAction($this->request, $this); - if ($value !== false) { + if (false !== $value) { $field = $dimension->getColumnName(); if (empty($field)) { - throw new Exception('Dimension ' . get_class($dimension) . ' does not define a field name'); + $dimensionClass = get_class($dimension); + throw new Exception('Dimension ' . $dimensionClass . ' does not define a field name'); } - $actions[$field] = array($value, $dimension->getActionId()); + $actionId = $dimension->getActionId(); + $actions[$field] = array($value, $actionId); Common::printDebug("$field = $value"); } } @@ -316,6 +318,7 @@ abstract class Action 'idaction_name_ref' => $idReferrerActionName ); + /** @var ActionDimension[] $dimensions */ $dimensions = ActionDimension::getAllDimensions(); foreach ($dimensions as $dimension) { @@ -347,18 +350,15 @@ abstract class Action } $visitAction = array_merge($visitAction, $customVariables); - $fields = implode(", ", array_keys($visitAction)); - $bind = array_values($visitAction); - $values = Common::getSqlStringFieldsArray($visitAction); - $sql = "INSERT INTO " . Common::prefixTable('log_link_visit_action') . " ($fields) VALUES ($values)"; - Tracker::getDatabase()->query($sql, $bind); + $this->idLinkVisitAction = $this->getModel()->createAction($visitAction); - $this->idLinkVisitAction = Tracker::getDatabase()->lastInsertId(); $visitAction['idlink_va'] = $this->idLinkVisitAction; Common::printDebug("Inserted new action:"); - Common::printDebug($visitAction); + $visitActionDebug = $visitAction; + $visitActionDebug['idvisitor'] = bin2hex($visitActionDebug['idvisitor']); + Common::printDebug($visitActionDebug); /** * Triggered after successfully persisting a [visit action entity](/guides/persistence-and-the-mysql-backend#visit-actions). @@ -370,13 +370,31 @@ abstract class Action Piwik::postEvent('Tracker.recordAction', array($trackerAction = $this, $visitAction)); } + public function writeDebugInfo() + { + $type = self::getTypeAsString($this->getActionType()); + $name = $this->getActionName(); + $url = $this->getActionUrl(); + + Common::printDebug("Action is a $type, + Action name = " . $name . ", + Action URL = " . $url); + + return true; + } + + private function getModel() + { + return new Model(); + } + /** * @return bool */ - protected function isActionHasActionName() + private function isActionHasActionName() { - return in_array($this->getActionType(), array(self::TYPE_PAGE_TITLE, - self::TYPE_PAGE_URL, - self::TYPE_SITE_SEARCH)); + $types = array(self::TYPE_PAGE_TITLE, self::TYPE_PAGE_URL, self::TYPE_SITE_SEARCH); + + return in_array($this->getActionType(), $types); } } diff --git a/core/Tracker/ActionPageview.php b/core/Tracker/ActionPageview.php index fa3d0146dc526613c879ec47a53ce21c2cad47c3..9088fc543a19a52e9033e7705137f2e805946716 100644 --- a/core/Tracker/ActionPageview.php +++ b/core/Tracker/ActionPageview.php @@ -38,7 +38,7 @@ class ActionPageview extends Action { return array( 'idaction_name' => array($this->getActionName(), Action::TYPE_PAGE_TITLE), - 'idaction_url' => $this->getUrlAndType() + 'idaction_url' => $this->getUrlAndType() ); } @@ -55,22 +55,38 @@ class ActionPageview extends Action private function cleanupActionName($actionName) { // get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067) - $actionCategoryDelimiter = isset(Config::getInstance()->General['action_category_delimiter']) - ? Config::getInstance()->General['action_category_delimiter'] - : Config::getInstance()->General['action_url_category_delimiter']; + $actionCategoryDelimiter = $this->getActionCategoryDelimiter(); // create an array of the categories delimited by the delimiter $split = explode($actionCategoryDelimiter, $actionName); + $split = $this->trimEveryCategory($split); + $split = $this->removeEmptyCategories($split); - // trim every category - $split = array_map('trim', $split); + return $this->rebuildNameOfCleanedCategories($actionCategoryDelimiter, $split); + } + + private function rebuildNameOfCleanedCategories($actionCategoryDelimiter, $split) + { + return implode($actionCategoryDelimiter, $split); + } - // remove empty categories - $split = array_filter($split, 'strlen'); + private function removeEmptyCategories($split) + { + return array_filter($split, 'strlen'); + } + + private function trimEveryCategory($split) + { + return array_map('trim', $split); + } + + private function getActionCategoryDelimiter() + { + if (isset(Config::getInstance()->General['action_category_delimiter'])) { + return Config::getInstance()->General['action_category_delimiter']; + } - // rebuild the name from the array of cleaned categories - $actionName = implode($actionCategoryDelimiter, $split); - return $actionName; + return Config::getInstance()->General['action_url_category_delimiter']; } } diff --git a/core/Tracker/Cache.php b/core/Tracker/Cache.php index 0217b175de1b3ae70bf3ac198e13d6e770c934b0..b8f0413c63cc686e6fb541f1425f41f3b5a72b24 100644 --- a/core/Tracker/Cache.php +++ b/core/Tracker/Cache.php @@ -8,6 +8,7 @@ */ namespace Piwik\Tracker; +use Piwik\Access; use Piwik\ArchiveProcessor\Rules; use Piwik\CacheFile; use Piwik\Common; @@ -45,56 +46,54 @@ class Cache */ static function getCacheWebsiteAttributes($idSite) { - if($idSite == 'all') { + if ('all' == $idSite) { return array(); } + $idSite = (int)$idSite; - if($idSite <= 0) { + if ($idSite <= 0) { return array(); } - $cache = self::getInstance(); - if (($cacheContent = $cache->get($idSite)) !== false) { + $cache = self::getInstance(); + $cacheContent = $cache->get($idSite); + + if (false !== $cacheContent) { return $cacheContent; } Tracker::initCorePiwikInTrackerMode(); - // save current user privilege and temporarily assume Super User privilege - $isSuperUser = Piwik::hasUserSuperUserAccess(); - Piwik::setUserHasSuperUserAccess(); - $content = array(); - - /** - * Triggered to get the attributes of a site entity that might be used by the - * Tracker. - * - * Plugins add new site attributes for use in other tracking events must - * use this event to put those attributes in the Tracker Cache. - * - * **Example** - * - * public function getSiteAttributes($content, $idSite) - * { - * $sql = "SELECT info FROM " . Common::prefixTable('myplugin_extra_site_info') . " WHERE idsite = ?"; - * $content['myplugin_site_data'] = Db::fetchOne($sql, array($idSite)); - * } - * - * @param array &$content Array mapping of site attribute names with values. - * @param int $idSite The site ID to get attributes for. - */ - Piwik::postEvent('Tracker.Cache.getSiteAttributes', array(&$content, $idSite)); - Common::printDebug("Website $idSite tracker cache was re-created."); - - // restore original user privilege - Piwik::setUserHasSuperUserAccess($isSuperUser); + Access::doAsSuperUser(function () use (&$content, $idSite) { + /** + * Triggered to get the attributes of a site entity that might be used by the + * Tracker. + * + * Plugins add new site attributes for use in other tracking events must + * use this event to put those attributes in the Tracker Cache. + * + * **Example** + * + * public function getSiteAttributes($content, $idSite) + * { + * $sql = "SELECT info FROM " . Common::prefixTable('myplugin_extra_site_info') . " WHERE idsite = ?"; + * $content['myplugin_site_data'] = Db::fetchOne($sql, array($idSite)); + * } + * + * @param array &$content Array mapping of site attribute names with values. + * @param int $idSite The site ID to get attributes for. + */ + Piwik::postEvent('Tracker.Cache.getSiteAttributes', array(&$content, $idSite)); + Common::printDebug("Website $idSite tracker cache was re-created."); + }); // if nothing is returned from the plugins, we don't save the content // this is not expected: all websites are expected to have at least one URL if (!empty($content)) { $cache->set($idSite, $content); } + return $content; } @@ -114,10 +113,12 @@ class Cache */ public static function getCacheGeneral() { - $cache = self::getInstance(); + $cache = self::getInstance(); $cacheId = 'general'; - if (($cacheContent = $cache->get($cacheId)) !== false) { + $cacheContent = $cache->get($cacheId); + + if (false !== $cacheContent) { return $cacheContent; } @@ -161,9 +162,10 @@ class Cache */ public static function setCacheGeneral($value) { - $cache = self::getInstance(); + $cache = self::getInstance(); $cacheId = 'general'; $cache->set($cacheId, $value); + return true; } @@ -177,6 +179,7 @@ class Cache if (!is_array($idSites)) { $idSites = array($idSites); } + foreach ($idSites as $idSite) { self::deleteCacheWebsiteAttributes($idSite); self::getCacheWebsiteAttributes($idSite); diff --git a/core/Tracker/Db.php b/core/Tracker/Db.php index 4ce13a08918739ec10b6bf5bf67a93f0d66259f3..1fe081daac1d1c96727ccab6e59fd8d0f8bd805e 100644 --- a/core/Tracker/Db.php +++ b/core/Tracker/Db.php @@ -77,10 +77,14 @@ abstract class Db */ protected function recordQueryProfile($query, $timer) { - if (!isset($this->queriesProfiling[$query])) $this->queriesProfiling[$query] = array('sum_time_ms' => 0, 'count' => 0); - $time = $timer->getTimeMs(2); + if (!isset($this->queriesProfiling[$query])) { + $this->queriesProfiling[$query] = array('sum_time_ms' => 0, 'count' => 0); + } + + $time = $timer->getTimeMs(2); $time += $this->queriesProfiling[$query]['sum_time_ms']; $count = $this->queriesProfiling[$query]['count'] + 1; + $this->queriesProfiling[$query] = array('sum_time_ms' => $time, 'count' => $count); } @@ -97,13 +101,12 @@ abstract class Db self::$profiling = false; foreach ($this->queriesProfiling as $query => $info) { - $time = $info['sum_time_ms']; + $time = $info['sum_time_ms']; $count = $info['count']; $queryProfiling = "INSERT INTO " . Common::prefixTable('log_profiling') . " (query,count,sum_time_ms) VALUES (?,$count,$time) - ON DUPLICATE KEY - UPDATE count=count+$count,sum_time_ms=sum_time_ms+$time"; + ON DUPLICATE KEY UPDATE count=count+$count,sum_time_ms=sum_time_ms+$time"; $this->query($queryProfiling, array($query)); } diff --git a/core/Tracker/Db/Mysqli.php b/core/Tracker/Db/Mysqli.php index 78e2d9ee291efac3c65b0b2d03e186e7a7f645c4..945255168371ff8696b5863fbe9eacb1d29d33c2 100644 --- a/core/Tracker/Db/Mysqli.php +++ b/core/Tracker/Db/Mysqli.php @@ -73,7 +73,14 @@ class Mysqli extends Db $timer = $this->initProfiler(); } - $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket); + $this->connection = mysqli_init(); + + // Make sure MySQL returns all matched rows on update queries including + // rows that actually didn't have to be updated because the values didn't + // change. This matches common behaviour among other database systems. + // See #6296 why this is important in tracker + $flags = MYSQLI_CLIENT_FOUND_ROWS; + mysqli_real_connect($this->connection, $this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket, $flags); if (!$this->connection || mysqli_connect_errno()) { throw new DbException("Connect failed: " . mysqli_connect_error()); } @@ -205,8 +212,8 @@ class Mysqli extends Db return $result; } catch (Exception $e) { throw new DbException("Error query: " . $e->getMessage() . " - In query: $query - Parameters: " . var_export($parameters, true)); + In query: $query + Parameters: " . var_export($parameters, true)); } } @@ -278,57 +285,58 @@ class Mysqli extends Db return mysqli_affected_rows($this->connection); } - /** - * Start Transaction - * @return string TransactionID - */ - - public function beginTransaction() - { - if(!$this->activeTransaction === false ) { - return; - } - - if( $this->connection->autocommit(false) ) { - $this->activeTransaction = uniqid(); - return $this->activeTransaction; - } - } - - /** - * Commit Transaction - * @param string TransactionID from beginTransaction - */ - - public function commit($xid) - { - if($this->activeTransaction != $xid || $this->activeTransaction === false ) { - - return; - } - $this->activeTransaction = false; - - if(!$this->connection->commit() ) { - throw new DbException("Commit failed"); - } - $this->connection->autocommit(true); - } - - /** - * Rollback Transaction - * @param string TransactionID from beginTransaction - */ - - public function rollBack($xid) - { - if($this->activeTransaction != $xid || $this->activeTransaction === false ) { - return; - } - $this->activeTransaction = false; - - if(!$this->connection->rollback() ) { - throw new DbException("Rollback failed"); - } - $this->connection->autocommit(true); - } + /** + * Start Transaction + * @return string TransactionID + */ + public function beginTransaction() + { + if (!$this->activeTransaction === false ) { + return; + } + + if ( $this->connection->autocommit(false) ) { + $this->activeTransaction = uniqid(); + return $this->activeTransaction; + } + } + + /** + * Commit Transaction + * @param $xid + * @throws DbException + * @internal param TransactionID $string from beginTransaction + */ + public function commit($xid) + { + if ($this->activeTransaction != $xid || $this->activeTransaction === false ) { + + return; + } + $this->activeTransaction = false; + + if (!$this->connection->commit() ) { + throw new DbException("Commit failed"); + } + $this->connection->autocommit(true); + } + + /** + * Rollback Transaction + * @param $xid + * @throws DbException + * @internal param TransactionID $string from beginTransaction + */ + public function rollBack($xid) + { + 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 a2f5a79b2cbbe3c5f1318e65e041750791089e25..1cb72c11a6df85687aaac2912ccf88febcffeec5 100644 --- a/core/Tracker/Db/Pdo/Mysql.php +++ b/core/Tracker/Db/Pdo/Mysql.php @@ -68,8 +68,17 @@ class Mysql extends Db $timer = $this->initProfiler(); } - $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config = array()); - $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + // Make sure MySQL returns all matched rows on update queries including + // rows that actually didn't have to be updated because the values didn't + // change. This matches common behaviour among other database systems. + // See #6296 why this is important in tracker + $config = array( + PDO::MYSQL_ATTR_FOUND_ROWS => true, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + + $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config); + // we may want to setAttribute(PDO::ATTR_TIMEOUT ) to a few seconds (default is 60) in case the DB is locked // the piwik.php would stay waiting for the database... bad! // we delete the password from this object "just in case" it could be printed @@ -195,8 +204,8 @@ class Mysql extends Db return $sth; } catch (PDOException $e) { throw new DbException("Error query: " . $e->getMessage() . " - In query: $query - Parameters: " . var_export($parameters, true)); + In query: $query + Parameters: " . var_export($parameters, true)); } } @@ -237,54 +246,55 @@ class Mysql extends Db return $queryResult->rowCount(); } - /** - * Start Transaction - * @return string TransactionID - */ - - public function beginTransaction() - { - if(!$this->activeTransaction === false ) { - return; - } - - if( $this->connection->beginTransaction() ) { - $this->activeTransaction = uniqid(); - return $this->activeTransaction; - } - } - - /** - * Commit Transaction - * @param string TransactionID from beginTransaction - */ + /** + * Start Transaction + * @return string TransactionID + */ + public function beginTransaction() + { + if (!$this->activeTransaction === false ) { + return; + } - public function commit($xid) - { - if($this->activeTransaction != $xid || $this->activeTransaction === false ) { - return; - } - $this->activeTransaction = false; + if ( $this->connection->beginTransaction() ) { + $this->activeTransaction = uniqid(); + return $this->activeTransaction; + } + } - if(!$this->connection->commit() ) { - throw new DbException("Commit failed"); - } - } + /** + * Commit Transaction + * @param $xid + * @throws DbException + * @internal param TransactionID $string from beginTransaction + */ + public function commit($xid) + { + if ($this->activeTransaction != $xid || $this->activeTransaction === false ) { + return; + } + $this->activeTransaction = false; - /** - * Rollback Transaction - * @param string TransactionID from beginTransaction - */ + if (!$this->connection->commit() ) { + throw new DbException("Commit failed"); + } + } - public function rollBack($xid) - { - if($this->activeTransaction != $xid || $this->activeTransaction === false ) { - return; - } - $this->activeTransaction = false; + /** + * Rollback Transaction + * @param $xid + * @throws DbException + * @internal param TransactionID $string from beginTransaction + */ + public function rollBack($xid) + { + if ($this->activeTransaction != $xid || $this->activeTransaction === false ) { + return; + } + $this->activeTransaction = false; - if(!$this->connection->rollBack() ) { - throw new DbException("Rollback failed"); - } - } + if (!$this->connection->rollBack() ) { + throw new DbException("Rollback failed"); + } + } } diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index 1a0944ad9a58cf53484f00b5f41b5b0fbd941810..69639fd1c63b7b42ac875dc19633ab8aefe58631 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -34,6 +34,25 @@ class GoalManager const REVENUE_PRECISION = 2; const MAXIMUM_PRODUCT_CATEGORIES = 5; + + // In the GET items parameter, each item has the following array of information + const INDEX_ITEM_SKU = 0; + const INDEX_ITEM_NAME = 1; + const INDEX_ITEM_CATEGORY = 2; + const INDEX_ITEM_PRICE = 3; + const INDEX_ITEM_QUANTITY = 4; + + // Used in the array of items, internally to this class + const INTERNAL_ITEM_SKU = 0; + const INTERNAL_ITEM_NAME = 1; + const INTERNAL_ITEM_CATEGORY = 2; + const INTERNAL_ITEM_CATEGORY2 = 3; + const INTERNAL_ITEM_CATEGORY3 = 4; + const INTERNAL_ITEM_CATEGORY4 = 5; + const INTERNAL_ITEM_CATEGORY5 = 6; + const INTERNAL_ITEM_PRICE = 7; + const INTERNAL_ITEM_QUANTITY = 8; + public $idGoal; public $requestIsEcommerce; private $isGoalAnOrder; @@ -65,7 +84,7 @@ class GoalManager $this->idGoal = $request->getParam('idgoal'); $this->isGoalAnOrder = !empty($this->orderId); - $this->requestIsEcommerce = ($this->idGoal == 0); + $this->requestIsEcommerce = (0 == $this->idGoal); } public function isGoalAnOrder() @@ -87,30 +106,36 @@ class GoalManager public static function getGoalDefinitions($idSite) { $websiteAttributes = Cache::getCacheWebsiteAttributes($idSite); + if (isset($websiteAttributes['goals'])) { return $websiteAttributes['goals']; } + return array(); } public static function getGoalDefinition($idSite, $idGoal) { $goals = self::getGoalDefinitions($idSite); + foreach ($goals as $goal) { if ($goal['idgoal'] == $idGoal) { return $goal; } } + throw new Exception('Goal not found'); } public static function getGoalIds($idSite) { - $goals = self::getGoalDefinitions($idSite); + $goals = self::getGoalDefinitions($idSite); $goalIds = array(); + foreach ($goals as $goal) { $goalIds[] = $goal['idgoal']; } + return $goalIds; } @@ -131,14 +156,15 @@ class GoalManager $decodedActionUrl = $action->getActionUrl(); $actionType = $action->getActionType(); $goals = $this->getGoalDefinitions($idSite); + foreach ($goals as $goal) { $attribute = $goal['match_attribute']; // if the attribute to match is not the type of the current action - if ( (($attribute == 'url' || $attribute == 'title') && $actionType != Action::TYPE_PAGE_URL) - || ($attribute == 'file' && $actionType != Action::TYPE_DOWNLOAD) - || ($attribute == 'external_website' && $actionType != Action::TYPE_OUTLINK) - || ($attribute == 'manually') - || in_array($attribute, array('event_action', 'event_name', 'event_category')) && $actionType != Action::TYPE_EVENT + if ((($attribute == 'url' || $attribute == 'title') && $actionType != Action::TYPE_PAGE_URL) + || ($attribute == 'file' && $actionType != Action::TYPE_DOWNLOAD) + || ($attribute == 'external_website' && $actionType != Action::TYPE_OUTLINK) + || ($attribute == 'manually') + || in_array($attribute, array('event_action', 'event_name', 'event_category')) && $actionType != Action::TYPE_EVENT ) { continue; } @@ -169,23 +195,33 @@ class GoalManager $this->convertedGoals[] = $goal; } } + return count($this->convertedGoals) > 0; } + public function isManualGoalConversion() + { + return $this->idGoal > 0; + } + public function detectGoalId($idSite) { if (!Common::isGoalPluginEnabled()) { return false; } + $goals = $this->getGoalDefinitions($idSite); + if (!isset($goals[$this->idGoal])) { return false; } + $goal = $goals[$this->idGoal]; - $url = $this->request->getParam('url'); + $url = $this->request->getParam('url'); $goal['url'] = PageUrl::excludeQueryParametersFromUrl($url, $idSite); $this->convertedGoals[] = $goal; + return true; } @@ -199,18 +235,7 @@ class GoalManager */ public function recordGoals(Visitor $visitor, $visitorInformation, $visitCustomVariables, $action) { - $goal = array( - 'idvisit' => $visitorInformation['idvisit'], - 'idvisitor' => $visitorInformation['idvisitor'], - 'server_time' => Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']) - ); - - foreach (VisitDimension::getAllDimensions() as $dimension) { - $value = $dimension->onAnyGoalConversion($this->request, $visitor, $action); - if (false !== $value) { - $goal[$dimension->getColumnName()] = $value; - } - } + $goal = $this->getGoalFromVisitor($visitor, $visitorInformation, $action); // Copy Custom Variables from Visit row to the Goal conversion // Otherwise, set the Custom Variables found in the cookie sent with this request @@ -249,6 +274,7 @@ class GoalManager if (round($revenue) == $revenue) { return $revenue; } + return round($revenue, self::REVENUE_PRECISION); } @@ -291,7 +317,8 @@ class GoalManager // INSERT or Sync items in the Cart / Order for this visit & order $items = $this->getEcommerceItemsFromRequest(); - if ($items === false) { + + if (false === $items) { return; } @@ -303,12 +330,7 @@ class GoalManager $conversion['items'] = $itemsCount; if ($this->isThereExistingCartInVisit) { - $updateWhere = array( - 'idvisit' => $visitInformation['idvisit'], - 'idgoal' => self::IDGOAL_CART, - 'buster' => 0, - ); - $recorded = $this->updateExistingConversion($conversion, $updateWhere); + $recorded = $this->getModel()->updateConversion($visitInformation['idvisit'], self::IDGOAL_CART, $conversion); } else { $recorded = $this->insertNewConversion($conversion, $visitInformation); } @@ -338,12 +360,15 @@ class GoalManager private function getEcommerceItemsFromRequest() { $items = Common::unsanitizeInputValue($this->request->getParam('ec_items')); + if (empty($items)) { Common::printDebug("There are no Ecommerce items in the request"); // we still record an Ecommerce order without any item in it return array(); } + $items = Common::json_decode($items, $assoc = true); + if (!is_array($items)) { Common::printDebug("Error while json_decode the Ecommerce items = " . var_export($items, true)); return false; @@ -368,23 +393,11 @@ class GoalManager $itemInCartBySku[$item[0]] = $item; } - // Select all items currently in the Cart if any - $sql = "SELECT idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, idorder as idorder_original_value - FROM " . Common::prefixTable('log_conversion_item') . " - WHERE idvisit = ? - AND (idorder = ? OR idorder = ?)"; + $itemsInDb = $this->getModel()->getAllItemsCurrentlyInTheCart($goal, self::ITEM_IDORDER_ABANDONED_CART); - $bind = array($goal['idvisit'], - isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART, - self::ITEM_IDORDER_ABANDONED_CART - ); - - $itemsInDb = Tracker::getDatabase()->fetchAll($sql, $bind); - - Common::printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true)); - Common::printDebug($itemsInDb); // Look at which items need to be deleted, which need to be added or updated, based on the SKU $skuFoundInDb = $itemsToUpdate = array(); + foreach ($itemsInDb as $itemInDb) { $skuFoundInDb[] = $itemInDb['idaction_sku']; @@ -435,27 +448,10 @@ class GoalManager $itemsToInsert[] = $item; } } + $this->insertEcommerceItems($goal, $itemsToInsert); } - // In the GET items parameter, each item has the following array of information - const INDEX_ITEM_SKU = 0; - const INDEX_ITEM_NAME = 1; - const INDEX_ITEM_CATEGORY = 2; - const INDEX_ITEM_PRICE = 3; - const INDEX_ITEM_QUANTITY = 4; - - // Used in the array of items, internally to this class - const INTERNAL_ITEM_SKU = 0; - const INTERNAL_ITEM_NAME = 1; - const INTERNAL_ITEM_CATEGORY = 2; - const INTERNAL_ITEM_CATEGORY2 = 3; - const INTERNAL_ITEM_CATEGORY3 = 4; - const INTERNAL_ITEM_CATEGORY4 = 5; - const INTERNAL_ITEM_CATEGORY5 = 6; - const INTERNAL_ITEM_PRICE = 7; - const INTERNAL_ITEM_QUANTITY = 8; - /** * Reads items from the request, then looks up the names from the lookup table * and returns a clean array of items ready for the database. @@ -468,9 +464,10 @@ class GoalManager // Clean up the items array $cleanedItems = array(); foreach ($items as $item) { - $name = $category = $category2 = $category3 = $category4 = $category5 = false; - $price = 0; + $name = $category = $category2 = $category3 = $category4 = $category5 = false; + $price = 0; $quantity = 1; + // items are passed in the request as an array: ( $sku, $name, $category, $price, $quantity ) if (empty($item[self::INDEX_ITEM_SKU])) { continue; @@ -562,6 +559,7 @@ class GoalManager $item[5] = $actionsLookedUp[$index * $columnsInEachRow + 5]; $item[6] = $actionsLookedUp[$index * $columnsInEachRow + 6]; } + return $cleanedItems; } @@ -579,29 +577,23 @@ class GoalManager if (empty($itemsToUpdate)) { return; } + Common::printDebug("Goal data used to update ecommerce items:"); Common::printDebug($goal); foreach ($itemsToUpdate as $item) { $newRow = $this->getItemRowEnriched($goal, $item); Common::printDebug($newRow); - $updateParts = $sqlBind = array(); - foreach ($newRow as $name => $value) { - $updateParts[] = $name . " = ?"; - $sqlBind[] = $value; - } - $sql = 'UPDATE ' . Common::prefixTable('log_conversion_item') . " - SET " . implode($updateParts, ', ') . " - WHERE idvisit = ? - AND idorder = ? - AND idaction_sku = ?"; - $sqlBind[] = $newRow['idvisit']; - $sqlBind[] = $item['idorder_original_value']; - $sqlBind[] = $newRow['idaction_sku']; - Tracker::getDatabase()->query($sql, $sqlBind); + + $this->getModel()->updateEcommerceItem($item['idorder_original_value'], $newRow); } } + private function getModel() + { + return new Model(); + } + /** * Inserts in the cart in the DB the new items * that were not previously in the cart @@ -616,27 +608,17 @@ class GoalManager if (empty($itemsToInsert)) { return; } + Common::printDebug("Ecommerce items that are added to the cart/order"); Common::printDebug($itemsToInsert); - $sql = "INSERT INTO " . Common::prefixTable('log_conversion_item') . " - (idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, - idorder, idsite, idvisitor, server_time, idvisit) - VALUES "; - $i = 0; - $bind = array(); + $items = array(); + foreach ($itemsToInsert as $item) { - if ($i > 0) { - $sql .= ','; - } - $newRow = array_values($this->getItemRowEnriched($goal, $item)); - $sql .= " ( " . Common::getSqlStringFieldsArray($newRow) . " ) "; - $i++; - $bind = array_merge($bind, $newRow); - } - Tracker::getDatabase()->query($sql, $bind); - Common::printDebug($sql); - Common::printDebug($bind); + $items[] = $this->getItemRowEnriched($goal, $item); + } + + $this->getModel()->createEcommerceItems($items); } protected function getItemRowEnriched($goal, $item) @@ -685,7 +667,7 @@ class GoalManager Common::printDebug("- Goal " . $convertedGoal['idgoal'] . " matched. Recording..."); $conversion = $goal; $conversion['idgoal'] = $convertedGoal['idgoal']; - $conversion['url'] = $convertedGoal['url']; + $conversion['url'] = $convertedGoal['url']; if (!is_null($action)) { $conversion['idaction_url'] = $action->getIdActionUrl(); @@ -741,15 +723,9 @@ class GoalManager $newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']); Common::printDebug($newGoalDebug); - $fields = implode(", ", array_keys($conversion)); - $bindFields = Common::getSqlStringFieldsArray($conversion); - $sql = 'INSERT IGNORE INTO ' . Common::prefixTable('log_conversion') . " - ($fields) VALUES ($bindFields) "; - $bind = array_values($conversion); - $result = Tracker::getDatabase()->query($sql, $bind); + $wasInserted = $this->getModel()->createConversion($conversion); - // If a record was inserted, we return true - return Tracker::getDatabase()->rowCount($result) > 0; + return $wasInserted; } /** @@ -772,30 +748,6 @@ class GoalManager ); } - protected function updateExistingConversion($newGoal, $updateWhere) - { - $updateParts = $sqlBind = $updateWhereParts = array(); - foreach ($newGoal as $name => $value) { - $updateParts[] = $name . " = ?"; - $sqlBind[] = $value; - } - foreach ($updateWhere as $name => $value) { - $updateWhereParts[] = $name . " = ?"; - $sqlBind[] = $value; - } - $sql = 'UPDATE ' . Common::prefixTable('log_conversion') . " - SET " . implode($updateParts, ', ') . " - WHERE " . implode($updateWhereParts, ' AND '); - - try { - Tracker::getDatabase()->query($sql, $sqlBind); - } catch(Exception $e){ - Common::printDebug("There was an error while updating the Conversion: " . $e->getMessage()); - return false; - } - return true; - } - /** * @param $goal * @param $pattern_type @@ -842,6 +794,7 @@ class GoalManager throw new Exception(Piwik::translate('General_ExceptionInvalidGoalPattern', array($pattern_type))); break; } + return $match; } @@ -859,7 +812,7 @@ class GoalManager foreach ($dimensions as $dimension) { $value = $dimension->$hook($this->request, $visitor, $action, $this); - if ($value !== false) { + if (false !== $value) { $fieldName = $dimension->getColumnName(); $visitor->setVisitorColumn($fieldName, $value); @@ -869,4 +822,24 @@ class GoalManager return $valuesToUpdate; } + + private function getGoalFromVisitor(Visitor $visitor, $visitorInformation, $action) + { + $goal = array( + 'idvisit' => $visitorInformation['idvisit'], + 'idvisitor' => $visitorInformation['idvisitor'], + 'server_time' => Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']) + ); + + $visitDimensions = VisitDimension::getAllDimensions(); + + foreach ($visitDimensions as $dimension) { + $value = $dimension->onAnyGoalConversion($this->request, $visitor, $action); + if (false !== $value) { + $goal[$dimension->getColumnName()] = $value; + } + } + + return $goal; + } } diff --git a/core/Tracker/Model.php b/core/Tracker/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..090cd8be596384c162eae09d14d8d03802928697 --- /dev/null +++ b/core/Tracker/Model.php @@ -0,0 +1,383 @@ +<?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 PDOStatement; +use Piwik\Common; +use Piwik\Tracker; +use Piwik\Tracker\Db\DbException; + +class Model +{ + + public function createAction($visitAction) + { + $fields = implode(", ", array_keys($visitAction)); + $values = Common::getSqlStringFieldsArray($visitAction); + $table = Common::prefixTable('log_link_visit_action'); + + $sql = "INSERT INTO $table ($fields) VALUES ($values)"; + $bind = array_values($visitAction); + + $db = $this->getDb(); + $db->query($sql, $bind); + + $id = $db->lastInsertId(); + + return $id; + } + + public function createConversion($conversion) + { + $fields = implode(", ", array_keys($conversion)); + $bindFields = Common::getSqlStringFieldsArray($conversion); + $table = Common::prefixTable('log_conversion'); + + $sql = "INSERT IGNORE INTO $table ($fields) VALUES ($bindFields) "; + $bind = array_values($conversion); + + $db = $this->getDb(); + $result = $db->query($sql, $bind); + + // If a record was inserted, we return true + return $db->rowCount($result) > 0; + } + + public function updateConversion($idVisit, $idGoal, $newConversion) + { + $updateWhere = array( + 'idvisit' => $idVisit, + 'idgoal' => $idGoal, + 'buster' => 0, + ); + + $updateParts = $sqlBind = $updateWhereParts = array(); + + foreach ($newConversion as $name => $value) { + $updateParts[] = $name . " = ?"; + $sqlBind[] = $value; + } + + foreach ($updateWhere as $name => $value) { + $updateWhereParts[] = $name . " = ?"; + $sqlBind[] = $value; + } + + $parts = implode($updateParts, ', '); + $table = Common::prefixTable('log_conversion'); + + $sql = "UPDATE $table SET $parts WHERE " . implode($updateWhereParts, ' AND '); + + try { + $this->getDb()->query($sql, $sqlBind); + } catch(Exception $e){ + Common::printDebug("There was an error while updating the Conversion: " . $e->getMessage()); + + return false; + } + + return true; + } + + + /** + * Loads the Ecommerce items from the request and records them in the DB + * + * @param array $goal + * @param int $defaultIdOrder + * @throws Exception + * @return array + */ + public function getAllItemsCurrentlyInTheCart($goal, $defaultIdOrder) + { + $sql = "SELECT idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, idorder as idorder_original_value + FROM " . Common::prefixTable('log_conversion_item') . " + WHERE idvisit = ? AND (idorder = ? OR idorder = ?)"; + + $bind = array( + $goal['idvisit'], + isset($goal['idorder']) ? $goal['idorder'] : $defaultIdOrder, + $defaultIdOrder + ); + + $itemsInDb = $this->getDb()->fetchAll($sql, $bind); + + Common::printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true)); + Common::printDebug($itemsInDb); + + return $itemsInDb; + } + + public function createEcommerceItems($ecommerceItems) + { + $sql = "INSERT INTO " . Common::prefixTable('log_conversion_item'); + $i = 0; + $bind = array(); + + foreach ($ecommerceItems as $item) { + + if ($i === 0) { + $fields = implode(', ', array_keys($item)); + $sql .= ' (' . $fields . ') VALUES '; + } elseif ($i > 0) { + $sql .= ','; + } + + $newRow = array_values($item); + $sql .= " ( " . Common::getSqlStringFieldsArray($newRow) . " ) "; + $bind = array_merge($bind, $newRow); + $i++; + } + + $this->getDb()->query($sql, $bind); + + Common::printDebug($sql); + Common::printDebug($bind); + } + + public function createNewIdAction($name, $type, $urlPrefix) + { + $table = Common::prefixTable('log_action'); + $sql = "INSERT INTO $table (name, hash, type, url_prefix) VALUES (?,CRC32(?),?,?)"; + + $db = $this->getDb(); + $db->query($sql, array($name, $name, $type, $urlPrefix)); + + $actionId = $db->lastInsertId(); + + return $actionId; + } + + private function getSqlSelectActionId() + { + $sql = "SELECT idaction, type, name FROM " . Common::prefixTable('log_action') + . " WHERE ( hash = CRC32(?) AND name = ? AND type = ? ) "; + + return $sql; + } + + public function getIdActionMatchingNameAndType($name, $type) + { + $sql = $this->getSqlSelectActionId(); + $bind = array($name, $name, $type); + + $idAction = $this->getDb()->fetchOne($sql, $bind); + + return $idAction; + } + + public function getIdsAction($actionsNameAndType) + { + $sql = $this->getSqlSelectActionId(); + $bind = array(); + + $i = 0; + foreach ($actionsNameAndType as $actionNameType) { + $name = $actionNameType['name']; + + if (empty($name)) { + continue; + } + + if ($i > 0) { + $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) "; + } + + $bind[] = $name; + $bind[] = $name; + $bind[] = $actionNameType['type']; + $i++; + } + + // Case URL & Title are empty + if (empty($bind)) { + return false; + } + + $actionIds = $this->getDb()->fetchAll($sql, $bind); + + return $actionIds; + } + + public function updateEcommerceItem($originalIdOrder, $newItem) + { + $updateParts = $sqlBind = array(); + foreach ($newItem as $name => $value) { + $updateParts[] = $name . " = ?"; + $sqlBind[] = $value; + } + + $parts = implode($updateParts, ', '); + $table = Common::prefixTable('log_conversion_item'); + + $sql = "UPDATE $table SET $parts WHERE idvisit = ? AND idorder = ? AND idaction_sku = ?"; + + $sqlBind[] = $newItem['idvisit']; + $sqlBind[] = $originalIdOrder; + $sqlBind[] = $newItem['idaction_sku']; + + $this->getDb()->query($sql, $sqlBind); + } + + public function createVisit($visit) + { + $fields = array_keys($visit); + $fields = implode(", ", $fields); + $values = Common::getSqlStringFieldsArray($visit); + $table = Common::prefixTable('log_visit'); + + $sql = "INSERT INTO $table ($fields) VALUES ($values)"; + $bind = array_values($visit); + + $db = $this->getDb(); + $db->query($sql, $bind); + + return $db->lastInsertId(); + } + + public function updateVisit($idSite, $idVisit, $valuesToUpdate) + { + list($updateParts, $sqlBind) = $this->visitFieldsToQuery($valuesToUpdate); + + $parts = implode($updateParts, ', '); + $table = Common::prefixTable('log_visit'); + + $sqlQuery = "UPDATE $table SET $parts WHERE idsite = ? AND idvisit = ?"; + + $sqlBind[] = $idSite; + $sqlBind[] = $idVisit; + + $db = $this->getDb(); + $result = $db->query($sqlQuery, $sqlBind); + $wasInserted = $db->rowCount($result) != 0; + + if (!$wasInserted) { + Common::printDebug("Visitor with this idvisit wasn't found in the DB."); + Common::printDebug("$sqlQuery --- "); + Common::printDebug($sqlBind); + } + + return $wasInserted; + } + + public function findVisitor($idSite, $configId, $idVisitor, $fieldsToRead, $numCustomVarsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead) + { + $selectCustomVariables = ''; + + if ($numCustomVarsToRead) { + for ($index = 1; $index <= $numCustomVarsToRead; $index++) { + $selectCustomVariables .= ', custom_var_k' . $index . ', custom_var_v' . $index; + } + } + + $selectFields = implode(', ', $fieldsToRead); + + $select = "SELECT $selectFields $selectCustomVariables "; + $from = "FROM " . Common::prefixTable('log_visit'); + + // Two use cases: + // 1) there is no visitor ID so we try to match only on config_id (heuristics) + // Possible causes of no visitor ID: no browser cookie support, direct Tracking API request without visitor ID passed, + // importing server access logs with import_logs.py, etc. + // In this case we use config_id heuristics to try find the visitor in tahhhe past. There is a risk to assign + // this page view to the wrong visitor, but this is better than creating artificial visits. + // 2) there is a visitor ID and we trust it (config setting trust_visitors_cookies, OR it was set using &cid= in tracking API), + // and in these cases, we force to look up this visitor id + $whereCommon = "visit_last_action_time >= ? AND visit_last_action_time <= ? AND idsite = ?"; + $bindSql = array( + $timeLookBack, + $timeLookAhead, + $idSite + ); + + if ($shouldMatchOneFieldOnly) { + if ($isVisitorIdToLookup) { + $whereCommon .= ' AND idvisitor = ?'; + $bindSql[] = $idVisitor; + } else { + $whereCommon .= ' AND config_id = ?'; + $bindSql[] = $configId; + } + + $sql = "$select $from + WHERE " . $whereCommon . " + ORDER BY visit_last_action_time DESC + LIMIT 1"; + } // We have a config_id AND a visitor_id. We match on either of these. + // Why do we also match on config_id? + // we do not trust the visitor ID only. Indeed, some browsers, or browser addons, + // cause the visitor id from the 1st party cookie to be different on each page view! + // It is not acceptable to create a new visit every time such browser does a page view, + // so we also backup by searching for matching config_id. + // We use a UNION here so that each sql query uses its own INDEX + else { + // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time) + $where = ' AND config_id = ? AND user_id IS NULL '; + $bindSql[] = $configId; + $sqlConfigId = "$select , + 0 as priority + $from + WHERE $whereCommon $where + ORDER BY visit_last_action_time DESC + LIMIT 1 + "; + // will use INDEX index_idsite_idvisitor (idsite, idvisitor) + $bindSql[] = $timeLookBack; + $bindSql[] = $timeLookAhead; + $bindSql[] = $idSite; + $where = ' AND idvisitor = ?'; + $bindSql[] = $idVisitor; + $sqlVisitorId = "$select , + 1 as priority + $from + WHERE $whereCommon $where + ORDER BY visit_last_action_time DESC + LIMIT 1 + "; + + // We join both queries and favor the one matching the visitor_id if it did match + $sql = " ( $sqlConfigId ) + UNION + ( $sqlVisitorId ) + ORDER BY priority DESC + LIMIT 1"; + } + + $visitRow = $this->getDb()->fetch($sql, $bindSql); + + return $visitRow; + } + + private function visitFieldsToQuery($valuesToUpdate) + { + $updateParts = array(); + $sqlBind = array(); + + foreach ($valuesToUpdate as $name => $value) { + // Case where bind parameters don't work + if ($value === $name . ' + 1') { + //$name = 'visit_total_events' + //$value = 'visit_total_events + 1'; + $updateParts[] = " $name = $value "; + } else { + $updateParts[] = $name . " = ?"; + $sqlBind[] = $value; + } + } + + return array($updateParts, $sqlBind); + } + + private function getDb() + { + return Tracker::getDatabase(); + } + +} diff --git a/core/Tracker/PageUrl.php b/core/Tracker/PageUrl.php index 1d8486eda05788772a502a5c1657f558f9dca3a3..ae55b48aac3169e8abd3bb72e9131fdfe2e45831 100644 --- a/core/Tracker/PageUrl.php +++ b/core/Tracker/PageUrl.php @@ -49,17 +49,22 @@ class PageUrl if (empty($parsedUrl['query'])) { if (empty($parsedUrl['fragment'])) { + return UrlHelper::getParseUrlReverse($parsedUrl); } + // Exclude from the hash tag as well $queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['fragment']); $parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude); $url = UrlHelper::getParseUrlReverse($parsedUrl); + return $url; } + $queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['query']); $parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude); $url = UrlHelper::getParseUrlReverse($parsedUrl); + return $url; } @@ -79,17 +84,15 @@ class PageUrl ); $website = Cache::getCacheWebsiteAttributes($idSite); - $excludedParameters = isset($website['excluded_parameters']) - ? $website['excluded_parameters'] - : array(); + $excludedParameters = self::getExcludedParametersFromWebsite($website); if (!empty($excludedParameters)) { Common::printDebug('Excluding parameters "' . implode(',', $excludedParameters) . '" from URL'); } $parametersToExclude = array_merge($excludedParameters, - self::$queryParametersToExclude, - $campaignTrackingParameters); + self::$queryParametersToExclude, + $campaignTrackingParameters); $parametersToExclude = array_map('strtolower', $parametersToExclude); return $parametersToExclude; @@ -152,6 +155,7 @@ class PageUrl if (empty($parsedUrl)) { return $parsedUrl; } + if (!empty($parsedUrl['host'])) { $parsedUrl['host'] = mb_strtolower($parsedUrl['host'], 'UTF-8'); } @@ -174,19 +178,24 @@ class PageUrl public static function convertMatrixUrl($originalUrl) { $posFirstSemiColon = strpos($originalUrl, ";"); - if ($posFirstSemiColon === false) { + + if (false === $posFirstSemiColon) { return $originalUrl; } + $posQuestionMark = strpos($originalUrl, "?"); - $replace = ($posQuestionMark === false); + $replace = (false === $posQuestionMark); + if ($posQuestionMark > $posFirstSemiColon) { $originalUrl = substr_replace($originalUrl, ";", $posQuestionMark, 1); $replace = true; } + if ($replace) { $originalUrl = substr_replace($originalUrl, "?", strpos($originalUrl, ";"), 1); $originalUrl = str_replace(";", "&", $originalUrl); } + return $originalUrl; } @@ -214,6 +223,7 @@ class PageUrl $value = urlencode(mb_convert_encoding($decoded, 'UTF-8', $encoding)); } } + return $value; } @@ -226,6 +236,7 @@ class PageUrl $value = PageUrl::reencodeParameterValue($value, $encoding); } } + return $queryParameters; } @@ -247,12 +258,13 @@ class PageUrl { // if query params are encoded w/ non-utf8 characters (due to browser bug or whatever), // encode to UTF-8. - if ($encoding !== false - && strtolower($encoding) != 'utf-8' + if (false !== $encoding + && 'utf-8' != strtolower($encoding) && function_exists('mb_check_encoding') ) { $queryParameters = PageUrl::reencodeParametersArray($queryParameters, $encoding); } + return $queryParameters; } @@ -261,6 +273,7 @@ class PageUrl $url = Common::unsanitizeInputValue($url); $url = PageUrl::cleanupString($url); $url = PageUrl::convertMatrixUrl($url); + return $url; } @@ -274,6 +287,7 @@ class PageUrl public static function reconstructNormalizedUrl($url, $prefixId) { $map = array_flip(self::$urlPrefixMap); + if ($prefixId !== null && isset($map[$prefixId])) { $fullUrl = $map[$prefixId] . $url; } else { @@ -283,7 +297,8 @@ class PageUrl // Clean up host & hash tags, for URLs $parsedUrl = @parse_url($fullUrl); $parsedUrl = PageUrl::cleanupHostAndHashTag($parsedUrl); - $url = UrlHelper::getParseUrlReverse($parsedUrl); + $url = UrlHelper::getParseUrlReverse($parsedUrl); + if (!empty($url)) { return $url; } @@ -302,12 +317,14 @@ class PageUrl { foreach (self::$urlPrefixMap as $prefix => $id) { if (strtolower(substr($url, 0, strlen($prefix))) == $prefix) { + return array( 'url' => substr($url, strlen($prefix)), 'prefixId' => $id ); } } + return array('url' => $url, 'prefixId' => null); } @@ -317,10 +334,20 @@ class PageUrl if (!UrlHelper::isLookLikeUrl($url)) { Common::printDebug("WARNING: URL looks invalid and is discarded"); - $url = false; - return $url; + + return false; } + return $url; } + + private static function getExcludedParametersFromWebsite($website) + { + if (isset($website['excluded_parameters'])) { + return $website['excluded_parameters']; + } + + return array(); + } } diff --git a/core/Tracker/Request.php b/core/Tracker/Request.php index b34ee88709477a15245967d56b0705d5fe1d826f..212abbd0d2670d2c9a71322de4f79dd2935a97f0 100644 --- a/core/Tracker/Request.php +++ b/core/Tracker/Request.php @@ -138,10 +138,12 @@ class Request if (!$this->isTimestampValid($cookieFirstVisitTimestamp)) { $cookieFirstVisitTimestamp = $this->getCurrentTimestamp(); } + $daysSinceFirstVisit = round(($this->getCurrentTimestamp() - $cookieFirstVisitTimestamp) / 86400, $precision = 0); if ($daysSinceFirstVisit < 0) { $daysSinceFirstVisit = 0; } + return $daysSinceFirstVisit; } @@ -152,12 +154,14 @@ class Request { $daysSinceLastOrder = false; $lastOrderTimestamp = $this->getParam('_ects'); + if ($this->isTimestampValid($lastOrderTimestamp)) { $daysSinceLastOrder = round(($this->getCurrentTimestamp() - $lastOrderTimestamp) / 86400, $precision = 0); if ($daysSinceLastOrder < 0) { $daysSinceLastOrder = 0; } } + return $daysSinceLastOrder; } @@ -168,12 +172,14 @@ class Request { $daysSinceLastVisit = 0; $lastVisitTimestamp = $this->getParam('_viewts'); + if ($this->isTimestampValid($lastVisitTimestamp)) { $daysSinceLastVisit = round(($this->getCurrentTimestamp() - $lastVisitTimestamp) / 86400, $precision = 0); if ($daysSinceLastVisit < 0) { $daysSinceLastVisit = 0; } } + return $daysSinceLastVisit; } @@ -297,6 +303,7 @@ class Request if (!isset($supportedParams[$name])) { throw new Exception("Requested parameter $name is not a known Tracking API Parameter."); } + $paramDefaultValue = $supportedParams[$name][0]; $paramType = $supportedParams[$name][1]; @@ -318,7 +325,7 @@ class Request protected function isTimestampValid($time) { return $time <= $this->getCurrentTimestamp() - && $time > $this->getCurrentTimestamp() - 10 * 365 * 86400; + && $time > $this->getCurrentTimestamp() - 10 * 365 * 86400; } public function getIdSite() @@ -338,9 +345,11 @@ class Request * request. */ Piwik::postEvent('Tracker.Request.getIdSite', array(&$idSite, $this->params)); + if ($idSite <= 0) { throw new Exception('Invalid idSite: \'' . $idSite . '\''); } + return $idSite; } @@ -359,9 +368,11 @@ class Request } $customVar = Common::unsanitizeInputValues(Common::getRequestVar($parameter, '', 'json', $this->params)); + if (!is_array($customVar)) { return array(); } + $customVariables = array(); $maxCustomVars = CustomVariables::getMaxCustomVariables(); foreach ($customVar as $id => $keyValue) { @@ -374,13 +385,14 @@ class Request Common::printDebug("Invalid custom variables detected (id=$id)"); continue; } + if (strlen($keyValue[1]) == 0) { $keyValue[1] = ""; } // 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]); + $key = self::truncateCustomVariable($keyValue[0]); $value = self::truncateCustomVariable($keyValue[1]); $customVariables['custom_var_k' . $id] = $key; $customVariables['custom_var_v' . $id] = $value; @@ -407,6 +419,7 @@ class Request if (!$this->shouldUseThirdPartyCookie()) { return; } + Common::printDebug("We manage the cookie..."); $cookie = $this->makeThirdPartyCookie(); @@ -455,7 +468,7 @@ class Request // If User ID is set it takes precedence $userId = $this->getForcedUserId(); - if($userId) { + if ($userId) { $userIdHashed = $this->getUserIdHashed($userId); $idVisitor = $this->truncateIdAsVisitorId($userIdHashed); Common::printDebug("Request will be recorded for this user_id = " . $userId . " (idvisitor = $idVisitor)"); @@ -502,6 +515,7 @@ class Request return $binVisitorId; } } + return false; } @@ -512,6 +526,7 @@ class Request } else { $ipString = IP::getIpFromHeader(); } + $ip = IP::P2N($ipString); return $ip; } @@ -536,10 +551,11 @@ class Request public function getForcedUserId() { $userId = $this->getParam('uid'); - if(strlen($userId) > 0) { + if (strlen($userId) > 0) { return $userId; } - return null; + + return false; } public function getForcedVisitorId() @@ -572,6 +588,7 @@ class Request ) { return (int)$generationTime; } + return false; } @@ -590,8 +607,8 @@ class Request * @param $userId * @return string */ - private function getUserIdHashed($userId) + public function getUserIdHashed($userId) { - return sha1($userId); + return substr( sha1( $userId ), 0, 16); } } diff --git a/core/Tracker/TableLogAction.php b/core/Tracker/TableLogAction.php index c547d742b80f87591c7cbaa13dbe3e5c6fb43d85..4f4849a3526baff23bfd4a1d6981c0061da3a230 100644 --- a/core/Tracker/TableLogAction.php +++ b/core/Tracker/TableLogAction.php @@ -36,34 +36,21 @@ class TableLogAction { // Add url prefix if not set foreach($actionsNameAndType as &$action) { - if(count($action) == 2) { + if (2 == count($action)) { $action[] = null; } } + $actionIds = self::queryIdsAction($actionsNameAndType); list($queriedIds, $fieldNamesToInsert) = self::processIdsToInsert($actionsNameAndType, $actionIds); $insertedIds = self::insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert); - - $queriedIds = $queriedIds + $insertedIds; + $queriedIds = $queriedIds + $insertedIds; return $queriedIds; } - /** - * @param $name - * @param $type - * @return string - */ - private static function getIdActionMatchingNameAndType($name, $type) - { - $sql = TableLogAction::getSqlSelectActionId(); - $bind = array($name, $name, $type); - $idAction = \Piwik\Db::fetchOne($sql, $bind); - return $idAction; - } - /** * @param $matchType * @param $actionType @@ -75,6 +62,7 @@ class TableLogAction // now, we handle the cases =@ (contains) and !@ (does not contain) // build the expression based on the match type $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE %s AND type = ' . $actionType . ' )'; + switch ($matchType) { case '=@': // use concat to make sure, no %s occurs because some plugins use %s in their sql @@ -87,61 +75,45 @@ class TableLogAction throw new \Exception("This match type $matchType is not available for action-segments."); break; } + $sql = sprintf($sql, $where); - return $sql; - } - private static function getSqlSelectActionId() - { - $sql = "SELECT idaction, type, name - FROM " . Common::prefixTable('log_action') - . " WHERE " - . " ( hash = CRC32(?) AND name = ? AND type = ? ) "; return $sql; } private static function insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert) { - $sql = "INSERT INTO " . Common::prefixTable('log_action') . - "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)"; // Then, we insert all new actions in the lookup table $inserted = array(); + foreach ($fieldNamesToInsert as $fieldName) { list($name, $type, $urlPrefix) = $actionsNameAndType[$fieldName]; - Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix)); - $actionId = Tracker::getDatabase()->lastInsertId(); - - $inserted[$fieldName] = $actionId; + $actionId = self::getModel()->createNewIdAction($name, $type, $urlPrefix); Common::printDebug("Recorded a new action (" . Action::getTypeAsString($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")"); + + $inserted[$fieldName] = $actionId; } + return $inserted; } + private static function getModel() + { + return new Model(); + } + private static function queryIdsAction($actionsNameAndType) { - $sql = TableLogAction::getSqlSelectActionId(); - $bind = array(); - $i = 0; + $toQuery = array(); foreach ($actionsNameAndType as &$actionNameType) { list($name, $type, $urlPrefix) = $actionNameType; - if (empty($name)) { - continue; - } - if ($i > 0) { - $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) "; - } - $bind[] = $name; - $bind[] = $name; - $bind[] = $type; - $i++; + $toQuery[] = array('name' => $name, 'type' => $type); } - // Case URL & Title are empty - if (empty($bind)) { - return false; - } - $actionIds = Tracker::getDatabase()->fetchAll($sql, $bind); + + $actionIds = self::getModel()->getIdsAction($toQuery); + return $actionIds; } @@ -150,6 +122,7 @@ class TableLogAction // For the Actions found in the lookup table, add the idaction in the array, // If not found in lookup table, queue for INSERT $fieldNamesToInsert = $fieldNameToActionId = array(); + foreach ($actionsNameAndType as $fieldName => &$actionNameType) { @list($name, $type, $urlPrefix) = $actionNameType; if (empty($name)) { @@ -172,6 +145,7 @@ class TableLogAction $fieldNamesToInsert[] = $fieldName; } } + return array($fieldNameToActionId, $fieldNamesToInsert); } @@ -197,12 +171,13 @@ class TableLogAction // for urls trim protocol and www because it is not recorded in the db $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch); } + $valueToMatch = Common::sanitizeInputValue(Common::unsanitizeInputValue($valueToMatch)); if ($matchType == SegmentExpression::MATCH_EQUAL || $matchType == SegmentExpression::MATCH_NOT_EQUAL ) { - $idAction = self::getIdActionMatchingNameAndType($valueToMatch, $actionType); + $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType); // if the action is not found, we hack -100 to ensure it tries to match against an integer // otherwise binding idaction_name to "false" returns some rows for some reasons (in case &segment=pageTitle==VÄ›trnásssssss) if (empty($idAction)) { @@ -214,6 +189,7 @@ class TableLogAction // "name contains $string" match can match several idaction so we cannot return yet an idaction // special case $sql = TableLogAction::getSelectQueryWhereNameContains($matchType, $actionType); + return array( // mark that the returned value is an sql-expression instead of a literal value 'SQL' => $sql, @@ -229,15 +205,16 @@ class TableLogAction private static function guessActionTypeFromSegment($segmentName) { $exactMatch = array( - 'eventAction' => Action::TYPE_EVENT_ACTION, - 'eventCategory' => Action::TYPE_EVENT_CATEGORY, - 'eventName' => Action::TYPE_EVENT_NAME, - 'contentPiece' => Action::TYPE_CONTENT_PIECE, - 'contentTarget' => Action::TYPE_CONTENT_TARGET, - 'contentName' => Action::TYPE_CONTENT_NAME, + 'eventAction' => Action::TYPE_EVENT_ACTION, + 'eventCategory' => Action::TYPE_EVENT_CATEGORY, + 'eventName' => Action::TYPE_EVENT_NAME, + 'contentPiece' => Action::TYPE_CONTENT_PIECE, + 'contentTarget' => Action::TYPE_CONTENT_TARGET, + 'contentName' => Action::TYPE_CONTENT_NAME, 'contentInteraction' => Action::TYPE_CONTENT_INTERACTION, ); - if(!empty($exactMatch[$segmentName])) { + + if (!empty($exactMatch[$segmentName])) { return $exactMatch[$segmentName]; } diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index 90b58a88b068e845a3f81b930b08d84b1cebf83a..0ce45c3659de613e5fa195c4b579123dd5287a5d 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -109,8 +109,9 @@ class Visit implements VisitInterface $visitIsConverted = false; $action = null; - $requestIsManualGoalConversion = ($this->goalManager->idGoal > 0); + $isManualGoalConversion = $this->goalManager->isManualGoalConversion(); $requestIsEcommerce = $this->goalManager->requestIsEcommerce; + if ($requestIsEcommerce) { $someGoalsConverted = true; @@ -118,23 +119,26 @@ class Visit implements VisitInterface if ($this->goalManager->isGoalAnOrder()) { $visitIsConverted = true; } - } // this request is from the JS call to piwikTracker.trackGoal() - elseif ($requestIsManualGoalConversion) { + + } elseif ($isManualGoalConversion) { + // this request is from the JS call to piwikTracker.trackGoal() $someGoalsConverted = $this->goalManager->detectGoalId($this->request->getIdSite()); - $visitIsConverted = $someGoalsConverted; + $visitIsConverted = $someGoalsConverted; + // if we find a idgoal in the URL, but then the goal is not valid, this is most likely a fake request if (!$someGoalsConverted) { Common::printDebug('Invalid goal tracking request for goal id = ' . $this->goalManager->idGoal); return; } - } // normal page view, potentially triggering a URL matching goal - else { + + } else { + // normal page view, potentially triggering a URL matching goal $action = Action::factory($this->request); $action->writeDebugInfo(); $someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->request->getIdSite(), $action); - $visitIsConverted = $someGoalsConverted; + $visitIsConverted = $someGoalsConverted; $action->loadIdsFromLogActionTable(); } @@ -153,6 +157,7 @@ class Visit implements VisitInterface if (!$isLastActionInTheSameVisit) { Common::printDebug("Visitor detected, but last action was more than 30 minutes ago..."); } + // Known visit when: // ( - the visitor has the Piwik cookie with the idcookie ID used by Piwik to match the visitor // OR @@ -165,12 +170,15 @@ class Visit implements VisitInterface ) { $idReferrerActionUrl = $this->visitorInfo['visit_exit_idaction_url']; $idReferrerActionName = $this->visitorInfo['visit_exit_idaction_name']; + try { $this->goalManager->detectIsThereExistingCartInVisit($this->visitorInfo); $this->handleExistingVisit($visitor, $action, $visitIsConverted); + if (!is_null($action)) { $action->record($visitor, $idReferrerActionUrl, $idReferrerActionName); } + } catch (VisitorNotFoundInDb $e) { // There is an edge case when: @@ -179,7 +187,7 @@ class Visit implements VisitInterface // because the UPDATE didn't affect any rows (one row was found, but not updated since no field changed) // - the exception is caught here and will result in a new visit incorrectly // In this case, we cancel the current conversion to be recorded: - if ($requestIsManualGoalConversion + if ($isManualGoalConversion || $requestIsEcommerce ) { $someGoalsConverted = $visitIsConverted = false; @@ -239,8 +247,7 @@ class Visit implements VisitInterface // TODO we should not have to sync this->visitorInfo and $visitor columns. // TODO it should be its own dimension - $this->visitorInfo['time_spent_ref_action'] = $this->getTimeSpentReferrerAction(); - $visitor->setVisitorColumn('time_spent_ref_action', $this->visitorInfo['time_spent_ref_action']); + $this->setVisitorColumn($visitor, 'time_spent_ref_action', $this->getTimeSpentReferrerAction()); // update visitorInfo foreach ($valuesToUpdate as $name => $value) { @@ -262,8 +269,7 @@ class Visit implements VisitInterface $this->updateExistingVisit($valuesToUpdate); - $this->visitorInfo['visit_last_action_time'] = $this->request->getCurrentTimestamp(); - $visitor->setVisitorColumn('visit_last_action_time', $this->visitorInfo['visit_last_action_time']); + $this->setVisitorColumn($visitor, 'visit_last_action_time', $this->request->getCurrentTimestamp()); } /** @@ -275,8 +281,8 @@ class Visit implements VisitInterface if ($timeSpent < 0) { $timeSpent = 0; } - $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length']; - if($timeSpent > $visitStandardLength) { + $visitStandardLength = $this->getVisitStandardLength(); + if ($timeSpent > $visitStandardLength) { $timeSpent = $visitStandardLength; } return $timeSpent; @@ -297,15 +303,7 @@ class Visit implements VisitInterface { Common::printDebug("New Visit (IP = " . IP::N2P($this->getVisitorIp()) . ")"); - $this->visitorInfo = $this->getNewVisitorInformation($visitor); - - // Add Custom variable key,value to the visitor array - $this->visitorInfo = array_merge($this->visitorInfo, $this->visitorCustomVariables); - - $visitor->clearVisitorInfo(); - foreach ($this->visitorInfo as $key => $value) { - $visitor->setVisitorColumn($key, $value); - } + $this->setNewVisitorInformation($visitor); $dimensions = $this->getAllVisitDimensions(); @@ -329,15 +327,16 @@ class Visit implements VisitInterface $this->printVisitorInformation(); - $idVisit = $this->insertNewVisit( $this->visitorInfo ); + $idVisit = $this->insertNewVisit($this->visitorInfo); - $this->visitorInfo['idvisit'] = $idVisit; - $this->visitorInfo['visit_first_action_time'] = $this->request->getCurrentTimestamp(); - $this->visitorInfo['visit_last_action_time'] = $this->request->getCurrentTimestamp(); + $this->setVisitorColumn($visitor, 'idvisit', $idVisit); + $this->setVisitorColumn($visitor, 'visit_first_action_time', $this->request->getCurrentTimestamp()); + $this->setVisitorColumn($visitor, 'visit_last_action_time', $this->request->getCurrentTimestamp()); + } - $visitor->setVisitorColumn('idvisit', $this->visitorInfo['idvisit']); - $visitor->setVisitorColumn('visit_first_action_time', $this->visitorInfo['visit_first_action_time']); - $visitor->setVisitorColumn('visit_last_action_time', $this->visitorInfo['visit_last_action_time']); + private function getModel() + { + return new Model(); } /** @@ -353,7 +352,7 @@ class Visit implements VisitInterface // If the visitor had a first party ID cookie, then we use this value if (!empty($this->visitorInfo['idvisitor']) - && strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID + && Tracker::LENGTH_BINARY_ID == strlen($this->visitorInfo['idvisitor']) ) { return $this->visitorInfo['idvisitor']; } @@ -366,8 +365,7 @@ class Visit implements VisitInterface */ public static function generateUniqueVisitorId() { - $uniqueId = substr(Common::generateUniqId(), 0, Tracker::LENGTH_HEX_ID_STRING); - return $uniqueId; + return substr(Common::generateUniqId(), 0, Tracker::LENGTH_HEX_ID_STRING); } /** @@ -387,9 +385,10 @@ class Visit implements VisitInterface */ protected function getSettingsObject() { - if(is_null($this->userSettings)) { + if (is_null($this->userSettings)) { $this->userSettings = new Settings( $this->request, $this->getVisitorIp() ); } + return $this->userSettings; } @@ -410,33 +409,26 @@ class Visit implements VisitInterface public static function isHostKnownAliasHost($urlHost, $idSite) { $websiteData = Cache::getCacheWebsiteAttributes($idSite); + if (isset($websiteData['hosts'])) { $canonicalHosts = array(); foreach ($websiteData['hosts'] as $host) { - $canonicalHosts[] = str_replace('www.', '', mb_strtolower($host, 'UTF-8')); + $canonicalHosts[] = self::toCanonicalHost($host); } - $canonicalHost = str_replace('www.', '', mb_strtolower($urlHost, 'UTF-8')); + + $canonicalHost = self::toCanonicalHost($urlHost); if (in_array($canonicalHost, $canonicalHosts)) { return true; } } + return false; } - /** - * @return mixed - */ - protected function insertNewVisit($visit) + private static function toCanonicalHost($host) { - $fields = implode(", ", array_keys($visit)); - $values = Common::getSqlStringFieldsArray($visit); - - $sql = "INSERT INTO " . Common::prefixTable('log_visit') . " ($fields) VALUES ($values)"; - $bind = array_values($visit); - Tracker::getDatabase()->query($sql, $bind); - - $idVisit = Tracker::getDatabase()->lastInsertId(); - return $idVisit; + $hostLower = mb_strtolower($host, 'UTF-8'); + return str_replace('www.', '', $hostLower); } /** @@ -445,45 +437,32 @@ class Visit implements VisitInterface */ protected function updateExistingVisit($valuesToUpdate) { - $sqlQuery = "UPDATE " . Common::prefixTable('log_visit') . " - SET %s - WHERE idsite = ? - AND idvisit = ?"; - // build sql query - $updateParts = $sqlBind = array(); - foreach ($valuesToUpdate as $name => $value) { - // Case where bind parameters don't work - if(strpos($value, $name) !== false) { - //$name = 'visit_total_events' - //$value = 'visit_total_events + 1'; - $updateParts[] = " $name = $value "; - } else { - $updateParts[] = $name . " = ?"; - $sqlBind[] = $value; - } - } - $sqlQuery = sprintf($sqlQuery, implode($updateParts, ', ') ); - array_push($sqlBind, $this->request->getIdSite(), (int)$this->visitorInfo['idvisit']); + $idSite = $this->request->getIdSite(); + $idVisit = (int) $this->visitorInfo['idvisit']; - $result = Tracker::getDatabase()->query($sqlQuery, $sqlBind); + $wasInserted = $this->getModel()->updateVisit($idSite, $idVisit, $valuesToUpdate); // Debug output if (isset($valuesToUpdate['idvisitor'])) { $valuesToUpdate['idvisitor'] = bin2hex($valuesToUpdate['idvisitor']); } - Common::printDebug('Updating existing visit: ' . var_export($valuesToUpdate, true)); - if (Tracker::getDatabase()->rowCount($result) == 0) { - Common::printDebug("Visitor with this idvisit wasn't found in the DB."); - Common::printDebug("$sqlQuery --- "); - Common::printDebug($sqlBind); + if ($wasInserted) { + Common::printDebug('Updated existing visit: ' . var_export($valuesToUpdate, true)); + } else { throw new VisitorNotFoundInDb( "The visitor with idvisitor=" . bin2hex($this->visitorInfo['idvisitor']) . " and idvisit=" . $this->visitorInfo['idvisit'] . " wasn't found in the DB, we fallback to a new visitor"); } } - protected function printVisitorInformation() + private function setVisitorColumn(Visitor $visitor, $key, $value) + { + $this->visitorInfo[$key] = $value; + $visitor->setVisitorColumn($key, $value); + } + + private function printVisitorInformation() { $debugVisitInfo = $this->visitorInfo; $debugVisitInfo['idvisitor'] = bin2hex($debugVisitInfo['idvisitor']); @@ -491,13 +470,22 @@ class Visit implements VisitInterface Common::printDebug($debugVisitInfo); } - protected function getNewVisitorInformation($visitor) + private function setNewVisitorInformation(Visitor $visitor) { - return array( - 'idvisitor' => $this->getVisitorIdcookie($visitor), - 'config_id' => $this->getSettingsObject()->getConfigId(), - 'location_ip' => $this->getVisitorIp(), - ); + $idVisitor = $this->getVisitorIdcookie($visitor); + $visitorIp = $this->getVisitorIp(); + $configId = $this->getSettingsObject()->getConfigId(); + + $this->visitorInfo = array(); + $visitor->clearVisitorInfo(); + + $this->setVisitorColumn($visitor, 'idvisitor', $idVisitor); + $this->setVisitorColumn($visitor, 'config_id', $configId); + $this->setVisitorColumn($visitor, 'location_ip', $visitorIp); + + foreach ($this->visitorCustomVariables as $key => $value) { + $this->setVisitorColumn($visitor, $key, $value); + } } /** @@ -508,15 +496,11 @@ class Visit implements VisitInterface * @param $visitIsConverted * @return array */ - protected function getExistingVisitFieldsToUpdate($visitor, $action, $visitIsConverted) + private function getExistingVisitFieldsToUpdate($visitor, $action, $visitIsConverted) { $valuesToUpdate = array(); - // Might update the idvisitor when it was forced or overwritten for this visit - if (strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID) { - $valuesToUpdate['idvisitor'] = $this->visitorInfo['idvisitor']; - $visitor->setVisitorColumn('idvisitor', $this->visitorInfo['idvisitor']); - } + $valuesToUpdate = $this->setIdVisitorForExistingVisit($visitor, $valuesToUpdate); $dimensions = $this->getAllVisitDimensions(); $valuesToUpdate = $this->triggerHookOnDimensions($dimensions, 'onExistingVisit', $visitor, $action, $valuesToUpdate); @@ -526,8 +510,7 @@ class Visit implements VisitInterface } // Custom Variables overwrite previous values on each page view - $valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables); - return $valuesToUpdate; + return array_merge($valuesToUpdate, $this->visitorCustomVariables); } /** @@ -567,7 +550,44 @@ class Visit implements VisitInterface foreach($dimensions as $dimension) { $dimensionNames[] = $dimension->getColumnName(); } + Common::printDebug("Following dimensions have been collected from plugins: " . implode(", ", $dimensionNames)); + return $dimensions; } + + private function getVisitStandardLength() + { + return Config::getInstance()->Tracker['visit_standard_length']; + } + + /** + * @param $visitor + * @param $valuesToUpdate + * @return mixed + */ + private function setIdVisitorForExistingVisit($visitor, $valuesToUpdate) + { + // Might update the idvisitor when it was forced or overwritten for this visit + if (strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID) { + $binIdVisitor = $this->visitorInfo['idvisitor']; + $visitor->setVisitorColumn('idvisitor', $binIdVisitor); + $valuesToUpdate['idvisitor'] = $binIdVisitor; + } + + // User ID takes precedence and overwrites idvisitor value + $userId = $this->request->getForcedUserId(); + if ($userId) { + $userIdHash = $this->request->getUserIdHashed($userId); + $binIdVisitor = Common::hex2bin($userIdHash); + $visitor->setVisitorColumn('idvisitor', $binIdVisitor); + $valuesToUpdate['idvisitor'] = $binIdVisitor; + } + return $valuesToUpdate; + } + + protected function insertNewVisit($visit) + { + return $this->getModel()->createVisit($visit); + } } diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php index 586c874a0c74351f00289937e37fcb394eca900a..48eb1e3db75e72434ef30299ca9c0303d84b5a02 100644 --- a/core/Tracker/VisitExcluded.php +++ b/core/Tracker/VisitExcluded.php @@ -8,7 +8,6 @@ */ namespace Piwik\Tracker; -use DeviceDetector\Parser\Bot; use Piwik\Common; use Piwik\Config; use Piwik\DeviceDetectorFactory; @@ -27,16 +26,16 @@ class VisitExcluded */ public function __construct(Request $request, $ip = false, $userAgent = false) { - if ($ip === false) { + if (false === $ip) { $ip = $request->getIp(); } - if ($userAgent === false) { + if (false === $userAgent) { $userAgent = $request->getUserAgent(); } - $this->request = $request; - $this->idSite = $request->getIdSite(); + $this->request = $request; + $this->idSite = $request->getIdSite(); $this->userAgent = $userAgent; $this->ip = $ip; } @@ -200,6 +199,7 @@ class VisitExcluded Common::printDebug('Piwik ignore cookie was found, visit not tracked.'); return true; } + return false; } @@ -211,12 +211,14 @@ class VisitExcluded protected function isVisitorIpExcluded() { $websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite); + if (!empty($websiteAttributes['excluded_ips'])) { if (IP::isIpInRange($this->ip, $websiteAttributes['excluded_ips'])) { Common::printDebug('Visitor IP ' . IP::N2P($this->ip) . ' is excluded from being tracked'); return true; } } + return false; } @@ -232,6 +234,7 @@ class VisitExcluded protected function isUserAgentExcluded() { $websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite); + if (!empty($websiteAttributes['excluded_user_agents'])) { foreach ($websiteAttributes['excluded_user_agents'] as $excludedUserAgent) { // if the excluded user agent string part is in this visit's user agent, this visit should be excluded @@ -240,6 +243,7 @@ class VisitExcluded } } } + return false; } @@ -254,12 +258,14 @@ class VisitExcluded $spamHosts = explode(",", $spamHosts); $referrerUrl = $this->request->getParam('urlref'); + foreach($spamHosts as $spamHost) { - if( strpos($referrerUrl, $spamHost) !== false) { + if ( strpos($referrerUrl, $spamHost) !== false) { Common::printDebug('Referrer URL is a known spam: ' . $spamHost); return true; } } + return false; } } diff --git a/core/Tracker/Visitor.php b/core/Tracker/Visitor.php index b4120781516bc9ae02b536ea08b470bbe6241e4e..afec61849e74f42c0d445f151c5e368291d9ac19 100644 --- a/core/Tracker/Visitor.php +++ b/core/Tracker/Visitor.php @@ -25,9 +25,9 @@ class Visitor public function __construct(Request $request, $configId, $visitorInfo = array(), $customVariables = null) { $this->request = $request; + $this->configId = $configId; $this->visitorInfo = $visitorInfo; $this->customVariables = $customVariables; - $this->configId = $configId; } /** @@ -41,119 +41,35 @@ class Visitor { $this->setIsVisitorKnown(false); - $configId = $this->configId; - + $configId = $this->configId; + $idSite = $this->request->getIdSite(); $idVisitor = $this->request->getVisitorId(); + $isVisitorIdToLookup = !empty($idVisitor); if ($isVisitorIdToLookup) { $this->visitorInfo['idvisitor'] = $idVisitor; - Common::printDebug("Matching visitors with: visitorId=" . bin2hex($this->visitorInfo['idvisitor']) . " OR configId=" . bin2hex($configId)); + Common::printDebug("Matching visitors with: visitorId=" . bin2hex($idVisitor) . " OR configId=" . bin2hex($configId)); } else { Common::printDebug("Visitor doesn't have the piwik cookie..."); } - $selectCustomVariables = ''; - // No custom var were found in the request, so let's copy the previous one in a potential conversion later + $numCustomVarsToRead = 0; if (!$this->customVariables) { - $maxCustomVariables = CustomVariables::getMaxCustomVariables(); - - for ($index = 1; $index <= $maxCustomVariables; $index++) { - $selectCustomVariables .= ', custom_var_k' . $index . ', custom_var_v' . $index; - } + // No custom var were found in the request, so let's copy the previous one in a potential conversion later + $numCustomVarsToRead = CustomVariables::getMaxCustomVariables(); } - $persistedVisitAttributes = self::getVisitFieldsPersist(); - array_unshift($persistedVisitAttributes, 'visit_first_action_time'); - array_unshift($persistedVisitAttributes, 'visit_last_action_time'); - $persistedVisitAttributes = array_unique($persistedVisitAttributes); - - $selectFields = implode(", ", $persistedVisitAttributes); - - $select = "SELECT - $selectFields - $selectCustomVariables - "; - $from = "FROM " . Common::prefixTable('log_visit'); - + $persistedVisitAttributes = $this->getVisitFieldsPersist(); + $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup); list($timeLookBack, $timeLookAhead) = $this->getWindowLookupThisVisit(); - $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup); - - // Two use cases: - // 1) there is no visitor ID so we try to match only on config_id (heuristics) - // Possible causes of no visitor ID: no browser cookie support, direct Tracking API request without visitor ID passed, - // importing server access logs with import_logs.py, etc. - // In this case we use config_id heuristics to try find the visitor in tahhhe past. There is a risk to assign - // this page view to the wrong visitor, but this is better than creating artificial visits. - // 2) there is a visitor ID and we trust it (config setting trust_visitors_cookies, OR it was set using &cid= in tracking API), - // and in these cases, we force to look up this visitor id - $whereCommon = "visit_last_action_time >= ? AND visit_last_action_time <= ? AND idsite = ?"; - $bindSql = array( - $timeLookBack, - $timeLookAhead, - $this->request->getIdSite() - ); - - if ($shouldMatchOneFieldOnly) { - if ($isVisitorIdToLookup) { - $whereCommon .= ' AND idvisitor = ?'; - $bindSql[] = $this->visitorInfo['idvisitor']; - } else { - $whereCommon .= ' AND config_id = ?'; - $bindSql[] = $configId; - } - - $sql = "$select - $from - WHERE " . $whereCommon . " - ORDER BY visit_last_action_time DESC - LIMIT 1"; - } // We have a config_id AND a visitor_id. We match on either of these. - // Why do we also match on config_id? - // we do not trust the visitor ID only. Indeed, some browsers, or browser addons, - // cause the visitor id from the 1st party cookie to be different on each page view! - // It is not acceptable to create a new visit every time such browser does a page view, - // so we also backup by searching for matching config_id. - // We use a UNION here so that each sql query uses its own INDEX - else { - // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time) - $where = ' AND config_id = ?'; - $bindSql[] = $configId; - $sqlConfigId = "$select , - 0 as priority - $from - WHERE $whereCommon $where - ORDER BY visit_last_action_time DESC - LIMIT 1 - "; - // will use INDEX index_idsite_idvisitor (idsite, idvisitor) - $bindSql[] = $timeLookBack; - $bindSql[] = $timeLookAhead; - $bindSql[] = $this->request->getIdSite(); - $where = ' AND idvisitor = ?'; - $bindSql[] = $this->visitorInfo['idvisitor']; - $sqlVisitorId = "$select , - 1 as priority - $from - WHERE $whereCommon $where - ORDER BY visit_last_action_time DESC - LIMIT 1 - "; - - // We join both queries and favor the one matching the visitor_id if it did match - $sql = " ( $sqlConfigId ) - UNION - ( $sqlVisitorId ) - ORDER BY priority DESC - LIMIT 1"; - } - - $visitRow = Tracker::getDatabase()->fetch($sql, $bindSql); + $model = $this->getModel(); + $visitRow = $model->findVisitor($idSite, $configId, $idVisitor, $persistedVisitAttributes, $numCustomVarsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead); $isNewVisitForced = $this->request->getParam('new_visit'); $isNewVisitForced = !empty($isNewVisitForced); - $enforceNewVisit = $isNewVisitForced || Config::getInstance()->Debug['tracker_always_new_visitor']; + $enforceNewVisit = $isNewVisitForced || Config::getInstance()->Debug['tracker_always_new_visitor']; if (!$enforceNewVisit && $visitRow @@ -161,17 +77,16 @@ class Visitor ) { // These values will be used throughout the request - foreach($persistedVisitAttributes as $field) { + foreach ($persistedVisitAttributes as $field) { $this->visitorInfo[$field] = $visitRow[$field]; } - $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']); + $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']); $this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']); // Custom Variables copied from Visit in potential later conversion - if (!empty($selectCustomVariables)) { - $maxCustomVariables = CustomVariables::getMaxCustomVariables(); - for ($i = 1; $i <= $maxCustomVariables; $i++) { + if (!empty($numCustomVarsToRead)) { + for ($i = 1; $i <= $numCustomVarsToRead; $i++) { if (isset($visitRow['custom_var_k' . $i]) && strlen($visitRow['custom_var_k' . $i]) ) { @@ -192,7 +107,6 @@ class Visitor last action = " . date("r", $this->visitorInfo['visit_last_action_time']) . ", first action = " . date("r", $this->visitorInfo['visit_first_action_time']) . ", visit_goal_buyer' = " . $this->visitorInfo['visit_goal_buyer'] . ")"); - //Common::printDebug($this->visitorInfo); } else { Common::printDebug("The visitor was not matched with an existing visitor..."); } @@ -209,16 +123,16 @@ class Visitor */ protected function getWindowLookupThisVisit() { - $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length']; + $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length']; $lookBackNSecondsCustom = Config::getInstance()->Tracker['window_look_back_for_visitor']; $lookAheadNSeconds = $visitStandardLength; - $lookBackNSeconds = $visitStandardLength; + $lookBackNSeconds = $visitStandardLength; if ($lookBackNSecondsCustom > $lookBackNSeconds) { $lookBackNSeconds = $lookBackNSecondsCustom; } - $timeLookBack = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() - $lookBackNSeconds); + $timeLookBack = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() - $lookBackNSeconds); $timeLookAhead = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() + $lookAheadNSeconds); return array($timeLookBack, $timeLookAhead); @@ -226,27 +140,38 @@ class Visitor protected function shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup) { + $isForcedUserIdMustMatch = (false !== $this->request->getForcedUserId()); + + if ($isForcedUserIdMustMatch) { + // if &iud was set, we must try and match both idvisitor and config_id + return false; + } + // This setting would be enabled for Intranet websites, to ensure that visitors using all the same computer config, same IP // are not counted as 1 visitor. In this case, we want to enforce and trust the visitor ID from the cookie. $trustCookiesOnly = Config::getInstance()->Tracker['trust_visitors_cookies']; + if ($isVisitorIdToLookup && $trustCookiesOnly) { + return true; + } // If a &cid= was set, we force to select this visitor (or create a new one) $isForcedVisitorIdMustMatch = ($this->request->getForcedVisitorId() != null); - // if &iud was set, we force to select this visitor (or create new one) - $isForcedUserIdMustMatch = ($this->request->getForcedUserId() !== null); + if ($isForcedVisitorIdMustMatch) { + return true; + } - $shouldMatchOneFieldOnly = (($isVisitorIdToLookup && $trustCookiesOnly) - || $isForcedVisitorIdMustMatch - || $isForcedUserIdMustMatch - || !$isVisitorIdToLookup); - return $shouldMatchOneFieldOnly; + if (!$isVisitorIdToLookup ) { + return true; + } + + return false; } /** * @return array */ - public static function getVisitFieldsPersist() + private function getVisitFieldsPersist() { $fields = array( 'idvisitor', @@ -296,6 +221,10 @@ class Visitor */ Piwik::postEvent('Tracker.getVisitFieldsToPersist', array(&$fields)); + array_unshift($fields, 'visit_first_action_time'); + array_unshift($fields, 'visit_last_action_time'); + $fields = array_unique($fields); + return $fields; } @@ -332,4 +261,9 @@ class Visitor { return $this->visitorKnown = $isVisitorKnown; } + + private function getModel() + { + return new Model(); + } } diff --git a/core/Translate.php b/core/Translate.php index 34bd35ba1223c8359deda10cdd737bee68f073a4..65381c0a2da811ec4d1144c1c275dfd3484b5a36 100644 --- a/core/Translate.php +++ b/core/Translate.php @@ -68,7 +68,7 @@ class Translate private static function loadCoreTranslationFile($language) { - if(empty($language)) { + if (empty($language)) { return; } $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.json'; @@ -174,7 +174,7 @@ class Translate } $js = 'var translations = ' . Common::json_encode($clientSideTranslations) . ';'; - $js .= "\n" . 'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }' . + $js .= "\n" . 'if (typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }' . 'for(var i in translations) { piwik_translations[i] = translations[i];} '; return $js; } diff --git a/core/Translate/Validate/NoScripts.php b/core/Translate/Validate/NoScripts.php index 0ee0fc9a57ca656b1cc5278bd323758a9082f223..e7f032ff55d2155d931b4607a7a3ef642be56b4b 100644 --- a/core/Translate/Validate/NoScripts.php +++ b/core/Translate/Validate/NoScripts.php @@ -28,12 +28,14 @@ class NoScripts extends ValidateAbstract // check if any translation contains restricted script tags $serializedStrings = serialize($translations); $invalids = array("<script", 'document.', 'javascript:', 'src=', 'background=', 'onload='); + foreach ($invalids as $invalid) { if (stripos($serializedStrings, $invalid) !== false) { $this->message = 'script tags restricted for language files'; return false; } } + return true; } } diff --git a/core/Translate/Writer.php b/core/Translate/Writer.php index 617b8b95526a3994629ed47867e6456f9fc0f362..c43beca189934bcfd6db7c58044811440fe48b54 100644 --- a/core/Translate/Writer.php +++ b/core/Translate/Writer.php @@ -71,7 +71,7 @@ class Writer protected $filterMessages = array(); const UNFILTERED = 'unfiltered'; - const FILTERED = 'filtered'; + const FILTERED = 'filtered'; protected $currentState = self::UNFILTERED; @@ -152,12 +152,14 @@ class Writer public function getTranslations($lang) { $path = $this->getTranslationPathBaseDirectory('lang', $lang); + if (!is_readable($path)) { return array(); } $data = file_get_contents($path); $translations = json_decode($data, true); + return $translations; } diff --git a/core/Twig.php b/core/Twig.php index b0707350c49d7d6626772509d56e3f96eaadd2d5..2e0ef95844671f3e65c11cf0abcc7701c852fd27 100755 --- a/core/Twig.php +++ b/core/Twig.php @@ -46,7 +46,7 @@ class Twig $loaders = array(); //create loader for custom theme to overwrite twig templates - if($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) { + if ($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) { $customLoader = $this->getCustomThemeLoader($theme); if ($customLoader) { //make it possible to overwrite plugin templates @@ -187,7 +187,7 @@ class Twig * @return \Twig_Loader_Filesystem */ protected function getCustomThemeLoader(Plugin $theme){ - if(!file_exists(sprintf("%s/plugins/%s/templates/", PIWIK_INCLUDE_PATH, $theme->getPluginName()))){ + if (!file_exists(sprintf("%s/plugins/%s/templates/", PIWIK_INCLUDE_PATH, $theme->getPluginName()))){ return false; } $themeLoader = new Twig_Loader_Filesystem(array( diff --git a/core/Unzip.php b/core/Unzip.php index 5b5495c4e4f5ffdbb57b4a3263989696b29819d8..547a1a84a5a5dd06422b5920a9fd6aa90df58b69 100644 --- a/core/Unzip.php +++ b/core/Unzip.php @@ -4,19 +4,17 @@ * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * */ namespace Piwik; -use Piwik\Unzip\Gzip; -use Piwik\Unzip\PclZip; -use Piwik\Unzip\Tar; -use Piwik\Unzip\ZipArchive; +use Piwik\Decompress\Gzip; +use Piwik\Decompress\PclZip; +use Piwik\Decompress\Tar; +use Piwik\Decompress\ZipArchive; /** - * Unzip wrapper around ZipArchive and PclZip - * + * Factory for Decompress adapters. */ class Unzip { @@ -25,7 +23,7 @@ class Unzip * * @param string $name Name of unarchiver * @param string $filename Name of .zip archive - * @return \Piwik\Unzip\UncompressInterface + * @return \Piwik\Decompress\DecompressInterface */ public static function factory($name, $filename) { diff --git a/core/Unzip/Gzip.php b/core/Unzip/Gzip.php deleted file mode 100755 index 16f08abf0ad5e83fd5c911cf710a6ead3d53769c..0000000000000000000000000000000000000000 --- a/core/Unzip/Gzip.php +++ /dev/null @@ -1,81 +0,0 @@ -<?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\Unzip; - -/** - * Unzip implementation for .gz files. - * - */ -class Gzip implements UncompressInterface -{ - /** - * Name of .gz file. - * - * @var string - */ - private $filename = null; - - /** - * Error string. - * - * @var string - */ - private $error = null; - - /** - * Constructor. - * - * @param string $filename Name of .gz file. - */ - public function __construct($filename) - { - $this->filename = $filename; - } - - /** - * Extracts the contents of the .gz file to $pathExtracted. - * - * @param string $pathExtracted Must be file, not directory. - * @return bool true if successful, false if otherwise. - */ - public function extract($pathExtracted) - { - $file = gzopen($this->filename, 'r'); - if ($file === false) { - $this->error = "gzopen failed"; - return false; - } - - $output = fopen($pathExtracted, 'w'); - while (!feof($file)) { - fwrite($output, fread($file, 1024 * 1024)); - } - fclose($output); - - $success = gzclose($file); - if ($success === false) { - $this->error = "gzclose failed"; - return false; - } - - return true; - } - - /** - * Get error status string for the latest error. - * - * @return string - */ - public function errorInfo() - { - return $this->error; - } -} - diff --git a/core/Unzip/PclZip.php b/core/Unzip/PclZip.php deleted file mode 100644 index b0ce4b3a75bb2269b64f5a3e25ebe2ba8bbbaa3c..0000000000000000000000000000000000000000 --- a/core/Unzip/PclZip.php +++ /dev/null @@ -1,88 +0,0 @@ -<?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\Unzip; - -/** - * @see libs/PclZip - */ -require_once PIWIK_INCLUDE_PATH . '/libs/PclZip/pclzip.lib.php'; - -/** - * Unzip wrapper around PclZip - * - */ -class PclZip implements UncompressInterface -{ - /** - * @var PclZip - */ - private $pclzip; - /** - * @var string - */ - public $filename; - - /** - * Constructor - * - * @param string $filename Name of the .zip archive - */ - public function __construct($filename) - { - $this->pclzip = new \PclZip($filename); - $this->filename = $filename; - } - - /** - * Extract files from archive to target directory - * - * @param string $pathExtracted Absolute path of target directory - * @return mixed Array of filenames if successful; or 0 if an error occurred - */ - public function extract($pathExtracted) - { - $pathExtracted = str_replace('\\', '/', $pathExtracted); - $list = $this->pclzip->listContent(); - if (empty($list)) { - return 0; - } - - foreach ($list as $entry) { - $filename = str_replace('\\', '/', $entry['stored_filename']); - $parts = explode('/', $filename); - - if (!strncmp($filename, '/', 1) || - array_search('..', $parts) !== false || - strpos($filename, ':') !== false - ) { - return 0; - } - } - - // PCLZIP_CB_PRE_EXTRACT callback returns 0 to skip, 1 to resume, or 2 to abort - return $this->pclzip->extract( - PCLZIP_OPT_PATH, $pathExtracted, - PCLZIP_OPT_STOP_ON_ERROR, - PCLZIP_OPT_REPLACE_NEWER, - PCLZIP_CB_PRE_EXTRACT, function ($p_event, &$p_header) use ($pathExtracted) { - return strncmp($p_header['filename'], $pathExtracted, strlen($pathExtracted)) ? 0 : 1; - } - ); - } - - /** - * Get error status string for the latest error - * - * @return string - */ - public function errorInfo() - { - return $this->pclzip->errorInfo(true); - } -} diff --git a/core/Unzip/Tar.php b/core/Unzip/Tar.php deleted file mode 100755 index 2c87882611cd884bae3536112fea7e846f369b2e..0000000000000000000000000000000000000000 --- a/core/Unzip/Tar.php +++ /dev/null @@ -1,84 +0,0 @@ -<?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\Unzip; - -use Archive_Tar; - -/** - * @see libs/Archive_Tar - */ -require_once PIWIK_INCLUDE_PATH . '/libs/Archive_Tar/Tar.php'; - -/** - * Unzip implementation for Archive_Tar PEAR lib. - * - */ -class Tar implements UncompressInterface -{ - /** - * Archive_Tar instance. - * - * @var Archive_Tar - */ - private $tarArchive = null; - - /** - * Constructor. - * - * @param string $filename Path to tar file. - * @param string|null $compression Either 'gz', 'bz2' or null for no compression. - */ - public function __construct($filename, $compression = null) - { - $this->tarArchive = new Archive_Tar($filename, $compression); - } - - /** - * Extracts the contents of the tar file to $pathExtracted. - * - * @param string $pathExtracted Directory to extract into. - * @return bool true if successful, false if otherwise. - */ - public function extract($pathExtracted) - { - return $this->tarArchive->extract($pathExtracted); - } - - /** - * Extracts one file held in a tar archive and returns the deflated file - * as a string. - * - * @param string $inArchivePath Path to file in the tar archive. - * @return bool true if successful, false if otherwise. - */ - public function extractInString($inArchivePath) - { - return $this->tarArchive->extractInString($inArchivePath); - } - - /** - * Lists the files held in the tar archive. - * - * @return array List of paths describing everything held in the tar archive. - */ - public function listContent() - { - return $this->tarArchive->listContent(); - } - - /** - * Get error status string for the latest error. - * - * @return string - */ - public function errorInfo() - { - return $this->tarArchive->error_object->getMessage(); - } -} diff --git a/core/Unzip/UncompressInterface.php b/core/Unzip/UncompressInterface.php deleted file mode 100644 index 072f476bb4291636929d9688d65c43e99ef65fbb..0000000000000000000000000000000000000000 --- a/core/Unzip/UncompressInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -<?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\Unzip; - -/** - * Unzip interface - * - */ -interface UncompressInterface -{ - /** - * Constructor - * - * @param string $filename Name of the .zip archive - */ - public function __construct($filename); - - /** - * Extract files from archive to target directory - * - * @param string $pathExtracted Absolute path of target directory - * @return mixed Array of filenames if successful; or 0 if an error occurred - */ - public function extract($pathExtracted); - - /** - * Get error status string for the latest error - * - * @return string - */ - public function errorInfo(); -} diff --git a/core/Unzip/ZipArchive.php b/core/Unzip/ZipArchive.php deleted file mode 100644 index c8d7edd87443f143c40dcd0827aadabc889148ea..0000000000000000000000000000000000000000 --- a/core/Unzip/ZipArchive.php +++ /dev/null @@ -1,133 +0,0 @@ -<?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\Unzip; - -use Exception; - -/** - * Unzip wrapper around ZipArchive - * - */ -class ZipArchive implements UncompressInterface -{ - /** - * @var \ZipArchive - */ - private $ziparchive; - /** - * @var string - */ - public $filename; - - /** - * Constructor - * - * @param string $filename Name of the .zip archive - * @throws Exception - */ - public function __construct($filename) - { - $this->filename = $filename; - $this->ziparchive = new \ZipArchive; - if ($this->ziparchive->open($filename) !== true) { - throw new Exception('Error opening ' . $filename); - } - } - - /** - * Extract files from archive to target directory - * - * @param string $pathExtracted Absolute path of target directory - * @return mixed Array of filenames if successful; or 0 if an error occurred - */ - public function extract($pathExtracted) - { - if (substr($pathExtracted, -1) !== '/') { - $pathExtracted .= '/'; - } - - $fileselector = array(); - $list = array(); - $count = $this->ziparchive->numFiles; - if ($count === 0) { - return 0; - } - - for ($i = 0; $i < $count; $i++) { - $entry = $this->ziparchive->statIndex($i); - - $filename = str_replace('\\', '/', $entry['name']); - $parts = explode('/', $filename); - - if (!strncmp($filename, '/', 1) || - array_search('..', $parts) !== false || - strpos($filename, ':') !== false - ) { - return 0; - } - $fileselector[] = $entry['name']; - $list[] = array( - 'filename' => $pathExtracted . $entry['name'], - 'stored_filename' => $entry['name'], - 'size' => $entry['size'], - 'compressed_size' => $entry['comp_size'], - 'mtime' => $entry['mtime'], - 'index' => $i, - 'crc' => $entry['crc'], - ); - } - - $res = $this->ziparchive->extractTo($pathExtracted, $fileselector); - if ($res === false) - return 0; - return $list; - } - - /** - * Get error status string for the latest error - * - * @return string - */ - public function errorInfo() - { - static $statusStrings = array( - \ZipArchive::ER_OK => 'No error', - \ZipArchive::ER_MULTIDISK => 'Multi-disk zip archives not supported', - \ZipArchive::ER_RENAME => 'Renaming temporary file failed', - \ZipArchive::ER_CLOSE => 'Closing zip archive failed', - \ZipArchive::ER_SEEK => 'Seek error', - \ZipArchive::ER_READ => 'Read error', - \ZipArchive::ER_WRITE => 'Write error', - \ZipArchive::ER_CRC => 'CRC error', - \ZipArchive::ER_ZIPCLOSED => 'Containing zip archive was closed', - \ZipArchive::ER_NOENT => 'No such file', - \ZipArchive::ER_EXISTS => 'File already exists', - \ZipArchive::ER_OPEN => 'Can\'t open file', - \ZipArchive::ER_TMPOPEN => 'Failure to create temporary file', - \ZipArchive::ER_ZLIB => 'Zlib error', - \ZipArchive::ER_MEMORY => 'Malloc failure', - \ZipArchive::ER_CHANGED => 'Entry has been changed', - \ZipArchive::ER_COMPNOTSUPP => 'Compression method not supported', - \ZipArchive::ER_EOF => 'Premature EOF', - \ZipArchive::ER_INVAL => 'Invalid argument', - \ZipArchive::ER_NOZIP => 'Not a zip archive', - \ZipArchive::ER_INTERNAL => 'Internal error', - \ZipArchive::ER_INCONS => 'Zip archive inconsistent', - \ZipArchive::ER_REMOVE => 'Can\'t remove file', - \ZipArchive::ER_DELETED => 'Entry has been deleted', - ); - - if (isset($statusStrings[$this->ziparchive->status])) { - $statusString = $statusStrings[$this->ziparchive->status]; - } else { - $statusString = 'Unknown status'; - } - return $statusString . '(' . $this->ziparchive->status . ')'; - } -} diff --git a/core/UpdateCheck.php b/core/UpdateCheck.php index 228ac23a856d45bbfffb0a3a8fa4b73d88d0b8ac..43639da9dc10fbf4099cc416040ed005403417ec 100644 --- a/core/UpdateCheck.php +++ b/core/UpdateCheck.php @@ -36,7 +36,7 @@ class UpdateCheck */ public static function check($force = false, $interval = null) { - if(!self::isAutoUpdateEnabled()) { + if (!self::isAutoUpdateEnabled()) { return; } diff --git a/core/Updater.php b/core/Updater.php index 1527da723a9ec778daa025c994a5e1cc14969915..0069d1b71bcddcb70b1ced4256e52eeb101f5017 100644 --- a/core/Updater.php +++ b/core/Updater.php @@ -355,6 +355,7 @@ class Updater * @param string $updateSql Update SQL query. * @param int|false $errorToIgnore A MySQL error code to ignore. * @param string $file The Update file that's calling this method. + * @throws UpdaterErrorException */ public static function handleQueryError($e, $updateSql, $errorToIgnore, $file) { @@ -371,6 +372,7 @@ class Updater * * @param int $error * @param int|int[] $errorCodesToIgnore + * @return boolean */ public static function isDbErrorOneOf($error, $errorCodesToIgnore) { diff --git a/core/Updates/0.2.34.php b/core/Updates/0.2.34.php deleted file mode 100644 index 5bdf982d8c0bf43a0af1ac0d5dbae535c48e2b8a..0000000000000000000000000000000000000000 --- a/core/Updates/0.2.34.php +++ /dev/null @@ -1,28 +0,0 @@ -<?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\Piwik; -use Piwik\Plugins\SitesManager\API; -use Piwik\Tracker\Cache; -use Piwik\Updates; - -/** - */ -class Updates_0_2_34 extends Updates -{ - static function update() - { - // force regeneration of cache files following #648 - Piwik::setUserHasSuperUserAccess(); - $allSiteIds = API::getInstance()->getAllSitesId(); - Cache::regenerateCacheWebsiteAttributes($allSiteIds); - } -} diff --git a/core/Updates/0.6.2.php b/core/Updates/0.6.2.php deleted file mode 100644 index 0fd9651736c9e4a87cfc946f3560994e4bb98986..0000000000000000000000000000000000000000 --- a/core/Updates/0.6.2.php +++ /dev/null @@ -1,47 +0,0 @@ -<?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\Filesystem; -use Piwik\Piwik; -use Piwik\Plugins\SitesManager\API; -use Piwik\Tracker\Cache; -use Piwik\Updates; - -/** - */ -class Updates_0_6_2 extends Updates -{ - static function update() - { - $obsoleteFiles = array( - PIWIK_INCLUDE_PATH . '/core/Db/Mysqli.php', - ); - foreach ($obsoleteFiles as $obsoleteFile) { - if (file_exists($obsoleteFile)) { - @unlink($obsoleteFile); - } - } - - $obsoleteDirectories = array( - PIWIK_INCLUDE_PATH . '/core/Db/Pdo', - ); - foreach ($obsoleteDirectories as $dir) { - if (file_exists($dir)) { - Filesystem::unlinkRecursive($dir, true); - } - } - - // force regeneration of cache files - Piwik::setUserHasSuperUserAccess(); - $allSiteIds = API::getInstance()->getAllSitesId(); - Cache::regenerateCacheWebsiteAttributes($allSiteIds); - } -} diff --git a/core/Updates/2.0-a17.php b/core/Updates/2.0-a17.php index 2df452c1abed5fb9a173b798b757f4ace9c56f88..5d6514359629472cfafe197e11d502f28b695b75 100644 --- a/core/Updates/2.0-a17.php +++ b/core/Updates/2.0-a17.php @@ -35,7 +35,7 @@ class Updates_2_0_a17 extends Updates } } - if(!empty($errors)) { + if (!empty($errors)) { throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors)); } } diff --git a/core/Updates/2.0-b13.php b/core/Updates/2.0-b13.php index 78cc791566ada02b4b514b73b2138e51c9cd3b3b..a5e9546b9065247020d29258fa49899251572071 100644 --- a/core/Updates/2.0-b13.php +++ b/core/Updates/2.0-b13.php @@ -35,7 +35,7 @@ class Updates_2_0_b13 extends Updates } } - if(!empty($errors)) { + if (!empty($errors)) { throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors)); } } diff --git a/core/Updates/2.0.3-b7.php b/core/Updates/2.0.3-b7.php index f3fa6a34aac4288d9ff75eb461c413148607094c..add119e462cb16bcbe11f340ecc5bb73ae1033d1 100644 --- a/core/Updates/2.0.3-b7.php +++ b/core/Updates/2.0.3-b7.php @@ -56,7 +56,7 @@ class Updates_2_0_3_b7 extends Updates } } - if(!empty($errors)) { + if (!empty($errors)) { throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors)); } } diff --git a/core/Updates/2.1.1-b11.php b/core/Updates/2.1.1-b11.php index 45b781a1cf868e8c945b5cfbe9725493055622ae..3fa0364e73d4026a103945a96bade144de636050 100644 --- a/core/Updates/2.1.1-b11.php +++ b/core/Updates/2.1.1-b11.php @@ -41,17 +41,14 @@ class Updates_2_1_1_b11 extends Updates // returning visit segment foreach ($archiveNumericTables as $table) { // get archives w/ *._returning - $sql = "SELECT idarchive, idsite, period, date1, date2 - FROM $table - WHERE name IN ('" . implode("','", $returningMetrics) . "') - GROUP BY idarchive"; + $sql = "SELECT idarchive, idsite, period, date1, date2 FROM $table + WHERE name IN ('" . implode("','", $returningMetrics) . "') + GROUP BY idarchive"; $idArchivesWithReturning = Db::fetchAll($sql); // get archives for visitssummary returning visitor segment - $sql = "SELECT idarchive, idsite, period, date1, date2 - FROM $table - WHERE name = ? - GROUP BY idarchive"; + $sql = "SELECT idarchive, idsite, period, date1, date2 FROM $table + WHERE name = ? GROUP BY idarchive"; $visitSummaryReturningSegmentDone = Rules::getDoneFlagArchiveContainsOnePlugin( new Segment(VisitFrequencyApi::RETURNING_VISITOR_SEGMENT, $idSites = array()), 'VisitsSummary'); $idArchivesWithVisitReturningSegment = Db::fetchAll($sql, array($visitSummaryReturningSegmentDone)); diff --git a/core/Updates/2.4.0-b1.php b/core/Updates/2.4.0-b1.php index a0f6d64f65984f036ec2628d61af78d357305c29..cc0d615d54317f16e1a485ddbb6b2805b30a9c03 100644 --- a/core/Updates/2.4.0-b1.php +++ b/core/Updates/2.4.0-b1.php @@ -8,9 +8,6 @@ */ namespace Piwik\Updates; -use Faker\Provider\File; -use Piwik\Filesystem; -use Piwik\Plugins\Installation\ServerFilesGenerator; use Piwik\Updates; class Updates_2_4_0_b1 extends Updates diff --git a/core/Url.php b/core/Url.php index 32ea140f2079f3c3a1365ca3d6b313ba36b7e195..1ed77f5fa5033a9e38f449fffbad36d16f867b26 100644 --- a/core/Url.php +++ b/core/Url.php @@ -10,12 +10,7 @@ namespace Piwik; use Exception; -use Piwik\Config; -use Piwik\Common; -use Piwik\IP; -use Piwik\ProxyHttp; use Piwik\Session; -use Piwik\UrlHelper; /** * Provides URL related helper methods. @@ -232,18 +227,18 @@ class Url $trustedHosts = self::getTrustedHosts(); + // Only punctuation we allow is '[', ']', ':', '.', '_' and '-' + $hostLength = strlen($host); + if ($hostLength !== strcspn($host, '`~!@#$%^&*()+={}\\|;"\'<>,?/ ')) { + return false; + } + // if no trusted hosts, just assume it's valid if (empty($trustedHosts)) { self::saveTrustedHostnameInConfig($host); return true; } - // Only punctuation we allow is '[', ']', ':', '.' and '-' - $hostLength = strlen($host); - if ($hostLength !== strcspn($host, '`~!@#$%^&*()_+={}\\|;"\'<>,?/ ')) { - return false; - } - foreach ($trustedHosts as &$trustedHost) { $trustedHost = preg_quote($trustedHost); } @@ -346,7 +341,7 @@ class Url $hostHeaders = array(); $config = Config::getInstance()->General; - if(isset($config['proxy_host_headers'])) { + if (isset($config['proxy_host_headers'])) { $hostHeaders = $config['proxy_host_headers']; } @@ -473,6 +468,7 @@ class Url * Redirects the user to the specified URL. * * @param string $url + * @throws Exception * @api */ public static function redirectToUrl($url) @@ -490,7 +486,7 @@ class Url echo "Invalid URL to redirect to."; } - if(Common::isPhpCliMode()) { + if (Common::isPhpCliMode()) { throw new Exception("If you were using a browser, Piwik would redirect you to this URL: $url \n\n"); } exit; @@ -501,7 +497,7 @@ class Url */ public static function redirectToHttps() { - if(ProxyHttp::isHttps()) { + if (ProxyHttp::isHttps()) { return; } $url = self::getCurrentUrl(); diff --git a/core/Version.php b/core/Version.php index 94c6da9cb7e45801b001c6b56dd0c618fdc705b3..cf2377419e12ad06bb6015fae5e6276157d86a45 100644 --- a/core/Version.php +++ b/core/Version.php @@ -21,5 +21,5 @@ final class Version * The current Piwik version. * @var string */ - const VERSION = '2.8.0-b1'; + const VERSION = '2.9.0-b1'; } diff --git a/core/View.php b/core/View.php index bc02e4c7924a2ef15697461e2b6b5a5f3f020f5b..6200a2311307bf7ced719cc92418ae784a661ab6 100644 --- a/core/View.php +++ b/core/View.php @@ -9,12 +9,8 @@ namespace Piwik; use Exception; -use Piwik\Access; use Piwik\AssetManager\UIAssetCacheBuster; -use Piwik\Common; use Piwik\Plugins\UsersManager\API as APIUsersManager; -use Piwik\SettingsPiwik; -use Piwik\Version; use Piwik\View\ViewInterface; use Twig_Environment; diff --git a/core/View/ReportsByDimension.php b/core/View/ReportsByDimension.php index e1cb058459354af9a2bc2b61d0ed8ee86e8f6249..dbb59be930b26705b2c10b3141511b76595e5afa 100644 --- a/core/View/ReportsByDimension.php +++ b/core/View/ReportsByDimension.php @@ -104,10 +104,10 @@ class ReportsByDimension extends View // display it initially $categories = $this->dimensionCategories; if (!empty($categories)) { - $firstCategory = reset($categories); + $firstCategory = reset($categories); $firstReportInfo = reset($firstCategory); - $oldGet = $_GET; + $oldGet = $_GET; $oldPost = $_POST; foreach ($firstReportInfo['params'] as $key => $value) { @@ -120,7 +120,7 @@ class ReportsByDimension extends View $action = $firstReportInfo['params']['action']; $this->firstReport = FrontController::getInstance()->fetchDispatch($module, $action); - $_GET = $oldGet; + $_GET = $oldGet; $_POST = $oldPost; } diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php index 2bd748d3e4ff09fee2d4045851da21617652d645..1706e4b15f454b163c04f5c7c786f30fd3dc2663 100644 --- a/core/ViewDataTable/Config.php +++ b/core/ViewDataTable/Config.php @@ -598,7 +598,7 @@ class Config // don't add the related report if it references this report if ($this->controllerName == $module && $this->controllerAction == $action) { - if(empty($queryParams)) { + if (empty($queryParams)) { return; } } diff --git a/core/ViewDataTable/Manager.php b/core/ViewDataTable/Manager.php index 19e2335ce1d137b6840c72aac9e67e3e5d0c57d4..5d056506574d2f6fbee754a6f3e0ef6b45d9474c 100644 --- a/core/ViewDataTable/Manager.php +++ b/core/ViewDataTable/Manager.php @@ -156,46 +156,7 @@ class Manager { $result = array(); - // add normal view icons (eg, normal table, all columns, goals) - $normalViewIcons = array( - 'class' => 'tableAllColumnsSwitch', - 'buttons' => array(), - ); - - if ($view->config->show_table) { - $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable::ID); - } - - if ($view->config->show_table_all_columns) { - $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable\AllColumns::ID); - } - - if ($view->config->show_goals) { - $goalButton = static::getFooterIconFor(Goals::ID); - if (Common::getRequestVar('idGoal', false) == 'ecommerceOrder') { - $goalButton['icon'] = 'plugins/Morpheus/images/ecommerceOrder.gif'; - } - - $normalViewIcons['buttons'][] = $goalButton; - } - - if ($view->config->show_ecommerce) { - $normalViewIcons['buttons'][] = array( - 'id' => 'ecommerceOrder', - 'title' => Piwik::translate('General_EcommerceOrders'), - 'icon' => 'plugins/Morpheus/images/ecommerceOrder.gif', - 'text' => Piwik::translate('General_EcommerceOrders') - ); - - $normalViewIcons['buttons'][] = array( - 'id' => 'ecommerceAbandonedCart', - 'title' => Piwik::translate('General_AbandonedCarts'), - 'icon' => 'plugins/Morpheus/images/ecommerceAbandonedCart.gif', - 'text' => Piwik::translate('General_AbandonedCarts') - ); - } - - $normalViewIcons['buttons'] = array_filter($normalViewIcons['buttons']); + $normalViewIcons = self::getNormalViewIcons($view); if (!empty($normalViewIcons['buttons'])) { $result[] = $normalViewIcons; @@ -207,25 +168,7 @@ class Manager 'buttons' => array(), ); - // add graph views - $graphViewIcons = array( - 'class' => 'tableGraphViews tableGraphCollapsed', - 'buttons' => array(), - ); - - if ($view->config->show_all_views_icons) { - if ($view->config->show_bar_chart) { - $graphViewIcons['buttons'][] = static::getFooterIconFor(Bar::ID); - } - - if ($view->config->show_pie_chart) { - $graphViewIcons['buttons'][] = static::getFooterIconFor(Pie::ID); - } - - if ($view->config->show_tag_cloud) { - $graphViewIcons['buttons'][] = static::getFooterIconFor(Cloud::ID); - } - } + $graphViewIcons = self::getGraphViewIcons($view); $nonCoreVisualizations = static::getNonCoreViewDataTables(); @@ -331,4 +274,75 @@ class Manager { return sprintf('viewDataTableParameters_%s_%s', $login, $controllerAction); } + + private static function getNormalViewIcons(ViewDataTable $view) + { + // add normal view icons (eg, normal table, all columns, goals) + $normalViewIcons = array( + 'class' => 'tableAllColumnsSwitch', + 'buttons' => array(), + ); + + if ($view->config->show_table) { + $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable::ID); + } + + if ($view->config->show_table_all_columns) { + $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable\AllColumns::ID); + } + + if ($view->config->show_goals) { + $goalButton = static::getFooterIconFor(Goals::ID); + if (Common::getRequestVar('idGoal', false) == 'ecommerceOrder') { + $goalButton['icon'] = 'plugins/Morpheus/images/ecommerceOrder.gif'; + } + + $normalViewIcons['buttons'][] = $goalButton; + } + + if ($view->config->show_ecommerce) { + $normalViewIcons['buttons'][] = array( + 'id' => 'ecommerceOrder', + 'title' => Piwik::translate('General_EcommerceOrders'), + 'icon' => 'plugins/Morpheus/images/ecommerceOrder.gif', + 'text' => Piwik::translate('General_EcommerceOrders') + ); + + $normalViewIcons['buttons'][] = array( + 'id' => 'ecommerceAbandonedCart', + 'title' => Piwik::translate('General_AbandonedCarts'), + 'icon' => 'plugins/Morpheus/images/ecommerceAbandonedCart.gif', + 'text' => Piwik::translate('General_AbandonedCarts') + ); + } + + $normalViewIcons['buttons'] = array_filter($normalViewIcons['buttons']); + + return $normalViewIcons; + } + + private static function getGraphViewIcons(ViewDataTable $view) + { + // add graph views + $graphViewIcons = array( + 'class' => 'tableGraphViews tableGraphCollapsed', + 'buttons' => array(), + ); + + if ($view->config->show_all_views_icons) { + if ($view->config->show_bar_chart) { + $graphViewIcons['buttons'][] = static::getFooterIconFor(Bar::ID); + } + + if ($view->config->show_pie_chart) { + $graphViewIcons['buttons'][] = static::getFooterIconFor(Pie::ID); + } + + if ($view->config->show_tag_cloud) { + $graphViewIcons['buttons'][] = static::getFooterIconFor(Cloud::ID); + } + } + + return $graphViewIcons; + } } diff --git a/core/ViewDataTable/Request.php b/core/ViewDataTable/Request.php index 94938899443aab7eefa2bf2589eecc5d2ea0be7b..352ce25899cac6ef39eb10f8212e62cc4b825b0d 100644 --- a/core/ViewDataTable/Request.php +++ b/core/ViewDataTable/Request.php @@ -119,8 +119,8 @@ class Request if (isset($_GET[$nameVar])) { return Common::sanitizeInputValue($_GET[$nameVar]); } - $default = $this->getDefault($nameVar); - return $default; + + return $this->getDefault($nameVar); } /** diff --git a/core/Visualization/Sparkline.php b/core/Visualization/Sparkline.php index 8a7b4063423c7cb4f7fc0f9ea1a3d6dd3c690a9c..642c1325cb00755799ef0f2fd429f7bf1ecc88b7 100644 --- a/core/Visualization/Sparkline.php +++ b/core/Visualization/Sparkline.php @@ -32,6 +32,7 @@ class Sparkline implements ViewInterface public static $enableSparklineImages = true; private static $colorNames = array('backgroundColor', 'lineColor', 'minPointColor', 'lastPointColor', 'maxPointColor'); + private $values = array(); /** * Width of the sparkline @@ -60,7 +61,6 @@ class Sparkline implements ViewInterface */ public function setHeight($height) { - if (!is_numeric($height) || $height <= 0) { return; } @@ -74,7 +74,6 @@ class Sparkline implements ViewInterface */ public function setWidth($width) { - if (!is_numeric($width) || $width <= 0) { return; } @@ -110,7 +109,9 @@ class Sparkline implements ViewInterface $min = $max = $last = null; $i = 0; - $toRemove = array('%', str_replace('%s', '', Piwik::translate('General_Seconds'))); + $seconds = Piwik::translate('General_Seconds'); + $toRemove = array('%', str_replace('%s', '', $seconds)); + foreach ($this->values as $value) { // 50% and 50s should be plotted as 50 $value = str_replace($toRemove, '', $value); @@ -158,6 +159,7 @@ class Sparkline implements ViewInterface private function setSparklineColors($sparkline) { $colors = Common::getRequestVar('colors', false, 'json'); + if (empty($colors)) { // quick fix so row evolution sparklines will have color in widgetize's iframes $colors = array( 'backgroundColor' => '#ffffff', diff --git a/core/dispatch.php b/core/dispatch.php index f18b73f0bde174b4244e5ef589cbda23c737bb14..133f26f3550b5549f71e3fa162cb79e4b10e2a99 100644 --- a/core/dispatch.php +++ b/core/dispatch.php @@ -24,7 +24,7 @@ if (!defined('PIWIK_ENABLE_ERROR_HANDLER') || PIWIK_ENABLE_ERROR_HANDLER) { FrontController::setUpSafeMode(); -if(!defined('PIWIK_ENABLE_DISPATCH')) { +if (!defined('PIWIK_ENABLE_DISPATCH')) { define('PIWIK_ENABLE_DISPATCH', true); } @@ -33,7 +33,7 @@ if (PIWIK_ENABLE_DISPATCH) { $controller->init(); $response = $controller->dispatch(); - if(is_array($response)) { + if (is_array($response)) { var_export($response); } elseif (!is_null($response)) { echo $response; diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php index bce404c9e6240a72290a99180e3d44481bce7fcd..3dd3defd3e0691e195e54668c47f897437243447 100644 --- a/core/testMinimumPhpVersion.php +++ b/core/testMinimumPhpVersion.php @@ -129,7 +129,7 @@ if (!function_exists('Piwik_ExitWithMessage')) { $message = str_replace("\t", "", $message); $message = strip_tags($message); - if($isCli) { + if ($isCli) { echo $message; } else { echo $headerPage . $content . $footerPage; diff --git a/libs/Archive_Tar/Tar.php b/libs/Archive_Tar/Tar.php deleted file mode 100755 index 167a485b50f3bce74034d085b911612bf5479c42..0000000000000000000000000000000000000000 --- a/libs/Archive_Tar/Tar.php +++ /dev/null @@ -1,1993 +0,0 @@ -<?php -/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ - -/** - * File::CSV - * - * PHP versions 4 and 5 - * - * Copyright (c) 1997-2008, - * Vincent Blavet <vincent@phpconcept.net> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @category File_Formats - * @package Archive_Tar - * @author Vincent Blavet <vincent@phpconcept.net> - * @copyright 1997-2010 The Authors - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/Archive_Tar - */ - -require_once __DIR__ . "/../PEAR.php"; - -define('ARCHIVE_TAR_ATT_SEPARATOR', 90001); -define('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); - -/** -* Creates a (compressed) Tar archive -* -* @package Archive_Tar -* @author Vincent Blavet <vincent@phpconcept.net> -* @license http://www.opensource.org/licenses/bsd-license.php New BSD License -* @version $Revision$ -*/ -class Archive_Tar extends PEAR -{ - /** - * @var string Name of the Tar - */ - var $_tarname=''; - - /** - * @var boolean if true, the Tar file will be gzipped - */ - var $_compress=false; - - /** - * @var string Type of compression : 'none', 'gz' or 'bz2' - */ - var $_compress_type='none'; - - /** - * @var string Explode separator - */ - var $_separator=' '; - - /** - * @var file descriptor - */ - var $_file=0; - - /** - * @var string Local Tar name of a remote Tar (http:// or ftp://) - */ - var $_temp_tarname=''; - - /** - * @var string regular expression for ignoring files or directories - */ - var $_ignore_regexp=''; - - /** - * @var object PEAR_Error object - */ - var $error_object=null; - - // {{{ constructor - /** - * Archive_Tar Class constructor. This flavour of the constructor only - * declare a new Archive_Tar object, identifying it by the name of the - * tar file. - * If the compress argument is set the tar will be read or created as a - * gzip or bz2 compressed TAR file. - * - * @param string $p_tarname The name of the tar archive to create - * @param string $p_compress can be null, 'gz' or 'bz2'. This - * parameter indicates if gzip or bz2 compression - * is required. For compatibility reason the - * boolean value 'true' means 'gz'. - * - * @access public - */ - function Archive_Tar($p_tarname, $p_compress = null) - { - $this->PEAR(); - $this->_compress = false; - $this->_compress_type = 'none'; - if (($p_compress === null) || ($p_compress == '')) { - if (@file_exists($p_tarname)) { - if ($fp = @fopen($p_tarname, "rb")) { - // look for gzip magic cookie - $data = fread($fp, 2); - fclose($fp); - if ($data == "\37\213") { - $this->_compress = true; - $this->_compress_type = 'gz'; - // No sure it's enought for a magic code .... - } elseif ($data == "BZ") { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } - } - } else { - // probably a remote file or some file accessible - // through a stream interface - if (substr($p_tarname, -2) == 'gz') { - $this->_compress = true; - $this->_compress_type = 'gz'; - } elseif ((substr($p_tarname, -3) == 'bz2') || - (substr($p_tarname, -2) == 'bz')) { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } - } - } else { - if (($p_compress === true) || ($p_compress == 'gz')) { - $this->_compress = true; - $this->_compress_type = 'gz'; - } else if ($p_compress == 'bz2') { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } else { - $this->_error("Unsupported compression type '$p_compress'\n". - "Supported types are 'gz' and 'bz2'.\n"); - return false; - } - } - $this->_tarname = $p_tarname; - if ($this->_compress) { // assert zlib or bz2 extension support - if ($this->_compress_type == 'gz') - $extname = 'zlib'; - else if ($this->_compress_type == 'bz2') - $extname = 'bz2'; - - if (!extension_loaded($extname)) { - PEAR::loadExtension($extname); - } - if (!extension_loaded($extname)) { - $this->_error("The extension '$extname' couldn't be found.\n". - "Please make sure your version of PHP was built ". - "with '$extname' support.\n"); - return false; - } - } - } - // }}} - - // {{{ destructor - function _Archive_Tar() - { - $this->_close(); - // ----- Look for a local copy to delete - if ($this->_temp_tarname != '') - @unlink($this->_temp_tarname); - $this->_PEAR(); - } - // }}} - - // {{{ create() - /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If a file with the same name exist and is writable, it is replaced - * by the new tar. - * The method return false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * For each directory added in the archive, the files and - * sub-directories are also added. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function create($p_filelist) - { - return $this->createModify($p_filelist, '', ''); - } - // }}} - - // {{{ add() - /** - * This method add the files / directories that are listed in $p_filelist in - * the archive. If the archive does not exist it is created. - * The method return false and a PEAR error text. - * The files and directories listed are only added at the end of the archive, - * even if a file with the same name is already archived. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function add($p_filelist) - { - return $this->addModify($p_filelist, '', ''); - } - // }}} - - // {{{ extract() - function extract($p_path='', $p_preserve=false) - { - return $this->extractModify($p_path, '', $p_preserve); - } - // }}} - - // {{{ listContent() - function listContent() - { - $v_list_detail = array(); - - if ($this->_openRead()) { - if (!$this->_extractList('', $v_list_detail, "list", '', '')) { - unset($v_list_detail); - $v_list_detail = 0; - } - $this->_close(); - } - - return $v_list_detail; - } - // }}} - - // {{{ createModify() - /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If the file already exists and is writable, it is replaced by the - * new tar. It is a create and not an add. If the file exists and is - * read-only or is a directory it is not replaced. The method return - * false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * See also addModify() method for file adding properties. - * - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated by - * a single blank space. - * @param string $p_add_dir A string which contains a path to be added - * to the memorized path of each element in - * the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of each - * element in the list, when relevant. - * - * @return boolean true on success, false on error. - * @access public - * @see addModify() - */ - function createModify($p_filelist, $p_add_dir, $p_remove_dir='') - { - $v_result = true; - - if (!$this->_openWrite()) - return false; - - if ($p_filelist != '') { - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_cleanFile(); - $this->_error('Invalid file list'); - return false; - } - - $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); - } - - if ($v_result) { - $this->_writeFooter(); - $this->_close(); - } else - $this->_cleanFile(); - - return $v_result; - } - // }}} - - // {{{ addModify() - /** - * This method add the files / directories listed in $p_filelist at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * If a file/dir is already in the archive it will only be added at the - * end of the archive. There is no update of the existing archived - * file/dir. However while extracting the archive, the last file will - * replace the first one. This results in a none optimization of the - * archive size. - * If a file/dir does not exist the file/dir is ignored. However an - * error text is send to PEAR error. - * If a file/dir is not readable the file/dir is ignored. However an - * error text is send to PEAR error. - * - * @param array $p_filelist An array of filenames and directory - * names, or a single string with names - * separated by a single blank space. - * @param string $p_add_dir A string which contains a path to be - * added to the memorized path of each - * element in the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of - * each element in the list, when - * relevant. - * - * @return true on success, false on error. - * @access public - */ - function addModify($p_filelist, $p_add_dir, $p_remove_dir='') - { - $v_result = true; - - if (!$this->_isArchive()) - $v_result = $this->createModify($p_filelist, $p_add_dir, - $p_remove_dir); - else { - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_error('Invalid file list'); - return false; - } - - $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); - } - - return $v_result; - } - // }}} - - // {{{ addString() - /** - * This method add a single string as a file at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * - * @param string $p_filename A string which contains the full - * filename path that will be associated - * with the string. - * @param string $p_string The content of the file added in - * the archive. - * @param int $p_datetime A custom date/time (unix timestamp) - * for the file (optional). - * - * @return true on success, false on error. - * @access public - */ - function addString($p_filename, $p_string, $p_datetime = false) - { - $v_result = true; - - if (!$this->_isArchive()) { - if (!$this->_openWrite()) { - return false; - } - $this->_close(); - } - - if (!$this->_openAppend()) - return false; - - // Need to check the get back to the temporary file ? .... - $v_result = $this->_addString($p_filename, $p_string, $p_datetime); - - $this->_writeFooter(); - - $this->_close(); - - return $v_result; - } - // }}} - - // {{{ extractModify() - /** - * This method extract all the content of the archive in the directory - * indicated by $p_path. When relevant the memorized path of the - * files/dir can be modified by removing the $p_remove_path path at the - * beginning of the file/dir path. - * While extracting a file, if the directory path does not exists it is - * created. - * While extracting a file, if the file already exists it is replaced - * without looking for last modification date. - * While extracting a file, if the file already exists and is write - * protected, the extraction is aborted. - * While extracting a file, if a directory with the same name already - * exists, the extraction is aborted. - * While extracting a directory, if a file with the same name already - * exists, the extraction is aborted. - * While extracting a file/directory if the destination directory exist - * and is write protected, or does not exist but can not be created, - * the extraction is aborted. - * If after extraction an extracted file does not show the correct - * stored file size, the extraction is aborted. - * When the extraction is aborted, a PEAR error text is set and false - * is returned. However the result can be a partial extraction that may - * need to be manually cleaned. - * - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @param boolean $p_preserve Preserve user/group ownership of files - * - * @return boolean true on success, false on error. - * @access public - * @see extractList() - */ - function extractModify($p_path, $p_remove_path, $p_preserve=false) - { - $v_result = true; - $v_list_detail = array(); - - if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, - "complete", 0, $p_remove_path, $p_preserve); - $this->_close(); - } - - return $v_result; - } - // }}} - - // {{{ extractInString() - /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or NULL on error. - * - * @param string $p_filename The path of the file to extract in a string. - * - * @return a string with the file content or NULL. - * @access public - */ - function extractInString($p_filename) - { - if ($this->_openRead()) { - $v_result = $this->_extractInString($p_filename); - $this->_close(); - } else { - $v_result = null; - } - - return $v_result; - } - // }}} - - // {{{ extractList() - /** - * This method extract from the archive only the files indicated in the - * $p_filelist. These files are extracted in the current directory or - * in the directory indicated by the optional $p_path parameter. - * If indicated the $p_remove_path can be used in the same way as it is - * used in extractModify() method. - * - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated - * by a single blank space. - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @param boolean $p_preserve Preserve user/group ownership of files - * - * @return true on success, false on error. - * @access public - * @see extractModify() - */ - function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false) - { - $v_result = true; - $v_list_detail = array(); - - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_error('Invalid string list'); - return false; - } - - if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, "partial", - $v_list, $p_remove_path, $p_preserve); - $this->_close(); - } - - return $v_result; - } - // }}} - - // {{{ setAttribute() - /** - * This method set specific attributes of the archive. It uses a variable - * list of parameters, in the format attribute code + attribute values : - * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); - * - * @param mixed $argv variable list of attributes and values - * - * @return true on success, false on error. - * @access public - */ - function setAttribute() - { - $v_result = true; - - // ----- Get the number of variable list of arguments - if (($v_size = func_num_args()) == 0) { - return true; - } - - // ----- Get the arguments - $v_att_list = &func_get_args(); - - // ----- Read the attributes - $i=0; - while ($i<$v_size) { - - // ----- Look for next option - switch ($v_att_list[$i]) { - // ----- Look for options that request a string value - case ARCHIVE_TAR_ATT_SEPARATOR : - // ----- Check the number of parameters - if (($i+1) >= $v_size) { - $this->_error('Invalid number of parameters for ' - .'attribute ARCHIVE_TAR_ATT_SEPARATOR'); - return false; - } - - // ----- Get the value - $this->_separator = $v_att_list[$i+1]; - $i++; - break; - - default : - $this->_error('Unknow attribute code '.$v_att_list[$i].''); - return false; - } - - // ----- Next attribute - $i++; - } - - return $v_result; - } - // }}} - - // {{{ setIgnoreRegexp() - /** - * This method sets the regular expression for ignoring files and directories - * at import, for example: - * $arch->setIgnoreRegexp("#CVS|\.svn#"); - * - * @param string $regexp regular expression defining which files or directories to ignore - * - * @access public - */ - function setIgnoreRegexp($regexp) - { - $this->_ignore_regexp = $regexp; - } - // }}} - - // {{{ setIgnoreList() - /** - * This method sets the regular expression for ignoring all files and directories - * matching the filenames in the array list at import, for example: - * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); - * - * @param array $list a list of file or directory names to ignore - * - * @access public - */ - function setIgnoreList($list) - { - $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); - $regexp = '#/'.join('$|/', $list).'#'; - $this->setIgnoreRegexp($regexp); - } - // }}} - - // {{{ _error() - function _error($p_message) - { - $this->error_object = &$this->raiseError($p_message); - } - // }}} - - // {{{ _warning() - function _warning($p_message) - { - $this->error_object = &$this->raiseError($p_message); - } - // }}} - - // {{{ _isArchive() - function _isArchive($p_filename=null) - { - if ($p_filename == null) { - $p_filename = $this->_tarname; - } - clearstatcache(); - return @is_file($p_filename) && !@is_link($p_filename); - } - // }}} - - // {{{ _openWrite() - function _openWrite() - { - if ($this->_compress_type == 'gz' && function_exists('gzopen')) - $this->_file = @gzopen($this->_tarname, "wb9"); - else if ($this->_compress_type == 'bz2' && function_exists('bzopen')) - $this->_file = @bzopen($this->_tarname, "w"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "wb"); - else { - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - return false; - } - - if ($this->_file == 0) { - $this->_error('Unable to open in write mode \'' - .$this->_tarname.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _openRead() - function _openRead() - { - if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { - - // ----- Look if a local copy need to be done - if ($this->_temp_tarname == '') { - $this->_temp_tarname = uniqid('tar').'.tmp'; - if (!$v_file_from = @fopen($this->_tarname, 'rb')) { - $this->_error('Unable to open in read mode \'' - .$this->_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { - $this->_error('Unable to open in write mode \'' - .$this->_temp_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - while ($v_data = @fread($v_file_from, 1024)) - @fwrite($v_file_to, $v_data); - @fclose($v_file_from); - @fclose($v_file_to); - } - - // ----- File to open if the local copy - $v_filename = $this->_temp_tarname; - - } else - // ----- File to open if the normal Tar file - $v_filename = $this->_tarname; - - if ($this->_compress_type == 'gz' && function_exists('gzopen')) - $this->_file = @gzopen($v_filename, "rb"); - else if ($this->_compress_type == 'bz2' && function_exists('bzopen')) - $this->_file = @bzopen($v_filename, "r"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($v_filename, "rb"); - else { - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - return false; - } - - if ($this->_file == 0) { - $this->_error('Unable to open in read mode \''.$v_filename.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _openReadWrite() - function _openReadWrite() - { - if ($this->_compress_type == 'gz') - $this->_file = @gzopen($this->_tarname, "r+b"); - else if ($this->_compress_type == 'bz2') { - $this->_error('Unable to open bz2 in read/write mode \'' - .$this->_tarname.'\' (limitation of bz2 extension)'); - return false; - } else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "r+b"); - else { - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - return false; - } - - if ($this->_file == 0) { - $this->_error('Unable to open in read/write mode \'' - .$this->_tarname.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _close() - function _close() - { - //if (isset($this->_file)) { - if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') - @gzclose($this->_file); - else if ($this->_compress_type == 'bz2') - @bzclose($this->_file); - else if ($this->_compress_type == 'none') - @fclose($this->_file); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - $this->_file = 0; - } - - // ----- Look if a local copy need to be erase - // Note that it might be interesting to keep the url for a time : ToDo - if ($this->_temp_tarname != '') { - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } - - return true; - } - // }}} - - // {{{ _cleanFile() - function _cleanFile() - { - $this->_close(); - - // ----- Look for a local copy - if ($this->_temp_tarname != '') { - // ----- Remove the local copy but not the remote tarname - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } else { - // ----- Remove the local tarname file - @unlink($this->_tarname); - } - $this->_tarname = ''; - - return true; - } - // }}} - - // {{{ _writeBlock() - function _writeBlock($p_binary_data, $p_len=null) - { - if (is_resource($this->_file)) { - if ($p_len === null) { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } else { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data, $p_len); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - } - return true; - } - // }}} - - // {{{ _readBlock() - function _readBlock() - { - $v_block = null; - if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') - $v_block = @gzread($this->_file, 512); - else if ($this->_compress_type == 'bz2') - $v_block = @bzread($this->_file, 512); - else if ($this->_compress_type == 'none') - $v_block = @fread($this->_file, 512); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } - return $v_block; - } - // }}} - - // {{{ _jumpBlock() - function _jumpBlock($p_len=null) - { - if (is_resource($this->_file)) { - if ($p_len === null) - $p_len = 1; - - if ($this->_compress_type == 'gz') { - @gzseek($this->_file, gztell($this->_file)+($p_len*512)); - } - else if ($this->_compress_type == 'bz2') { - // ----- Replace missing bztell() and bzseek() - for ($i=0; $i<$p_len; $i++) - $this->_readBlock(); - } else if ($this->_compress_type == 'none') - @fseek($this->_file, $p_len*512, SEEK_CUR); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - return true; - } - // }}} - - // {{{ _writeFooter() - function _writeFooter() - { - if (is_resource($this->_file)) { - // ----- Write the last 0 filled block for end of archive - $v_binary_data = pack('a1024', ''); - $this->_writeBlock($v_binary_data); - } - return true; - } - // }}} - - // {{{ _addList() - function _addList($p_list, $p_add_dir, $p_remove_dir) - { - $v_result=true; - $v_header = array(); - - // ----- Remove potential windows directory separator - $p_add_dir = $this->_translateWinPath($p_add_dir); - $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); - - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if (sizeof($p_list) == 0) - return true; - - foreach ($p_list as $v_filename) { - if (!$v_result) { - break; - } - - // ----- Skip the current tar name - if ($v_filename == $this->_tarname) - continue; - - if ($v_filename == '') - continue; - - // ----- ignore files and directories matching the ignore regular expression - if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) { - $this->_warning("File '$v_filename' ignored"); - continue; - } - - if (!file_exists($v_filename) && !is_link($v_filename)) { - $this->_warning("File '$v_filename' does not exist"); - continue; - } - - // ----- Add the file or directory header - if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) - return false; - - if (@is_dir($v_filename) && !@is_link($v_filename)) { - if (!($p_hdir = opendir($v_filename))) { - $this->_warning("Directory '$v_filename' can not be read"); - continue; - } - while (false !== ($p_hitem = readdir($p_hdir))) { - if (($p_hitem != '.') && ($p_hitem != '..')) { - if ($v_filename != ".") - $p_temp_list[0] = $v_filename.'/'.$p_hitem; - else - $p_temp_list[0] = $p_hitem; - - $v_result = $this->_addList($p_temp_list, - $p_add_dir, - $p_remove_dir); - } - } - - unset($p_temp_list); - unset($p_hdir); - unset($p_hitem); - } - } - - return $v_result; - } - // }}} - - // {{{ _addFile() - function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir) - { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - $v_stored_filename = $p_filename; - if (strcmp($p_filename, $p_remove_dir) == 0) { - return true; - } - if ($p_remove_dir != '') { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= '/'; - - if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) - $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); - } - $v_stored_filename = $this->_translateWinPath($v_stored_filename); - if ($p_add_dir != '') { - if (substr($p_add_dir, -1) == '/') - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir.'/'.$v_stored_filename; - } - - $v_stored_filename = $this->_pathReduction($v_stored_filename); - - if ($this->_isArchive($p_filename)) { - if (($v_file = @fopen($p_filename, "rb")) == 0) { - $this->_warning("Unable to open file '".$p_filename - ."' in binary read mode"); - return true; - } - - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - - while (($v_buffer = fread($v_file, 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); - $this->_writeBlock($v_binary_data); - } - - fclose($v_file); - - } else { - // ----- Only header for dir - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - } - - return true; - } - // }}} - - // {{{ _addString() - function _addString($p_filename, $p_string, $p_datetime = false) - { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - - // ----- If datetime is not specified, set current time - if ($p_datetime === false) { - $p_datetime = time(); - } - - if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), - $p_datetime, 384, "", 0, 0)) - return false; - - $i=0; - while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') { - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - return true; - } - // }}} - - // {{{ _writeHeader() - function _writeHeader($p_filename, $p_stored_filename) - { - if ($p_stored_filename == '') - $p_stored_filename = $p_filename; - $v_reduce_filename = $this->_pathReduction($p_stored_filename); - - if (strlen($v_reduce_filename) > 99) { - if (!$this->_writeLongHeader($v_reduce_filename)) - return false; - } - - $v_info = lstat($p_filename); - $v_uid = sprintf("%07s", DecOct($v_info[4])); - $v_gid = sprintf("%07s", DecOct($v_info[5])); - $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); - - $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); - - $v_linkname = ''; - - if (@is_link($p_filename)) { - $v_typeflag = '2'; - $v_linkname = readlink($p_filename); - $v_size = sprintf("%011s", DecOct(0)); - } elseif (@is_dir($p_filename)) { - $v_typeflag = "5"; - $v_size = sprintf("%011s", DecOct(0)); - } else { - $v_typeflag = '0'; - clearstatcache(); - $v_size = sprintf("%011s", DecOct($v_info['size'])); - } - - $v_magic = 'ustar '; - - $v_version = ' '; - - if (function_exists('posix_getpwuid')) - { - $userinfo = posix_getpwuid($v_info[4]); - $groupinfo = posix_getgrgid($v_info[5]); - - $v_uname = $userinfo['name']; - $v_gname = $groupinfo['name']; - } - else - { - $v_uname = ''; - $v_gname = ''; - } - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12a12", - $v_reduce_filename, $v_perms, $v_uid, - $v_gid, $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - return true; - } - // }}} - - // {{{ _writeHeaderBlock() - function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, - $p_type='', $p_uid=0, $p_gid=0) - { - $p_filename = $this->_pathReduction($p_filename); - - if (strlen($p_filename) > 99) { - if (!$this->_writeLongHeader($p_filename)) - return false; - } - - if ($p_type == "5") { - $v_size = sprintf("%011s", DecOct(0)); - } else { - $v_size = sprintf("%011s", DecOct($p_size)); - } - - $v_uid = sprintf("%07s", DecOct($p_uid)); - $v_gid = sprintf("%07s", DecOct($p_gid)); - $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); - - $v_mtime = sprintf("%11s", DecOct($p_mtime)); - - $v_linkname = ''; - - $v_magic = 'ustar '; - - $v_version = ' '; - - if (function_exists('posix_getpwuid')) - { - $userinfo = posix_getpwuid($p_uid); - $groupinfo = posix_getgrgid($p_gid); - - $v_uname = $userinfo['name']; - $v_gname = $groupinfo['name']; - } - else - { - $v_uname = ''; - $v_gname = ''; - } - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12A12", - $p_filename, $v_perms, $v_uid, $v_gid, - $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $p_type, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - return true; - } - // }}} - - // {{{ _writeLongHeader() - function _writeLongHeader($p_filename) - { - $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); - - $v_typeflag = 'L'; - - $v_linkname = ''; - - $v_magic = ''; - - $v_version = ''; - - $v_uname = ''; - - $v_gname = ''; - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12a12", - '././@LongLink', 0, 0, 0, $v_size, 0); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - // ----- Write the filename as content of the block - $i=0; - while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); - $this->_writeBlock($v_binary_data); - } - - return true; - } - // }}} - - // {{{ _readHeader() - function _readHeader($v_binary_data, &$v_header) - { - if (strlen($v_binary_data)==0) { - $v_header['filename'] = ''; - return true; - } - - if (strlen($v_binary_data) != 512) { - $v_header['filename'] = ''; - $this->_error('Invalid block size : '.strlen($v_binary_data)); - return false; - } - - if (!is_array($v_header)) { - $v_header = array(); - } - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156; $i<512; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - - if (version_compare(PHP_VERSION,"5.5.0-dev")<0) { - $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . - "a8checksum/a1typeflag/a100link/a6magic/a2version/" . - "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; - } else { - $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . - "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . - "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; - } - $v_data = unpack($fmt, $v_binary_data); - - if (strlen($v_data["prefix"]) > 0) { - $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; - } - - // ----- Extract the checksum - $v_header['checksum'] = OctDec(trim($v_data['checksum'])); - if ($v_header['checksum'] != $v_checksum) { - $v_header['filename'] = ''; - - // ----- Look for last block (empty block) - if (($v_checksum == 256) && ($v_header['checksum'] == 0)) - return true; - - $this->_error('Invalid checksum for file "'.$v_data['filename'] - .'" : '.$v_checksum.' calculated, ' - .$v_header['checksum'].' expected'); - return false; - } - - // ----- Extract the properties - $v_header['filename'] = $v_data['filename']; - if ($this->_maliciousFilename($v_header['filename'])) { - $this->_error('Malicious .tar detected, file "' . $v_header['filename'] . - '" will not install in desired directory tree'); - return false; - } - $v_header['mode'] = OctDec(trim($v_data['mode'])); - $v_header['uid'] = OctDec(trim($v_data['uid'])); - $v_header['gid'] = OctDec(trim($v_data['gid'])); - $v_header['size'] = OctDec(trim($v_data['size'])); - $v_header['mtime'] = OctDec(trim($v_data['mtime'])); - if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { - $v_header['size'] = 0; - } - $v_header['link'] = trim($v_data['link']); - /* ----- All these fields are removed form the header because - they do not carry interesting info - $v_header[magic] = trim($v_data[magic]); - $v_header[version] = trim($v_data[version]); - $v_header[uname] = trim($v_data[uname]); - $v_header[gname] = trim($v_data[gname]); - $v_header[devmajor] = trim($v_data[devmajor]); - $v_header[devminor] = trim($v_data[devminor]); - */ - - return true; - } - // }}} - - // {{{ _maliciousFilename() - /** - * Detect and report a malicious file name - * - * @param string $file - * - * @return bool - * @access private - */ - function _maliciousFilename($file) - { - if (strpos($file, '/../') !== false) { - return true; - } - if (strpos($file, '../') === 0) { - return true; - } - return false; - } - // }}} - - // {{{ _readLongHeader() - function _readLongHeader(&$v_header) - { - $v_filename = ''; - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_filename .= trim($v_content); - } - - // ----- Read the next header - $v_binary_data = $this->_readBlock(); - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - $v_filename = trim($v_filename); - $v_header['filename'] = $v_filename; - if ($this->_maliciousFilename($v_filename)) { - $this->_error('Malicious .tar detected, file "' . $v_filename . - '" will not install in desired directory tree'); - return false; - } - - return true; - } - // }}} - - // {{{ _extractInString() - /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or null on error. - * - * @param string $p_filename The path of the file to extract in a string. - * - * @return a string with the file content or null. - * @access private - */ - function _extractInString($p_filename) - { - $v_result_str = ""; - - While (strlen($v_binary_data = $this->_readBlock()) != 0) - { - if (!$this->_readHeader($v_binary_data, $v_header)) - return null; - - if ($v_header['filename'] == '') - continue; - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return null; - } - - if ($v_header['filename'] == $p_filename) { - if ($v_header['typeflag'] == "5") { - $this->_error('Unable to extract in string a directory ' - .'entry {'.$v_header['filename'].'}'); - return null; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_result_str .= $this->_readBlock(); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_result_str .= substr($v_content, 0, - ($v_header['size'] % 512)); - } - return $v_result_str; - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - } - - return null; - } - // }}} - - // {{{ _extractList() - function _extractList($p_path, &$p_list_detail, $p_mode, - $p_file_list, $p_remove_path, $p_preserve=false) - { - $v_result=true; - $v_nb = 0; - $v_extract_all = true; - $v_listing = false; - - $p_path = $this->_translateWinPath($p_path, false); - if ($p_path == '' || (substr($p_path, 0, 1) != '/' - && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { - $p_path = "./".$p_path; - } - $p_remove_path = $this->_translateWinPath($p_remove_path); - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) - $p_remove_path .= '/'; - $p_remove_path_size = strlen($p_remove_path); - - switch ($p_mode) { - case "complete" : - $v_extract_all = true; - $v_listing = false; - break; - case "partial" : - $v_extract_all = false; - $v_listing = false; - break; - case "list" : - $v_extract_all = false; - $v_listing = true; - break; - default : - $this->_error('Invalid extract mode ('.$p_mode.')'); - return false; - } - - clearstatcache(); - - while (strlen($v_binary_data = $this->_readBlock()) != 0) - { - $v_extract_file = FALSE; - $v_extraction_stopped = 0; - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - if ($v_header['filename'] == '') { - continue; - } - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return false; - } - - if ((!$v_extract_all) && (is_array($p_file_list))) { - // ----- By default no unzip if the file is not found - $v_extract_file = false; - - for ($i=0; $i<sizeof($p_file_list); $i++) { - // ----- Look if it is a directory - if (substr($p_file_list[$i], -1) == '/') { - // ----- Look if the directory is in the filename path - if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) - && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) - == $p_file_list[$i])) { - $v_extract_file = true; - break; - } - } - - // ----- It is a file, so compare the file names - elseif ($p_file_list[$i] == $v_header['filename']) { - $v_extract_file = true; - break; - } - } - } else { - $v_extract_file = true; - } - - // ----- Look if this file need to be extracted - if (($v_extract_file) && (!$v_listing)) - { - if (($p_remove_path != '') - && (substr($v_header['filename'].'/', 0, $p_remove_path_size) - == $p_remove_path)) { - $v_header['filename'] = substr($v_header['filename'], - $p_remove_path_size); - if( $v_header['filename'] == '' ){ - continue; - } - } - if (($p_path != './') && ($p_path != '/')) { - while (substr($p_path, -1) == '/') - $p_path = substr($p_path, 0, strlen($p_path)-1); - - if (substr($v_header['filename'], 0, 1) == '/') - $v_header['filename'] = $p_path.$v_header['filename']; - else - $v_header['filename'] = $p_path.'/'.$v_header['filename']; - } - if (file_exists($v_header['filename'])) { - if ( (@is_dir($v_header['filename'])) - && ($v_header['typeflag'] == '')) { - $this->_error('File '.$v_header['filename'] - .' already exists as a directory'); - return false; - } - if ( ($this->_isArchive($v_header['filename'])) - && ($v_header['typeflag'] == "5")) { - $this->_error('Directory '.$v_header['filename'] - .' already exists as a file'); - return false; - } - if (!is_writeable($v_header['filename'])) { - $this->_error('File '.$v_header['filename'] - .' already exists and is write protected'); - return false; - } - if (filemtime($v_header['filename']) > $v_header['mtime']) { - // To be completed : An error or silent no replace ? - } - } - - // ----- Check the directory availability and create it if necessary - elseif (($v_result - = $this->_dirCheck(($v_header['typeflag'] == "5" - ?$v_header['filename'] - :dirname($v_header['filename'])))) != 1) { - $this->_error('Unable to create path for '.$v_header['filename']); - return false; - } - - if ($v_extract_file) { - if ($v_header['typeflag'] == "5") { - if (!@file_exists($v_header['filename'])) { - if (!@mkdir($v_header['filename'], 0777)) { - $this->_error('Unable to create directory {' - .$v_header['filename'].'}'); - return false; - } - } - } elseif ($v_header['typeflag'] == "2") { - if (@file_exists($v_header['filename'])) { - @unlink($v_header['filename']); - } - if (!@symlink($v_header['link'], $v_header['filename'])) { - $this->_error('Unable to extract symbolic link {' - .$v_header['filename'].'}'); - return false; - } - } else { - if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { - $this->_error('Error while opening {'.$v_header['filename'] - .'} in write binary mode'); - return false; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, 512); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); - } - - @fclose($v_dest_file); - - if ($p_preserve) { - @chown($v_header['filename'], $v_header['uid']); - @chgrp($v_header['filename'], $v_header['gid']); - } - - // ----- Change the file mode, mtime - @touch($v_header['filename'], $v_header['mtime']); - if ($v_header['mode'] & 0111) { - // make file executable, obey umask - $mode = fileperms($v_header['filename']) | (~umask() & 0111); - @chmod($v_header['filename'], $mode); - } - } - - // ----- Check the file size - clearstatcache(); - if (!is_file($v_header['filename'])) { - $this->_error('Extracted file '.$v_header['filename'] - .'does not exist. Archive may be corrupted.'); - return false; - } - - $filesize = filesize($v_header['filename']); - if ($filesize != $v_header['size']) { - $this->_error('Extracted file '.$v_header['filename'] - .' does not have the correct file size \'' - .$filesize - .'\' ('.$v_header['size'] - .' expected). Archive may be corrupted.'); - return false; - } - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - - /* TBC : Seems to be unused ... - if ($this->_compress) - $v_end_of_file = @gzeof($this->_file); - else - $v_end_of_file = @feof($this->_file); - */ - - if ($v_listing || $v_extract_file || $v_extraction_stopped) { - // ----- Log extracted files - if (($v_file_dir = dirname($v_header['filename'])) - == $v_header['filename']) - $v_file_dir = ''; - if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) - $v_file_dir = '/'; - - $p_list_detail[$v_nb++] = $v_header; - if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { - return true; - } - } - } - - return true; - } - // }}} - - // {{{ _openAppend() - function _openAppend() - { - if (filesize($this->_tarname) == 0) - return $this->_openWrite(); - - if ($this->_compress) { - $this->_close(); - - if (!@rename($this->_tarname, $this->_tarname.".tmp")) { - $this->_error('Error while renaming \''.$this->_tarname - .'\' to temporary file \''.$this->_tarname - .'.tmp\''); - return false; - } - - if ($this->_compress_type == 'gz') - $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb"); - elseif ($this->_compress_type == 'bz2') - $v_temp_tar = @bzopen($this->_tarname.".tmp", "r"); - - if ($v_temp_tar == 0) { - $this->_error('Unable to open file \''.$this->_tarname - .'.tmp\' in binary read mode'); - @rename($this->_tarname.".tmp", $this->_tarname); - return false; - } - - if (!$this->_openWrite()) { - @rename($this->_tarname.".tmp", $this->_tarname); - return false; - } - - if ($this->_compress_type == 'gz') { - $end_blocks = 0; - - while (!@gzeof($v_temp_tar)) { - $v_buffer = @gzread($v_temp_tar, 512); - if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { - $end_blocks++; - // do not copy end blocks, we will re-make them - // after appending - continue; - } elseif ($end_blocks > 0) { - for ($i = 0; $i < $end_blocks; $i++) { - $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); - } - $end_blocks = 0; - } - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - @gzclose($v_temp_tar); - } - elseif ($this->_compress_type == 'bz2') { - $end_blocks = 0; - - while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { - if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { - $end_blocks++; - // do not copy end blocks, we will re-make them - // after appending - continue; - } elseif ($end_blocks > 0) { - for ($i = 0; $i < $end_blocks; $i++) { - $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); - } - $end_blocks = 0; - } - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - @bzclose($v_temp_tar); - } - - if (!@unlink($this->_tarname.".tmp")) { - $this->_error('Error while deleting temporary file \'' - .$this->_tarname.'.tmp\''); - } - - } else { - // ----- For not compressed tar, just add files before the last - // one or two 512 bytes block - if (!$this->_openReadWrite()) - return false; - - clearstatcache(); - $v_size = filesize($this->_tarname); - - // We might have zero, one or two end blocks. - // The standard is two, but we should try to handle - // other cases. - fseek($this->_file, $v_size - 1024); - if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { - fseek($this->_file, $v_size - 1024); - } - elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { - fseek($this->_file, $v_size - 512); - } - } - - return true; - } - // }}} - - // {{{ _append() - function _append($p_filelist, $p_add_dir='', $p_remove_dir='') - { - if (!$this->_openAppend()) - return false; - - if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) - $this->_writeFooter(); - - $this->_close(); - - return true; - } - // }}} - - // {{{ _dirCheck() - - /** - * Check if a directory exists and create it (including parent - * dirs) if not. - * - * @param string $p_dir directory to check - * - * @return bool true if the directory exists or was created - */ - function _dirCheck($p_dir) - { - clearstatcache(); - if ((@is_dir($p_dir)) || ($p_dir == '')) - return true; - - $p_parent_dir = dirname($p_dir); - - if (($p_parent_dir != $p_dir) && - ($p_parent_dir != '') && - (!$this->_dirCheck($p_parent_dir))) - return false; - - if (!@mkdir($p_dir, 0777)) { - $this->_error("Unable to create directory '$p_dir'"); - return false; - } - - return true; - } - - // }}} - - // {{{ _pathReduction() - - /** - * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", - * rand emove double slashes. - * - * @param string $p_dir path to reduce - * - * @return string reduced path - * - * @access private - * - */ - function _pathReduction($p_dir) - { - $v_result = ''; - - // ----- Look for not empty path - if ($p_dir != '') { - // ----- Explode path by directory names - $v_list = explode('/', $p_dir); - - // ----- Study directories from last to first - for ($i=sizeof($v_list)-1; $i>=0; $i--) { - // ----- Look for current path - if ($v_list[$i] == ".") { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") { - // ----- Ignore it and ignore the $i-1 - $i--; - } - else if ( ($v_list[$i] == '') - && ($i!=(sizeof($v_list)-1)) - && ($i!=0)) { - // ----- Ignore only the double '//' in path, - // but not the first and last / - } else { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/' - .$v_result:''); - } - } - } - - if (defined('OS_WINDOWS') && OS_WINDOWS) { - $v_result = strtr($v_result, '\\', '/'); - } - - return $v_result; - } - - // }}} - - // {{{ _translateWinPath() - function _translateWinPath($p_path, $p_remove_disk_letter=true) - { - if (defined('OS_WINDOWS') && OS_WINDOWS) { - // ----- Look for potential disk letter - if ( ($p_remove_disk_letter) - && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; - } - // }}} - -} -?> diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php index 8605e38e1f5a5b41cd6800c834950573566e0d18..15e3b8420c209a9f273427be36b2cd5c45fedfcc 100644 --- a/libs/PiwikTracker/PiwikTracker.php +++ b/libs/PiwikTracker/PiwikTracker.php @@ -1063,6 +1063,8 @@ class PiwikTracker /** * Hash function used internally by Piwik to hash a User ID into the Visitor ID. * + * Note: matches implementation of Tracker\Request->getUserIdHashed() + * * @param $id * @return string */ @@ -1071,7 +1073,6 @@ class PiwikTracker return substr( sha1( $id ), 0, 16); } - /** * Forces the requests to be recorded for the specified Visitor ID. * Note: it is recommended to use ->setUserId($userId); instead. diff --git a/libs/README.md b/libs/README.md index 959d38e4b900072fa0f001a799918b294c62e5e9..8d7c7d8e6e74b2afb1aa7a82af344143f294aa19 100644 --- a/libs/README.md +++ b/libs/README.md @@ -17,8 +17,6 @@ third-party libraries: - The bug #4206 (GD with JIS-mapped Japanese Font Support) was fixed in this commit: https://github.com/piwik/piwik/commit/516c13d9b13ca3b908575eb809f7ad9d9397f0e1 Changed files: class/pImage.class.php class/pDraw.class.php - * PclZip/ - - in r1960, ignore touch() - utime failed warning * PEAR/, PEAR.php - in r2419, add static keyword to isError and raiseError as it throws notices in HTML_Quickform2 diff --git a/misc/phpstorm-codestyles/Piwik_codestyle.xml b/misc/phpstorm-codestyles/Piwik_codestyle.xml index 0fd9a921b5248da8b4e4e7023673eef4e45b980a..ed09f367d723f1b12d996a287d931435412ba5a4 100644 --- a/misc/phpstorm-codestyles/Piwik_codestyle.xml +++ b/misc/phpstorm-codestyles/Piwik_codestyle.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <code_scheme name="Piwik-codestyle"> <option name="LINE_SEPARATOR" value=" " /> <option name="RIGHT_MARGIN" value="160" /> @@ -24,136 +23,6 @@ <order>BREADTH_FIRST</order> </group> </groups> - <rules> - <rule> - <match> - <CONST /> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PUBLIC /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PROTECTED /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PRIVATE /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PUBLIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PROTECTED /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <FIELD /> - <PRIVATE /> - </AND> - </match> - </rule> - <rule> - <match> - <CONSTRUCTOR /> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PUBLIC /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PROTECTED /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PRIVATE /> - <STATIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PUBLIC /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PROTECTED /> - </AND> - </match> - </rule> - <rule> - <match> - <AND> - <METHOD /> - <PRIVATE /> - </AND> - </match> - </rule> - <rule> - <match> - <TRAIT /> - </match> - </rule> - <rule> - <match> - <INTERFACE /> - </match> - </rule> - <rule> - <match> - <CLASS /> - </match> - </rule> - </rules> </arrangement> </codeStyleSettings> -</code_scheme> - +</code_scheme> \ No newline at end of file diff --git a/misc/proxy-hide-piwik-url/piwik.php b/misc/proxy-hide-piwik-url/piwik.php index 80721cd6ea38b8c2ddf800b837bb90ecdb7c6a5d..d1c9e9ca3c29c795bd666493471cdae94221ae63 100644 --- a/misc/proxy-hide-piwik-url/piwik.php +++ b/misc/proxy-hide-piwik-url/piwik.php @@ -76,7 +76,7 @@ if (empty($_GET)) { $url = sprintf("%spiwik.php?cip=%s&token_auth=%s&", $PIWIK_URL, getVisitIp(), $TOKEN_AUTH); foreach ($_GET as $key => $value) { - $url .= $key . '=' . urlencode($value) . '&'; + $url .= urlencode($key ). '=' . urlencode($value) . '&'; } sendHeader("Content-Type: image/gif"); $stream_options = array('http' => array( diff --git a/plugins/API/API.php b/plugins/API/API.php index 00758ca6bb6f96a8607e15fc610e9b9e4f2df52a..c65c04fffe8236642857921c8187953f01b7710e 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -259,7 +259,7 @@ class API extends \Piwik\Plugin\API SegmentExpression::MATCH_IS_NULL_OR_EMPTY, SegmentExpression::MATCH_NOT_EQUAL, ); - if(in_array($matchType, $acceptedMatches)) { + if (in_array($matchType, $acceptedMatches)) { return $value; } $message = "Invalid Segment match type: try using 'userId' segment with one of the following match types: %s."; @@ -524,7 +524,7 @@ class API extends \Piwik\Plugin\API */ public function getSuggestedValuesForSegment($segmentName, $idSite) { - if(empty(Config::getInstance()->General['enable_segment_suggested_values'])) { + if (empty(Config::getInstance()->General['enable_segment_suggested_values'])) { return array(); } Piwik::checkUserHasViewAccess($idSite); @@ -549,7 +549,7 @@ class API extends \Piwik\Plugin\API } // if period=range is disabled, do not proceed - if(!Period\Factory::isPeriodEnabledForAPI('range')) { + if (!Period\Factory::isPeriodEnabledForAPI('range')) { return array(); } diff --git a/plugins/API/Controller.php b/plugins/API/Controller.php index 9a6b3fc5e7b40723faba25e5d5d3d76be8ea5c68..18e39d58a2f34da3dd28da405236ca38df30b507 100644 --- a/plugins/API/Controller.php +++ b/plugins/API/Controller.php @@ -60,7 +60,7 @@ class Controller extends \Piwik\Plugin\Controller foreach ($segments as $segment) { // Eg. Event Value is a metric, not in the Visit metric category, // we make sure it is displayed along with the Events dimensions - if($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) { + if ($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) { $segment['type'] = 'dimension'; } diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php index 7a5290c44fdd824740adedbd4147ddadb1254018..b57a788050d54e553497ee5cf85e1fc1c228866e 100644 --- a/plugins/API/RowEvolution.php +++ b/plugins/API/RowEvolution.php @@ -279,7 +279,7 @@ class RowEvolution // note: some reports should not be filtered with AddColumnProcessedMetrics // specifically, reports without the Metrics::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion // this is because the AddColumnProcessedMetrics filter removes all datable rows lacking this metric - if( isset($metadata['metrics']['nb_visits']) + if ( isset($metadata['metrics']['nb_visits']) && !empty($label)) { $parameters['filter_add_columns_when_show_all_columns'] = '1'; } @@ -430,7 +430,7 @@ class RowEvolution $labelRow, $apiModule, $apiAction, $labelUseAbsoluteUrl); $prettyLabel = $labelRow->getColumn('label_html'); - if($prettyLabel !== false) { + if ($prettyLabel !== false) { $actualLabels[$labelIdx] = $prettyLabel; } diff --git a/plugins/Actions/Actions/ActionSiteSearch.php b/plugins/Actions/Actions/ActionSiteSearch.php index 9631aa0473ffda07bbf41757d6c064bc2cf97909..d19cfaa6fc71ac0942b926ed2e25c803d017e1ba 100644 --- a/plugins/Actions/Actions/ActionSiteSearch.php +++ b/plugins/Actions/Actions/ActionSiteSearch.php @@ -73,7 +73,7 @@ class ActionSiteSearch extends Action { $siteSearch = $this->detectSiteSearch($this->originalUrl); - if(empty($siteSearch)) { + if (empty($siteSearch)) { return false; } @@ -169,10 +169,10 @@ class ActionSiteSearch extends Action // @see excludeQueryParametersFromUrl() // Excluded the detected parameters from the URL $parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw); - if(isset($parsedUrl['query'])) { + if (isset($parsedUrl['query'])) { $parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude); } - if(isset($parsedUrl['fragment'])) { + if (isset($parsedUrl['fragment'])) { $parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude); } } diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php index 14d42147444f3fdac34d2eb6600298af9dd68538..67408ced64aba07a08a384a04ac981f3ad211777 100644 --- a/plugins/Actions/Archiver.php +++ b/plugins/Actions/Archiver.php @@ -241,7 +241,7 @@ class Archiver extends \Piwik\Plugin\Archiver // 1) No result Keywords // 2) For each page view, count number of times the referrer page was a Site Search if ($this->isSiteSearchEnabled()) { - $this->updateQuerySelectFromForSiteSearch($select, $from); + $this->updateQuerySelectFromForSiteSearch($select, $from); } $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name", $rankingQuery); diff --git a/plugins/Annotations/AnnotationList.php b/plugins/Annotations/AnnotationList.php index 89a5b9131f1dad15601b8731bd40c4428f834d95..cf068e6f35e889f0d1dcd44ba62b6904a8a4f843 100755 --- a/plugins/Annotations/AnnotationList.php +++ b/plugins/Annotations/AnnotationList.php @@ -331,7 +331,7 @@ class AnnotationList if ($serialized !== false) { $result[$id] = @unserialize($serialized); - if(empty($result[$id])) { + if (empty($result[$id])) { // in case unserialize failed $result[$id] = array(); } diff --git a/plugins/Contents/Controller.php b/plugins/Contents/Controller.php index daf319bb535a683791f2977b059a1746aeba959c..5785ba478c5d81e6ae147cd398d8b40fcb20118d 100644 --- a/plugins/Contents/Controller.php +++ b/plugins/Contents/Controller.php @@ -27,7 +27,7 @@ class Controller extends \Piwik\Plugin\Controller $reportsView->addReport( $report->getCategory(), $report->getName(), - 'Contents.menu' . ucfirst($report->getAction()) + 'Contents.' . Report::PREFIX_ACTION_IN_MENU . ucfirst($report->getAction()) ); } diff --git a/plugins/Contents/tests/ContentsTest.php b/plugins/Contents/tests/ContentsTest.php index 2bd9549502ea338ca076642876343df82f0322b4..68875db127c9428f67db148bf204798d3fbfaee1 100644 --- a/plugins/Contents/tests/ContentsTest.php +++ b/plugins/Contents/tests/ContentsTest.php @@ -36,7 +36,7 @@ class ContentsTest extends IntegrationTestCase 'Contents.getContentPieces', 'Actions.get', 'Actions.getPageUrls', - //'Live.getLastVisitsDetails' + 'Live.getLastVisitsDetails' ); } @@ -55,7 +55,7 @@ class ContentsTest extends IntegrationTestCase $dayPeriod = 'day'; $periods = array($dayPeriod, 'month'); - $apisToTest = array('Contents', 'Actions.getPageUrls'); // , 'Live.getLastVisitsDetails' + $apisToTest = array('Contents', 'Actions.getPageUrls', 'Live.getLastVisitsDetails'); $result = array( array($apiToCallProcessedReportMetadata, array( 'idSite' => $idSite1, diff --git a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml index c1e492c05ad7e70025c56ac29bc0de765d19fc84..fde347f2b53dc66196772a7ea0e6d522367e707d 100644 --- a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml +++ b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml @@ -5,18 +5,18 @@ <nb_visits>2</nb_visits> <nb_uniq_visitors>2</nb_uniq_visitors> <nb_hits>2</nb_hits> - <sum_time_spent>0</sum_time_spent> + <sum_time_spent>540</sum_time_spent> <nb_hits_with_time_generation>2</nb_hits_with_time_generation> <min_time_generation>0.333</min_time_generation> <max_time_generation>0.333</max_time_generation> <entry_nb_uniq_visitors>2</entry_nb_uniq_visitors> <entry_nb_visits>2</entry_nb_visits> <entry_nb_actions>2</entry_nb_actions> - <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_sum_visit_length>542</entry_sum_visit_length> <entry_bounce_count>2</entry_bounce_count> <exit_nb_uniq_visitors>2</exit_nb_uniq_visitors> <exit_nb_visits>2</exit_nb_visits> - <avg_time_on_page>0</avg_time_on_page> + <avg_time_on_page>270</avg_time_on_page> <bounce_rate>100%</bounce_rate> <exit_rate>100%</exit_rate> <avg_time_generation>0.333</avg_time_generation> diff --git a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml index 56760f4816e6fa769fae9b6c5eef29ed97ab764c..3dc775d40a8885e1c31b84093d990294ea6b06a2 100644 --- a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml +++ b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml @@ -4,19 +4,19 @@ <label>/page</label> <nb_visits>2</nb_visits> <nb_hits>2</nb_hits> - <sum_time_spent>0</sum_time_spent> + <sum_time_spent>540</sum_time_spent> <nb_hits_with_time_generation>2</nb_hits_with_time_generation> <min_time_generation>0.333</min_time_generation> <max_time_generation>0.333</max_time_generation> <entry_nb_visits>2</entry_nb_visits> <entry_nb_actions>2</entry_nb_actions> - <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_sum_visit_length>542</entry_sum_visit_length> <entry_bounce_count>2</entry_bounce_count> <exit_nb_visits>2</exit_nb_visits> <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> <sum_daily_entry_nb_uniq_visitors>2</sum_daily_entry_nb_uniq_visitors> <sum_daily_exit_nb_uniq_visitors>2</sum_daily_exit_nb_uniq_visitors> - <avg_time_on_page>0</avg_time_on_page> + <avg_time_on_page>270</avg_time_on_page> <bounce_rate>100%</bounce_rate> <exit_rate>100%</exit_rate> <avg_time_generation>0.333</avg_time_generation> diff --git a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml index 6a083ba44892e9e18b3e2bec7f95c082d9b31303..989f816722569544d52920872adc903958cc58b6 100644 --- a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml +++ b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml @@ -1,450 +1,9 @@ <?xml version="1.0" encoding="utf-8" ?> <result> - <row> - <idSite>1</idSite> - <idVisit>6</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>4</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>61</visitDuration> - <visitDurationPretty>1 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>5</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>2</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>121</visitDuration> - <visitDurationPretty>2 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>3</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> <row> <idSite>1</idSite> <idVisit>1</idVisit> <visitIp>156.5.3.2</visitIp> - <userId /> <actionDetails> <row> @@ -469,6 +28,7 @@ <searches>0</searches> <actions>1</actions> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -479,8 +39,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> @@ -542,179 +102,22 @@ </row> <row> <idSite>1</idSite> - <idVisit>13</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>8</idVisit> + <idVisit>2</idVisit> <visitIp>111.1.1.1</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>181</visitDuration> - <visitDurationPretty>3 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>13</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>11</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -724,91 +127,8 @@ <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>12</idVisit> - <visitIp>111.1.1.1</visitIp> + <actions>1</actions> <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -819,8 +139,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> diff --git a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml index 6a083ba44892e9e18b3e2bec7f95c082d9b31303..989f816722569544d52920872adc903958cc58b6 100644 --- a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml +++ b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml @@ -1,450 +1,9 @@ <?xml version="1.0" encoding="utf-8" ?> <result> - <row> - <idSite>1</idSite> - <idVisit>6</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>4</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>61</visitDuration> - <visitDurationPretty>1 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>5</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>2</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>121</visitDuration> - <visitDurationPretty>2 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>3</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> <row> <idSite>1</idSite> <idVisit>1</idVisit> <visitIp>156.5.3.2</visitIp> - <userId /> <actionDetails> <row> @@ -469,6 +28,7 @@ <searches>0</searches> <actions>1</actions> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -479,8 +39,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> @@ -542,179 +102,22 @@ </row> <row> <idSite>1</idSite> - <idVisit>13</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>8</idVisit> + <idVisit>2</idVisit> <visitIp>111.1.1.1</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>181</visitDuration> - <visitDurationPretty>3 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>13</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>11</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -724,91 +127,8 @@ <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>12</idVisit> - <visitIp>111.1.1.1</visitIp> + <actions>1</actions> <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -819,8 +139,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> diff --git a/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml index dfd70cba41e3e52c693c1f1700b1b74b7e08b56c..989f816722569544d52920872adc903958cc58b6 100644 --- a/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml +++ b/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml @@ -2,99 +2,22 @@ <result> <row> <idSite>1</idSite> - <idVisit>6</idVisit> + <idVisit>1</idVisit> <visitIp>156.5.3.2</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>1</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>5</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -104,7 +27,8 @@ <searches>0</searches> - <actions>0</actions> + <actions>1</actions> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -115,8 +39,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> @@ -178,95 +102,22 @@ </row> <row> <idSite>1</idSite> - <idVisit>13</idVisit> + <idVisit>2</idVisit> <visitIp>111.1.1.1</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>13</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>12</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -276,7 +127,8 @@ <searches>0</searches> - <actions>0</actions> + <actions>1</actions> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -287,8 +139,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> diff --git a/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml index bed0f8ffd99cfd090bee2c6c9a88b0eae4d24350..989f816722569544d52920872adc903958cc58b6 100644 --- a/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml +++ b/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml @@ -2,99 +2,22 @@ <result> <row> <idSite>1</idSite> - <idVisit>4</idVisit> + <idVisit>1</idVisit> <visitIp>156.5.3.2</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>61</visitDuration> - <visitDurationPretty>1 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>1</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>2</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -104,95 +27,8 @@ <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>121</visitDuration> - <visitDurationPretty>2 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>3</idVisit> - <visitIp>156.5.3.2</visitIp> + <actions>1</actions> <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -203,8 +39,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> @@ -266,95 +102,22 @@ </row> <row> <idSite>1</idSite> - <idVisit>8</idVisit> + <idVisit>2</idVisit> <visitIp>111.1.1.1</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>181</visitDuration> - <visitDurationPretty>3 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>13</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>9</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -364,91 +127,8 @@ <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>10</idVisit> - <visitIp>111.1.1.1</visitIp> + <actions>1</actions> <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -459,8 +139,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> diff --git a/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml index 9595395b50c3befcac40bdabbc309faee9053546..989f816722569544d52920872adc903958cc58b6 100644 --- a/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml +++ b/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml @@ -1,450 +1,9 @@ <?xml version="1.0" encoding="utf-8" ?> <result> - <row> - <idSite>1</idSite> - <idVisit>6</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>4</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>61</visitDuration> - <visitDurationPretty>1 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>5</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>2</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>121</visitDuration> - <visitDurationPretty>2 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>3</idVisit> - <visitIp>156.5.3.2</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>flash, java</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon> - <pluginName>flash</pluginName> - </row> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon> - <pluginName>java</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> <row> <idSite>1</idSite> <idVisit>1</idVisit> <visitIp>156.5.3.2</visitIp> - <userId /> <actionDetails> <row> @@ -469,6 +28,7 @@ <searches>0</searches> <actions>1</actions> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -479,8 +39,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>1</visitDuration> - <visitDurationPretty>1s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> @@ -542,179 +102,22 @@ </row> <row> <idSite>1</idSite> - <idVisit>13</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>91</visitDuration> - <visitDurationPretty>1 min 31s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>8</idVisit> + <idVisit>2</idVisit> <visitIp>111.1.1.1</visitIp> - <userId /> <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>181</visitDuration> - <visitDurationPretty>3 min 1s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> + <type>action</type> + <url>http://www.example.org/page</url> + <pageTitle>Ads</pageTitle> + <pageIdAction>2</pageIdAction> + + <pageId>13</pageId> + <generationTime>0.33s</generationTime> + <timeSpent>0</timeSpent> + <timeSpentPretty>0s</timeSpentPretty> + <icon /> </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>12</idVisit> - <visitIp>111.1.1.1</visitIp> - <userId /> - - <actionDetails> </actionDetails> <goalConversions>0</goalConversions> <siteCurrency>USD</siteCurrency> @@ -724,91 +127,8 @@ <searches>0</searches> - <actions>0</actions> - <visitorType>new</visitorType> - <visitorTypeIcon /> - <visitConverted>0</visitConverted> - <visitConvertedIcon /> - <visitCount>1</visitCount> - - <visitEcommerceStatus>none</visitEcommerceStatus> - <visitEcommerceStatusIcon /> - <daysSinceFirstVisit>0</daysSinceFirstVisit> - <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> - <customVariables> - </customVariables> - <deviceType>Desktop</deviceType> - <events>0</events> - <provider>Unknown</provider> - <providerName>Unknown</providerName> - <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl> - <referrerType>direct</referrerType> - <referrerTypeName>Direct Entry</referrerTypeName> - <referrerName /> - <referrerKeyword /> - <referrerKeywordPosition /> - <referrerUrl /> - <referrerSearchEngineUrl /> - <referrerSearchEngineIcon /> - <continent>Europe</continent> - <continentCode>eur</continentCode> - <country>France</country> - <countryCode>fr</countryCode> - <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag> - <region /> - <regionCode /> - <city /> - <location>France</location> - <latitude /> - <longitude /> - <operatingSystem>Windows XP</operatingSystem> - <operatingSystemCode>WXP</operatingSystemCode> - <operatingSystemShortName>Win XP</operatingSystemShortName> - <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon> - <browserFamily>gecko</browserFamily> - <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription> - <browserName>Firefox 3.6</browserName> - <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon> - <browserCode>FF</browserCode> - <browserVersion>3.6</browserVersion> - <screenType>normal</screenType> - <resolution>1024x768</resolution> - <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon> - <plugins>director</plugins> - <pluginsIcons> - <row> - <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon> - <pluginName>director</pluginName> - </row> - </pluginsIcons> - <visitLocalTime>12:34:06</visitLocalTime> - <visitLocalHour>12</visitLocalHour> - <daysSinceLastVisit>0</daysSinceLastVisit> - - - - - - </row> - <row> - <idSite>1</idSite> - <idVisit>9</idVisit> - <visitIp>111.1.1.1</visitIp> + <actions>1</actions> <userId /> - - <actionDetails> - </actionDetails> - <goalConversions>0</goalConversions> - <siteCurrency>USD</siteCurrency> - <siteCurrencySymbol>$</siteCurrencySymbol> - - - - - <searches>0</searches> - <actions>0</actions> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -819,8 +139,8 @@ <visitEcommerceStatusIcon /> <daysSinceFirstVisit>0</daysSinceFirstVisit> <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder> - <visitDuration>0</visitDuration> - <visitDurationPretty>0s</visitDurationPretty> + <visitDuration>271</visitDuration> + <visitDurationPretty>4 min 31s</visitDurationPretty> <customVariables> </customVariables> <deviceType>Desktop</deviceType> diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php index 530cc101561f8b79d3dbc35db36a9ca4e128997b..a25a522cb4c539f80e67c9e8b5ba7acfc78927bf 100644 --- a/plugins/CoreAdminHome/API.php +++ b/plugins/CoreAdminHome/API.php @@ -66,19 +66,23 @@ class API extends \Piwik\Plugin\API public function invalidateArchivedReports($idSites, $dates, $period = false) { $idSites = Site::getIdSitesFromIdSitesString($idSites); + if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } + Piwik::checkUserHasAdminAccess($idSites); - if(!empty($period)) { + if (!empty($period)) { $period = Period\Factory::build($period, Date::today()); } // Ensure the specified dates are valid $toInvalidate = $invalidDates = array(); + $dates = explode(',', trim($dates)); $dates = array_unique($dates); + foreach ($dates as $theDate) { $theDate = trim($theDate); try { @@ -141,7 +145,7 @@ class API extends \Piwik\Plugin\API } } - if(empty($minDate)) { + if (empty($minDate)) { throw new Exception("Check the 'dates' parameter is a valid date."); } @@ -169,7 +173,7 @@ class API extends \Piwik\Plugin\API $sql = implode(" OR ", $sql); $sqlPeriod = ""; - if($invalidateForPeriod) { + if ($invalidateForPeriod) { $sqlPeriod = " AND period = ? "; $bind[] = $invalidateForPeriod; } diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php index 6b672d07e8bc0ecc4c14856bbf0756b8b3848818..d1278607fdf7fb57e56a950a5f4ce463d4839fcd 100644 --- a/plugins/CoreAdminHome/Controller.php +++ b/plugins/CoreAdminHome/Controller.php @@ -278,7 +278,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $successLogo = $logo->copyUploadedLogoToFilesystem(); $successFavicon = $logo->copyUploadedFaviconToFilesystem(); - if($successLogo || $successFavicon) { + if ($successLogo || $successFavicon) { return '1'; } return '0'; @@ -291,7 +291,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin private function saveGeneralSettings() { - if(!self::isGeneralSettingsAdminEnabled()) { + if (!self::isGeneralSettingsAdminEnabled()) { // General settings + Beta channel + SMTP settings is disabled return; } @@ -339,7 +339,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin { // Whether to display or not the general settings (cron, beta, smtp) $view->isGeneralSettingsAdminEnabled = self::isGeneralSettingsAdminEnabled(); - if($view->isGeneralSettingsAdminEnabled) { + if ($view->isGeneralSettingsAdminEnabled) { $this->displayWarningIfConfigFileNotWritable(); } diff --git a/plugins/CoreAdminHome/CustomLogo.php b/plugins/CoreAdminHome/CustomLogo.php index fe7e78ac6eb8f92dd1361e742a511bb1637ef52f..eda7ef06383f94d40940516eb9c79951e2086ed3 100644 --- a/plugins/CoreAdminHome/CustomLogo.php +++ b/plugins/CoreAdminHome/CustomLogo.php @@ -88,7 +88,7 @@ class CustomLogo */ public function isCustomLogoWritable() { - if(Config::getInstance()->General['enable_custom_logo_check'] == 0) { + if (Config::getInstance()->General['enable_custom_logo_check'] == 0) { return true; } $pathUserLogo = $this->getPathUserLogo(); @@ -196,7 +196,7 @@ class CustomLogo $image = imagecreatefrompng($file); break; case 'image/gif': - $image = imagecreatefromgif($file); + $image = imagecreatefromgif ($file); break; default: return false; diff --git a/plugins/CoreAdminHome/Tasks.php b/plugins/CoreAdminHome/Tasks.php index 461ca5ca5240140d2262464a94a14ab8c9b29c6e..d633b9fd5d68efc5e2e033185c69d2d39c673266 100644 --- a/plugins/CoreAdminHome/Tasks.php +++ b/plugins/CoreAdminHome/Tasks.php @@ -35,7 +35,7 @@ class Tasks extends \Piwik\Plugin\Tasks list($year, $month) = explode('_', $date); // Somehow we may have archive tables created with older dates, prevent exception from being thrown - if($year > 1990) { + if ($year > 1990) { ArchivePurger::purgeOutdatedArchives(Date::factory("$year-$month-15")); } } diff --git a/plugins/CoreConsole/Commands/GenerateCommand.php b/plugins/CoreConsole/Commands/GenerateCommand.php index e8661f028ea4ca676a9bb026ba4c19c16567a334..9fe95c06d3bebabb1591d3255d6e511d98a8ed4e 100644 --- a/plugins/CoreConsole/Commands/GenerateCommand.php +++ b/plugins/CoreConsole/Commands/GenerateCommand.php @@ -34,7 +34,9 @@ class GenerateCommand extends GeneratePluginBase $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleCommand'; $replace = array( + 'ExampleCommandDescription' => $commandName, 'ExampleCommand' => $pluginName, + 'examplecommand:helloworld' => strtolower($pluginName) . ':' . $this->buildCommandName($commandName), 'examplecommand' => strtolower($pluginName), 'HelloWorld' => $commandName, 'helloworld' => strtolower($commandName) @@ -49,6 +51,15 @@ class GenerateCommand extends GeneratePluginBase )); } + /** + * Convert MyComponentName => my-component-name + * @param string $commandNameCamelCase + * @return string + */ + protected function buildCommandName($commandNameCamelCase) + { + return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $commandNameCamelCase)); + } /** * @param InputInterface $input * @param OutputInterface $output @@ -64,12 +75,16 @@ class GenerateCommand extends GeneratePluginBase throw new \InvalidArgumentException('You have to enter a command name'); } + if (!ctype_alnum($testname)) { + throw new \InvalidArgumentException('Only alphanumeric characters are allowed as a command name. Use CamelCase if the name of your command contains multiple words.'); + } + return $testname; }; if (empty($testname)) { $dialog = $this->getHelperSet()->get('dialog'); - $testname = $dialog->askAndValidate($output, 'Enter the name of the command: ', $validate); + $testname = $dialog->askAndValidate($output, 'Enter the name of the command (CamelCase): ', $validate); } else { $validate($testname); } diff --git a/plugins/CoreConsole/Commands/GenerateTest.php b/plugins/CoreConsole/Commands/GenerateTest.php index a9ace19a8b8d2aaf33e43269e865ac48d3b29845..ff68eec784b9ad5b5494a7ec6a5632bf796a7ddb 100644 --- a/plugins/CoreConsole/Commands/GenerateTest.php +++ b/plugins/CoreConsole/Commands/GenerateTest.php @@ -42,7 +42,7 @@ class GenerateTest extends GeneratePluginBase ); $testClass = $this->getTestClass($testType); - if(!empty($testClass)) { + if (!empty($testClass)) { $replace['\PHPUnit_Framework_TestCase'] = $testClass; } @@ -167,7 +167,7 @@ class GenerateTest extends GeneratePluginBase */ protected function getTestFilesWhitelist($testType) { - if('Integration' == $testType) { + if ('Integration' == $testType) { return array( '/.gitignore', '/tests', diff --git a/plugins/CoreConsole/TravisYmlView.php b/plugins/CoreConsole/TravisYmlView.php index cb4fabfda2662cfa3ec74ceb017aa57256338347..a89b5f1df52f7c03301c10ae01dccec97e0f5b63 100644 --- a/plugins/CoreConsole/TravisYmlView.php +++ b/plugins/CoreConsole/TravisYmlView.php @@ -89,6 +89,7 @@ class TravisYmlView extends View $this->pluginName = $pluginName; $customTravisBuildSteps = array(); + foreach (self::$travisYmlExtendableSectionNames as $name) { $customTravisBuildSteps[$name] = array(); @@ -102,6 +103,7 @@ class TravisYmlView extends View $customTravisBuildSteps[$name]['after'] = $this->changeIndent(file_get_contents($afterStepsTemplate), ' '); } } + $this->customTravisBuildSteps = $customTravisBuildSteps; } @@ -160,12 +162,14 @@ class TravisYmlView extends View preg_match_all("/^[a-zA-Z_]+:/m", $yamlText, $allMatches, PREG_OFFSET_CAPTURE); $result = array(); + foreach ($allMatches[0] as $match) { $matchLength = strlen($match[0]); $sectionName = substr($match[0], 0, $matchLength - 1); $result[$sectionName] = $match[1] + $matchLength; } + return $result; } @@ -206,6 +210,7 @@ class TravisYmlView extends View $testsToExclude[] = array('php' => '5.4', 'env' => 'TEST_SUITE=PluginTests MYSQL_ADAPTER=PDO_MYSQL TEST_AGAINST_CORE=latest_stable'); } + if ($this->isTargetPluginContainsUITests()) { $testsToRun[] = array('name' => 'UITests', 'vars' => "MYSQL_ADAPTER=PDO_MYSQL"); diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php index 146a7ea4c68a1e2d1d6264b0b10cdb8bf537e39b..55fea6b5173fe6345efde2730c5510a5268eeabe 100644 --- a/plugins/CoreHome/Columns/UserId.php +++ b/plugins/CoreHome/Columns/UserId.php @@ -48,12 +48,7 @@ class UserId extends VisitDimension */ public function onExistingVisit(Request $request, Visitor $visitor, $action) { - $forcedUserId = $request->getForcedUserId(); - if ($forcedUserId) { - return $forcedUserId; - } - - return false; + return $request->getForcedUserId(); } } \ No newline at end of file diff --git a/plugins/CoreHome/Columns/VisitGoalBuyer.php b/plugins/CoreHome/Columns/VisitGoalBuyer.php index 1fcf91cb965b85c2b944d65988b774097e1a7a4d..8867efd70848a27d5dfad7a7c73dc46a4880cc8d 100644 --- a/plugins/CoreHome/Columns/VisitGoalBuyer.php +++ b/plugins/CoreHome/Columns/VisitGoalBuyer.php @@ -72,7 +72,7 @@ class VisitGoalBuyer extends VisitDimension // Ecommerce buyer status $visitEcommerceStatus = $this->getBuyerType($request, $goalBuyer); - if($visitEcommerceStatus != self::TYPE_BUYER_NONE + if ($visitEcommerceStatus != self::TYPE_BUYER_NONE // only update if the value has changed (prevents overwriting the value in case a request has // updated it in the meantime) && $visitEcommerceStatus != $goalBuyer) { diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php index b0d3770cbe12e344b98c4dac97f20d759d745009..387c31e9e9a221b2a22d84d36745186a7329ffd8 100644 --- a/plugins/CoreHome/Controller.php +++ b/plugins/CoreHome/Controller.php @@ -103,9 +103,11 @@ class Controller extends \Piwik\Plugin\Controller ) { $module = 'MultiSites'; } + if ($defaultReport == Piwik::getLoginPluginName()) { $module = Piwik::getLoginPluginName(); } + $idSite = Common::getRequestVar('idSite', false, 'int'); parent::redirectToIndex($module, $action, $idSite); } @@ -113,10 +115,12 @@ class Controller extends \Piwik\Plugin\Controller public function showInContext() { $controllerName = Common::getRequestVar('moduleToLoad'); - $actionName = Common::getRequestVar('actionToLoad', 'index'); + $actionName = Common::getRequestVar('actionToLoad', 'index'); + if ($actionName == 'showInContext') { throw new Exception("Preventing infinite recursion..."); } + $view = $this->getDefaultIndexView(); $view->content = FrontController::getInstance()->fetchDispatch($controllerName, $actionName); return $view->render(); @@ -146,12 +150,16 @@ class Controller extends \Piwik\Plugin\Controller ) { return; } + $websiteId = Common::getRequestVar('idSite', false, 'int'); + if ($websiteId) { + $website = new Site($websiteId); - $datetimeCreationDate = $website->getCreationDate()->getDatetime(); + $datetimeCreationDate = $website->getCreationDate()->getDatetime(); $creationDateLocalTimezone = Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d'); - $todayLocalTimezone = Date::factory('now', $website->getTimezone())->toString('Y-m-d'); + $todayLocalTimezone = Date::factory('now', $website->getTimezone())->toString('Y-m-d'); + if ($creationDateLocalTimezone == $todayLocalTimezone) { Piwik::redirectToModule('CoreHome', 'index', array('date' => 'today', diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 187f356c5e5e9928cc511e1d31a27e6df96cc473..f833b1e5cc8c838359d499ce93022a0872caf606 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -69,6 +69,7 @@ class CoreHome extends \Piwik\Plugin $stylesheets[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less"; $stylesheets[] = "plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less"; $stylesheets[] = "plugins/CoreHome/angularjs/dialogtoggler/ngdialog.less"; + $stylesheets[] = "plugins/CoreHome/angularjs/notification/notification.directive.less"; } public function getJsFiles(&$jsFiles) @@ -149,6 +150,9 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.directive.js"; $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.controller.js"; $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler-urllistener.service.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.controller.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.directive.js"; } public function getClientSideTranslationKeys(&$translationKeys) diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php index 915139d30000fcf0d8ddd6e95314469270b362f1..b0922f05bfbce38bb1e873caebc1ea248f2c2b2e 100644 --- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php +++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php @@ -86,7 +86,7 @@ class RowEvolution if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set."); $this->label = ResponseBuilder::getLabelFromRequest($_GET); - if(!is_array($this->label)) { + if (!is_array($this->label)) { throw new Exception("Expected label to be an array, got instead: " . $this->label); } $this->label = $this->label[0]; @@ -337,7 +337,7 @@ class RowEvolution $labelPretty = $dataTableMap->getColumn('label_html'); $labelPretty = array_filter($labelPretty, 'strlen'); $labelPretty = current($labelPretty); - if(!empty($labelPretty)) { + if (!empty($labelPretty)) { return $labelPretty; } return $rowLabel; diff --git a/plugins/CoreHome/Visitor.php b/plugins/CoreHome/Visitor.php index 688d1c8bb62c1ce387d6542949b6a1c8b6e6b57d..35b64b3d30eb099e77aed4928bf1a3b0ba6d6120 100644 --- a/plugins/CoreHome/Visitor.php +++ b/plugins/CoreHome/Visitor.php @@ -102,10 +102,10 @@ class Visitor function getUserId() { if (isset($this->details['user_id']) - && !is_null($this->details['user_id'])) { + && strlen($this->details['user_id']) > 0) { return $this->details['user_id']; } - return false; + return null; } } \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/common/directives/translate.js b/plugins/CoreHome/angularjs/common/directives/translate.js index 4cc20e83b3d88da0465320c35b872eb1b7d962e8..5a9588f7c6f31fab33ca9d85ac056890f3c7f751 100644 --- a/plugins/CoreHome/angularjs/common/directives/translate.js +++ b/plugins/CoreHome/angularjs/common/directives/translate.js @@ -12,6 +12,8 @@ * within the sprintf args. Using the filter, this is not possible w/o manually sanitizing * and creating trusted HTML, which is not as safe. * + * Note: nesting this directive is not supported. + * * Usage: * <span piwik-translate="Plugin_TranslationToken"> * first arg::<strong>second arg</strong>::{{ unsafeDataThatWillBeSanitized }} @@ -22,10 +24,8 @@ function piwikTranslate() { return { + priority: 1, restrict: 'A', - scope: { - piwikTranslate: '@' - }, compile: function(element, attrs) { var parts = element.html().split('::'), translated = _pk_translate(attrs.piwikTranslate, parts); diff --git a/plugins/CoreHome/angularjs/common/services/piwik-api.js b/plugins/CoreHome/angularjs/common/services/piwik-api.js index 7b15c48fa8ab7a5ae34993cff601b191b4a1cc19..80ac84312d956f628ec047f86471676d1d9dd67b 100644 --- a/plugins/CoreHome/angularjs/common/services/piwik-api.js +++ b/plugins/CoreHome/angularjs/common/services/piwik-api.js @@ -44,7 +44,9 @@ } function createResponseErrorNotification(response, options) { - if (response.message) { + if (response.message + && options.createErrorNotification + ) { var UI = require('piwik/UI'); var notification = new UI.Notification(); notification.show(response.message, { @@ -66,6 +68,10 @@ options = {}; } + if (options.createErrorNotification === undefined) { + options.createErrorNotification = true; + } + var deferred = $q.defer(), requestPromise = deferred.promise; @@ -271,7 +277,7 @@ * @deprecated */ abort: abort, - abortAll: abortAll, + abortAll: abortAll }; } })(); \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.controller.js b/plugins/CoreHome/angularjs/notification/notification.controller.js new file mode 100644 index 0000000000000000000000000000000000000000..645cc13e594c7587f995cc750e45f571c4851562 --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.controller.js @@ -0,0 +1,34 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('NotificationController', NotificationController); + + NotificationController.$inject = ['piwikApi']; + + function NotificationController(piwikApi) { + /** + * Marks a persistent notification as read so it will not reappear on the next page + * load. + */ + this.markNotificationAsRead = function () { + var notificationId = this.notificationId; + if (!notificationId) { + return; + } + + piwikApi.post( + { // GET params + module: 'CoreHome', + action: 'markNotificationAsRead' + }, + { // POST params + notificationId: notificationId + } + ); + }; + } +})(); \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.html b/plugins/CoreHome/angularjs/notification/notification.directive.html new file mode 100644 index 0000000000000000000000000000000000000000..a643be8a3b3da2df52f46e0bd4ac28843c9efdfc --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.html @@ -0,0 +1,8 @@ +<div class="notification system"> + <button type="button" class="close" data-dismiss="alert" ng-if="!noclear" ng-click="notification.markNotificationAsRead()">×</button> + <strong ng-if="title">{{ title }}</strong> + + <!-- ng-transclude causes directive child elements to be added here --> + <div ng-transclude style="display:inline-block"></div> + +</div> \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.js b/plugins/CoreHome/angularjs/notification/notification.directive.js new file mode 100644 index 0000000000000000000000000000000000000000..167c61575b317e3835f83d738ea9a2542af70eac --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.js @@ -0,0 +1,95 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Directive to show a notification. + * + * Note: using this directive is preferred over the Notification class (which uses jquery + * exclusively). + * + * Supports the following attributes: + * + * * **context**: Either 'success', 'error', 'info', 'warning' + * * **type**: Either 'toast', 'persistent', 'transient' + * * **noclear**: If truthy, no clear button is displayed. For persistent notifications, has no effect. + * + * Usage: + * + * <div piwik-notification context="success" type="persistent" noclear="true"> + * <strong>Info: </strong>My notification message. + * </div> + */ +(function () { + angular.module('piwikApp').directive('piwikNotification', piwikNotification); + + piwikNotification.$inject = ['piwik', '$timeout']; + + function piwikNotification(piwik, $timeout) { + return { + restrict: 'A', + scope: { + notificationId: '@?', + title: '@?notificationTitle', // TODO: shouldn't need this since the title can be specified within + // HTML of the node that uses the directive. + context: '@?', + type: '@?', + noclear: '=?' + }, + transclude: true, + templateUrl: 'plugins/CoreHome/angularjs/notification/notification.directive.html?cb=' + piwik.cacheBuster, + controller: 'NotificationController', + controllerAs: 'notification', + link: function (scope, element, attrs) { + if (scope.context) { + element.children('.notification').addClass('notification-' + scope.context); + } + + if (scope.type == 'persistent') { + // otherwise it is never possible to dismiss the notification + scope.noclear = false; + } + + if (scope.notificationId) { + closeExistingNotificationHavingSameIdIfNeeded(); + } + + if ('toast' == scope.type) { + addToastEvent(); + } + + if (!scope.noclear) { + addCloseEvent(); + } + + function closeExistingNotificationHavingSameIdIfNeeded() { + // TODO: instead of doing a global query for notification, there should be a notification-container + // directive that manages notifications. + var existingNode = angular.element('.system.notification[notification-id=' + attrs.id + ']'); + if (existingNode && existingNode.length) { + existingNode.remove(); + } + } + + function addToastEvent() { + $timeout(function () { + element.fadeOut('slow', function() { + element.remove(); + }); + }, 12 * 1000); + } + + function addCloseEvent() { + element.on('click', '.close', function (event) { + if (event && event.delegateTarget) { + angular.element(event.delegateTarget).remove(); + } + }); + } + } + }; + } +})(); \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.less b/plugins/CoreHome/angularjs/notification/notification.directive.less new file mode 100644 index 0000000000000000000000000000000000000000..bc3c5e3f5fd6922bde7bec2b6c56240022c6e9d4 --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.less @@ -0,0 +1,84 @@ +.system.notification { + color: #9b7a44; + float: none; + + padding: 15px 35px 15px 15px; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: #ffffe0; + border: 1px solid #e6db55; + border-radius: 3px; + font-size: 14px; + margin: 0px; + margin-bottom: 16px; + + a { + color: #9b7a44; + text-decoration: underline; + } + + .close { + position: relative; + top: -5px; + right: -28px; + line-height: 20px; + } + + button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + } + .close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); + } + + &.notification-success { + background-color: #dff0d8; + border-color: #c3d6b7; + color: #468847; + + a { + color: #468847 + } + } + &.notification-danger, + &.notification-error { + background-color: #f2dede; + border-color: #d5bfc4; + color: #b94a48; + + a { + color: #b94a48 + } + } + &.notification-info { + background-color: #d9edf7; + border-color: #a7d3e3; + color: #3a87ad; + + a { + color: #3a87ad + } + } + + &.notification-block { + padding-top: 14px; + padding-bottom: 14px; + } + &.notification-block > p, + &.notification-block > ul { + margin-bottom: 0; + } + &.notification-block p + p { + margin-top: 5px; + } +} \ No newline at end of file diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js index 7bd92042a5f21a1a4ff583c74d87a47a66e24eb9..266477446e619cd938980b0e6e4a5ef01c042bbe 100644 --- a/plugins/CoreHome/javascripts/notification.js +++ b/plugins/CoreHome/javascripts/notification.js @@ -50,26 +50,8 @@ options = {}; } - if ('persistent' == options.type) { - // otherwise it is never possible to dismiss the notification - options.noclear = false; - } - - closeExistingNotificationHavingSameIdIfNeeded(options); - - var template = generateNotificationHtmlMarkup(options, message); - var $notificationNode = placeNotification(template, options); - this.$node = $notificationNode; - - if ('persistent' == options.type) { - addPersistentEvent($notificationNode); - } else if ('toast' == options.type) { - addToastEvent($notificationNode); - } - - if (!options.noclear) { - addCloseEvent($notificationNode); - } + var template = generateNotificationHtmlMarkup(options, message); + this.$node = placeNotification(template, options); }; Notification.prototype.scrollToNotification = function () { @@ -80,69 +62,37 @@ exports.Notification = Notification; - function closeExistingNotificationHavingSameIdIfNeeded(options) - { - if (!options.id) { - return; - } - - var $existingNode = $('.system.notification[data-id=' + options.id + ']'); - if ($existingNode && $existingNode.length) { - $existingNode.remove(); - } - } - function generateNotificationHtmlMarkup(options, message) { - var template = buildNotificationStart(options); - - if (!options.noclear) { - template += buildClearButton(); - } - - if (options.title) { - template += buildTitle(options); - } - - template += message; - template += buildNotificationEnd(); - - return template; - } - - function buildNotificationStart(options) { - var template = '<div class="notification system'; - - if (options.context) { - template += ' notification-' + options.context; - } - - template += '"'; - - if (options.id) { - template += ' data-id="' + options.id + '"'; + var attributeMapping = { + id: 'notification-id', + title: 'notification-title', + context: 'context', + type: 'type', + noclear: 'noclear' + }, + html = '<div piwik-notification'; + + for (var key in attributeMapping) { + if (attributeMapping.hasOwnProperty(key) + && options[key] + ) { + html += ' ' + attributeMapping[key] + '="' + options[key].toString().replace(/"/g, """) + '"'; + } } - template += '>'; - - return template; - } - - function buildNotificationEnd() { - return '</div>'; - } - - function buildClearButton() { - return '<button type="button" class="close" data-dismiss="alert">×</button>'; - } + html += '>' + message + '</div>'; - function buildTitle(options) { - return '<strong>' + options.title + '</strong> '; + return html; } function placeNotification(template, options) { - var $notificationNode = $(template); + // compile the template in angular + angular.element(document).injector().invoke(function ($compile, $rootScope) { + $compile($notificationNode)($rootScope.$new(true)); + }); + if (options.style) { $notificationNode.css(options.style); } @@ -158,41 +108,4 @@ return $notificationNode; } - - function addToastEvent($notificationNode) - { - setTimeout(function () { - $notificationNode.fadeOut( 'slow', function() { - $notificationNode.remove(); - $notificationNode = null; - }); - }, 12 * 1000); - } - - function addCloseEvent($notificationNode) { - $notificationNode.on('click', '.close', function (event) { - if (event && event.delegateTarget) { - $(event.delegateTarget).remove(); - } - }); - }; - - function addPersistentEvent($notificationNode) { - var notificationId = $notificationNode.data('id'); - - if (!notificationId) { - return; - } - - $notificationNode.on('click', '.close', function (event) { - var ajaxHandler = new ajaxHelper(); - ajaxHandler.addParams({ - module: 'CoreHome', - action: 'markNotificationAsRead' - }, 'GET'); - ajaxHandler.addParams({notificationId: notificationId}, 'POST'); - ajaxHandler.send(true); - }); - }; - })(jQuery, require); \ No newline at end of file diff --git a/plugins/CoreHome/stylesheets/notification.less b/plugins/CoreHome/stylesheets/notification.less index 23b29e1fe42070e316b0efb2e88241c415788212..36f7085f6f8263d1d8c8950a547446d05ebdd4ec 100644 --- a/plugins/CoreHome/stylesheets/notification.less +++ b/plugins/CoreHome/stylesheets/notification.less @@ -5,89 +5,4 @@ .notification { margin: 10px; } -} - -.system.notification { - color: #9b7a44; - float: none; - - padding: 15px 35px 15px 15px; - text-shadow: 0 1px 0 rgba(255,255,255,.5); - background-color: #ffffe0; - border: 1px solid #e6db55; - border-radius: 3px; - font-size: 14px; - margin: 0px; - margin-bottom: 16px; - - a { - color: #9b7a44; - text-decoration: underline; - } - - .close { - position: relative; - top: -5px; - right: -28px; - line-height: 20px; - } - - button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; - } - .close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 20px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); - } - - &.notification-success { - background-color: #dff0d8; - border-color: #c3d6b7; - color: #468847; - - a { - color: #468847 - } - } - &.notification-danger, - &.notification-error { - background-color: #f2dede; - border-color: #d5bfc4; - color: #b94a48; - - a { - color: #b94a48 - } - } - &.notification-info { - background-color: #d9edf7; - border-color: #a7d3e3; - color: #3a87ad; - - a { - color: #3a87ad - } - } - - &.notification-block { - padding-top: 14px; - padding-bottom: 14px; - } - &.notification-block > p, - &.notification-block > ul { - margin-bottom: 0; - } - &.notification-block p + p { - margin-top: 5px; - } -} +} \ No newline at end of file diff --git a/plugins/CoreHome/templates/_topBarTopMenu.twig b/plugins/CoreHome/templates/_topBarTopMenu.twig index 5f5831237aa12e0907242e7cdb4693d373fab5d4..fc09b62f78f9f1f3ca99d66fcff275cbdbe1b542 100644 --- a/plugins/CoreHome/templates/_topBarTopMenu.twig +++ b/plugins/CoreHome/templates/_topBarTopMenu.twig @@ -36,17 +36,8 @@ menu-title="{{ helloAlias|trim }}" piwik-menudropdown> - {% if userLogin != 'anonymous' %} - {% if isAdminLayout is defined and currentModule == 'UsersManager' and currentAction == 'userSettings' %} - <a class="item active" href="index.php?module=CoreAdminHome">{{ 'General_Settings'|translate }}</a> - {% else %} - <a class="item" href="index.php?module=CoreAdminHome">{{ 'General_Settings'|translate }}</a> - {% endif %} - {% endif %} - {% for lev1UserLabel,lev1UserMenu in userMenu if lev1UserLabel|slice(0,1) != '_' %} - - {% if userLogin != 'anonymous' or not loop.first %} + {% if not loop.first %} <hr class="item separator"/> {% endif %} diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index 01c28378578f3cb388b6ae5608e9ab293bd8479a..70942c7715bc8668512a01b0780a75adad5b4080 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -293,7 +293,7 @@ class Controller extends Plugin\ControllerAdmin $suffix = Piwik::translate('CorePluginsAdmin_PluginNotWorkingAlternative'); // If the plugin has been renamed, we do not show message to ask user to update plugin - if($pluginName != Request::renameModule($pluginName)) { + if ($pluginName != Request::renameModule($pluginName)) { $suffix = "You may uninstall the plugin or manually delete the files in piwik/plugins/$pluginName/"; } @@ -359,7 +359,7 @@ class Controller extends Plugin\ControllerAdmin return $message; } - if(Common::isPhpCliMode()) { + if (Common::isPhpCliMode()) { Piwik_ExitWithMessage("Error:" . var_export($lastError, true)); } diff --git a/plugins/CorePluginsAdmin/PluginInstaller.php b/plugins/CorePluginsAdmin/PluginInstaller.php index 58fd074909b5ea1d23198f05de75d0e5d7e1a034..8bb9c475d07be1d8d067af4b2944f46271a6af2c 100644 --- a/plugins/CorePluginsAdmin/PluginInstaller.php +++ b/plugins/CorePluginsAdmin/PluginInstaller.php @@ -155,7 +155,7 @@ class PluginInstaller private function makeSureThereAreNoMissingRequirements($metadata) { $requires = array(); - if(!empty($metadata->require)) { + if (!empty($metadata->require)) { $requires = (array) $metadata->require; } diff --git a/plugins/CorePluginsAdmin/UpdateCommunication.php b/plugins/CorePluginsAdmin/UpdateCommunication.php index ad6c570e7e7450c381c9d0aed2da38f6fb799e90..3ed004993f5b937a2819879760b0f9c05e23eba9 100644 --- a/plugins/CorePluginsAdmin/UpdateCommunication.php +++ b/plugins/CorePluginsAdmin/UpdateCommunication.php @@ -109,34 +109,8 @@ class UpdateCommunication $hasPluginUpdate = $hasPluginUpdate || !$plugin['isTheme']; } - $subject = Piwik::translate('CoreUpdater_NotificationSubjectAvailablePluginUpdate'); - $message = Piwik::translate('ScheduledReports_EmailHello'); - $message .= "\n\n"; - $message .= Piwik::translate('CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate'); - $message .= "\n\n"; - - foreach ($pluginsToBeNotified as $plugin) { - $message .= sprintf(' * %s %s', $plugin['name'], $plugin['latestVersion']); - $message .= "\n"; - } - - $message .= "\n"; - - $host = SettingsPiwik::getPiwikUrl(); - if ($hasThemeUpdate) { - $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdateThemes') . "\n"; - $message .= $host. 'index.php?module=CorePluginsAdmin&action=themes'; - } - if ($hasPluginUpdate) { - if ($hasThemeUpdate) { - $message .= "\n\n"; - } - $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdatePlugins') . "\n"; - $message .= $host. 'index.php?module=CorePluginsAdmin&action=plugins'; - } - - $message .= "\n\n"; - $message .= Piwik::translate('Installation_HappyAnalysing'); + $subject = Piwik::translate('CoreUpdater_NotificationSubjectAvailablePluginUpdate'); + $message = $this->buildNotificationMessage($pluginsToBeNotified, $hasThemeUpdate, $hasPluginUpdate); $this->sendEmailNotification($subject, $message); } @@ -161,24 +135,24 @@ class UpdateCommunication } } - private function setHasLatestUpdateNotificationReceived($plugin) + protected function setHasLatestUpdateNotificationReceived($plugin) { $latestVersion = $this->getLatestVersion($plugin); Option::set($this->getNotificationSentOptionName($plugin), $latestVersion); } - private function getLatestVersionSent($plugin) + protected function getLatestVersionSent($plugin) { return Option::get($this->getNotificationSentOptionName($plugin)); } - private function getLatestVersion($plugin) + protected function getLatestVersion($plugin) { return $plugin['latestVersion']; } - private function hasNotificationAlreadyReceived($plugin) + protected function hasNotificationAlreadyReceived($plugin) { $latestVersion = $this->getLatestVersion($plugin); $lastVersionSent = $this->getLatestVersionSent($plugin); @@ -192,7 +166,7 @@ class UpdateCommunication return false; } - private function getNotificationSentOptionName($plugin) + protected function getNotificationSentOptionName($plugin) { return 'last_update_communication_sent_plugin_' . $plugin['name']; } @@ -207,4 +181,39 @@ class UpdateCommunication return $plugins; } + + protected function buildNotificationMessage($pluginsToBeNotified, $hasThemeUpdate, $hasPluginUpdate) + { + $message = Piwik::translate('ScheduledReports_EmailHello'); + $message .= "\n\n"; + $message .= Piwik::translate('CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate'); + $message .= "\n\n"; + + foreach ($pluginsToBeNotified as $plugin) { + $message .= sprintf(' * %s %s', $plugin['name'], $plugin['latestVersion']); + $message .= "\n"; + } + + $message .= "\n"; + + $host = SettingsPiwik::getPiwikUrl(); + + if ($hasThemeUpdate) { + $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdateThemes') . "\n"; + $message .= $host . 'index.php?module=CorePluginsAdmin&action=themes'; + } + + if ($hasPluginUpdate) { + if ($hasThemeUpdate) { + $message .= "\n\n"; + } + $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdatePlugins') . "\n"; + $message .= $host . 'index.php?module=CorePluginsAdmin&action=plugins'; + } + + $message .= "\n\n"; + $message .= Piwik::translate('Installation_HappyAnalysing'); + + return $message; + } } diff --git a/plugins/CoreUpdater/Commands/Update.php b/plugins/CoreUpdater/Commands/Update.php index e5a0e39d33ff6c7dccd0a0f133e3aa9790db96c4..108281c4607520a7d1e5297f26e8a2629d3d9b98 100644 --- a/plugins/CoreUpdater/Commands/Update.php +++ b/plugins/CoreUpdater/Commands/Update.php @@ -40,7 +40,7 @@ class Update extends ConsoleCommand try { $this->makeUpdate($input, $output, $doDryRun); - if(!$doDryRun) { + if (!$doDryRun) { $this->writeSuccessMessage($output, array("Piwik has been successfully updated!")); } diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php index 1d1d689b56c51b7a3c2fdf179c939aba49308c11..cc27c3e9aa4a518c0f3c249ef467787b6ab47872 100644 --- a/plugins/CoreUpdater/Controller.php +++ b/plugins/CoreUpdater/Controller.php @@ -327,11 +327,11 @@ class Controller extends \Piwik\Plugin\Controller $doExecuteUpdates = Common::getRequestVar('updateCorePlugins', 0, 'integer') == 1; - if(is_null($doDryRun)) { + if (is_null($doDryRun)) { $doDryRun = !$doExecuteUpdates; } - if($doDryRun) { + if ($doDryRun) { $viewWelcome->queries = $updater->getSqlQueriesToExecute(); $viewWelcome->isMajor = $updater->hasMajorDbUpdate(); $this->doWelcomeUpdates($viewWelcome, $componentsWithUpdateFile); diff --git a/plugins/CoreUpdater/UpdateCommunication.php b/plugins/CoreUpdater/UpdateCommunication.php index 5a113f74d53364b03e2729b80f565b6c42ab614a..cd52e80a3ef64fbed49beb3dc9dacadb26123c55 100644 --- a/plugins/CoreUpdater/UpdateCommunication.php +++ b/plugins/CoreUpdater/UpdateCommunication.php @@ -76,7 +76,7 @@ class UpdateCommunication $this->sendEmailNotification($subject, $message); } - private function isVersionLike($latestVersion) + protected function isVersionLike($latestVersion) { return strlen($latestVersion) < 18; } @@ -101,7 +101,7 @@ class UpdateCommunication } } - private function isNewVersionAvailable() + protected function isNewVersionAvailable() { UpdateCheck::check(); @@ -119,7 +119,7 @@ class UpdateCommunication return $hasUpdate; } - private function hasNotificationAlreadyReceived() + protected function hasNotificationAlreadyReceived() { $latestVersion = $this->getLatestVersion(); $lastVersionSent = $this->getLatestVersionSent(); diff --git a/plugins/CustomVariables/Archiver.php b/plugins/CustomVariables/Archiver.php index 241e2772a0a5ba211abbae293866f33e39a9151d..47e1363dc4e3b52026887ad0e672addc59ce018d 100644 --- a/plugins/CustomVariables/Archiver.php +++ b/plugins/CustomVariables/Archiver.php @@ -39,7 +39,7 @@ class Archiver extends \Piwik\Plugin\Archiver { parent::__construct($processor); - if($processor->getParams()->getSite()->isEcommerceEnabled()) { + if ($processor->getParams()->getSite()->isEcommerceEnabled()) { $this->maximumRowsInDataTableLevelZero = self::MAX_ROWS_WHEN_ECOMMERCE; $this->maximumRowsInSubDataTable = self::MAX_ROWS_WHEN_ECOMMERCE; } else { diff --git a/plugins/CustomVariables/tests/ModelTest.php b/plugins/CustomVariables/tests/ModelTest.php index bfca81c777c003f20bb43b45b6ef79fc3c253a95..3ed82f552a734800d03505760f3a87c5d9cf7deb 100644 --- a/plugins/CustomVariables/tests/ModelTest.php +++ b/plugins/CustomVariables/tests/ModelTest.php @@ -7,16 +7,36 @@ */ namespace Piwik\Plugins\CustomVariables\tests; +use Piwik\Common; use Piwik\Db; +use Piwik\DbHelper; use Piwik\Plugins\CustomVariables\Model; /** * @group CustomVariables * @group ModelTest * @group Database + * @group CustomVariables_ModelTest */ class ModelTest extends \DatabaseTestCase { + private static $cvarScopes = array('log_link_visit_action', 'log_visit', 'log_conversion'); + + public function setUp() + { + // do not call parent::setUp() since it expects database to be created, + // but DB for this test is removed in tearDown + + self::$fixture->performSetUp(); + } + + public function tearDown() + { + parent::tearDown(); + + self::$fixture->performTearDown(); + } + /** * @expectedException \Exception */ @@ -35,7 +55,7 @@ class ModelTest extends \DatabaseTestCase public function testGetAllScopes() { - $this->assertEquals(array('log_link_visit_action', 'log_visit', 'log_conversion'), Model::getScopes()); + $this->assertEquals(self::$cvarScopes, Model::getScopes()); } public function test_Install_Uninstall() diff --git a/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml b/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml index 6626b1a24dc79921d1bab7b1931671706e293fd2..2e53cbf4d8ecd7e8cf81fd88e1b6373b38465a01 100644 --- a/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml +++ b/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml @@ -69,7 +69,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml b/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml index 6626b1a24dc79921d1bab7b1931671706e293fd2..2e53cbf4d8ecd7e8cf81fd88e1b6373b38465a01 100644 --- a/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml +++ b/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml @@ -69,7 +69,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/plugins/Dashboard/Controller.php b/plugins/Dashboard/Controller.php index 5e319ddb2500d224e713abca7cbbc9bb0e5c10c2..c7a89a75a5d86d82634a1368c966e5963c627064 100644 --- a/plugins/Dashboard/Controller.php +++ b/plugins/Dashboard/Controller.php @@ -104,38 +104,13 @@ class Controller extends \Piwik\Plugin\Controller $session->dashboardLayout = $layout; $session->setExpirationSeconds(1800); } else { - $this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout); + $this->getModel()->updateLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout); } } - /** - * Records the layout in the DB for the given user. - * - * @param string $login - * @param int $idDashboard - * @param string $layout - */ - protected function saveLayoutForUser($login, $idDashboard, $layout) - { - $paramsBind = array($login, $idDashboard, $layout, $layout); - $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?', - Common::prefixTable('user_dashboard')); - Db::query($query, $paramsBind); - } - - /** - * Updates the name of a dashboard - * - * @param string $login - * @param int $idDashboard - * @param string $name - */ - protected function updateDashboardName($login, $idDashboard, $name) + private function getModel() { - $paramsBind = array($name, $login, $idDashboard); - $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?', - Common::prefixTable('user_dashboard')); - Db::query($query, $paramsBind); + return new Model(); } /** @@ -153,9 +128,7 @@ class Controller extends \Piwik\Plugin\Controller // first layout can't be removed if ($idDashboard != 1) { - $query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?', - Common::prefixTable('user_dashboard')); - Db::query($query, array($idDashboard, Piwik::getCurrentUserLogin())); + $this->getModel()->deleteDashboardForUser($idDashboard, Piwik::getCurrentUserLogin()); } } @@ -171,7 +144,7 @@ class Controller extends \Piwik\Plugin\Controller return '[]'; } - $login = Piwik::getCurrentUserLogin(); + $login = Piwik::getCurrentUserLogin(); $dashboards = $this->dashboard->getAllDashboards($login); Json::sendHeaderJSON(); @@ -189,38 +162,22 @@ class Controller extends \Piwik\Plugin\Controller if (Piwik::isUserIsAnonymous()) { return '0'; } - $user = Piwik::getCurrentUserLogin(); - $nextId = $this->getNextIdDashboard($user); - $name = urldecode(Common::getRequestVar('name', '', 'string')); - $type = urldecode(Common::getRequestVar('type', 'default', 'string')); + $name = urldecode(Common::getRequestVar('name', '', 'string')); + $type = urldecode(Common::getRequestVar('type', 'default', 'string')); $layout = '{}'; + $login = Piwik::getCurrentUserLogin(); if ($type == 'default') { $layout = $this->dashboard->getDefaultLayout(); } - $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)', - Common::prefixTable('user_dashboard')); - Db::query($query, array($user, $nextId, $name, $layout)); + $nextId = $this->getModel()->createNewDashboardForUser($login, $name, $layout); Json::sendHeaderJSON(); return Common::json_encode($nextId); } - private function getNextIdDashboard($login) - { - $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?', - Common::prefixTable('user_dashboard')); - $nextId = Db::fetchOne($nextIdQuery, array($login)); - - if (empty($nextId)) { - $nextId = 1; - return $nextId; - } - return $nextId; - } - public function copyDashboardToUser() { $this->checkTokenInUrl(); @@ -228,18 +185,16 @@ class Controller extends \Piwik\Plugin\Controller if (!Piwik::hasUserSuperUserAccess()) { return '0'; } + $login = Piwik::getCurrentUserLogin(); - $name = urldecode(Common::getRequestVar('name', '', 'string')); - $user = urldecode(Common::getRequestVar('user', '', 'string')); + $name = urldecode(Common::getRequestVar('name', '', 'string')); + $user = urldecode(Common::getRequestVar('user', '', 'string')); $idDashboard = Common::getRequestVar('dashboardId', 0, 'int'); + $layout = $this->dashboard->getLayoutForUser($login, $idDashboard); if ($layout !== false) { - $nextId = $this->getNextIdDashboard($user); - - $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)', - Common::prefixTable('user_dashboard')); - Db::query($query, array($user, $nextId, $name, $layout)); + $nextId = $this->getModel()->createNewDashboardForUser($user, $name, $layout); Json::sendHeaderJSON(); return Common::json_encode($nextId); @@ -255,17 +210,18 @@ class Controller extends \Piwik\Plugin\Controller { $this->checkTokenInUrl(); - $layout = Common::unsanitizeInputValue(Common::getRequestVar('layout')); + $layout = Common::unsanitizeInputValue(Common::getRequestVar('layout')); $idDashboard = Common::getRequestVar('idDashboard', 1, 'int'); - $name = Common::getRequestVar('name', '', 'string'); + $name = Common::getRequestVar('name', '', 'string'); + if (Piwik::isUserIsAnonymous()) { $session = new SessionNamespace("Dashboard"); $session->dashboardLayout = $layout; $session->setExpirationSeconds(1800); } else { - $this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout); + $this->getModel()->updateLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout); if (!empty($name)) { - $this->updateDashboardName(Piwik::getCurrentUserLogin(), $idDashboard, $name); + $this->getModel()->updateDashboardName(Piwik::getCurrentUserLogin(), $idDashboard, $name); } } } @@ -279,10 +235,7 @@ class Controller extends \Piwik\Plugin\Controller if (Piwik::hasUserSuperUserAccess()) { $layout = Common::unsanitizeInputValue(Common::getRequestVar('layout')); - $paramsBind = array('', '1', $layout, $layout); - $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?', - Common::prefixTable('user_dashboard')); - Db::query($query, $paramsBind); + $this->getModel()->createOrUpdateDashboard('', '1', $layout); } } diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php index cb545465ff215f99e053c88bd2434d06544aa84d..1ff5a98182fbc209f41164b169bb518005c9f257 100644 --- a/plugins/Dashboard/Dashboard.php +++ b/plugins/Dashboard/Dashboard.php @@ -10,7 +10,6 @@ namespace Piwik\Plugins\Dashboard; use Piwik\Common; use Piwik\Db; -use Piwik\DbHelper; use Piwik\Piwik; use Piwik\WidgetsList; @@ -42,10 +41,7 @@ class Dashboard extends \Piwik\Plugin */ public function getLayoutForUser($login, $idDashboard) { - $paramsBind = array($login, $idDashboard); - $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?', - Common::prefixTable('user_dashboard')); - $return = Db::fetchAll($query, $paramsBind); + $return = $this->getModel()->getLayoutForUser($login, $idDashboard); if (count($return) == 0) { return false; @@ -54,6 +50,11 @@ class Dashboard extends \Piwik\Plugin return $return[0]['layout']; } + private function getModel() + { + return new Model(); + } + public function getDefaultLayout() { $defaultLayout = $this->getLayoutForUser('', 1); @@ -107,9 +108,7 @@ class Dashboard extends \Piwik\Plugin public function getAllDashboards($login) { - $dashboards = Db::fetchAll('SELECT iddashboard, name, layout - FROM ' . Common::prefixTable('user_dashboard') . - ' WHERE login = ? ORDER BY iddashboard', array($login)); + $dashboards = $this->getModel()->getAllDashboardsForUser($login); $nameless = 1; foreach ($dashboards as &$dashboard) { @@ -219,23 +218,17 @@ class Dashboard extends \Piwik\Plugin public function deleteDashboardLayout($userLogin) { - Db::query('DELETE FROM ' . Common::prefixTable('user_dashboard') . ' WHERE login = ?', array($userLogin)); + $this->getModel()->deleteAllLayoutsForUser($userLogin); } public function install() { - $dashboard = "login VARCHAR( 100 ) NOT NULL , - iddashboard INT NOT NULL , - name VARCHAR( 100 ) NULL DEFAULT NULL , - layout TEXT NOT NULL, - PRIMARY KEY ( login , iddashboard )"; - - DbHelper::createTable('user_dashboard', $dashboard); + Model::install(); } public function uninstall() { - Db::dropTables(Common::prefixTable('user_dashboard')); + Model::uninstall(); } public function getClientSideTranslationKeys(&$translationKeys) diff --git a/plugins/Dashboard/Menu.php b/plugins/Dashboard/Menu.php index 2ee0521623026145354bf34dc2f47ac26be6545c..f90c117e109b447c499dfde7a9d1625e6cc1cffb 100644 --- a/plugins/Dashboard/Menu.php +++ b/plugins/Dashboard/Menu.php @@ -41,11 +41,9 @@ class Menu extends \Piwik\Plugin\Menu { $userPreferences = new UserPreferences(); $idSite = $userPreferences->getDefaultWebsiteId(); - $tooltip = Piwik::translate('Dashboard_TopLinkTooltip', Site::getNameFor($idSite)); - $urlParams = $this->urlForModuleAction('CoreHome', 'index', array('idSite' => $idSite)) ; - + $urlParams = $this->urlForModuleActionWithDefaultUserParams('CoreHome', 'index') ; $menu->addItem('Dashboard_Dashboard', null, $urlParams, 1, $tooltip); } } diff --git a/plugins/Dashboard/Model.php b/plugins/Dashboard/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..39dcb065571add0153d099a1e99a542c466bd364 --- /dev/null +++ b/plugins/Dashboard/Model.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\Dashboard; + +use Piwik\Common; +use Piwik\Db; +use Piwik\DbHelper; + +class Model +{ + private static $rawPrefix = 'user_dashboard'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + + /** + * Returns the layout in the DB for the given user, or false if the layout has not been set yet. + * Parameters must be checked BEFORE this function call + * + * @param string $login + * @param int $idDashboard + * + * @return bool|string + */ + public function getLayoutForUser($login, $idDashboard) + { + $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?', + $this->table); + $bind = array($login, $idDashboard); + $layouts = Db::fetchAll($query, $bind); + + return $layouts; + } + + public function getAllDashboardsForUser($login) + { + $dashboards = Db::fetchAll('SELECT iddashboard, name, layout FROM ' . $this->table . + ' WHERE login = ? ORDER BY iddashboard', array($login)); + + return $dashboards; + } + + public function deleteAllLayoutsForUser($userLogin) + { + Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', array($userLogin)); + } + + /** + * Updates the name of a dashboard + * + * @param string $login + * @param int $idDashboard + * @param string $name + */ + public function updateDashboardName($login, $idDashboard, $name) + { + $bind = array($name, $login, $idDashboard); + $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?', $this->table); + Db::query($query, $bind); + } + + /** + * Removes the dashboard with the given id + */ + public function deleteDashboardForUser($idDashboard, $login) + { + $query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?', $this->table); + Db::query($query, array($idDashboard, $login)); + } + + /** + * Creates a new dashboard for the current user + * User needs to be logged in + */ + public function createNewDashboardForUser($login, $name, $layout) + { + $nextId = $this->getNextIdDashboard($login); + + $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)', $this->table); + $bind = array($login, $nextId, $name, $layout); + Db::query($query, $bind); + + return $nextId; + } + + /** + * Saves the layout as default + */ + public function createOrUpdateDashboard($login, $idDashboard, $layout) + { + $bind = array($login, $idDashboard, $layout, $layout); + $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?', + $this->table); + Db::query($query, $bind); + } + + private function getNextIdDashboard($login) + { + $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?', $this->table); + $nextId = Db::fetchOne($nextIdQuery, array($login)); + + if (empty($nextId)) { + $nextId = 1; + } + + return $nextId; + } + + /** + * Records the layout in the DB for the given user. + * + * @param string $login + * @param int $idDashboard + * @param string $layout + */ + public function updateLayoutForUser($login, $idDashboard, $layout) + { + $bind = array($login, $idDashboard, $layout, $layout); + $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?', + $this->table); + Db::query($query, $bind); + } + + public static function install() + { + $dashboard = "login VARCHAR( 100 ) NOT NULL , + iddashboard INT NOT NULL , + name VARCHAR( 100 ) NULL DEFAULT NULL , + layout TEXT NOT NULL, + PRIMARY KEY ( login , iddashboard )"; + + DbHelper::createTable(self::$rawPrefix, $dashboard); + } + + public static function uninstall() + { + Db::dropTables(Common::prefixTable(self::$rawPrefix)); + } +} diff --git a/plugins/DevicesDetection/functions.php b/plugins/DevicesDetection/functions.php index 1112be30502ff6618a67e2de97b3c27a638cf14c..12f18b19d171d753620e3dd8a5e8aeb1609332d6 100644 --- a/plugins/DevicesDetection/functions.php +++ b/plugins/DevicesDetection/functions.php @@ -179,7 +179,7 @@ function getOSFamilyFullNameExtended($label) return 'Bot'; } $label = OperatingSystemParser::getOsFamily($label); - if($label !== false) { + if ($label !== false) { return $label; } return Piwik::translate('General_Unknown'); @@ -292,4 +292,4 @@ function getBrowserEngineName($engineName) { return $engineName; } return Piwik::translate('General_Unknown'); -} \ No newline at end of file +} diff --git a/plugins/Events/API.php b/plugins/Events/API.php index 361f0ef965ff886063e56a8c9bd8060dde4d21f7..890979db2c73109e469ebda5f640c83e2aa7c0ba 100644 --- a/plugins/Events/API.php +++ b/plugins/Events/API.php @@ -94,7 +94,7 @@ class API extends \Piwik\Plugin\API */ public function getDefaultSecondaryDimension($apiMethod) { - if(isset($this->defaultMappingApiToSecondaryDimension[$apiMethod])) { + if (isset($this->defaultMappingApiToSecondaryDimension[$apiMethod])) { return $this->defaultMappingApiToSecondaryDimension[$apiMethod]; } return false; @@ -106,11 +106,11 @@ class API extends \Piwik\Plugin\API $secondaryDimension = $this->getDefaultSecondaryDimension($apiMethod); } $record = $this->mappingApiToRecord[$apiMethod]; - if(!is_array($record)) { + if (!is_array($record)) { return $record; } // when secondaryDimension is incorrectly set - if(empty($record[$secondaryDimension])) { + if (empty($record[$secondaryDimension])) { return key($record); } return $record[$secondaryDimension]; @@ -124,7 +124,7 @@ class API extends \Piwik\Plugin\API public function getSecondaryDimensions($apiMethod) { $records = $this->mappingApiToRecord[$apiMethod]; - if(!is_array($records)) { + if (!is_array($records)) { return false; } return array_keys($records); diff --git a/plugins/Events/Archiver.php b/plugins/Events/Archiver.php index 02fe3a8d83689e6fb101e34f8dafb43548704834..eb33d4899fee28bdb350884c3aabd449caa04570 100644 --- a/plugins/Events/Archiver.php +++ b/plugins/Events/Archiver.php @@ -225,7 +225,7 @@ class Archiver extends \Piwik\Plugin\Archiver */ protected function getDataArray($name) { - if(empty($this->arrays[$name])) { + if (empty($this->arrays[$name])) { $this->arrays[$name] = new DataArray(); } return $this->arrays[$name]; diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php index 3d335bd229f9ee955a0aa84bf98b2b5dd062e6df..6a55588af44138c8aceae95ff9287be3c8fc3843 100644 --- a/plugins/Events/Events.php +++ b/plugins/Events/Events.php @@ -184,7 +184,7 @@ class Events extends \Piwik\Plugin private function addRelatedReports($view, $secondaryDimension) { - if(empty($secondaryDimension)) { + if (empty($secondaryDimension)) { // eg. Row Evolution return; } @@ -194,7 +194,7 @@ class Events extends \Piwik\Plugin $apiMethod = $view->requestConfig->getApiMethodToRequest(); $secondaryDimensions = API::getInstance()->getSecondaryDimensions($apiMethod); - if(empty($secondaryDimensions)) { + if (empty($secondaryDimensions)) { return; } @@ -205,7 +205,7 @@ class Events extends \Piwik\Plugin . Piwik::translate('Events_SwitchToSecondaryDimension', ''); foreach($secondaryDimensions as $dimension) { - if($dimension == $secondaryDimension) { + if ($dimension == $secondaryDimension) { // don't show as related report the currently selected dimension continue; } diff --git a/plugins/ExampleCommand/Commands/HelloWorld.php b/plugins/ExampleCommand/Commands/HelloWorld.php index 5c0d415dcd9c6e02454039627849134974d9c4a3..d8dc781e3f1e1e37c54e267a46bab436b96f7500 100644 --- a/plugins/ExampleCommand/Commands/HelloWorld.php +++ b/plugins/ExampleCommand/Commands/HelloWorld.php @@ -15,18 +15,36 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** + * This class lets you define a new command. To read more about commands have a look at our Piwik Console guide on + * http://developer.piwik.org/guides/piwik-on-the-command-line + * + * As Piwik Console is based on the Symfony Console you might also want to have a look at + * http://symfony.com/doc/current/components/console/index.html */ class HelloWorld extends ConsoleCommand { + + /** + * This methods allows you to configure your command. Here you can define the name and description of your command + * as well as all options and arguments you expect when executing it. + */ protected function configure() { $this->setName('examplecommand:helloworld'); - $this->setDescription('ExampleCommand'); + $this->setDescription('ExampleCommandDescription'); $this->addOption('name', null, InputOption::VALUE_REQUIRED, 'Your name:'); } /** - * Execute command like: ./console examplecommand:helloworld --name="The Piwik Team" + * The actual task is defined in this method. Here you can access any option or argument that was defined on the + * command line via $input and write anything to the console via $output argument. + * In case anything went wrong during the execution you should throw an exception to make sure the user will get a + * useful error message and to make sure the command does not exit with the status code 0. + * + * Ideally, the actual command is quite short as it acts like a controller. It should only receive the input values, + * execute the task by calling a method of another class and output any useful information. + * + * Execute the command like: ./console examplecommand:helloworld --name="The Piwik Team" */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/plugins/ExampleRssWidget/RssRenderer.php b/plugins/ExampleRssWidget/RssRenderer.php index 254a30ce6f8cf31dc7e09ea0a05d42514069f3e8..7990b1acb8d021c935c95d2eb9af7d30884c74f6 100644 --- a/plugins/ExampleRssWidget/RssRenderer.php +++ b/plugins/ExampleRssWidget/RssRenderer.php @@ -54,7 +54,7 @@ class RssRenderer $i = 0; $items = array(); - if(!empty($rss->channel->item)) { + if (!empty($rss->channel->item)) { $items = $rss->channel->item; } foreach ($items as $post) { diff --git a/plugins/ExampleUI/templates/notifications.twig b/plugins/ExampleUI/templates/notifications.twig index 2212ead3d9550cd068f032831ec102a410aa2b69..1058a50a61dbe842c7e3b6a85f7dab2e689cb579 100644 --- a/plugins/ExampleUI/templates/notifications.twig +++ b/plugins/ExampleUI/templates/notifications.twig @@ -4,6 +4,11 @@ <h2>Inline notification example:</h2> <div style="display:inline-block;margin-top:10px;" id="exampleUI_notifications"> - {{ 'This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds?'|notification({'placeAt': '#exampleUI_notifications', 'title': 'Info: ', 'noclear': true, 'context': 'info'}) }} + <div piwik-notification + notification-title="Info:" + noclear="true" + context="info"> + This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds? + </div> </div> {% endblock %} \ No newline at end of file diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index 176cafad24d3400c8de0832a16d20e177da6e477..c76054aab9974eb1f9a694496b7c62bb35d389d7 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -54,14 +54,15 @@ class API extends \Piwik\Plugin\API //TODO calls to this function could be cached as static // would help UI at least, since some UI requests would call this 2-3 times.. $idSite = Site::getIdSitesFromIdSitesString($idSite); + if (empty($idSite)) { return array(); } + Piwik::checkUserHasViewAccess($idSite); - $goals = Db::fetchAll("SELECT * - FROM " . Common::prefixTable('goal') . " - WHERE idsite IN (" . implode(", ", $idSite) . ") - AND deleted = 0"); + + $goals = $this->getModel()->getActiveGoals($idSite); + $cleanedGoals = array(); foreach ($goals as &$goal) { if ($goal['match_attribute'] == 'manually') { @@ -71,6 +72,7 @@ class API extends \Piwik\Plugin\API } $cleanedGoals[$goal['idgoal']] = $goal; } + return $cleanedGoals; } @@ -91,35 +93,33 @@ class API extends \Piwik\Plugin\API public function addGoal($idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false) { Piwik::checkUserHasAdminAccess($idSite); + $this->checkPatternIsValid($patternType, $pattern, $matchAttribute); - $name = $this->checkName($name); + $name = $this->checkName($name); $pattern = $this->checkPattern($pattern); - // save in db - $db = Db::get(); - $idGoal = $db->fetchOne("SELECT max(idgoal) + 1 - FROM " . Common::prefixTable('goal') . " - WHERE idsite = ?", $idSite); - if ($idGoal == false) { - $idGoal = 1; - } - $db->insert(Common::prefixTable('goal'), - array( - 'idsite' => $idSite, - 'idgoal' => $idGoal, - 'name' => $name, - 'match_attribute' => $matchAttribute, - 'pattern' => $pattern, - 'pattern_type' => $patternType, - 'case_sensitive' => (int)$caseSensitive, - 'allow_multiple' => (int)$allowMultipleConversionsPerVisit, - 'revenue' => (float)$revenue, - 'deleted' => 0, - )); + $goal = array( + 'name' => $name, + 'match_attribute' => $matchAttribute, + 'pattern' => $pattern, + 'pattern_type' => $patternType, + 'case_sensitive' => (int)$caseSensitive, + 'allow_multiple' => (int)$allowMultipleConversionsPerVisit, + 'revenue' => (float)$revenue, + 'deleted' => 0, + ); + + $idGoal = $this->getModel()->createGoalForSite($idSite, $goal); + Cache::regenerateCacheWebsiteAttributes($idSite); return $idGoal; } + private function getModel() + { + return new Model(); + } + /** * Updates a Goal description. * Will not update or re-process the conversions already recorded @@ -139,21 +139,21 @@ class API extends \Piwik\Plugin\API public function updateGoal($idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false) { Piwik::checkUserHasAdminAccess($idSite); - $name = $this->checkName($name); + + $name = $this->checkName($name); $pattern = $this->checkPattern($pattern); $this->checkPatternIsValid($patternType, $pattern, $matchAttribute); - Db::get()->update(Common::prefixTable('goal'), - array( - 'name' => $name, - 'match_attribute' => $matchAttribute, - 'pattern' => $pattern, - 'pattern_type' => $patternType, - 'case_sensitive' => (int)$caseSensitive, - 'allow_multiple' => (int)$allowMultipleConversionsPerVisit, - 'revenue' => (float)$revenue, - ), - "idsite = '$idSite' AND idgoal = '$idGoal'" - ); + + $this->getModel()->updateGoal($idSite, $idGoal, array( + 'name' => $name, + 'match_attribute' => $matchAttribute, + 'pattern' => $pattern, + 'pattern_type' => $patternType, + 'case_sensitive' => (int) $caseSensitive, + 'allow_multiple' => (int) $allowMultipleConversionsPerVisit, + 'revenue' => (float) $revenue, + )); + Cache::regenerateCacheWebsiteAttributes($idSite); } @@ -188,12 +188,10 @@ class API extends \Piwik\Plugin\API public function deleteGoal($idSite, $idGoal) { Piwik::checkUserHasAdminAccess($idSite); - Db::query("UPDATE " . Common::prefixTable('goal') . " - SET deleted = 1 - WHERE idsite = ? - AND idgoal = ?", - array($idSite, $idGoal)); - Db::deleteAllRows(Common::prefixTable("log_conversion"), "WHERE idgoal = ? AND idsite = ?", "idvisit", 100000, array($idGoal, $idSite)); + + $this->getModel()->deleteGoal($idSite, $idGoal); + $this->getModel()->deleteGoalConversions($idSite, $idGoal); + Cache::regenerateCacheWebsiteAttributes($idSite); } @@ -204,11 +202,13 @@ class API extends \Piwik\Plugin\API protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts, $segment) { Piwik::checkUserHasViewAccess($idSite); + $recordNameFinal = $recordName; if ($abandonedCarts) { $recordNameFinal = Archiver::getItemRecordNameAbandonedCart($recordName); } - $archive = Archive::build($idSite, $period, $date, $segment); + + $archive = Archive::build($idSite, $period, $date, $segment); $dataTable = $archive->getDataTable($recordNameFinal); $dataTable->filter('Sort', array(Metrics::INDEX_ECOMMERCE_ITEM_REVENUE)); @@ -250,6 +250,7 @@ class API extends \Piwik\Plugin\API } return; } + $rowNotDefined = $dataTable->getRowFromLabel(\Piwik\Plugins\CustomVariables\Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED); if ($rowNotDefined) { $rowNotDefined->setColumn('label', $notDefinedStringPretty); diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php index f5a3f1f735fd61bfd3309a11178755c318ba3e0a..e7af6d6243d4ccc2c6038499fd603afbee070200 100644 --- a/plugins/Goals/Controller.php +++ b/plugins/Goals/Controller.php @@ -98,7 +98,7 @@ class Controller extends \Piwik\Plugin\Controller { $saveGET = $_GET; $filterEcommerce = Common::getRequestVar('filterEcommerce', self::ECOMMERCE_LOG_SHOW_ORDERS, 'int'); - if($filterEcommerce == self::ECOMMERCE_LOG_SHOW_ORDERS) { + if ($filterEcommerce == self::ECOMMERCE_LOG_SHOW_ORDERS) { $segment = urlencode('visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart'); } else { $segment = urlencode('visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart'); @@ -458,7 +458,7 @@ class Controller extends \Piwik\Plugin\Controller foreach ($allReports as $category => $reports) { $categoryText = Piwik::translate('Goals_ViewGoalsBy', $category); foreach ($reports as $report) { - if(empty($report['viewDataTable'])) { + if (empty($report['viewDataTable'])) { $report['viewDataTable'] = 'tableGoals'; } $customParams['viewDataTable'] = $report['viewDataTable']; diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php index 39a7c0b2dcb53758c29ec4cb65dd46f986c3e713..381c37f7eebb51be6b8e529ceea31459194c0512 100644 --- a/plugins/Goals/Goals.php +++ b/plugins/Goals/Goals.php @@ -135,7 +135,8 @@ class Goals extends \Piwik\Plugin */ public function deleteSiteGoals($idSite) { - Db::query("DELETE FROM " . Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite)); + $model = new Model(); + $model->deleteGoalsForSite($idSite); } /** diff --git a/plugins/Goals/Model.php b/plugins/Goals/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..2b35f7947d0d40abc4956847baffe8088a945864 --- /dev/null +++ b/plugins/Goals/Model.php @@ -0,0 +1,95 @@ +<?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\Goals; + +use Piwik\Common; +use Piwik\Db; + +class Model +{ + private static $rawPrefix = 'goal'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + + private function getNextIdGoal($idSite) + { + $db = $this->getDb(); + $idGoal = $db->fetchOne("SELECT max(idgoal) + 1 FROM " . $this->table . " + WHERE idsite = ?", $idSite); + + if (empty($idGoal)) { + $idGoal = 1; + } + + return $idGoal; + } + + public function createGoalForSite($idSite, $goal) + { + $db = $this->getDb(); + $goalId = $this->getNextIdGoal($idSite); + + $goal['idgoal'] = $goalId; + $goal['idsite'] = $idSite; + + $db->insert($this->table, $goal); + + return $goalId; + } + + public function updateGoal($idSite, $idGoal, $goal) + { + $idSite = (int) $idSite; + $idGoal = (int) $idGoal; + + $db = $this->getDb(); + $db->update($this->table, $goal, "idsite = '$idSite' AND idgoal = '$idGoal'"); + } + + // actually this should be in a log_conversion model + public function deleteGoalConversions($idSite, $idGoal) + { + $table = Common::prefixTable("log_conversion"); + + Db::deleteAllRows($table, "WHERE idgoal = ? AND idsite = ?", "idvisit", 100000, array($idGoal, $idSite)); + } + + public function getActiveGoals($idSite) + { + $idSite = array_map('intval', $idSite); + $goals = Db::fetchAll("SELECT * FROM " . $this->table . " + WHERE idsite IN (" . implode(", ", $idSite) . ") + AND deleted = 0"); + + return $goals; + } + + public function deleteGoalsForSite($idSite) + { + Db::query("DELETE FROM " . $this->table . " WHERE idsite = ? ", array($idSite)); + } + + public function deleteGoal($idSite, $idGoal) + { + $query = "UPDATE " . $this->table . " SET deleted = 1 + WHERE idsite = ? AND idgoal = ?"; + $bind = array($idSite, $idGoal); + + Db::query($query, $bind); + } + + private function getDb() + { + return Db::get(); + } +} diff --git a/plugins/Goals/Visualizations/Goals.php b/plugins/Goals/Visualizations/Goals.php index 0b8c2c5365523f9c60fe344a7b925922edd14617..9808621b145a569ef8bc0c1e1b3f4c04b7aa3d9d 100644 --- a/plugins/Goals/Visualizations/Goals.php +++ b/plugins/Goals/Visualizations/Goals.php @@ -31,7 +31,7 @@ class Goals extends HtmlTable { parent::beforeLoadDataTable(); - if($this->config->disable_subtable_when_show_goals) { + if ($this->config->disable_subtable_when_show_goals) { $this->config->subtable_controller_action = null; } diff --git a/plugins/Goals/tests/APITest.php b/plugins/Goals/tests/APITest.php index 0f393385d953cb6ed5ed7029e53bc3249ba70792..3263bc4972a78dd9a99bea28260d4482907d9956 100644 --- a/plugins/Goals/tests/APITest.php +++ b/plugins/Goals/tests/APITest.php @@ -8,6 +8,7 @@ namespace Piwik\Plugins\Goals\tests; use Piwik\Access; +use Piwik\Piwik; use Piwik\Plugins\Goals\API; use Piwik\Tests\Fixture; @@ -31,6 +32,9 @@ class APITest extends \DatabaseTestCase parent::setUp(); $this->api = API::getInstance(); + Fixture::createAccessInstance(); + Piwik::setUserHasSuperUserAccess(); + Fixture::createWebsite('2014-01-01 00:00:00'); Fixture::createWebsite('2014-01-01 00:00:00'); } diff --git a/plugins/ImageGraph/StaticGraph/GridGraph.php b/plugins/ImageGraph/StaticGraph/GridGraph.php index 3eeb1393db470a920c7d2779ddabb85e8a4d3235..4ffde4c2c478dafed3e957eba8532d21ff0ef6e2 100644 --- a/plugins/ImageGraph/StaticGraph/GridGraph.php +++ b/plugins/ImageGraph/StaticGraph/GridGraph.php @@ -419,11 +419,11 @@ abstract class GridGraph extends StaticGraph // see https://github.com/piwik/piwik/issues/3396 // protected function displayMinMaxValues() // { -// if($displayMinMax) +// if ($displayMinMax) // { // // when plotting multiple metrics, display min & max on both series // // to fix: in vertical bars, labels are hidden when multiple metrics are plotted, hence the restriction on count($this->ordinateSeries) == 1 -// if($this->multipleMetrics && count($this->ordinateSeries) == 1) +// if ($this->multipleMetrics && count($this->ordinateSeries) == 1) // { // $colorIndex = 1; // foreach($this->ordinateSeries as $column => $data) @@ -467,13 +467,13 @@ abstract class GridGraph extends StaticGraph // $maxValueIndex = 0; // foreach($data as $index => $value) // { -// if($value > $maxValue) +// if ($value > $maxValue) // { // $maxValue = $value; // $maxValueIndex = $index; // } // -// if($value < $minValue) +// if ($value < $minValue) // { // $minValue = $value; // $minValueIndex = $index; diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php index d3da5402f01dfbbef799b262a0b62586f5e59b5a..574af47ad7edf36070a12e96d0965c291e8967bc 100644 --- a/plugins/Installation/Controller.php +++ b/plugins/Installation/Controller.php @@ -84,7 +84,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin // Delete merged js/css files to force regenerations based on updated activated plugin list Filesystem::deleteAllCacheOnUpdate(); - if(empty($message)) { + if (empty($message)) { $this->checkPiwikIsNotInstalled(); } $view = new View( @@ -196,14 +196,16 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->tablesInstalled = implode(', ', $tablesInstalled); $view->someTablesInstalled = true; - Access::getInstance(); - Piwik::setUserHasSuperUserAccess(); - if ($this->hasEnoughTablesToReuseDb($tablesInstalled) && - count(APISitesManager::getInstance()->getAllSitesId()) > 0 && - count(APIUsersManager::getInstance()->getUsers()) > 0 - ) { - $view->showReuseExistingTables = true; - } + $self = $this; + Access::doAsSuperUser(function () use ($self, $tablesInstalled, $view) { + Access::getInstance(); + if ($self->hasEnoughTablesToReuseDb($tablesInstalled) && + count(APISitesManager::getInstance()->getAllSitesId()) > 0 && + count(APIUsersManager::getInstance()->getUsers()) > 0 + ) { + $view->showReuseExistingTables = true; + } + }); } else { DbHelper::createTables(); @@ -234,7 +236,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin ); $result = $this->updateComponents(); - if($result === false) { + if ($result === false) { $this->redirectToNextStep('tablesCreation'); } @@ -258,8 +260,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin { $this->checkPiwikIsNotInstalled(); - $this->initObjectsToCallAPI(); - if(count(APIUsersManager::getInstance()->getUsersHavingSuperUserAccess()) > 0) { + $superUserAlreadyExists = Access::doAsSuperUser(function () { + return count(APIUsersManager::getInstance()->getUsersHavingSuperUserAccess()) > 0; + }); + + if ($superUserAlreadyExists) { $this->redirectToNextStep('setupSuperUser'); } @@ -301,9 +306,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin { $this->checkPiwikIsNotInstalled(); - $this->initObjectsToCallAPI(); + $siteIdsCount = Access::doAsSuperUser(function () { + return count(APISitesManager::getInstance()->getAllSitesId()); + }); - if(count(APISitesManager::getInstance()->getAllSitesId()) > 0) { + if ($siteIdsCount > 0) { // if there is a already a website, skip this step and trackingCode step $this->redirectToNextStep('trackingCode'); } @@ -317,12 +324,15 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $form = new FormFirstWebsiteSetup(); if ($form->validate()) { - $name = Common::unsanitizeInputValue($form->getSubmitValue('siteName')); + $name = Common::sanitizeInputValue($form->getSubmitValue('siteName')); $url = Common::unsanitizeInputValue($form->getSubmitValue('url')); $ecommerce = (int)$form->getSubmitValue('ecommerce'); try { - $result = APISitesManager::getInstance()->addSite($name, $url, $ecommerce); + $result = Access::doAsSuperUser(function () use ($name, $url, $ecommerce) { + return APISitesManager::getInstance()->addSite($name, $url, $ecommerce); + }); + $params = array( 'site_idSite' => $result, 'site_name' => urlencode($name) @@ -336,7 +346,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } // Display previous step success message, when current step form was not submitted yet - if(count($form->getErrorMessages()) == 0) { + if (count($form->getErrorMessages()) == 0) { $view->displayGeneralSetupSuccess = true; } @@ -463,14 +473,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return Common::getRequestVar($name, false, 'string'); } - /** - * Instantiate access and log objects - */ - private function initObjectsToCallAPI() - { - Piwik::setUserHasSuperUserAccess(); - } - /** * Write configuration file from session-store */ @@ -513,7 +515,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin private function checkPiwikIsNotInstalled() { - if(!SettingsPiwik::isPiwikInstalled()) { + if (!SettingsPiwik::isPiwikInstalled()) { return; } \Piwik\Plugins\Login\Controller::clearSession(); @@ -633,13 +635,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin private function createSuperUser($login, $password, $email) { - $this->initObjectsToCallAPI(); - - $api = APIUsersManager::getInstance(); - $api->addUser($login, $password, $email); - - $this->initObjectsToCallAPI(); - $api->setSuperUserAccess($login, true); + $self = $this; + Access::doAsSuperUser(function () use ($self, $login, $password, $email) { + $api = APIUsersManager::getInstance(); + $api->addUser($login, $password, $email); + $api->setSuperUserAccess($login, true); + }); } private function hasEnoughTablesToReuseDb($tablesInstalled) @@ -658,7 +659,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin private function deleteConfigFileIfNeeded() { $config = Config::getInstance(); - if($config->existsLocalConfig()) { + if ($config->existsLocalConfig()) { $config->deleteLocalConfig(); } } @@ -702,16 +703,16 @@ class Controller extends \Piwik\Plugin\ControllerAdmin protected function updateComponents() { Access::getInstance(); - Piwik::setUserHasSuperUserAccess(); - $updater = new Updater(); - $componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater); + return Access::doAsSuperUser(function () { + $updater = new Updater(); + $componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater); - if (empty($componentsWithUpdateFile)) { - return false; - } - $result = CoreUpdater::updateComponents($updater, $componentsWithUpdateFile); - return $result; + if (empty($componentsWithUpdateFile)) { + return false; + } + $result = CoreUpdater::updateComponents($updater, $componentsWithUpdateFile); + return $result; + }); } - } diff --git a/plugins/Installation/FormFirstWebsiteSetup.php b/plugins/Installation/FormFirstWebsiteSetup.php index 83c68bea3462e9598a38267880029b50c9eb48d8..924c711a30f908eaa4776d6b1abfd6df9ab1afd3 100644 --- a/plugins/Installation/FormFirstWebsiteSetup.php +++ b/plugins/Installation/FormFirstWebsiteSetup.php @@ -12,6 +12,8 @@ namespace Piwik\Plugins\Installation; use HTML_QuickForm2_DataSource_Array; use HTML_QuickForm2_Factory; use HTML_QuickForm2_Rule; +use Piwik\Log; +use Piwik\Access; use Piwik\Piwik; use Piwik\Plugins\SitesManager\API; use Piwik\QuickForm2; @@ -31,7 +33,7 @@ class FormFirstWebsiteSetup extends QuickForm2 HTML_QuickForm2_Factory::registerRule('checkTimezone', 'Piwik\Plugins\Installation\Rule_isValidTimezone'); $urlExample = 'http://example.org'; - $javascriptOnClickUrlExample = "javascript:if(this.value=='$urlExample'){this.value='http://';} this.style.color='black';"; + $javascriptOnClickUrlExample = "javascript:if (this.value=='$urlExample'){this.value='http://';} this.style.color='black';"; $timezones = API::getInstance()->getTimezonesList(); $timezones = array_merge(array('No timezone' => Piwik::translate('SitesManager_SelectACity')), $timezones); @@ -79,7 +81,9 @@ class Rule_isValidTimezone extends HTML_QuickForm2_Rule try { $timezone = $this->owner->getValue(); if (!empty($timezone)) { - API::getInstance()->setDefaultTimezone($timezone); + Access::doAsSuperUser(function () use ($timezone) { + API::getInstance()->setDefaultTimezone($timezone); + }); } } catch (\Exception $e) { return false; diff --git a/plugins/Installation/ServerFilesGenerator.php b/plugins/Installation/ServerFilesGenerator.php index 5ce2abf844f5f0beb3a45aa2e6c94e8c073f9968..9551340e4ac51ac392bae89b4d6a6e86b45053d3 100644 --- a/plugins/Installation/ServerFilesGenerator.php +++ b/plugins/Installation/ServerFilesGenerator.php @@ -18,7 +18,7 @@ class ServerFilesGenerator */ public static function createHtAccessFiles() { - if(!SettingsServer::isApache()) { + if (!SettingsServer::isApache()) { return; } $denyAll = self::getDenyAllHtaccessContent(); diff --git a/plugins/Installation/SystemCheck.php b/plugins/Installation/SystemCheck.php index f6d8c42e028ff146819e2b09af85917bc20599b0..29f87f2415ce0d0ee81887703bd86476e2c928c2 100644 --- a/plugins/Installation/SystemCheck.php +++ b/plugins/Installation/SystemCheck.php @@ -140,7 +140,7 @@ class SystemCheck } $sessionAutoStarted = (int)ini_get('session.auto_start'); - if($sessionAutoStarted) { + if ($sessionAutoStarted) { $infos['missing_desired_functions'][] = 'session.auto_start'; } diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php index e95931ae8721ca6246b91b23178da654fdb207ae..e715770279574f9824139946f754947ed1fddc58 100644 --- a/plugins/LanguagesManager/API.php +++ b/plugins/LanguagesManager/API.php @@ -9,7 +9,6 @@ */ namespace Piwik\Plugins\LanguagesManager; -use Piwik\Common; use Piwik\Db; use Piwik\Filesystem; use Piwik\Piwik; @@ -229,12 +228,20 @@ class API extends \Piwik\Plugin\API */ public function getLanguageForUser($login) { - if($login == 'anonymous') { + if ($login == 'anonymous') { return false; } + Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); - return Db::fetchOne('SELECT language FROM ' . Common::prefixTable('user_language') . - ' WHERE login = ? ', array($login)); + + $lang = $this->getModel()->getLanguageForUser($login); + + return $lang; + } + + private function getModel() + { + return new Model(); } /** @@ -248,15 +255,13 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); Piwik::checkUserIsNotAnonymous(); + if (!$this->isLanguageAvailable($languageCode)) { return false; } - $paramsBind = array($login, $languageCode, $languageCode); - Db::query('INSERT INTO ' . Common::prefixTable('user_language') . - ' (login, language) - VALUES (?,?) - ON DUPLICATE KEY UPDATE language=?', - $paramsBind); + + $this->getModel()->setLanguageForUser($login, $languageCode); + return true; } diff --git a/plugins/LanguagesManager/Commands/CompareKeys.php b/plugins/LanguagesManager/Commands/CompareKeys.php index 088bb492aeb12ae2f89c262f7d271bc5c9c4c647..821342a90c63538f65a703cd723827ff9107671e 100644 --- a/plugins/LanguagesManager/Commands/CompareKeys.php +++ b/plugins/LanguagesManager/Commands/CompareKeys.php @@ -62,7 +62,7 @@ class CompareKeys extends ConsoleCommand { if (!empty($englishFromOTrance[$category])) { foreach ($englishFromOTrance[$category] as $key => $value) { - if(!array_key_exists($category, $availableTranslations) || !array_key_exists($key, $availableTranslations[$category])) { + if (!array_key_exists($category, $availableTranslations) || !array_key_exists($key, $availableTranslations[$category])) { $unnecessary[] = sprintf('%s_%s', $category, $key); continue; } else if (html_entity_decode($availableTranslations[$category][$key]) != html_entity_decode($englishFromOTrance[$category][$key])) { @@ -73,7 +73,7 @@ class CompareKeys extends ConsoleCommand } if (!empty($availableTranslations[$category])) { foreach ($availableTranslations[$category] as $key => $value) { - if(!array_key_exists($category, $englishFromOTrance) || !array_key_exists($key, $englishFromOTrance[$category])) { + if (!array_key_exists($category, $englishFromOTrance) || !array_key_exists($key, $englishFromOTrance[$category])) { $missing[] = sprintf('%s_%s', $category, $key); continue; } diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php index 66fba008e59e361ededd3871e22c146f8ac41c04..6a0fcf10e14d8b23a6308813b4efc08ef2299a57 100644 --- a/plugins/LanguagesManager/LanguagesManager.php +++ b/plugins/LanguagesManager/LanguagesManager.php @@ -14,7 +14,6 @@ use Piwik\Common; use Piwik\Config; use Piwik\Cookie; use Piwik\Db; -use Piwik\DbHelper; use Piwik\Piwik; use Piwik\Translate; use Piwik\View; @@ -102,7 +101,8 @@ class LanguagesManager extends \Piwik\Plugin public function deleteUserLanguage($userLogin) { - Db::query('DELETE FROM ' . Common::prefixTable('user_language') . ' WHERE login = ?', $userLogin); + $model = new Model(); + $model->deleteUserLanguage($userLogin); } /** @@ -110,10 +110,7 @@ class LanguagesManager extends \Piwik\Plugin */ public function install() { - $userLanguage = "login VARCHAR( 100 ) NOT NULL , - language VARCHAR( 10 ) NOT NULL , - PRIMARY KEY ( login )"; - DbHelper::createTable('user_language', $userLanguage); + Model::install(); } /** @@ -121,7 +118,7 @@ class LanguagesManager extends \Piwik\Plugin */ public function uninstall() { - Db::dropTables(Common::prefixTable('user_language')); + Model::uninstall(); } /** diff --git a/plugins/LanguagesManager/Model.php b/plugins/LanguagesManager/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..e40452c1344897f199aa2e716def0cba7b912ef9 --- /dev/null +++ b/plugins/LanguagesManager/Model.php @@ -0,0 +1,72 @@ +<?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\LanguagesManager; + +use Piwik\Common; +use Piwik\Db; +use Piwik\DbHelper; + +class Model +{ + private static $rawPrefix = 'user_language'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + + public function deleteUserLanguage($userLogin) + { + Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', $userLogin); + } + + /** + * Returns the language for the user + * + * @param string $userLogin + * @return string + */ + public function getLanguageForUser($userLogin) + { + return Db::fetchOne('SELECT language FROM ' . $this->table . + ' WHERE login = ? ', array($userLogin)); + } + + /** + * Sets the language for the user + * + * @param string $login + * @param string $languageCode + * @return bool + */ + public function setLanguageForUser($login, $languageCode) + { + $query = 'INSERT INTO ' . $this->table . + ' (login, language) VALUES (?,?) ON DUPLICATE KEY UPDATE language=?'; + $bind = array($login, $languageCode, $languageCode); + Db::query($query, $bind); + + return true; + } + + public static function install() + { + $userLanguage = "login VARCHAR( 100 ) NOT NULL , + language VARCHAR( 10 ) NOT NULL , + PRIMARY KEY ( login )"; + DbHelper::createTable(self::$rawPrefix, $userLanguage); + } + + public static function uninstall() + { + Db::dropTables(Common::prefixTable(self::$rawPrefix)); + } +} diff --git a/plugins/Live/API.php b/plugins/Live/API.php index c2acabb6191fa4f025550919ae0497b9459d890f..d15cca9e36c0b435104118e81b2eab1a0a76aad2 100644 --- a/plugins/Live/API.php +++ b/plugins/Live/API.php @@ -306,7 +306,7 @@ class API extends \Piwik\Plugin\API $cities[$countryCode] = array(); } $city = $visit->getColumn('city'); - if(!empty($city)) { + if (!empty($city)) { $cities[$countryCode][] = $city; } } @@ -325,7 +325,7 @@ class API extends \Piwik\Plugin\API 'nb_visits' => $nbVisits, 'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode), 'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode)); - if(!empty($cities[$countryCode])) { + if (!empty($cities[$countryCode])) { $countryInfo['cities'] = array_unique($cities[$countryCode]); } $result['countries'][] = $countryInfo; @@ -476,8 +476,7 @@ class API extends \Piwik\Plugin\API $segment = new Segment($segment, $idSite); $queryInfo = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy); - $sql = "SELECT sub.idvisitor, sub.visit_last_action_time - FROM ({$queryInfo['sql']}) as sub + $sql = "SELECT sub.idvisitor, sub.visit_last_action_time FROM ({$queryInfo['sql']}) as sub WHERE $visitLastActionTimeCondition LIMIT 1"; $bind = array_merge($queryInfo['bind'], array($visitLastActionTime)); @@ -589,7 +588,7 @@ class API extends \Piwik\Plugin\API $visitorDetailsArray['serverTimestamp'] = $visitorDetailsArray['lastActionTimestamp']; $dateTimeVisit = Date::factory($visitorDetailsArray['lastActionTimestamp'], $timezone); - if($dateTimeVisit) { + if ($dateTimeVisit) { $visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%time%'); $visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Piwik::translate('CoreHome_ShortDateFormat')); } @@ -703,8 +702,7 @@ class API extends \Piwik\Plugin\API // Group by idvisit so that a visitor converting 2 goals only appears once $sql = " - SELECT sub.* - FROM ( + SELECT sub.* FROM ( " . $subQuery['sql'] . " $sqlLimit ) AS sub diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php index ae01ed4e1840a749061652a15b466182fcc2572a..55fd3fed8344eb7ef3b4e951c650657b2ce6195a 100644 --- a/plugins/Live/Visitor.php +++ b/plugins/Live/Visitor.php @@ -309,7 +309,7 @@ class Visitor implements VisitorInterface } elseif ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { // Handle Event - if(strlen($actionDetail['pageTitle']) > 0) { + if (strlen($actionDetail['pageTitle']) > 0) { $actionDetail['eventName'] = $actionDetail['pageTitle']; } @@ -322,8 +322,8 @@ class Visitor implements VisitorInterface } // Event value / Generation time - if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { - if(strlen($actionDetail['custom_float']) > 0) { + if ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { + if (strlen($actionDetail['custom_float']) > 0) { $actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION); } } elseif ($actionDetail['custom_float'] > 0) { diff --git a/plugins/Live/VisitorLog.php b/plugins/Live/VisitorLog.php index 2afc67349bcfb0f77eff5d70639b762d601277ad..054b2f50ae5d46324ec7347ea9df464f5d9a858d 100644 --- a/plugins/Live/VisitorLog.php +++ b/plugins/Live/VisitorLog.php @@ -105,7 +105,7 @@ class VisitorLog extends Visualization && $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ORDERS; $isAbandonedCart = $action['type'] == 'ecommerceAbandonedCart' && $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ABANDONED_CARTS; - if($isAbandonedCart || $isEcommerceOrder) { + if ($isAbandonedCart || $isEcommerceOrder) { return true; } } diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php index 82ae9ccab26e4fd99359bb4466970629610d93d2..62c8b407407bbdaa1357ccf9ba8c69082a88ff0a 100644 --- a/plugins/Login/Controller.php +++ b/plugins/Login/Controller.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\Login; use Exception; +use Piwik\Access; use Piwik\Auth as AuthInterface; use Piwik\Common; use Piwik\Config; @@ -278,7 +279,7 @@ class Controller extends \Piwik\Plugin\Controller // have to do this as super user since redirectToIndex checks if there's a default website ID for // the current user and if not, doesn't redirect to the requested action. TODO: this behavior is wrong. somehow. $self = $this; - Piwik::doAsSuperUser(function () use ($self) { + Access::doAsSuperUser(function () use ($self) { $self->redirectToIndex(Piwik::getLoginPluginName(), 'resetPasswordSuccess'); }); return null; @@ -330,4 +331,4 @@ class Controller extends \Piwik\Plugin\Controller Url::redirectToUrl($logoutUrl); } } -} \ No newline at end of file +} diff --git a/plugins/Login/Login.php b/plugins/Login/Login.php index 4c33579176850611c9b2ecdb0643ce824522bd3a..8b65e2e1751fb3e377f37fdd479d911280932213 100644 --- a/plugins/Login/Login.php +++ b/plugins/Login/Login.php @@ -85,7 +85,7 @@ class Login extends \Piwik\Plugin */ public static function initAuthenticationFromCookie(\Piwik\Auth $auth, $activateCookieAuth) { - if(self::isModuleIsAPI() && !$activateCookieAuth) { + if (self::isModuleIsAPI() && !$activateCookieAuth) { return; } diff --git a/plugins/Login/PasswordResetter.php b/plugins/Login/PasswordResetter.php index 479a8a10a33ef858758d5ebd54d883b540f2e124..d2631abdf980b6dda30bc37c088690a87a9c0fb3 100644 --- a/plugins/Login/PasswordResetter.php +++ b/plugins/Login/PasswordResetter.php @@ -8,6 +8,7 @@ namespace Piwik\Plugins\Login; use Exception; +use Piwik\Access; use Piwik\Common; use Piwik\Config; use Piwik\IP; @@ -209,7 +210,7 @@ class PasswordResetter // reset password of user $usersManager = $this->usersManagerApi; - Piwik::doAsSuperUser(function () use ($usersManager, $user, $resetPassword) { + Access::doAsSuperUser(function () use ($usersManager, $user, $resetPassword) { $usersManager->updateUser( $user['login'], $resetPassword, $email = false, $alias = false, $isPasswordHashed = true); }); @@ -359,7 +360,7 @@ class PasswordResetter protected function getUserInformation($loginOrMail) { $usersManager = $this->usersManagerApi; - return Piwik::doAsSuperUser(function () use ($loginOrMail, $usersManager) { + return Access::doAsSuperUser(function () use ($loginOrMail, $usersManager) { $user = null; if ($usersManager->userExists($loginOrMail)) { $user = $usersManager->getUser($loginOrMail); diff --git a/plugins/Login/SessionInitializer.php b/plugins/Login/SessionInitializer.php index 1ede89c63904dced1c374de01989b8bbd1365e0d..cd41b36295e47a7264deb0f73064fe0afb76961f 100644 --- a/plugins/Login/SessionInitializer.php +++ b/plugins/Login/SessionInitializer.php @@ -14,6 +14,7 @@ use Piwik\AuthResult; use Piwik\Config; use Piwik\Cookie; use Piwik\Db; +use Piwik\Log; use Piwik\Piwik; use Piwik\Plugins\UsersManager\API as UsersManagerAPI; use Piwik\ProxyHttp; @@ -135,7 +136,17 @@ class SessionInitializer */ protected function doAuthenticateSession(AuthInterface $auth) { - $tokenAuth = $this->usersManagerAPI->getTokenAuth($auth->getLogin(), $auth->getTokenAuthSecret()); + $login = $auth->getLogin(); + $tokenAuthSecret = null; + + try { + $tokenAuthSecret = $auth->getTokenAuthSecret(); + } catch (Exception $ex) { + Log::debug("SessionInitializer::doAuthenticateSession: token_auth secret for %s not avaialble before user" + . " is authenticated.", $login); + } + + $tokenAuth = empty($tokenAuthSecret) ? null : $this->usersManagerAPI->getTokenAuth($login, $tokenAuthSecret); /** * @deprecated Create a custom SessionInitializer instead. diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php index 41031fae8f19c7f5dafec58be2d5f92a91c174f1..ea393414f7b83505015f61978b720a228addeabd 100755 --- a/plugins/MultiSites/API.php +++ b/plugins/MultiSites/API.php @@ -346,7 +346,7 @@ class API extends \Piwik\Plugin\API { $metrics = self::$baseMetrics; - if(Common::isActionsPluginEnabled()) { + if (Common::isActionsPluginEnabled()) { $metrics[self::NB_PAGEVIEWS_LABEL] = array( self::METRIC_TRANSLATION_KEY => 'General_ColumnPageviews', self::METRIC_EVOLUTION_COL_NAME_KEY => 'pageviews_evolution', diff --git a/plugins/MultiSites/Menu.php b/plugins/MultiSites/Menu.php index 5059cfc09dc2094a71a0bc4ce7d2763506ae5d8c..8860eacec097b63b0a8b395869a6db6e6b2cbfed 100644 --- a/plugins/MultiSites/Menu.php +++ b/plugins/MultiSites/Menu.php @@ -15,7 +15,7 @@ class Menu extends \Piwik\Plugin\Menu { public function configureTopMenu(MenuTop $menu) { - $urlParams = $this->urlForAction('index', array('segment' => false)); + $urlParams = $this->urlForActionWithDefaultUserParams('index', array('segment' => false, 'idSite' => false)); $tooltip = Piwik::translate('MultiSites_TopLinkTooltip'); $menu->add('General_MultiSitesSummary', null, $urlParams, true, 3, $tooltip); diff --git a/plugins/PrivacyManager/DoNotTrackHeaderChecker.php b/plugins/PrivacyManager/DoNotTrackHeaderChecker.php index b857debf853db5a2ebe8e2afbcf71ddc08029f4b..14938f426e4461c409a762a942255e127dc7a5f5 100644 --- a/plugins/PrivacyManager/DoNotTrackHeaderChecker.php +++ b/plugins/PrivacyManager/DoNotTrackHeaderChecker.php @@ -26,7 +26,7 @@ class DoNotTrackHeaderChecker */ public function checkHeaderInTracker(&$exclude) { - if($exclude) { + if ($exclude) { Common::printDebug("Visit is already excluded, no need to check DoNotTrack support."); return; } diff --git a/plugins/PrivacyManager/LogDataPurger.php b/plugins/PrivacyManager/LogDataPurger.php index 2ec73deb81d9676903a9359eced830bf23e301dc..6fe1381c502a795d201f3dadbeb3e9f6bf201c03 100755 --- a/plugins/PrivacyManager/LogDataPurger.php +++ b/plugins/PrivacyManager/LogDataPurger.php @@ -173,7 +173,7 @@ class LogDataPurger private function getLogTableDeleteCount($table, $maxIdVisit) { $sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?"; - return (int)Db::fetchOne($sql, array($maxIdVisit)); + return (int) Db::fetchOne($sql, array($maxIdVisit)); } private function createTempTable() diff --git a/plugins/PrivacyManager/ReportsPurger.php b/plugins/PrivacyManager/ReportsPurger.php index a35169e6efaf49222b133c9ecafbd87a95f33162..9889c00e9d72530385fd551919d94666c4801891 100755 --- a/plugins/PrivacyManager/ReportsPurger.php +++ b/plugins/PrivacyManager/ReportsPurger.php @@ -260,12 +260,11 @@ class ReportsPurger { $maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table"); - $sql = "SELECT COUNT(*) - FROM $table - WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "') - AND name NOT LIKE 'done%' - AND idarchive >= ? - AND idarchive < ?"; + $sql = "SELECT COUNT(*) FROM $table + WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "') + AND name NOT LIKE 'done%' + AND idarchive >= ? + AND idarchive < ?"; $segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize); return array_sum($segments); @@ -275,11 +274,10 @@ class ReportsPurger { $maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table"); - $sql = "SELECT COUNT(*) - FROM $table - WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . " - AND idarchive >= ? - AND idarchive < ?"; + $sql = "SELECT COUNT(*) FROM $table + WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . " + AND idarchive >= ? + AND idarchive < ?"; $segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize); return array_sum($segments); @@ -325,12 +323,11 @@ class ReportsPurger $maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table"); - $sql = "SELECT idarchive - FROM $table - WHERE name != 'done' - AND name LIKE 'done_%.%' - AND idarchive >= ? - AND idarchive < ?"; + $sql = "SELECT idarchive FROM $table + WHERE name != 'done' + AND name LIKE 'done_%.%' + AND idarchive >= ? + AND idarchive < ?"; if (is_null($this->segmentArchiveIds)) { $this->segmentArchiveIds = array(); diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php index de871401b386359bf0b2590c948f406b1872a10a..a69fcc54bfe3639fd3d4b6104cb476738d0ca2e6 100644 --- a/plugins/Referrers/Columns/Base.php +++ b/plugins/Referrers/Columns/Base.php @@ -244,14 +244,14 @@ abstract class Base extends VisitDimension protected function detectCampaignKeywordFromReferrerUrl() { - if(!empty($this->nameReferrerAnalyzed) + if (!empty($this->nameReferrerAnalyzed) && !empty($this->keywordReferrerAnalyzed)) { // keyword is already set, we skip return true; } // Set the Campaign keyword to the keyword found in the Referrer URL if any - if(!empty($this->nameReferrerAnalyzed)) { + if (!empty($this->nameReferrerAnalyzed)) { $referrerUrlInfo = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl); if (!empty($referrerUrlInfo['keywords'])) { $this->keywordReferrerAnalyzed = $referrerUrlInfo['keywords']; @@ -270,7 +270,7 @@ abstract class Base extends VisitDimension $parsedAdsenseReferrerUrl = parse_url($value); if (!empty($parsedAdsenseReferrerUrl['host'])) { - if(empty($this->nameReferrerAnalyzed)) { + if (empty($this->nameReferrerAnalyzed)) { $type = $this->getParameterValueFromReferrerUrl('ad_type'); $type = $type ? " ($type)" : ''; $this->nameReferrerAnalyzed = self::LABEL_ADWORDS_NAME . $type; @@ -304,7 +304,7 @@ abstract class Base extends VisitDimension return false; } // if we detected a campaign but there is still no keyword set, we set the keyword to the Referrer host - if(empty($this->keywordReferrerAnalyzed)) { + if (empty($this->keywordReferrerAnalyzed)) { $this->keywordReferrerAnalyzed = $this->referrerHost; } diff --git a/plugins/Referrers/Controller.php b/plugins/Referrers/Controller.php index e683cb7f9ef99112086806c55de33cfbb1025aa5..f01c61f64703dda1bcf9da03c30c43905890e2ad 100644 --- a/plugins/Referrers/Controller.php +++ b/plugins/Referrers/Controller.php @@ -332,7 +332,7 @@ function DisplayTopKeywords($url = "") $url = empty($url) ? "http://". $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] : $url; $api = "' . $api . '&url=" . urlencode($url); $keywords = @unserialize(file_get_contents($api)); - if($keywords === false || isset($keywords["result"])) { + if ($keywords === false || isset($keywords["result"])) { // DEBUG ONLY: uncomment for troubleshooting an empty output (the URL output reveals the token_auth) // echo "Error while fetching the <a href=\'$api\'>Top Keywords from Piwik</a>"; return; @@ -344,7 +344,7 @@ function DisplayTopKeywords($url = "") foreach($keywords as $keyword) { $output .= "<li>". $keyword . "</li>"; } - if(empty($keywords)) { $output .= "Nothing yet..."; } + if (empty($keywords)) { $output .= "Nothing yet..."; } $output .= "</ul>"; echo $output; } diff --git a/plugins/Referrers/functions.php b/plugins/Referrers/functions.php index 8f7a4023dfbbf6231f773cf7ddd5c6cbbfc2de52..e0fee308336f492e8f7c90b6f11fe14424415a64 100644 --- a/plugins/Referrers/functions.php +++ b/plugins/Referrers/functions.php @@ -39,7 +39,7 @@ function getSocialMainUrl($url) $social = getSocialNetworkFromDomain($url); foreach (Common::getSocialUrls() as $domain => $name) { - if($name == $social) { + if ($name == $social) { return $domain; } @@ -57,7 +57,7 @@ function getSocialNetworkFromDomain($url) { foreach (Common::getSocialUrls() as $domain => $name) { - if(preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { + if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { return $name; } diff --git a/plugins/ScheduledReports/API.php b/plugins/ScheduledReports/API.php index 3ea8a05c28b4883282bf85d28b4ecbfe0eaf11fd..a42b5bb209ad90fb0593051079757546d9859ebd 100644 --- a/plugins/ScheduledReports/API.php +++ b/plugins/ScheduledReports/API.php @@ -53,6 +53,9 @@ class API extends \Piwik\Plugin\API const REPORT_TRUNCATE = 23; + // static cache storing reports + public static $cache = array(); + /** * Creates a new report and schedules it. * @@ -84,29 +87,20 @@ class API extends \Piwik\Plugin\API // validation of requested reports $reports = self::validateRequestedReports($idSite, $reportType, $reports); - $db = Db::get(); - $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM " . Common::prefixTable('report')); - - if ($idReport == false) { - $idReport = 1; - } - - $db->insert(Common::prefixTable('report'), - array( - 'idreport' => $idReport, - 'idsite' => $idSite, - 'login' => $currentUser, - 'description' => $description, - 'idsegment' => $idSegment, - 'period' => $period, - 'hour' => $hour, - 'type' => $reportType, - 'format' => $reportFormat, - 'parameters' => $parameters, - 'reports' => $reports, - 'ts_created' => Date::now()->getDatetime(), - 'deleted' => 0, - )); + $idReport = $this->getModel()->createReport(array( + 'idsite' => $idSite, + 'login' => $currentUser, + 'description' => $description, + 'idsegment' => $idSegment, + 'period' => $period, + 'hour' => $hour, + 'type' => $reportType, + 'format' => $reportFormat, + 'parameters' => $parameters, + 'reports' => $reports, + 'ts_created' => Date::now()->getDatetime(), + 'deleted' => 0, + )); return $idReport; } @@ -130,7 +124,7 @@ class API extends \Piwik\Plugin\API Piwik::checkUserHasViewAccess($idSite); $scheduledReports = $this->getReports($idSite, $periodSearch = false, $idReport); - $report = reset($scheduledReports); + $report = reset($scheduledReports); $idReport = $report['idreport']; $currentUser = Piwik::getCurrentUserLogin(); @@ -144,19 +138,16 @@ class API extends \Piwik\Plugin\API // validation of requested reports $reports = self::validateRequestedReports($idSite, $reportType, $reports); - Db::get()->update(Common::prefixTable('report'), - array( - 'description' => $description, - 'idsegment' => $idSegment, - 'period' => $period, - 'hour' => $hour, - 'type' => $reportType, - 'format' => $reportFormat, - 'parameters' => $parameters, - 'reports' => $reports, - ), - "idreport = '$idReport'" - ); + $this->getModel()->updateReport($idReport, array( + 'description' => $description, + 'idsegment' => $idSegment, + 'period' => $period, + 'hour' => $hour, + 'type' => $reportType, + 'format' => $reportFormat, + 'parameters' => $parameters, + 'reports' => $reports, + )); self::$cache = array(); } @@ -172,18 +163,13 @@ class API extends \Piwik\Plugin\API $report = reset($APIScheduledReports); Piwik::checkUserHasSuperUserAccessOrIsTheUser($report['login']); - Db::get()->update(Common::prefixTable('report'), - array( - 'deleted' => 1, - ), - "idreport = '$idReport'" - ); + $this->getModel()->updateReport($idReport, array( + 'deleted' => 1, + )); + self::$cache = array(); } - // static cache storing reports - public static $cache = array(); - /** * Returns the list of reports matching the passed parameters * @@ -198,6 +184,7 @@ class API extends \Piwik\Plugin\API public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false, $idSegment = false) { Piwik::checkUserHasSomeViewAccess(); + $cacheKey = (int)$idSite . '.' . (string)$period . '.' . (int)$idReport . '.' . (int)$ifSuperUserReturnOnlySuperUserReports; if (isset(self::$cache[$cacheKey])) { return self::$cache[$cacheKey]; @@ -427,7 +414,7 @@ class API extends \Piwik\Plugin\API array(&$reportRenderer, $reportType, $outputType, $report) ); - if(is_null($reportRenderer)) { + if (is_null($reportRenderer)) { throw new Exception("A report renderer was not supplied in the event " . self::GET_RENDERER_INSTANCE_EVENT); } @@ -556,10 +543,8 @@ class API extends \Piwik\Plugin\API ); // Update flag in DB - Db::get()->update(Common::prefixTable('report'), - array('ts_last_sent' => Date::now()->getDatetime()), - "idreport = " . $report['idreport'] - ); + $now = Date::now()->getDatetime(); + $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']) { @@ -567,6 +552,11 @@ class API extends \Piwik\Plugin\API } } + private function getModel() + { + return new Model(); + } + private static function getReportSubjectAndReportTitle($websiteName, $reports) { // if the only report is "All websites", we don't display the site name diff --git a/plugins/ScheduledReports/Model.php b/plugins/ScheduledReports/Model.php new file mode 100644 index 0000000000000000000000000000000000000000..01507f65f9c41aaf2be2fd3dfe271688ac913d05 --- /dev/null +++ b/plugins/ScheduledReports/Model.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\ScheduledReports; + +use Piwik\Common; +use Piwik\Db; +use Piwik\DbHelper; +use Piwik\ReportRenderer; +use Piwik\Translate; + +class Model +{ + private static $rawPrefix = 'report'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + + public function deleteUserReportForSite($userLogin, $idSite) + { + $query = 'DELETE FROM ' . $this->table . ' WHERE login = ? and idsite = ?'; + $bind = array($userLogin, $idSite); + Db::query($query, $bind); + } + + public function deleteAllReportForUser($userLogin) + { + Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', $userLogin); + } + + public function updateReport($idReport, $report) + { + $idReport = (int) $idReport; + + $this->getDb()->update($this->table, $report, "idreport = " . $idReport); + } + + public function createReport($report) + { + $nextId = $this->getNextReportId(); + $report['idreport'] = $nextId; + + $this->getDb()->insert($this->table, $report); + + return $nextId; + } + + private function getNextReportId() + { + $db = $this->getDb(); + $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM " . $this->table); + + if ($idReport == false) { + $idReport = 1; + } + + return $idReport; + } + + private function getDb() + { + return Db::get(); + } + + public static function install() + { + $reportTable = "`idreport` INT(11) NOT NULL AUTO_INCREMENT, + `idsite` INTEGER(11) NOT NULL, + `login` VARCHAR(100) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `idsegment` INT(11), + `period` VARCHAR(10) NOT NULL, + `hour` tinyint NOT NULL default 0, + `type` VARCHAR(10) NOT NULL, + `format` VARCHAR(10) NOT NULL, + `reports` TEXT NOT NULL, + `parameters` TEXT NULL, + `ts_created` TIMESTAMP NULL, + `ts_last_sent` TIMESTAMP NULL, + `deleted` tinyint(4) NOT NULL default 0, + PRIMARY KEY (`idreport`)"; + + DbHelper::createTable(self::$rawPrefix, $reportTable); + } +} diff --git a/plugins/ScheduledReports/ScheduledReports.php b/plugins/ScheduledReports/ScheduledReports.php index ca5ebb9b418474cf8784ea26de0b3988fd8f5d74..e59e9140abad99fcbeb38040ce2a782084932b06 100644 --- a/plugins/ScheduledReports/ScheduledReports.php +++ b/plugins/ScheduledReports/ScheduledReports.php @@ -9,9 +9,7 @@ namespace Piwik\Plugins\ScheduledReports; use Exception; -use Piwik\Common; use Piwik\Db; -use Piwik\DbHelper; use Piwik\Mail; use Piwik\Piwik; use Piwik\Plugins\MobileMessaging\MobileMessaging; @@ -469,27 +467,27 @@ class ScheduledReports extends \Piwik\Plugin $reportsNeedSegment = array(); - if(!$updatedSegment['enable_all_users']) { + if (!$updatedSegment['enable_all_users']) { // which reports would become invisible to other users? foreach($reportsUsingSegment as $report) { - if($report['login'] == Piwik::getCurrentUserLogin()) { + if ($report['login'] == Piwik::getCurrentUserLogin()) { continue; } $reportsNeedSegment[] = $report; } } - if($updatedSegment['enable_only_idsite']) { + if ($updatedSegment['enable_only_idsite']) { // which reports from other websites are set to use this segment restricted to one website? foreach($reportsUsingSegment as $report) { - if($report['idsite'] == $updatedSegment['enable_only_idsite']) { + if ($report['idsite'] == $updatedSegment['enable_only_idsite']) { continue; } $reportsNeedSegment[] = $report; } } - if(empty($reportsNeedSegment)) { + if (empty($reportsNeedSegment)) { return; } @@ -526,7 +524,7 @@ class ScheduledReports extends \Piwik\Plugin public function deleteUserReport($userLogin) { - Db::query('DELETE FROM ' . Common::prefixTable('report') . ' WHERE login = ?', $userLogin); + $this->getModel()->deleteAllReportForUser($userLogin); } public function deleteUserReportForSites($userLogin, $idSites) @@ -535,33 +533,21 @@ class ScheduledReports extends \Piwik\Plugin return; } - $table = Common::prefixTable('report'); + $model = $this->getModel(); foreach ($idSites as $idSite) { - Db::query('DELETE FROM ' . $table . ' WHERE login = ? and idsite = ?', - array($userLogin, $idSite)); + $model->deleteUserReportForSite($userLogin, $idSite); } } + private function getModel() + { + return new Model(); + } + public function install() { - $reportTable = "`idreport` INT(11) NOT NULL AUTO_INCREMENT, - `idsite` INTEGER(11) NOT NULL, - `login` VARCHAR(100) NOT NULL, - `description` VARCHAR(255) NOT NULL, - `idsegment` INT(11), - `period` VARCHAR(10) NOT NULL, - `hour` tinyint NOT NULL default 0, - `type` VARCHAR(10) NOT NULL, - `format` VARCHAR(10) NOT NULL, - `reports` TEXT NOT NULL, - `parameters` TEXT NULL, - `ts_created` TIMESTAMP NULL, - `ts_last_sent` TIMESTAMP NULL, - `deleted` tinyint(4) NOT NULL default 0, - PRIMARY KEY (`idreport`)"; - - DbHelper::createTable('report', $reportTable); + Model::install(); } private static function checkAdditionalEmails($additionalEmails) diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php index 98b752cc2aea949b899861a508e092767514c94c..699ad3a4eae2ca5014f5b5b94aa65c15fae43ae6 100644 --- a/plugins/SegmentEditor/API.php +++ b/plugins/SegmentEditor/API.php @@ -103,6 +103,7 @@ class API extends \Piwik\Plugin\API if (empty($segment)) { throw new Exception("Requested segment not found"); } + return $segment; } @@ -128,7 +129,7 @@ class API extends \Piwik\Plugin\API public function isUserCanAddNewSegment($idSite) { - if(Piwik::isUserIsAnonymous()) { + if (Piwik::isUserIsAnonymous()) { return false; } @@ -145,13 +146,13 @@ class API extends \Piwik\Plugin\API protected function checkUserCanEditOrDeleteSegment($segment) { - if(Piwik::hasUserSuperUserAccess()) { + if (Piwik::hasUserSuperUserAccess()) { return; } $this->checkUserIsNotAnonymous(); - if($segment['login'] != Piwik::getCurrentUserLogin()) { + if ($segment['login'] != Piwik::getCurrentUserLogin()) { throw new Exception($this->getMessageCannotEditSegmentCreatedBySuperUser()); } } @@ -177,11 +178,16 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent('SegmentEditor.deactivate', array($idSegment)); - $db = Db::get(); - $db->delete(Common::prefixTable('segment'), 'idsegment = ' . $idSegment); + $this->getModel()->deleteSegment($idSegment); + return true; } + private function getModel() + { + return new Model(); + } + /** * Modifies an existing stored segment. * @@ -201,9 +207,9 @@ class API extends \Piwik\Plugin\API $idSite = $this->checkIdSite($idSite); $this->checkSegmentName($name); - $definition = $this->checkSegmentValue($definition, $idSite); + $definition = $this->checkSegmentValue($definition, $idSite); $enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers); - $autoArchive = $this->checkAutoArchive($autoArchive, $idSite); + $autoArchive = $this->checkAutoArchive($autoArchive, $idSite); $bind = array( 'name' => $name, @@ -224,11 +230,8 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent('SegmentEditor.update', array($idSegment, $bind)); - $db = Db::get(); - $db->update(Common::prefixTable("segment"), - $bind, - "idsegment = $idSegment" - ); + $this->getModel()->updateSegment($idSegment, $bind); + return true; } @@ -252,7 +255,6 @@ class API extends \Piwik\Plugin\API $enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers); $autoArchive = $this->checkAutoArchive($autoArchive, $idSite); - $db = Db::get(); $bind = array( 'name' => $name, 'definition' => $definition, @@ -263,8 +265,10 @@ class API extends \Piwik\Plugin\API 'ts_created' => Date::now()->getDatetime(), 'deleted' => 0, ); - $db->insert(Common::prefixTable("segment"), $bind); - return $db->lastInsertId(); + + $id = $this->getModel()->createSegment($bind); + + return $id; } /** @@ -277,12 +281,12 @@ class API extends \Piwik\Plugin\API public function get($idSegment) { Piwik::checkUserHasSomeViewAccess(); + if (!is_numeric($idSegment)) { throw new Exception("idSegment should be numeric."); } - $segment = Db::get()->fetchRow("SELECT * " . - " FROM " . Common::prefixTable("segment") . - " WHERE idsegment = ?", $idSegment); + + $segment = $this->getModel()->getSegment($idSegment); if (empty($segment)) { return false; @@ -300,6 +304,7 @@ class API extends \Piwik\Plugin\API if ($segment['deleted']) { throw new Exception("This segment is marked as deleted. "); } + return $segment; } @@ -319,7 +324,7 @@ class API extends \Piwik\Plugin\API $userLogin = Piwik::getCurrentUserLogin(); - $model = new Model(); + $model = $this->getModel(); if (empty($idSite)) { $segments = $model->getAllSegments($userLogin); } else { diff --git a/plugins/SegmentEditor/Model.php b/plugins/SegmentEditor/Model.php index 54c0e07ddfe0bf5722f5e122cea59f9a8810f8e9..8bfcc957ae6893c905765ae636bd6bb78440f214 100644 --- a/plugins/SegmentEditor/Model.php +++ b/plugins/SegmentEditor/Model.php @@ -10,12 +10,21 @@ namespace Piwik\Plugins\SegmentEditor; use Piwik\Common; use Piwik\Db; +use Piwik\DbHelper; /** * The SegmentEditor Model lets you persist and read custom Segments from the backend without handling any logic. */ class Model { + private static $rawPrefix = 'segment'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + /** * Returns all stored segments. * @@ -36,7 +45,7 @@ class Model $sql = $this->buildQuerySortedByName("($whereIdSite enable_only_idsite = 0) AND deleted = 0 AND auto_archive = 1"); - $segments = Db::get()->fetchAll($sql, $bind); + $segments = $this->getDb()->fetchAll($sql, $bind); return $segments; } @@ -52,7 +61,7 @@ class Model $bind = array($userLogin); $sql = $this->buildQuerySortedByName('deleted = 0 AND (enable_all_users = 1 OR login = ?)'); - $segments = Db::get()->fetchAll($sql, $bind); + $segments = $this->getDb()->fetchAll($sql, $bind); return $segments; } @@ -70,16 +79,69 @@ class Model $sql = $this->buildQuerySortedByName('(enable_only_idsite = ? OR enable_only_idsite = 0) AND deleted = 0 AND (enable_all_users = 1 OR login = ?)'); - $segments = Db::get()->fetchAll($sql, $bind); + $segments = $this->getDb()->fetchAll($sql, $bind); return $segments; } + public function deleteSegment($idSegment) + { + $db = $this->getDb(); + $db->delete($this->table, 'idsegment = ' . (int) $idSegment); + } + + public function updateSegment($idSegment, $segment) + { + $idSegment = (int) $idSegment; + + $db = $this->getDb(); + $db->update($this->table, $segment, "idsegment = $idSegment"); + + return true; + } + + public function createSegment($segment) + { + $db = $this->getDb(); + $db->insert($this->table, $segment); + $id = $db->lastInsertId(); + + return $id; + } + + public function getSegment($idSegment) + { + $db = $this->getDb(); + $segment = $db->fetchRow("SELECT * FROM " . $this->table . " WHERE idsegment = ?", $idSegment); + + return $segment; + } + + private function getDb() + { + return Db::get(); + } + private function buildQuerySortedByName($where) { - $sql = "SELECT * FROM " . Common::prefixTable("segment") . - " WHERE $where ORDER BY name ASC"; + return "SELECT * FROM " . $this->table . " WHERE $where ORDER BY name ASC"; + } + + public static function install() + { + $segmentTable = "`idsegment` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `definition` TEXT NOT NULL, + `login` VARCHAR(100) NOT NULL, + `enable_all_users` tinyint(4) NOT NULL default 0, + `enable_only_idsite` INTEGER(11) NULL, + `auto_archive` tinyint(4) NOT NULL default 0, + `ts_created` TIMESTAMP NULL, + `ts_last_edit` TIMESTAMP NULL, + `deleted` tinyint(4) NOT NULL default 0, + PRIMARY KEY (`idsegment`)"; - return $sql; + DbHelper::createTable(self::$rawPrefix, $segmentTable); } + } diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php index bd957c54c011dd57278fd0426beade837b968f8e..aef619156dd85281c28cd4a5c9b815dbf980a1b8 100644 --- a/plugins/SegmentEditor/SegmentEditor.php +++ b/plugins/SegmentEditor/SegmentEditor.php @@ -67,27 +67,17 @@ class SegmentEditor extends \Piwik\Plugin { $model = new Model(); $segmentToAutoArchive = $model->getSegmentsToAutoArchive($idSite); + foreach ($segmentToAutoArchive as $segmentInfo) { $segments[] = $segmentInfo['definition']; } + $segments = array_unique($segments); } public function install() { - $segmentTable = "`idsegment` INT(11) NOT NULL AUTO_INCREMENT, - `name` VARCHAR(255) NOT NULL, - `definition` TEXT NOT NULL, - `login` VARCHAR(100) NOT NULL, - `enable_all_users` tinyint(4) NOT NULL default 0, - `enable_only_idsite` INTEGER(11) NULL, - `auto_archive` tinyint(4) NOT NULL default 0, - `ts_created` TIMESTAMP NULL, - `ts_last_edit` TIMESTAMP NULL, - `deleted` tinyint(4) NOT NULL default 0, - PRIMARY KEY (`idsegment`)"; - - DbHelper::createTable('segment', $segmentTable); + Model::install(); } public function getJsFiles(&$jsFiles) diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 5b856cd862a04f3ad077a56d3f9cec500c645d54..1bb4d4b304b6e8769fc9a4d433f6c0508ce88717 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -143,11 +143,9 @@ class API extends \Piwik\Plugin\API public function getSitesFromGroup($group) { Piwik::checkUserHasSuperUserAccess(); - $group = trim($group); - $sites = Db::get()->fetchAll("SELECT * - FROM " . Common::prefixTable("site") . " - WHERE `group` = ?", $group); + $group = trim($group); + $sites = $this->getModel()->getSitesFromGroup($group); Site::setSitesFromArray($sites); return $sites; @@ -162,12 +160,10 @@ class API extends \Piwik\Plugin\API public function getSitesGroups() { Piwik::checkUserHasSuperUserAccess(); - $groups = Db::get()->fetchAll("SELECT DISTINCT `group` FROM " . Common::prefixTable("site")); - $cleanedGroups = array(); - foreach ($groups as $group) { - $cleanedGroups[] = $group['group']; - } - $cleanedGroups = array_map('trim', $cleanedGroups); + + $groups = $this->getModel()->getSitesGroups(); + $cleanedGroups = array_map('trim', $groups); + return $cleanedGroups; } @@ -219,12 +215,15 @@ class API extends \Piwik\Plugin\API public function getAllSites() { Piwik::checkUserHasSuperUserAccess(); - $sites = Db::get()->fetchAll("SELECT * FROM " . Common::prefixTable("site") . " ORDER BY idsite ASC"); + + $sites = $this->getModel()->getAllSites(); $return = array(); foreach ($sites as $site) { $return[$site['idsite']] = $site; } + Site::setSitesFromArray($return); + return $return; } @@ -258,24 +257,16 @@ class API extends \Piwik\Plugin\API if (empty($timestamp)) $timestamp = time(); - $time = Date::factory((int)$timestamp)->getDatetime(); - $result = Db::fetchAll(" - SELECT - idsite - FROM - " . Common::prefixTable('site') . " s - WHERE EXISTS ( - SELECT 1 - FROM " . Common::prefixTable('log_visit') . " v - WHERE v.idsite = s.idsite - AND visit_last_action_time > ? - AND visit_last_action_time <= ? - LIMIT 1) - ", array($time, $now = Date::now()->addHour(1)->getDatetime())); + $time = Date::factory((int)$timestamp)->getDatetime(); + $now = Date::now()->addHour(1)->getDatetime(); + + $result = $this->getModel()->getSitesWithVisits($time, $now); + $idSites = array(); foreach ($result as $idSite) { $idSites[] = $idSite['idsite']; } + return $idSites; } @@ -291,9 +282,11 @@ class API extends \Piwik\Plugin\API $sitesId = $this->getSitesIdWithAdminAccess(); $sites = $this->getSitesFromIds($sitesId); - if ($fetchAliasUrls) - foreach ($sites as &$site) + if ($fetchAliasUrls) { + foreach ($sites as &$site) { $site['alias_urls'] = API::getInstance()->getSiteUrlsFromId($site['idsite']); + } + } return $sites; } @@ -373,10 +366,12 @@ class API extends \Piwik\Plugin\API } $accessRaw = Access::getInstance()->getRawSitesWithSomeViewAccess($_restrictSitesToLogin); - $sitesId = array(); + $sitesId = array(); + foreach ($accessRaw as $access) { $sitesId[] = $access['idsite']; } + return $sitesId; } else { return Access::getInstance()->getSitesIdWithAtLeastViewAccess(); @@ -407,6 +402,7 @@ class API extends \Piwik\Plugin\API } else { $urlBis = str_replace('://', '://www.', $url); } + return array($url, $urlBis); } @@ -420,28 +416,12 @@ class API extends \Piwik\Plugin\API { $url = $this->removeTrailingSlash($url); list($url, $urlBis) = $this->getNormalizedUrls($url); + if (Piwik::hasUserSuperUserAccess()) { - $ids = Db::get()->fetchAll( - 'SELECT idsite - FROM ' . Common::prefixTable('site') . ' - WHERE (main_url = ? OR main_url = ?) ' . - 'UNION - SELECT idsite - FROM ' . Common::prefixTable('site_url') . ' - WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis)); + $ids = $this->getModel()->getAllSitesIdFromSiteUrl($url, $urlBis); } else { $login = Piwik::getCurrentUserLogin(); - $ids = Db::get()->fetchAll( - 'SELECT idsite - FROM ' . Common::prefixTable('site') . ' - WHERE (main_url = ? OR main_url = ?)' . - 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ') ' . - 'UNION - SELECT idsite - FROM ' . Common::prefixTable('site_url') . ' - WHERE (url = ? OR url = ?)' . - 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ')', - array($url, $urlBis, $login, $url, $urlBis, $login)); + $ids = $this->getModel()->getSitesIdFromSiteUrlHavingAccess($url, $urlBis, $login); } return $ids; @@ -457,18 +437,17 @@ class API extends \Piwik\Plugin\API public function getSitesIdFromTimezones($timezones) { Piwik::checkUserHasSuperUserAccess(); + $timezones = Piwik::getArrayFromApiParameter($timezones); $timezones = array_unique($timezones); - $ids = Db::get()->fetchAll( - 'SELECT idsite - FROM ' . Common::prefixTable('site') . ' - WHERE timezone IN (' . Common::getSqlStringFieldsArray($timezones) . ') - ORDER BY idsite ASC', - $timezones); + + $ids = $this->getModel()->getSitesFromTimezones($timezones); + $return = array(); foreach ($ids as $id) { $return[] = $id['idsite']; } + return $return; } @@ -538,9 +517,7 @@ class API extends \Piwik\Plugin\API } $this->checkValidCurrency($currency); - $db = Db::get(); - - $url = $urls[0]; + $url = $urls[0]; $urls = array_slice($urls, 1); $bind = array('name' => $siteName, @@ -549,31 +526,31 @@ class API extends \Piwik\Plugin\API ); $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); - $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); + $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents); - $bind['keep_url_fragment'] = $keepURLFragments; - $bind['timezone'] = $timezone; - $bind['currency'] = $currency; - $bind['ecommerce'] = (int)$ecommerce; + $bind['keep_url_fragment'] = $keepURLFragments; + $bind['timezone'] = $timezone; + $bind['currency'] = $currency; + $bind['ecommerce'] = (int)$ecommerce; $bind['sitesearch'] = $siteSearch; - $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters; + $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters; $bind['sitesearch_category_parameters'] = $searchCategoryParameters; - $bind['ts_created'] = !is_null($startDate) - ? Date::factory($startDate)->getDatetime() - : Date::now()->getDatetime(); + + if (is_null($startDate)) { + $bind['ts_created'] = Date::now()->getDatetime(); + } else { + $bind['ts_created'] = Date::factory($startDate)->getDatetime(); + } + $bind['type'] = $this->checkAndReturnType($type); - if (!empty($group) - && Piwik::hasUserSuperUserAccess() - ) { + if (!empty($group) && Piwik::hasUserSuperUserAccess()) { $bind['group'] = trim($group); } else { $bind['group'] = ""; } - $db->insert(Common::prefixTable("site"), $bind); - - $idSite = $db->lastInsertId(); + $idSite = $this->getModel()->createSite($bind); $this->insertSiteUrls($idSite, $urls); @@ -588,7 +565,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent('SitesManager.addSite.end', array($idSite)); - return (int)$idSite; + return (int) $idSite; } private function postUpdateWebsite($idSite) @@ -619,16 +596,7 @@ class API extends \Piwik\Plugin\API throw new Exception(Piwik::translate("SitesManager_ExceptionDeleteSite")); } - $db = Db::get(); - - $db->query("DELETE FROM " . Common::prefixTable("site") . " - WHERE idsite = ?", $idSite); - - $db->query("DELETE FROM " . Common::prefixTable("site_url") . " - WHERE idsite = ?", $idSite); - - $db->query("DELETE FROM " . Common::prefixTable("access") . " - WHERE idsite = ?", $idSite); + $this->getModel()->deleteSite($idSite); // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data) Cache::deleteCacheWebsiteAttributes($idSite); @@ -682,12 +650,14 @@ class API extends \Piwik\Plugin\API private function checkAndReturnType($type) { - if(empty($type)) { + if (empty($type)) { $type = Site::DEFAULT_SITE_TYPE; } - if(!is_string($type)) { + + if (!is_string($type)) { throw new Exception("Invalid website type $type"); } + return $type; } @@ -704,14 +674,17 @@ class API extends \Piwik\Plugin\API if (empty($excludedIps)) { return ''; } + $ips = explode(',', $excludedIps); $ips = array_map('trim', $ips); $ips = array_filter($ips, 'strlen'); + foreach ($ips as $ip) { if (!$this->isValidIp($ip)) { throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5"))); } } + $ips = implode(',', $ips); return $ips; } @@ -756,7 +729,7 @@ class API extends \Piwik\Plugin\API $urls = $this->cleanParameterUrls($urls); $this->checkUrls($urls); - $this->deleteSiteAliasUrls($idSite); + $this->getModel()->deleteSiteAliasUrls($idSite); $this->insertSiteUrls($idSite, $urls); $this->postUpdateWebsite($idSite); @@ -1131,17 +1104,15 @@ class API extends \Piwik\Plugin\API $bind['sitesearch_category_parameters'] = $searchCategoryParameters; $bind['type'] = $this->checkAndReturnType($type); - $db = Db::get(); - $db->update(Common::prefixTable("site"), - $bind, - "idsite = $idSite" - ); + $this->getModel()->updateSite($bind, $idSite); // we now update the main + alias URLs - $this->deleteSiteAliasUrls($idSite); + $this->getModel()->deleteSiteAliasUrls($idSite); + if (count($urls) > 1) { $this->addSiteAliasUrls($idSite, array_slice($urls, 1)); } + $this->postUpdateWebsite($idSite); } @@ -1158,14 +1129,9 @@ class API extends \Piwik\Plugin\API $idSites = Site::getIdSitesFromIdSitesString($idSites); Piwik::checkUserHasAdminAccess($idSites); - // Update piwik_site.ts_created - $query = "UPDATE " . Common::prefixTable("site") . - " SET ts_created = ?" . - " WHERE idsite IN ( " . implode(",", $idSites) . " ) - AND ts_created > ?"; $minDateSql = $minDate->subDay(1)->getDatetime(); - $bind = array($minDateSql, $minDateSql); - Db::query($query, $bind); + + $this->getModel()->updateSiteCreatedTime($idSites, $minDateSql); } private function checkAndReturnCommaSeparatedStringList($parameters) @@ -1296,12 +1262,8 @@ class API extends \Piwik\Plugin\API public function getUniqueSiteTimezones() { Piwik::checkUserHasSuperUserAccess(); - $results = Db::fetchAll("SELECT distinct timezone FROM " . Common::prefixTable('site')); - $timezones = array(); - foreach ($results as $result) { - $timezones[] = $result['timezone']; - } - return $timezones; + + return $this->getModel()->getUniqueSiteTimezones(); } /** @@ -1311,14 +1273,9 @@ class API extends \Piwik\Plugin\API private function insertSiteUrls($idSite, $urls) { if (count($urls) != 0) { - $db = Db::get(); foreach ($urls as $url) { try { - $db->insert(Common::prefixTable("site_url"), array( - 'idsite' => $idSite, - 'url' => $url - ) - ); + $this->getModel()->insertSiteUrl($idSite, $url); } catch(Exception $e) { // See bug #4149 } @@ -1326,16 +1283,6 @@ class API extends \Piwik\Plugin\API } } - /** - * Delete all the alias URLs for the given idSite. - */ - private function deleteSiteAliasUrls($idsite) - { - $db = Db::get(); - $db->query("DELETE FROM " . Common::prefixTable("site_url") . " - WHERE idsite = ?", $idsite); - } - /** * Remove the final slash in the URLs if found * @@ -1350,6 +1297,7 @@ class API extends \Piwik\Plugin\API ) { $url = substr($url, 0, strlen($url) - 1); } + return $url; } @@ -1404,6 +1352,7 @@ class API extends \Piwik\Plugin\API if (empty($searchKeywordParameters)) { $searchKeywordParameters = ''; } + if (empty($searchCategoryParameters)) { $searchCategoryParameters = ''; } @@ -1439,9 +1388,10 @@ class API extends \Piwik\Plugin\API if (!is_array($urls)) { $urls = array($urls); } - $urls = array_filter($urls); + $urls = array_filter($urls); $urls = array_map('urldecode', $urls); + foreach ($urls as &$url) { $url = $this->removeTrailingSlash($url); if (strpos($url, 'http') !== 0) { @@ -1450,6 +1400,7 @@ class API extends \Piwik\Plugin\API $url = trim($url); $url = Common::sanitizeInputValue($url); } + $urls = array_unique($urls); return $urls; } @@ -1489,30 +1440,9 @@ class API extends \Piwik\Plugin\API return array(); } - $ids_str = ''; - foreach ($ids as $id_val) { - $ids_str .= $id_val . ' , '; - } - $ids_str .= $id_val; - - $db = Db::get(); - $bind = array('%' . $pattern . '%', 'http%' . $pattern . '%', '%' . $pattern . '%'); + $limit = SettingsPiwik::getWebsitesCountToDisplay(); + $sites = $this->getModel()->getPatternMatchSites($ids, $pattern, $limit); - // Also match the idsite - $where = ''; - if (is_numeric($pattern)) { - $bind[] = $pattern; - $where = 'OR s.idsite = ?'; - } - $sites = $db->fetchAll("SELECT idsite, name, main_url, `group` - FROM " . Common::prefixTable('site') . " s - WHERE ( s.name like ? - OR s.main_url like ? - OR s.`group` like ? - $where ) - AND idsite in ($ids_str) - LIMIT " . SettingsPiwik::getWebsitesCountToDisplay(), - $bind); return $sites; } diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php index 207f59bdfd1965ba4a3349c75addd3901247d4e3..971844c40b0a634d69ca87dafcf4850802101ef2 100644 --- a/plugins/SitesManager/Model.php +++ b/plugins/SitesManager/Model.php @@ -8,13 +8,172 @@ */ namespace Piwik\Plugins\SitesManager; +use Piwik\Access; +use Piwik\Date; use Piwik\Db; use Piwik\Common; use Exception; -use Piwik\Site; class Model { + private static $rawPrefix = 'site'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + + public function createSite($site) + { + $db = $this->getDb(); + $db->insert($this->table, $site); + + $idSite = $db->lastInsertId(); + + return $idSite; + } + + /** + * Returns all websites belonging to the specified group + * @param string $group Group name + * @return array of sites + */ + public function getSitesFromGroup($group) + { + $sites = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . " + WHERE `group` = ?", $group); + + return $sites; + } + + /** + * Returns the list of website groups, including the empty group + * if no group were specified for some websites + * + * @return array of group names strings + */ + public function getSitesGroups() + { + $groups = $this->getDb()->fetchAll("SELECT DISTINCT `group` FROM " . $this->table); + + $cleanedGroups = array(); + foreach ($groups as $group) { + $cleanedGroups[] = $group['group']; + } + + return $cleanedGroups; + } + + /** + * Returns all websites + * + * @return array The list of websites, indexed by idsite + */ + public function getAllSites() + { + $sites = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . " ORDER BY idsite ASC"); + + return $sites; + } + + /** + * Returns the list of the website IDs that received some visits since the specified timestamp. + * + * @param \Piwik\Date $time + * @param \Piwik\Date $now + * @return array The list of website IDs + */ + public function getSitesWithVisits($time, $now) + { + $sites = Db::fetchAll(" + SELECT idsite FROM " . $this->table . " s + WHERE EXISTS ( + SELECT 1 + FROM " . Common::prefixTable('log_visit') . " v + WHERE v.idsite = s.idsite + AND visit_last_action_time > ? + AND visit_last_action_time <= ? + LIMIT 1) + ", array($time, $now)); + + return $sites; + } + + + /** + * Returns the list of websites ID associated with a URL. + * + * @param string $url + * @param string $urlBis + * @return array list of websites ID + */ + public function getAllSitesIdFromSiteUrl($url, $urlBis) + { + $siteUrlTable = Common::prefixTable('site_url'); + + $ids = $this->getDb()->fetchAll( + 'SELECT idsite FROM ' . $this->table . ' + WHERE (main_url = ? OR main_url = ?) ' . + 'UNION + SELECT idsite FROM ' . $siteUrlTable . ' + WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis)); + + return $ids; + } + + /** + * Returns the list of websites ID associated with a URL. + * + * @param string $url + * @return array list of websites ID + */ + public function getSitesIdFromSiteUrlHavingAccess($url, $urlBis, $login) + { + $siteUrlTable = Common::prefixTable('site_url'); + $sqlAccessSite = Access::getSqlAccessSite('idsite'); + + $ids = $this->getDb()->fetchAll( + 'SELECT idsite + FROM ' . $this->table . ' + WHERE (main_url = ? OR main_url = ?)' . + 'AND idsite IN (' . $sqlAccessSite . ') ' . + 'UNION + SELECT idsite + FROM ' . $siteUrlTable . ' + WHERE (url = ? OR url = ?)' . + 'AND idsite IN (' . $sqlAccessSite . ')', + array($url, $urlBis, $login, $url, $urlBis, $login)); + + return $ids; + } + + /** + * Returns all websites with a timezone matching one the specified timezones + * + * @param array $timezones + * @return array + * @ignore + */ + public function getSitesFromTimezones($timezones) + { + $query = 'SELECT idsite FROM ' . $this->table . ' + WHERE timezone IN (' . Common::getSqlStringFieldsArray($timezones) . ') + ORDER BY idsite ASC'; + $sites = $this->getDb()->fetchAll($query, $timezones); + + return $sites; + } + + public function deleteSite($idSite) + { + $db = $this->getDb(); + + $db->query("DELETE FROM " . $this->table . " WHERE idsite = ?", $idSite); + $db->query("DELETE FROM " . Common::prefixTable("site_url") . " WHERE idsite = ?", $idSite); + $db->query("DELETE FROM " . Common::prefixTable("access") . " WHERE idsite = ?", $idSite); + } + /** * Returns the list of websites from the ID array in parameters. * @@ -36,11 +195,10 @@ class Model $idSites = array_map('intval', $idSites); - $db = Db::get(); - $sites = $db->fetchAll("SELECT * - FROM " . Common::prefixTable("site") . " - WHERE idsite IN (" . implode(", ", $idSites) . ") - ORDER BY idsite ASC $limit"); + $db = $this->getDb(); + $sites = $db->fetchAll("SELECT * FROM " . $this->table . " + WHERE idsite IN (" . implode(", ", $idSites) . ") + ORDER BY idsite ASC $limit"); return $sites; } @@ -54,9 +212,8 @@ class Model */ public function getSiteFromId($idSite) { - $site = Db::get()->fetchRow("SELECT * - FROM " . Common::prefixTable("site") . " - WHERE idsite = ?", $idSite); + $site = $this->getDb()->fetchRow("SELECT * FROM " . $this->table . " + WHERE idsite = ?", $idSite); return $site; } @@ -69,11 +226,13 @@ class Model */ public function getSitesId() { - $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site')); + $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site')); + $idSites = array(); foreach ($result as $idSite) { $idSites[] = $idSite['idsite']; } + return $idSites; } @@ -87,7 +246,6 @@ class Model public function getSiteUrlsFromId($idSite) { $urls = $this->getAliasSiteUrlsFromId($idSite); - $site = $this->getSiteFromId($idSite); if (empty($site)) { @@ -106,14 +264,119 @@ class Model */ public function getAliasSiteUrlsFromId($idSite) { - $db = Db::get(); - $result = $db->fetchAll("SELECT url - FROM " . Common::prefixTable("site_url") . " - WHERE idsite = ?", $idSite); + $db = $this->getDb(); + $result = $db->fetchAll("SELECT url FROM " . Common::prefixTable("site_url") . " + WHERE idsite = ?", $idSite); $urls = array(); foreach ($result as $url) { $urls[] = $url['url']; } + return $urls; } + + public function updateSite($site, $idSite) + { + $idSite = (int) $idSite; + + $db = $this->getDb(); + $db->update($this->table, $site, "idsite = $idSite"); + } + + /** + * Returns the list of unique timezones from all configured sites. + * + * @return array ( string ) + */ + public function getUniqueSiteTimezones() + { + $results = Db::fetchAll("SELECT distinct timezone FROM " . $this->table); + + $timezones = array(); + foreach ($results as $result) { + $timezones[] = $result['timezone']; + } + + return $timezones; + } + + /** + * Updates the field ts_created for the specified websites. + * + * @param $idSites int Id Site to update ts_created + * @param string Date to set as creation date. + * + * @ignore + */ + public function updateSiteCreatedTime($idSites, $minDateSql) + { + $idSites = array_map('intval', $idSites); + + $query = "UPDATE " . $this->table . " SET ts_created = ?" . + " WHERE idsite IN ( " . implode(",", $idSites) . " ) AND ts_created > ?"; + + $bind = array($minDateSql, $minDateSql); + + Db::query($query, $bind); + } + + /** + * Insert the list of alias URLs for the website. + * The URLs must not exist already for this website! + */ + public function insertSiteUrl($idSite, $url) + { + $db = $this->getDb(); + $db->insert(Common::prefixTable("site_url"), array( + 'idsite' => (int) $idSite, + 'url' => $url + ) + ); + } + + public function getPatternMatchSites($ids, $pattern, $limit) + { + $ids_str = ''; + foreach ($ids as $id_val) { + $ids_str .= (int) $id_val . ' , '; + } + $ids_str .= (int) $id_val; + + $bind = array('%' . $pattern . '%', 'http%' . $pattern . '%', '%' . $pattern . '%'); + + // Also match the idsite + $where = ''; + if (is_numeric($pattern)) { + $bind[] = $pattern; + $where = 'ORs s.idsite = ?'; + } + + $query = "SELECT idsite, name, main_url, `group` + FROM " . $this->table . " s + WHERE ( s.name like ? + OR s.main_url like ? + OR s.`group` like ? + $where ) + AND idsite in ($ids_str) + LIMIT " . (int) $limit; + + $db = $this->getDb(); + $sites = $db->fetchAll($query, $bind); + + return $sites; + } + + /** + * Delete all the alias URLs for the given idSite. + */ + public function deleteSiteAliasUrls($idsite) + { + $db = $this->getDb(); + $db->query("DELETE FROM " . Common::prefixTable("site_url") . " WHERE idsite = ?", $idsite); + } + + private function getDb() + { + return Db::get(); + } } diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php index faeddf9f0b66ac54c856205a8af44b60b8fbaaa9..32abffdc06e690d5b98c1ec05b1a1902dfa85c97 100644 --- a/plugins/Transitions/API.php +++ b/plugins/Transitions/API.php @@ -172,7 +172,6 @@ class API extends \Piwik\Plugin\API */ private function addInternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping) { - $data = $this->queryInternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping); if ($data['pageviews'] == 0) { @@ -198,7 +197,6 @@ class API extends \Piwik\Plugin\API */ private function addFollowingActions($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops = false) { - $data = $this->queryFollowingActions( $idaction, $actionType, $logAggregator, $limitBeforeGrouping, $includeLoops); @@ -225,7 +223,7 @@ class API extends \Piwik\Plugin\API if ($actionType != 'title') { // specific setup for page urls $types[Action::TYPE_PAGE_URL] = 'followingPages'; - $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )'; + $dimension = 'if ( idaction_url IS NULL, idaction_name, idaction_url )'; // site search referrers are logged with url=NULL // when we find one, we have to join on name $joinLogActionColumn = $dimension; @@ -408,7 +406,7 @@ class API extends \Piwik\Plugin\API if ($dimension == 'idaction_url_ref') { // site search referrers are logged with url_ref=NULL // when we find one, we have to join on name_ref - $dimension = 'IF( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )'; + $dimension = 'if ( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )'; $joinLogActionOn = $dimension; } else { $joinLogActionOn = $dimension; @@ -514,7 +512,6 @@ class API extends \Piwik\Plugin\API */ private function addExternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping) { - $data = $this->queryExternalReferrers( $idaction, $actionType, $logAggregator, $limitBeforeGrouping); diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php index 483b2d844f05112055297ab95ed3155c7909e6d9..a6ed712dbe76658cf00eb2201becf6e323dd2d10 100644 --- a/plugins/UserCountry/Controller.php +++ b/plugins/UserCountry/Controller.php @@ -369,7 +369,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin private function dieIfGeolocationAdminIsDisabled() { - if(!UserCountry::isGeoLocationAdminEnabled()) { + if (!UserCountry::isGeoLocationAdminEnabled()) { throw new \Exception('Geo location setting page has been disabled.'); } } diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php index 61b26323da2d524e59c20320a64bae7ed861584d..7a1c9faf7a8aed4ba59e45db195c5924c65d402d 100644 --- a/plugins/UsersManager/API.php +++ b/plugins/UsersManager/API.php @@ -318,7 +318,7 @@ class API extends \Piwik\Plugin\API * * @exception in case of an invalid parameter */ - public function addUser($userLogin, $password, $email, $alias = false) + public function addUser($userLogin, $password, $email, $alias = false, $_isPasswordHashed = false) { Piwik::checkUserHasSuperUserAccess(); @@ -326,10 +326,15 @@ class API extends \Piwik\Plugin\API $this->checkEmail($email); $password = Common::unsanitizeInputValue($password); - UsersManager::checkPassword($password); + if (!$_isPasswordHashed) { + UsersManager::checkPassword($password); + + $passwordTransformed = UsersManager::getPasswordHash($password); + } else { + $passwordTransformed = $password; + } $alias = $this->getCleanAlias($alias, $userLogin); - $passwordTransformed = UsersManager::getPasswordHash($password); $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed); @@ -435,7 +440,7 @@ class API extends \Piwik\Plugin\API $this->checkEmail($email); } - $alias = $this->getCleanAlias($alias, $userLogin); + $alias = $this->getCleanAlias($alias, $userLogin); $token_auth = $this->getTokenAuth($userLogin, $password); $this->model->updateUser($userLogin, $password, $email, $alias, $token_auth); diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php index 56974b1efc30b8722d708598dfdf667b40268b48..d22240b977c495e3fe4222d7689b64b62a6b2226 100644 --- a/plugins/UsersManager/Controller.php +++ b/plugins/UsersManager/Controller.php @@ -177,7 +177,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin ); // assertion - if(count($dates) != count($mappingDatesToPeriods)) { + if (count($dates) != count($mappingDatesToPeriods)) { throw new Exception("some metadata is missing in getDefaultDates()"); } @@ -251,7 +251,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin Piwik::checkUserIsNotAnonymous(); $salt = Common::getRequestVar('ignoreSalt', false, 'string'); - if($salt !== $this->getIgnoreCookieSalt()) { + if ($salt !== $this->getIgnoreCookieSalt()) { throw new Exception("Not authorized"); } diff --git a/plugins/UsersManager/Menu.php b/plugins/UsersManager/Menu.php index 8335d97305c1fcd09fa189a1e821450db2b16a80..88aeb051f5b8cb79b561d8bb80ccd5f38bfc24ad 100644 --- a/plugins/UsersManager/Menu.php +++ b/plugins/UsersManager/Menu.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\UsersManager; use Piwik\Menu\MenuAdmin; +use Piwik\Menu\MenuUser; use Piwik\Piwik; class Menu extends \Piwik\Plugin\Menu @@ -20,4 +21,11 @@ class Menu extends \Piwik\Plugin\Menu $menu->addManageItem('UsersManager_MenuUserSettings', $this->urlForAction('userSettings'), $order = 3); } } + + public function configureUserMenu(MenuUser $menu) + { + if (!Piwik::isUserIsAnonymous()) { + $menu->addItem('', 'General_Settings', $this->urlForAction('index'), 0); + } + } } diff --git a/plugins/UsersManager/Model.php b/plugins/UsersManager/Model.php index 872d693ee6d6006711c80ac4b813c2c70d1f27b4..3d62ca61fd8e43265503e4331c279e81b0a493d1 100644 --- a/plugins/UsersManager/Model.php +++ b/plugins/UsersManager/Model.php @@ -26,6 +26,14 @@ use Piwik\Piwik; */ class Model { + private static $rawPrefix = 'user'; + private $table; + + public function __construct() + { + $this->table = Common::prefixTable(self::$rawPrefix); + } + /** * Returns the list of all the users * @@ -39,13 +47,12 @@ class Model if (!empty($userLogins)) { $where = 'WHERE login IN (' . Common::getSqlStringFieldsArray($userLogins) . ')'; - $bind = $userLogins; + $bind = $userLogins; } - $users = Db::get()->fetchAll("SELECT * - FROM " . Common::prefixTable("user") . " - $where - ORDER BY login ASC", $bind); + $users = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . " + $where + ORDER BY login ASC", $bind); return $users; } @@ -57,9 +64,8 @@ class Model */ public function getUsersLogin() { - $users = Db::get()->fetchAll("SELECT login - FROM " . Common::prefixTable("user") . " - ORDER BY login ASC"); + $users = $this->getDb()->fetchAll("SELECT login FROM " . $this->table . " ORDER BY login ASC"); + $return = array(); foreach ($users as $login) { $return[] = $login['login']; @@ -70,10 +76,9 @@ class Model public function getUsersSitesFromAccess($access) { - $users = Db::get()->fetchAll("SELECT login,idsite - FROM " . Common::prefixTable("access") - . " WHERE access = ? - ORDER BY login, idsite", $access); + $users = $this->getDb()->fetchAll("SELECT login,idsite FROM " . Common::prefixTable("access") + . " WHERE access = ? + ORDER BY login, idsite", $access); $return = array(); foreach ($users as $user) { @@ -85,9 +90,8 @@ class Model public function getUsersAccessFromSite($idSite) { - $users = Db::get()->fetchAll("SELECT login,access - FROM " . Common::prefixTable("access") - . " WHERE idsite = ?", $idSite); + $users = $this->getDb()->fetchAll("SELECT login,access FROM " . Common::prefixTable("access") + . " WHERE idsite = ?", $idSite); $return = array(); foreach ($users as $user) { @@ -99,9 +103,9 @@ class Model public function getUsersLoginWithSiteAccess($idSite, $access) { - $users = Db::get()->fetchAll("SELECT login - FROM " . Common::prefixTable("access") - . " WHERE idsite = ? AND access = ?", array($idSite, $access)); + $users = $this->getDb()->fetchAll("SELECT login + FROM " . Common::prefixTable("access") + . " WHERE idsite = ? AND access = ?", array($idSite, $access)); $logins = array(); foreach ($users as $user) { @@ -129,9 +133,8 @@ class Model */ public function getSitesAccessFromUser($userLogin) { - $users = Db::get()->fetchAll("SELECT idsite,access - FROM " . Common::prefixTable("access") - . " WHERE login = ?", $userLogin); + $users = $this->getDb()->fetchAll("SELECT idsite,access FROM " . Common::prefixTable("access") + . " WHERE login = ?", $userLogin); $return = array(); foreach ($users as $user) { @@ -146,23 +149,20 @@ class Model public function getUser($userLogin) { - return Db::get()->fetchRow("SELECT * - FROM " . Common::prefixTable("user") - . " WHERE login = ?", $userLogin); + return $this->getDb()->fetchRow("SELECT * FROM " . $this->table + . " WHERE login = ?", $userLogin); } public function getUserByEmail($userEmail) { - return Db::get()->fetchRow("SELECT * - FROM " . Common::prefixTable("user") - . " WHERE email = ?", $userEmail); + return $this->getDb()->fetchRow("SELECT * FROM " . $this->table + . " WHERE email = ?", $userEmail); } public function getUserByTokenAuth($tokenAuth) { - return Db::get()->fetchRow('SELECT * - FROM ' . Common::prefixTable('user') . ' - WHERE token_auth = ?', $tokenAuth); + return $this->getDb()->fetchRow('SELECT * FROM ' . $this->table . ' + WHERE token_auth = ?', $tokenAuth); } public function addUser($userLogin, $passwordTransformed, $email, $alias, $tokenAuth, $dateRegistered) @@ -177,12 +177,12 @@ class Model 'superuser_access' => 0 ); - Db::get()->insert(Common::prefixTable("user"), $user); + $this->getDb()->insert($this->table, $user); } public function setSuperUserAccess($userLogin, $hasSuperUserAccess) { - Db::get()->update(Common::prefixTable("user"), + $this->getDb()->update($this->table, array( 'superuser_access' => $hasSuperUserAccess ? 1 : 0 ), @@ -192,17 +192,17 @@ class Model public function getUsersHavingSuperUserAccess() { - $users = Db::get()->fetchAll("SELECT login, email - FROM " . Common::prefixTable("user") . " - WHERE superuser_access = 1 - ORDER BY date_registered ASC"); + $users = $this->getDb()->fetchAll("SELECT login, email + FROM " . Common::prefixTable("user") . " + WHERE superuser_access = 1 + ORDER BY date_registered ASC"); return $users; } public function updateUser($userLogin, $password, $email, $alias, $tokenAuth) { - Db::get()->update(Common::prefixTable("user"), + $this->getDb()->update($this->table, array( 'password' => $password, 'alias' => $alias, @@ -215,24 +215,22 @@ class Model public function userExists($userLogin) { - $count = Db::get()->fetchOne("SELECT count(*) - FROM " . Common::prefixTable("user") . " - WHERE login = ?", $userLogin); + $count = $this->getDb()->fetchOne("SELECT count(*) FROM " . $this->table . " + WHERE login = ?", $userLogin); return $count != 0; } public function userEmailExists($userEmail) { - $count = Db::get()->fetchOne("SELECT count(*) - FROM " . Common::prefixTable("user") . " - WHERE email = ?", $userEmail); + $count = $this->getDb()->fetchOne("SELECT count(*) FROM " . $this->table . " + WHERE email = ?", $userEmail); return $count != 0; } public function addUserAccess($userLogin, $access, $idSites) { foreach ($idSites as $idsite) { - Db::get()->insert(Common::prefixTable("access"), + $this->getDb()->insert(Common::prefixTable("access"), array("idsite" => $idsite, "login" => $userLogin, "access" => $access) @@ -242,7 +240,7 @@ class Model public function deleteUserOnly($userLogin) { - Db::get()->query("DELETE FROM " . Common::prefixTable("user") . " WHERE login = ?", $userLogin); + $this->getDb()->query("DELETE FROM " . $this->table . " WHERE login = ?", $userLogin); /** * Triggered after a user has been deleted. @@ -258,12 +256,12 @@ class Model public function deleteUserAccess($userLogin, $idSites = null) { if (is_null($idSites)) { - Db::get()->query("DELETE FROM " . Common::prefixTable("access") . + $this->getDb()->query("DELETE FROM " . Common::prefixTable("access") . " WHERE login = ?", array($userLogin)); } else { foreach ($idSites as $idsite) { - Db::get()->query("DELETE FROM " . Common::prefixTable("access") . + $this->getDb()->query("DELETE FROM " . Common::prefixTable("access") . " WHERE idsite = ? AND login = ?", array($idsite, $userLogin) ); @@ -271,4 +269,9 @@ class Model } } + private function getDb() + { + return Db::get(); + } + } diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php index b2c4b2e8f3e2e8bb7a39d953f0366a186bf730d9..cdc383d041711750d509e03087c8299459ae5c84 100644 --- a/plugins/UsersManager/UsersManager.php +++ b/plugins/UsersManager/UsersManager.php @@ -34,7 +34,8 @@ class UsersManager extends \Piwik\Plugin 'SitesManager.deleteSite.end' => 'deleteSite', 'Tracker.Cache.getSiteAttributes' => 'recordAdminUsersInCache', 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', - 'Platform.initialized' => 'onPlatformInitialized' + 'Platform.initialized' => 'onPlatformInitialized', + 'CronArchive.getTokenAuth' => 'getCronArchiveTokenAuth' ); } @@ -62,9 +63,22 @@ class UsersManager extends \Piwik\Plugin foreach ($users as $user) { $tokens[] = $user['token_auth']; } + $attributes['admin_token_auth'] = $tokens; } + public function getCronArchiveTokenAuth(&$token) + { + $model = new Model(); + $superUsers = $model->getUsersHavingSuperUserAccess(); + + if (!empty($superUsers)) { + $superUser = array_shift($superUsers); + + $token = $superUser['token_auth']; + } + } + /** * Delete user preferences associated with a particular site */ @@ -105,7 +119,9 @@ class UsersManager extends \Piwik\Plugin ) { return true; } + $l = strlen($input); + return $l >= self::PASSWORD_MIN_LENGTH && $l <= self::PASSWORD_MAX_LENGTH; } diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator index 90327ea31c298d17cf14312d4eeefad7228c54a4..5d6355fcb38e7b8964d697da3cf9e2d201bf2245 160000 --- a/plugins/VisitorGenerator +++ b/plugins/VisitorGenerator @@ -1 +1 @@ -Subproject commit 90327ea31c298d17cf14312d4eeefad7228c54a4 +Subproject commit 5d6355fcb38e7b8964d697da3cf9e2d201bf2245 diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php index 903b2477cf5ecd203bd04b080cedc0f4d55223d5..63fe8ef2050431fec2914156432bf41f987c5c70 100644 --- a/plugins/VisitsSummary/Controller.php +++ b/plugins/VisitsSummary/Controller.php @@ -159,7 +159,7 @@ class Controller extends \Piwik\Plugin\Controller $view->maxActions = (int)$dataRow->getColumn('max_actions'); $view->nbActionsPerVisit = $dataRow->getColumn('nb_actions_per_visit'); - if(Common::isActionsPluginEnabled()) { + if (Common::isActionsPluginEnabled()) { $view->showActionsPluginReports = true; $dataTableActions = APIActions::getInstance()->get($idSite, Common::getRequestVar('period'), Common::getRequestVar('date'), \Piwik\API\Request::getRawSegmentFromRequest()); diff --git a/tests/PHPUnit/Core/AssetManagerTest.php b/tests/PHPUnit/Core/AssetManagerTest.php index 2e2f27f498adc7495f56c3ec90aeccc47cfdcc83..85fc6d9764cd2a9a60010ec139e71728372dcc5e 100644 --- a/tests/PHPUnit/Core/AssetManagerTest.php +++ b/tests/PHPUnit/Core/AssetManagerTest.php @@ -420,7 +420,7 @@ class AssetManagerTest extends PHPUnit_Framework_TestCase return '<script type="text/javascript">' . PHP_EOL . 'var translations = [];' . PHP_EOL . - 'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }for(var i in translations) { piwik_translations[i] = translations[i];} ' . PHP_EOL . + 'if (typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }for(var i in translations) { piwik_translations[i] = translations[i];} ' . PHP_EOL . '</script>'; } diff --git a/tests/PHPUnit/Core/Unzip/relative.zip b/tests/PHPUnit/Core/Http/fixture.zip similarity index 100% rename from tests/PHPUnit/Core/Unzip/relative.zip rename to tests/PHPUnit/Core/Http/fixture.zip diff --git a/tests/PHPUnit/Core/HttpTest.php b/tests/PHPUnit/Core/HttpTest.php index 36af08b943d31b99fb07a7229bfdb83f3b769952..af2711c530c4d71b79637bd4668412023d42242e 100644 --- a/tests/PHPUnit/Core/HttpTest.php +++ b/tests/PHPUnit/Core/HttpTest.php @@ -19,9 +19,9 @@ class HttpTest extends PHPUnit_Framework_TestCase public function getMethodsToTest() { return array( - array('curl'), - array('fopen'), - array('socket'), + 'curl' => array('curl'), + 'fopen' => array('fopen'), + 'socket' => array('socket'), ); } @@ -33,7 +33,7 @@ class HttpTest extends PHPUnit_Framework_TestCase public function testFetchRemoteFile($method) { $this->assertNotNull(Http::getTransportMethod()); - $result = Http::sendHttpRequestBy($method, 'http://localhost/piwik.js', 30); + $result = Http::sendHttpRequestBy($method, Fixture::getRootUrl() . 'piwik.js', 30); $this->assertTrue(strpos($result, 'Piwik') !== false); } @@ -43,7 +43,7 @@ class HttpTest extends PHPUnit_Framework_TestCase public function testFetchApiLatestVersion() { $destinationPath = PIWIK_USER_PATH . '/tmp/latest/LATEST'; - Http::fetchRemoteFile('http://localhost/', $destinationPath, 3); + Http::fetchRemoteFile(Fixture::getRootUrl(), $destinationPath, 3); $this->assertFileExists($destinationPath); $this->assertGreaterThan(0, filesize($destinationPath)); } @@ -54,7 +54,7 @@ class HttpTest extends PHPUnit_Framework_TestCase public function testFetchLatestZip() { $destinationPath = PIWIK_USER_PATH . '/tmp/latest/latest.zip'; - Http::fetchRemoteFile('http://localhost/tests/PHPUnit/Core/Unzip/relative.zip', $destinationPath, 3, 30); + Http::fetchRemoteFile(Fixture::getRootUrl() . 'tests/PHPUnit/Core/Http/fixture.zip', $destinationPath, 3, 30); $this->assertFileExists($destinationPath); $this->assertGreaterThan(0, filesize($destinationPath)); } @@ -100,7 +100,7 @@ class HttpTest extends PHPUnit_Framework_TestCase $result = Http::sendHttpRequestBy( $method, - 'http://localhost/tests/PHPUnit/Core/Unzip/relative.zip', + Fixture::getRootUrl() . 'tests/PHPUnit/Core/Http/fixture.zip', 30, $userAgent = null, $destinationPath = null, diff --git a/tests/PHPUnit/Core/Unzip/empty.zip b/tests/PHPUnit/Core/Unzip/empty.zip deleted file mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/tests/PHPUnit/Core/Unzip/test.gz b/tests/PHPUnit/Core/Unzip/test.gz deleted file mode 100755 index 113cbed07f15e555920c15ac10317e8645b15d47..0000000000000000000000000000000000000000 Binary files a/tests/PHPUnit/Core/Unzip/test.gz and /dev/null differ diff --git a/tests/PHPUnit/Core/Unzip/test.tar.gz b/tests/PHPUnit/Core/Unzip/test.tar.gz deleted file mode 100755 index b9cc91965d020c8931877d861539a57ab2226514..0000000000000000000000000000000000000000 Binary files a/tests/PHPUnit/Core/Unzip/test.tar.gz and /dev/null differ diff --git a/tests/PHPUnit/Core/Unzip/zaabs.zip b/tests/PHPUnit/Core/Unzip/zaabs.zip deleted file mode 100644 index c5517f940ddd3335a5fe9f49ab51ad8c4d4f29a5..0000000000000000000000000000000000000000 Binary files a/tests/PHPUnit/Core/Unzip/zaabs.zip and /dev/null differ diff --git a/tests/PHPUnit/Core/Unzip/zaatt.zip b/tests/PHPUnit/Core/Unzip/zaatt.zip deleted file mode 100644 index 91e0c4864a3d68c86e22057070cbf72aa41afd47..0000000000000000000000000000000000000000 Binary files a/tests/PHPUnit/Core/Unzip/zaatt.zip and /dev/null differ diff --git a/tests/PHPUnit/Core/UnzipTest.php b/tests/PHPUnit/Core/UnzipTest.php deleted file mode 100644 index 12f754c8b7c2970fb3d22509d0d15e518670f6a6..0000000000000000000000000000000000000000 --- a/tests/PHPUnit/Core/UnzipTest.php +++ /dev/null @@ -1,217 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ -use Piwik\Unzip\Gzip; -use Piwik\Unzip; -use Piwik\Unzip\PclZip; -use Piwik\Unzip\Tar; -use Piwik\Unzip\ZipArchive; - -class UnzipTest extends PHPUnit_Framework_TestCase -{ - /** - * @group Core - */ - public function testRelativePath() - { - clearstatcache(); - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $test = 'relative'; - $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip'; - - if (class_exists('ZipArchive', false)) { - $unzip = Unzip::factory('ZipArchive', $filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(1, count($res)); - $this->assertFileExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt'); - unlink($extractDir . $test . '.txt'); - - $unzip = new ZipArchive($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(1, count($res)); - $this->assertFileExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt'); - unlink($extractDir . $test . '.txt'); - } - - $unzip = Unzip::factory('PclZip', $filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(1, count($res)); - $this->assertFileExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt'); - unlink($extractDir . $test . '.txt'); - - $unzip = new PclZip($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(1, count($res)); - $this->assertFileExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt'); - unlink($extractDir . $test . '.txt'); - } - - /** - * @group Core - */ - public function testRelativePathAttack() - { - clearstatcache(); - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $test = 'zaatt'; - $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip'; - - if (class_exists('ZipArchive', false)) { - $unzip = new ZipArchive($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - $this->assertFileNotExists($extractDir . $test . '.txt'); - $this->assertFileNotExists($extractDir . '../' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../' . $test . '.txt'); - } - - $unzip = new PclZip($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - $this->assertFileNotExists($extractDir . $test . '.txt'); - $this->assertFileNotExists($extractDir . '../' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../' . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/../../' . $test . '.txt'); - } - - /** - * @group Core - */ - public function testAbsolutePathAttack() - { - clearstatcache(); - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $test = 'zaabs'; - $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip'; - - if (class_exists('ZipArchive', false)) { - $unzip = new ZipArchive($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - $this->assertFileNotExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - } - - $unzip = new PclZip($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - $this->assertFileNotExists($extractDir . $test . '.txt'); - $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt'); - } - - /** - * @group Core - */ - public function testUnzipErrorInfo() - { - clearstatcache(); - $filename = dirname(__FILE__) . '/Unzip/zaabs.zip'; - - $unzip = new ZipArchive($filename); - $this->assertContains('No error', $unzip->errorInfo()); - } - - /** - * @group Core - */ - public function testUnzipEmptyFile() - { - clearstatcache(); - $filename = dirname(__FILE__) . '/Unzip/empty.zip'; - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - - $unzip = new ZipArchive($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - } - - /** - * @group Core - */ - public function testUnzipNotExistingFile() - { - clearstatcache(); - $filename = dirname(__FILE__) . '/Unzip/NotExisting.zip'; - - try { - new ZipArchive($filename); - } catch (Exception $e) { - return; - } - $this->fail('Exception not raised'); - } - - /** - * @group Core - */ - public function testUnzipInvalidFile2() - { - clearstatcache(); - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $filename = dirname(__FILE__) . '/Unzip/NotExisting.zip'; - - $unzip = new PclZip($filename); - $res = $unzip->extract($extractDir); - $this->assertEquals(0, $res); - - $this->assertContains('PCLZIP_ERR_MISSING_FILE', $unzip->errorInfo()); - } - - /** - * @group Core - */ - public function testGzipFile() - { - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $extractFile = $extractDir . 'testgz.txt'; - $filename = dirname(__FILE__) . '/Unzip/test.gz'; - - $unzip = new Gzip($filename); - $res = $unzip->extract($extractFile); - $this->assertTrue($res); - - $this->assertFileContentsEquals('TESTSTRING', $extractFile); - } - - /** - * @group Core - */ - public function testTarGzFile() - { - $extractDir = PIWIK_USER_PATH . '/tmp/latest/'; - $filename = dirname(__FILE__) . '/Unzip/test.tar.gz'; - - $unzip = new Tar($filename, 'gz'); - $res = $unzip->extract($extractDir); - $this->assertTrue($res); - - $this->assertFileContentsEquals('TESTDATA', $extractDir . 'tarout1.txt'); - $this->assertFileContentsEquals('MORETESTDATA', $extractDir . 'tardir/tarout2.txt'); - } - - private function assertFileContentsEquals($expectedContent, $path) - { - $this->assertTrue(file_exists($path)); - - $fd = fopen($path, 'rb'); - $actualContent = fread($fd, filesize($path)); - fclose($fd); - - $this->assertEquals($expectedContent, $actualContent); - } -} diff --git a/tests/PHPUnit/DatabaseTestCase.php b/tests/PHPUnit/DatabaseTestCase.php index ef247e0f1e86e8ada54e3b9d14e681d9e5ba8c9d..715571eba45848b36824b933d159a5b95837e422 100644 --- a/tests/PHPUnit/DatabaseTestCase.php +++ b/tests/PHPUnit/DatabaseTestCase.php @@ -5,8 +5,10 @@ * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ +use Piwik\Config; use Piwik\Db; use Piwik\Tests\Fixture; +use Piwik\Tests\IntegrationTestCase; /** * Tests extending DatabaseTestCase are much slower to run: the setUp will @@ -16,12 +18,48 @@ use Piwik\Tests\Fixture; * then test it. * */ -class DatabaseTestCase extends PHPUnit_Framework_TestCase +class DatabaseTestCase extends IntegrationTestCase { /** * @var Fixture */ - protected $fixture = null; + public static $fixture; + public static $tableData; + + /** + * Implementation details: + * + * To increase speed of tests, database setup is done once in setUpBeforeClass. + * Afterwards, the content of the tables is stored in a static class variable, + * self::$tableData. Before each individual test, the database tables are + * truncated and the data in self::$tableData is restored. + * + * If your test modifies table columns, you will need to recreate the database + * completely. This can be accomplished by: + * + * public function setUp() + * { + * self::$fixture->performSetUp(); + * } + * + * public function tearDown() + * { + * parent::tearDown(); + * self::$fixture->performTearDown(); + * } + */ + public static function setUpBeforeClass() + { + static::configureFixture(static::$fixture); + parent::setUpBeforeClass(); + + self::$tableData = self::getDbTablesWithData(); + } + + public static function tearDownAfterClass() + { + self::$tableData = array(); + } /** * Setup the database and create the base tables for all tests @@ -30,9 +68,11 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase { parent::setUp(); - $this->fixture = new Fixture(); - $this->configureFixture(); - $this->fixture->performSetUp(); + Config::getInstance()->setTestEnvironment(); + + if (!empty(self::$tableData)) { + self::restoreDbTables(self::$tableData); + } } /** @@ -40,14 +80,17 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase */ public function tearDown() { + self::$fixture->clearInMemoryCaches(); + parent::tearDown(); - $this->fixture->performTearDown(); } - protected function configureFixture() + protected static function configureFixture($fixture) { - $this->fixture->loadTranslations = false; - $this->fixture->createSuperUser = false; - $this->fixture->configureComponents = false; + $fixture->loadTranslations = false; + $fixture->createSuperUser = false; + $fixture->configureComponents = false; } } + +DatabaseTestCase::$fixture = new Fixture(); \ No newline at end of file diff --git a/tests/PHPUnit/Fixture.php b/tests/PHPUnit/Fixture.php index f625f98001d06d85c747cecaffbf4766c04896fc..cc72b8498d836ea6334474e833b77aa15c5a7024 100644 --- a/tests/PHPUnit/Fixture.php +++ b/tests/PHPUnit/Fixture.php @@ -185,7 +185,7 @@ class Fixture extends PHPUnit_Framework_Assert static::createAccessInstance(); // We need to be SU to create websites for tests - Piwik::setUserHasSuperUserAccess(); + Access::getInstance()->setSuperUserAccess(); Cache::deleteTrackerCache(); @@ -275,6 +275,11 @@ class Fixture extends PHPUnit_Framework_Assert $this->dropDatabase(); } + $this->clearInMemoryCaches(); + } + + public function clearInMemoryCaches() + { DataTableManager::getInstance()->deleteAll(); Option::clearCache(); Site::clearCache(); @@ -291,7 +296,7 @@ class Fixture extends PHPUnit_Framework_Assert Config::unsetInstance(); \Piwik\Config::getInstance()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager - // since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object + // since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object } public static function loadAllPlugins($testEnvironment = null, $testCaseClass = false, $extraPluginsToLoad = array()) diff --git a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php index 30accf676deff468e75d529384866484ed890cfc..f0395054609576715c623b7af3818a2378868e87 100644 --- a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php +++ b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php @@ -80,12 +80,20 @@ class FewVisitsWithSetVisitorId extends Fixture private function trackVisits_setUserId() { + $userId = self::USER_ID_EXAMPLE_COM; // total = 2 visitors, 3 page views $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true); // First, some basic tests $this->settingInvalidUserIdShouldThrow($t); + // We create a visit with no User ID. + // When User ID will be set below, then it will UPDATE this visit here that starts without UserID + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(1.9)->getDatetime()); + $t->setVisitorId('6be121d126d93581'); + $t->setUrl('http://example.org/no-user-id-set-but-should-appear-in-user-id-visit'); + self::checkResponse($t->doTrackPageView('no User Id set but it should appear in '. $userId .'!')); + // A NEW VISIT // Setting both Visitor ID and User ID // -> User ID takes precedence @@ -98,22 +106,21 @@ class FewVisitsWithSetVisitorId extends Fixture $this->assertEquals($generatedVisitorId, $t->getVisitorId()); // Set User ID - $userId = self::USER_ID_EXAMPLE_COM; $t->setUserId($userId); - $this->assertEquals($userId, $t->getUserId()); // User ID takes precedence over any previously set Visitor ID - $hashUserId = $t->getUserIdHashed($userId); - $this->assertEquals($hashUserId, $t->getVisitorId()); + $this->assertEquals($t->getUserIdHashed($userId), $t->getVisitorId()); + $this->assertEquals('9395988394d4568d', $t->getVisitorId()); + $this->assertEquals($userId, $t->getUserId()); // Track a pageview with this user id self::checkResponse($t->doTrackPageView('incredible title!')); // Track another pageview $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(2.1)->getDatetime()); + $this->assertEquals($userId, $t->getUserId()); self::checkResponse($t->doTrackPageView('second page')); - // A NEW VISIT WITH A SET USER ID // Change User ID -> This will create a new visit $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(2.2)->getDatetime()); diff --git a/tests/PHPUnit/Fixtures/SqlDump.php b/tests/PHPUnit/Fixtures/SqlDump.php index 7aea83b781bb18ad04c6db98b3332b06b5c208af..06c094de036eaf73df4a8df73f97d9cdbd57a826 100644 --- a/tests/PHPUnit/Fixtures/SqlDump.php +++ b/tests/PHPUnit/Fixtures/SqlDump.php @@ -7,6 +7,7 @@ */ namespace Piwik\Tests\Fixtures; +use Piwik\Access; use Piwik\ArchiveProcessor\Rules; use Piwik\Config; use Piwik\Db; @@ -79,7 +80,7 @@ class SqlDump extends Fixture Rules::setBrowserTriggerArchiving(true); // reload access - Piwik::setUserHasSuperUserAccess(); + Access::getInstance()->reloadAccess(); $this->getTestEnvironment()->configOverride = array( 'database' => array( diff --git a/tests/PHPUnit/Integration/ArchiveInvalidationTest.php b/tests/PHPUnit/Integration/ArchiveInvalidationTest.php index a45aaac72c107b90e41c6c3b227388f74a745540..1d04920a8c56d4178c20c73ef4499667b2758f60 100644 --- a/tests/PHPUnit/Integration/ArchiveInvalidationTest.php +++ b/tests/PHPUnit/Integration/ArchiveInvalidationTest.php @@ -86,7 +86,6 @@ class ArchiveInvalidationTest extends IntegrationTestCase $this->invalidateTestArchives(); $this->runApiTests($api, $params); - } /** diff --git a/tests/PHPUnit/Integration/Core/AccessTest.php b/tests/PHPUnit/Integration/Core/AccessTest.php index 06a9b4a036769534b3d0d05433f93cdff30d39c8..ec1eb9bd29800c9c4fb7ffbff567a4ac522a3bb9 100644 --- a/tests/PHPUnit/Integration/Core/AccessTest.php +++ b/tests/PHPUnit/Integration/Core/AccessTest.php @@ -11,7 +11,6 @@ use Piwik\AuthResult; /** * Class Core_AccessTest * - * @group Core_AccessTest * @group Core */ class Core_AccessTest extends DatabaseTestCase @@ -308,4 +307,58 @@ class Core_AccessTest extends DatabaseTestCase $this->assertTrue($access->reloadAccess($mock)); $this->assertFalse($access->hasSuperUserAccess()); } + + public function test_doAsSuperUser_ChangesSuperUserAccessCorrectly() + { + Access::getInstance()->setSuperUserAccess(false); + + $this->assertFalse(Access::getInstance()->hasSuperUserAccess()); + + Access::doAsSuperUser(function () { + Core_AccessTest::assertTrue(Access::getInstance()->hasSuperUserAccess()); + }); + + $this->assertFalse(Access::getInstance()->hasSuperUserAccess()); + } + + public function test_doAsSuperUser_RemovesSuperUserAccess_IfExceptionThrown() + { + Access::getInstance()->setSuperUserAccess(false); + + $this->assertFalse(Access::getInstance()->hasSuperUserAccess()); + + try { + Access::doAsSuperUser(function () { + throw new Exception(); + }); + + $this->fail("Exception was not propagated by doAsSuperUser."); + } catch (Exception $ex) + { + // pass + } + + $this->assertFalse(Access::getInstance()->hasSuperUserAccess()); + } + + public function test_doAsSuperUser_ReturnsCallbackResult() + { + $result = Access::doAsSuperUser(function () { + return 24; + }); + $this->assertEquals(24, $result); + } + + public function test_reloadAccess_DoesNotRemoveSuperUserAccess_IfUsedInDoAsSuperUser() + { + Access::getInstance()->setSuperUserAccess(false); + + Access::doAsSuperUser(function () { + $access = Access::getInstance(); + + Core_AccessTest::assertTrue($access->hasSuperUserAccess()); + $access->reloadAccess(); + Core_AccessTest::assertTrue($access->hasSuperUserAccess()); + }); + } } \ No newline at end of file diff --git a/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php b/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php index 5183177aa93ced8ee0987afbc95911d9c6704a06..eca1d85aeab1594b603a1037fe70744fa674f8e5 100644 --- a/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php +++ b/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php @@ -10,6 +10,7 @@ use Piwik\CronArchive\SharedSiteIds; /** * @group Core + * @group SharedSiteIdsTest */ class SharedSiteIdsTest extends DatabaseTestCase { @@ -21,7 +22,6 @@ class SharedSiteIdsTest extends DatabaseTestCase public function setUp() { parent::setUp(); - $this->fixture->performSetUp(true); $this->sharedSiteIds = new SharedSiteIds(array(1,2,5,9)); } diff --git a/tests/PHPUnit/Integration/Core/LogTest.php b/tests/PHPUnit/Integration/Core/LogTest.php index f7d35e451b334d8e1430ae38aa1f3214515697b9..a3923183ae4a6f0d11e51e6a6feb60c44a93d8ba 100644 --- a/tests/PHPUnit/Integration/Core/LogTest.php +++ b/tests/PHPUnit/Integration/Core/LogTest.php @@ -31,9 +31,9 @@ class Core_LogTest extends DatabaseTestCase 'screen' => 'dummy error message<br /> <br /> --> To temporarily debug this error further, set const PIWIK_PRINT_ERROR_BACKTRACE=true; in index.php', - 'file' => '[Core_LogTest] LogTest.php(161): dummy error message + 'file' => '[Core_LogTest] LogTest.php(165): dummy error message dummy backtrace', - 'database' => '[Core_LogTest] LogTest.php(161): dummy error message + 'database' => '[Core_LogTest] LogTest.php(165): dummy error message dummy backtrace' ); @@ -55,6 +55,8 @@ dummy backtrace' public static function setUpBeforeClass() { + parent::setUpBeforeClass(); + Error::setErrorHandler(); ExceptionHandler::setUp(); } @@ -63,6 +65,8 @@ dummy backtrace' { restore_error_handler(); restore_exception_handler(); + + parent::tearDownAfterClass(); } public function setUp() diff --git a/tests/PHPUnit/Integration/Core/OptionTest.php b/tests/PHPUnit/Integration/Core/OptionTest.php index c926e1c808140962907f7928d863dc615ba3cb71..41a12df3fd6e368069b4443795032174f05fd7b5 100644 --- a/tests/PHPUnit/Integration/Core/OptionTest.php +++ b/tests/PHPUnit/Integration/Core/OptionTest.php @@ -13,6 +13,7 @@ use Piwik\Option; * Class Core_OptionTest * * @group Core + * @group Core_OptionTest */ class Core_OptionTest extends DatabaseTestCase { diff --git a/tests/PHPUnit/Integration/Core/SegmentTest.php b/tests/PHPUnit/Integration/Core/SegmentTest.php index ce7a0c363f82350689cf4b0aa3c9100b73d8583f..3fddab94b27907341d57224752fb1afd991added 100644 --- a/tests/PHPUnit/Integration/Core/SegmentTest.php +++ b/tests/PHPUnit/Integration/Core/SegmentTest.php @@ -8,8 +8,10 @@ use Piwik\Access; use Piwik\Common; use Piwik\Segment; -use Piwik\Tests\Fixture; +/** + * @group SegmentTest + */ class SegmentTest extends DatabaseTestCase { public function setUp() @@ -20,14 +22,11 @@ class SegmentTest extends DatabaseTestCase $pseudoMockAccess = new FakeAccess; FakeAccess::$superUser = true; Access::setSingletonInstance($pseudoMockAccess); - - Fixture::loadAllPlugins(); } public function tearDown() { parent::tearDown(); - Fixture::unloadAllPlugins(); } protected function _filterWhitsSpaces($valueToFilter) diff --git a/tests/PHPUnit/Integration/Core/Tracker/DbTest.php b/tests/PHPUnit/Integration/Core/Tracker/DbTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ef738d55151027245afbae565131437cbd7cb0e0 --- /dev/null +++ b/tests/PHPUnit/Integration/Core/Tracker/DbTest.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 + */ +use Piwik\Common; +use Piwik\Db; + +/** + * Tracker DB test + * + * @group Core + * @group TrackerDbTest + */ +class Core_Tracker_DbTest extends DatabaseTestCase +{ + public function test_rowCount_whenUpdating_returnsAllMatchedRowsNotOnlyUpdatedRows() + { + $db = \Piwik\Tracker::getDatabase(); + // insert one record + $db->query("INSERT INTO `" . Common::prefixTable('option') . "` VALUES ('rowid', '1', false)"); + + // We will now UPDATE this table and check rowCount() value + $sqlUpdate = "UPDATE `" . Common::prefixTable('option') . "` SET option_value = 2"; + + // when no record was updated, return 0 + $result = $db->query($sqlUpdate . " WHERE option_name = 'NOT FOUND'"); + $this->assertSame(0, $db->rowCount($result)); + + // when one record was found and updated, returns 1 + $result = $db->query($sqlUpdate . " WHERE option_name = 'rowid'"); + $this->assertSame(1, $db->rowCount($result)); + + // when one record was found but NOT actually updated (as values have not changed), we make sure to return 1 + // testing for MYSQLI_CLIENT_FOUND_ROWS and MYSQL_ATTR_FOUND_ROWS + $result = $db->query($sqlUpdate . " WHERE option_name = 'rowid'"); + $this->assertSame(1, $db->rowCount($result)); + } + +} \ No newline at end of file diff --git a/tests/PHPUnit/Integration/Core/TrackerTest.php b/tests/PHPUnit/Integration/Core/TrackerTest.php index ad2810f7898e36b62b7d2b80db249b11fcb4d161..a5eb8cbcb8bbc00b3af6b218784277eae91ba2e3 100644 --- a/tests/PHPUnit/Integration/Core/TrackerTest.php +++ b/tests/PHPUnit/Integration/Core/TrackerTest.php @@ -16,13 +16,12 @@ class Core_TrackerTest extends DatabaseTestCase public function setUp() { parent::setUp(); - \Piwik\Piwik::setUserHasSuperUserAccess(true); Fixture::createWebsite('2014-02-04'); } - protected function configureFixture() + protected static function configureFixture($fixture) { - $this->fixture->createSuperUser = true; + $fixture->createSuperUser = true; } /** diff --git a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php index a7f41f6b3be43a5db2f51dbcdb0a294eb5fb4773..c7f3d9ca0039f7b931c950d8b6fe8889bcaac2a3 100755 --- a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php +++ b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php @@ -108,6 +108,7 @@ class ManyVisitorsOneWebsiteTest extends IntegrationTestCase // Randomly fails on 5.3 if(!self::isPhpVersion53()) { + $apiToTest[] = array('Live.getLastVisitsDetails', array( 'idSite' => $idSite, 'date' => $dateString, @@ -140,7 +141,11 @@ class ManyVisitorsOneWebsiteTest extends IntegrationTestCase 'date' => $dateString, 'periods' => 'month', 'testSuffix' => '_Live.getLastVisitsDetails_sortByIdVisitAsc', - 'otherRequestParameters' => array('filter_sort_order' => 'asc', 'filter_sort_column' => 'idVisit', 'filter_limit' => 7) + 'otherRequestParameters' => array('filter_sort_order' => 'asc', + 'filter_sort_column' => 'idVisit', + 'filter_limit' => 7, + 'hideColumns' => 'latitude,longitude' // Mysqli has troubles with lat/long rounding + ) )); } diff --git a/tests/PHPUnit/Integration/PivotByQueryParamTest.php b/tests/PHPUnit/Integration/PivotByQueryParamTest.php index b668a68f7ad4728e2f119be6222a54ee8c80d6d5..7e579f4b5efbb8c905838eefb6a05619cdc01d40 100644 --- a/tests/PHPUnit/Integration/PivotByQueryParamTest.php +++ b/tests/PHPUnit/Integration/PivotByQueryParamTest.php @@ -43,7 +43,7 @@ class PivotByQueryParamTest extends IntegrationTestCase )); } - public function test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded() + public function test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded() { $this->assertApiResponseEqualsExpected("Referrers.getKeywords", array( 'idSite' => self::$fixture->idSite, @@ -186,4 +186,4 @@ class PivotByQueryParamTest extends IntegrationTestCase } } -PivotByQueryParamTest::$fixture = new ManyVisitsWithMockLocationProvider(); \ No newline at end of file +PivotByQueryParamTest::$fixture = new ManyVisitsWithMockLocationProvider(); diff --git a/tests/PHPUnit/Integration/PrivacyManagerTest.php b/tests/PHPUnit/Integration/PrivacyManagerTest.php index b78a93d32ab7d8f57564fea42485efd04bc6854e..c229f08d8c3a2d60a95c7e3e7c468bc0a31ee036 100644 --- a/tests/PHPUnit/Integration/PrivacyManagerTest.php +++ b/tests/PHPUnit/Integration/PrivacyManagerTest.php @@ -71,15 +71,9 @@ class PrivacyManagerTest extends IntegrationTestCase { parent::setUpBeforeClass(); - // Temporarily disable the purge of old archives so that getNumeric('nb_visits') - // in _addReportData does not trigger the data purge of data we've just imported - \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives(); - self::_addLogData(); self::_addReportData(); - \Piwik\ArchiveProcessor\Rules::enablePurgeOutdatedArchives(); - self::$dbData = self::getDbTablesWithData(); } diff --git a/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml index ef32d680f81888b683a134b40e1ff8c5240a6aba..32e753a325b7efbec888a5be7f87ae9da07f19c0 100644 --- a/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml +++ b/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml @@ -4,7 +4,6 @@ <idSite>1</idSite> <idVisit>35</idVisit> <visitIp>194.57.91.215</visitIp> - <userId>userid.email@example.org</userId> <actionDetails> <row> @@ -33,7 +32,8 @@ - + + <userId>userid.email@example.org</userId> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -111,7 +111,6 @@ <idSite>1</idSite> <idVisit>18</idVisit> <visitIp>1.2.4.8</visitIp> - <userId /> <actionDetails> <row> @@ -181,7 +180,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -267,7 +267,6 @@ <idSite>1</idSite> <idVisit>17</idVisit> <visitIp>1.2.4.8</visitIp> - <userId /> <actionDetails> <row> @@ -306,7 +305,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -392,7 +392,6 @@ <idSite>1</idSite> <idVisit>16</idVisit> <visitIp>1.2.4.7</visitIp> - <userId /> <actionDetails> <row> @@ -445,7 +444,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -531,7 +531,6 @@ <idSite>1</idSite> <idVisit>15</idVisit> <visitIp>1.2.4.7</visitIp> - <userId /> <actionDetails> <row> @@ -570,7 +569,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -656,7 +656,6 @@ <idSite>1</idSite> <idVisit>14</idVisit> <visitIp>1.2.4.6</visitIp> - <userId /> <actionDetails> <row> @@ -726,7 +725,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -812,7 +812,6 @@ <idSite>1</idSite> <idVisit>13</idVisit> <visitIp>1.2.4.6</visitIp> - <userId /> <actionDetails> <row> @@ -851,7 +850,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -937,7 +937,6 @@ <idSite>1</idSite> <idVisit>12</idVisit> <visitIp>1.2.4.5</visitIp> - <userId /> <actionDetails> <row> @@ -990,7 +989,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1076,7 +1076,6 @@ <idSite>1</idSite> <idVisit>11</idVisit> <visitIp>1.2.4.5</visitIp> - <userId /> <actionDetails> <row> @@ -1115,7 +1114,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1201,7 +1201,6 @@ <idSite>1</idSite> <idVisit>10</idVisit> <visitIp>1.2.4.4</visitIp> - <userId /> <actionDetails> <row> @@ -1271,7 +1270,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1357,7 +1357,6 @@ <idSite>1</idSite> <idVisit>9</idVisit> <visitIp>1.2.4.4</visitIp> - <userId /> <actionDetails> <row> @@ -1396,7 +1395,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1482,7 +1482,6 @@ <idSite>1</idSite> <idVisit>8</idVisit> <visitIp>1.2.4.3</visitIp> - <userId /> <actionDetails> <row> @@ -1535,7 +1534,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1621,7 +1621,6 @@ <idSite>1</idSite> <idVisit>30</idVisit> <visitIp>113.62.1.1</visitIp> - <userId /> <actionDetails> <row> @@ -1674,7 +1673,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1752,7 +1752,6 @@ <idSite>1</idSite> <idVisit>7</idVisit> <visitIp>1.2.4.3</visitIp> - <userId /> <actionDetails> <row> @@ -1791,7 +1790,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1877,7 +1877,6 @@ <idSite>1</idSite> <idVisit>29</idVisit> <visitIp>113.62.1.1</visitIp> - <userId /> <actionDetails> <row> @@ -1916,7 +1915,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2002,7 +2002,6 @@ <idSite>1</idSite> <idVisit>6</idVisit> <visitIp>1.2.4.2</visitIp> - <userId /> <actionDetails> <row> @@ -2072,7 +2071,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2158,7 +2158,6 @@ <idSite>1</idSite> <idVisit>28</idVisit> <visitIp>2001:db8:85a3::8a2e:370:7334</visitIp> - <userId /> <actionDetails> <row> @@ -2228,7 +2227,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2306,7 +2306,6 @@ <idSite>1</idSite> <idVisit>5</idVisit> <visitIp>1.2.4.2</visitIp> - <userId /> <actionDetails> <row> @@ -2345,7 +2344,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2431,7 +2431,6 @@ <idSite>1</idSite> <idVisit>27</idVisit> <visitIp>2001:db8:85a3::8a2e:370:7334</visitIp> - <userId /> <actionDetails> <row> @@ -2470,7 +2469,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2556,7 +2556,6 @@ <idSite>1</idSite> <idVisit>4</idVisit> <visitIp>1.2.4.1</visitIp> - <userId /> <actionDetails> <row> @@ -2609,7 +2608,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2695,7 +2695,6 @@ <idSite>1</idSite> <idVisit>22</idVisit> <visitIp>::ffff:137.82.130.49</visitIp> - <userId /> <actionDetails> <row> @@ -2748,7 +2747,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2834,7 +2834,6 @@ <idSite>1</idSite> <idVisit>26</idVisit> <visitIp>137.82.0.0</visitIp> - <userId /> <actionDetails> <row> @@ -2887,7 +2886,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2965,7 +2965,6 @@ <idSite>1</idSite> <idVisit>34</idVisit> <visitIp>103.29.196.229</visitIp> - <userId /> <actionDetails> <row> @@ -3018,7 +3017,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3104,7 +3104,6 @@ <idSite>1</idSite> <idVisit>3</idVisit> <visitIp>1.2.4.1</visitIp> - <userId /> <actionDetails> <row> @@ -3143,7 +3142,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3229,7 +3229,6 @@ <idSite>1</idSite> <idVisit>21</idVisit> <visitIp>::ffff:137.82.130.49</visitIp> - <userId /> <actionDetails> <row> @@ -3268,7 +3267,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3354,7 +3354,6 @@ <idSite>1</idSite> <idVisit>25</idVisit> <visitIp>137.82.0.0</visitIp> - <userId /> <actionDetails> <row> @@ -3393,7 +3392,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3479,7 +3479,6 @@ <idSite>1</idSite> <idVisit>33</idVisit> <visitIp>103.29.196.229</visitIp> - <userId /> <actionDetails> <row> @@ -3518,7 +3517,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3604,7 +3604,6 @@ <idSite>1</idSite> <idVisit>2</idVisit> <visitIp>1.2.4.0</visitIp> - <userId /> <actionDetails> <row> @@ -3674,7 +3673,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3760,7 +3760,6 @@ <idSite>1</idSite> <idVisit>20</idVisit> <visitIp>194.57.91.215</visitIp> - <userId /> <actionDetails> <row> @@ -3830,7 +3829,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -3916,7 +3916,6 @@ <idSite>1</idSite> <idVisit>24</idVisit> <visitIp>137.82.130.0</visitIp> - <userId /> <actionDetails> <row> @@ -3986,7 +3985,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -4064,7 +4064,6 @@ <idSite>1</idSite> <idVisit>32</idVisit> <visitIp>151.100.101.92</visitIp> - <userId /> <actionDetails> <row> @@ -4134,7 +4133,8 @@ - + + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -4220,7 +4220,6 @@ <idSite>1</idSite> <idVisit>1</idVisit> <visitIp>1.2.4.0</visitIp> - <userId /> <actionDetails> <row> @@ -4259,7 +4258,8 @@ - + + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4345,7 +4345,6 @@ <idSite>1</idSite> <idVisit>19</idVisit> <visitIp>194.57.91.215</visitIp> - <userId /> <actionDetails> <row> @@ -4384,7 +4383,8 @@ - + + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4470,7 +4470,6 @@ <idSite>1</idSite> <idVisit>23</idVisit> <visitIp>137.82.130.0</visitIp> - <userId /> <actionDetails> <row> @@ -4509,7 +4508,8 @@ - + + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4595,7 +4595,6 @@ <idSite>1</idSite> <idVisit>31</idVisit> <visitIp>151.100.101.92</visitIp> - <userId /> <actionDetails> <row> @@ -4634,7 +4633,8 @@ - + + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml index 2c741a31b0f055701cd2d58e55c2cd18cb7fe88d..e058d9fe14cc0e1e21b064a3ffa0fbd4898e6295 100644 --- a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml @@ -26,7 +26,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -128,7 +128,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -298,7 +298,7 @@ <searches>0</searches> <actions>6</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -400,7 +400,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -737,7 +737,7 @@ <searches>0</searches> <actions>15</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -839,7 +839,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -937,7 +937,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1103,7 +1103,7 @@ <searches>0</searches> <actions>6</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1201,7 +1201,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1534,7 +1534,7 @@ <searches>0</searches> <actions>15</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml index 2c741a31b0f055701cd2d58e55c2cd18cb7fe88d..e058d9fe14cc0e1e21b064a3ffa0fbd4898e6295 100644 --- a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml @@ -26,7 +26,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -128,7 +128,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -298,7 +298,7 @@ <searches>0</searches> <actions>6</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -400,7 +400,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -737,7 +737,7 @@ <searches>0</searches> <actions>15</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -839,7 +839,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -937,7 +937,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1103,7 +1103,7 @@ <searches>0</searches> <actions>6</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1201,7 +1201,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -1534,7 +1534,7 @@ <searches>0</searches> <actions>15</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml index dfb3d767360bd4c1abb532b397d3b5dbbf5af927..979dcb11467e4e344251594ca9b84fa0c36ab8e7 100644 --- a/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml +++ b/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml @@ -42,7 +42,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -180,7 +180,7 @@ <searches>0</searches> <actions>2</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -289,7 +289,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -439,7 +439,7 @@ <searches>0</searches> <actions>2</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -554,7 +554,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>0</visitConverted> @@ -680,7 +680,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -813,7 +813,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -935,7 +935,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1049,7 +1049,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1218,7 +1218,7 @@ <searches>0</searches> <actions>3</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -1336,7 +1336,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1462,7 +1462,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -1750,7 +1750,7 @@ <searches>0</searches> <actions>10</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -1862,7 +1862,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -1979,7 +1979,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -2112,7 +2112,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -2237,7 +2237,7 @@ <searches>0</searches> <actions>2</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -2369,7 +2369,7 @@ <searches>0</searches> <actions>3</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -2477,7 +2477,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -2585,7 +2585,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -2683,7 +2683,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -2791,7 +2791,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -2899,7 +2899,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3007,7 +3007,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3111,7 +3111,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3215,7 +3215,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3319,7 +3319,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3423,7 +3423,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3527,7 +3527,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3631,7 +3631,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3735,7 +3735,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3839,7 +3839,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -3943,7 +3943,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4047,7 +4047,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4151,7 +4151,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4255,7 +4255,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4359,7 +4359,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4463,7 +4463,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4567,7 +4567,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -4671,7 +4671,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml index 050f358af479ef0d25af9128ac1abbc850c102c6..314b95c25ad478044f3ae822bdf81242b81d5074 100644 --- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml @@ -43,7 +43,7 @@ - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -101,8 +101,6 @@ <regionCode>P3</regionCode> <city>Stratford-upon-Avon</city> <location>Stratford-upon-Avon, Warwickshire, United Kingdom</location> - <latitude>123.456001</latitude> - <longitude>21.320999</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>0</daysSinceLastVisit> @@ -199,7 +197,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -257,8 +255,6 @@ <regionCode>P3</regionCode> <city>Stratford-upon-Avon</city> <location>Stratford-upon-Avon, Warwickshire, United Kingdom</location> - <latitude>123.456001</latitude> - <longitude>21.320999</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>10</daysSinceLastVisit> @@ -324,7 +320,7 @@ - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -382,8 +378,6 @@ <regionCode>A6</regionCode> <city>Besançon</city> <location>Besançon, Franche-Comte, France</location> - <latitude>47.249001</latitude> - <longitude>6.018000</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>0</daysSinceLastVisit> @@ -480,7 +474,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -538,8 +532,6 @@ <regionCode>A6</regionCode> <city>Besançon</city> <location>Besançon, Franche-Comte, France</location> - <latitude>47.249001</latitude> - <longitude>6.018000</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>10</daysSinceLastVisit> @@ -605,7 +597,7 @@ - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -663,8 +655,6 @@ <regionCode>BC</regionCode> <city>Vancouver</city> <location>Vancouver, British Columbia, Canada</location> - <latitude>49.250000</latitude> - <longitude>-123.133003</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>0</daysSinceLastVisit> @@ -761,7 +751,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -819,8 +809,6 @@ <regionCode>BC</regionCode> <city>Vancouver</city> <location>Vancouver, British Columbia, Canada</location> - <latitude>49.250000</latitude> - <longitude>-123.133003</longitude> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>10</daysSinceLastVisit> @@ -878,7 +866,7 @@ - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -936,8 +924,6 @@ <regionCode /> <city /> <location>Italy</location> - <latitude /> - <longitude /> <visitLocalTime>12:34:06</visitLocalTime> <visitLocalHour>12</visitLocalHour> <daysSinceLastVisit>0</daysSinceLastVisit> diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml index 6f86215248d94c969dd2179bd1659e3db5220fea..3b6d2c7b2d31b79d6c2360302a170fa88dfd4410 100644 --- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml @@ -181,7 +181,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -306,7 +306,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -445,7 +445,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -570,7 +570,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -726,7 +726,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -851,7 +851,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml index e9caa3ba66c5f44f180abeaa5c6191fa782efbfb..bf27da99957a429626d6ac7858e4431281973ffe 100644 --- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml @@ -74,7 +74,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -199,7 +199,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -355,7 +355,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -494,7 +494,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -619,7 +619,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -744,7 +744,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml index 6f86215248d94c969dd2179bd1659e3db5220fea..3b6d2c7b2d31b79d6c2360302a170fa88dfd4410 100644 --- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml @@ -181,7 +181,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -306,7 +306,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -445,7 +445,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -570,7 +570,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -726,7 +726,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -851,7 +851,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml index fc8478d9b33eaf6f3d550206957adcdf1553776e..d4f1c0f44f39f98d8479cb1d38e88c9773eaba86 100644 --- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml @@ -181,7 +181,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -306,7 +306,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -445,7 +445,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -570,7 +570,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -726,7 +726,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -851,7 +851,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -990,7 +990,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1115,7 +1115,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -1271,7 +1271,7 @@ - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml index d8a06d259e0814b52889aa9d43306b265eab682f..dca93d3104e7d56189cec2595766e0889c666373 100644 --- a/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml @@ -36,7 +36,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -224,7 +224,7 @@ <searches>1</searches> <actions>8</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml index d6cf0ea497ffcd121913da84dac7fed3c65d13ee..5fde4bea232fd8a3c48e5f22acdb9bee6856c664 100644 --- a/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml @@ -4,7 +4,6 @@ <idSite>1</idSite> <idVisit>2</idVisit> <visitIp>156.5.0.0</visitIp> - <userId /> <actionDetails> <row> @@ -24,6 +23,7 @@ + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -101,7 +101,6 @@ <idSite>1</idSite> <idVisit>1</idVisit> <visitIp>156.5.0.0</visitIp> - <userId /> <actionDetails> <row> @@ -131,6 +130,7 @@ + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml index d40e18c7d9cc56226a03594f1cda9be38cda998b..eda23c25e715259447a0806926f9629b749ad506 100644 --- a/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml @@ -30,7 +30,7 @@ <lastActionDateTime>2013-04-07 10:00:00</lastActionDateTime> <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -123,7 +123,7 @@ <lastActionDateTime>2013-04-06 11:00:00</lastActionDateTime> <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -216,7 +216,7 @@ <lastActionDateTime>2013-04-05 12:00:00</lastActionDateTime> <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml b/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml similarity index 100% rename from tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml rename to tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml diff --git a/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml index 35533fdac6f32afac9899091d394c1e037540f05..ea4cebdafe863eb811a673facc8bdec5fe0efcdf 100644 --- a/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml @@ -25,7 +25,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html index 25de6d905786d2015f960482b8a94b5a71f3e27e..8f057be5e7da938c9121f474278c50883555b371 100644 --- a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html +++ b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html @@ -143,6 +143,16 @@ <a href="#Actions_getDownloads" style="text-decoration:none; color: rgb(68,68,68);"> Downloads </a> + </li> + <li> + <a href="#Contents_getContentNames" style="text-decoration:none; color: rgb(68,68,68);"> + Content Name + </a> + </li> + <li> + <a href="#Contents_getContentPieces" style="text-decoration:none; color: rgb(68,68,68);"> + Content Piece + </a> </li> <li> <a href="#Events_getCategory" style="text-decoration:none; color: rgb(68,68,68);"> @@ -438,7 +448,7 @@ <td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;"> Users </td> <td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;"> - 1 + 0 </td> </tr> @@ -3705,6 +3715,16 @@ Downloads </h2> + There is no data for this report. +<h2 id="Contents_getContentNames" style="color: rgb(126,115,99); font-size: 11pt;"> + Content Name +</h2> + + There is no data for this report. +<h2 id="Contents_getContentPieces" style="color: rgb(126,115,99); font-size: 11pt;"> + Content Piece +</h2> + There is no data for this report. <h2 id="Events_getCategory" style="color: rgb(126,115,99); font-size: 11pt;"> Event Categories diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf index 5fecafa3697b33829a433ff4a7d32c4f65e76309..7891c7d5ca81b8bb7d192678110ae18739417f34 100644 Binary files a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf and b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf differ diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml index 1946cec5ba97237a2f2ebab8a8b38cda03699933..cc28b360a0c13cc0967aad92997eb6f8f887179d 100644 --- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml @@ -16,7 +16,7 @@ </actionDetails> <lastActionDateTime>2010-03-06 11:22:33</lastActionDateTime> <actions>1</actions> - <userId>0</userId> + <userId /> </row> <row> <idVisit>2</idVisit> @@ -45,19 +45,30 @@ </actionDetails> <lastActionDateTime>2010-03-06 11:28:33</lastActionDateTime> <actions>2</actions> - <userId>0</userId> + <userId /> </row> <row> <idVisit>3</idVisit> <visitorId>9395988394d4568d</visitorId> <actionDetails> + <row> + <type>action</type> + <url>http://example.org/no-user-id-set-but-should-appear-in-user-id-visit</url> + <pageTitle>no User Id set but it should appear in email@example.com!</pageTitle> + <pageIdAction>6</pageIdAction> + <serverTimePretty>Sat 6 Mar 13:16:33</serverTimePretty> + <pageId>4</pageId> + <timeSpent>360</timeSpent> + <timeSpentPretty>6 min 0s</timeSpentPretty> + <icon /> + </row> <row> <type>action</type> <url>http://example.org/index.htm</url> <pageTitle>incredible title!</pageTitle> <pageIdAction>2</pageIdAction> <serverTimePretty>Sat 6 Mar 13:22:33</serverTimePretty> - <pageId>4</pageId> + <pageId>5</pageId> <timeSpent>360</timeSpent> <timeSpentPretty>6 min 0s</timeSpentPretty> <icon /> @@ -68,12 +79,12 @@ <pageTitle>second page</pageTitle> <pageIdAction>2</pageIdAction> <serverTimePretty>Sat 6 Mar 13:28:33</serverTimePretty> - <pageId>5</pageId> + <pageId>6</pageId> <icon /> </row> </actionDetails> <lastActionDateTime>2010-03-06 13:28:33</lastActionDateTime> - <actions>2</actions> + <actions>3</actions> <userId>email@example.com</userId> </row> <row> @@ -86,7 +97,7 @@ <pageTitle>a new user id was set -> new visit</pageTitle> <pageIdAction>2</pageIdAction> <serverTimePretty>Sat 6 Mar 13:34:33</serverTimePretty> - <pageId>6</pageId> + <pageId>7</pageId> <icon /> </row> </actionDetails> @@ -102,9 +113,9 @@ <type>action</type> <url>http://example.org/home</url> <pageTitle>same user id was set -> this is the same unique user</pageTitle> - <pageIdAction>8</pageIdAction> + <pageIdAction>10</pageIdAction> <serverTimePretty>Sat 6 Mar 16:22:33</serverTimePretty> - <pageId>7</pageId> + <pageId>8</pageId> <timeSpent>360</timeSpent> <timeSpentPretty>6 min 0s</timeSpentPretty> <icon /> @@ -113,9 +124,9 @@ <type>action</type> <url>http://example.org/home</url> <pageTitle>second pageview - by this user id</pageTitle> - <pageIdAction>8</pageIdAction> + <pageIdAction>10</pageIdAction> <serverTimePretty>Sat 6 Mar 16:28:33</serverTimePretty> - <pageId>8</pageId> + <pageId>9</pageId> <icon /> </row> <row> @@ -157,15 +168,15 @@ <type>action</type> <url>http://example.org/home</url> <pageTitle>pageview - should not be tracked by our user id but in a new visit</pageTitle> - <pageIdAction>8</pageIdAction> + <pageIdAction>10</pageIdAction> <serverTimePretty>Sat 6 Mar 16:28:33</serverTimePretty> - <pageId>9</pageId> + <pageId>10</pageId> <icon /> </row> </actionDetails> <lastActionDateTime>2010-03-06 16:28:33</lastActionDateTime> <actions>1</actions> - <userId>0</userId> + <userId /> </row> <row> <idVisit>7</idVisit> @@ -177,7 +188,7 @@ <pageTitle>Page view by email@example.com</pageTitle> <pageIdAction>2</pageIdAction> <serverTimePretty>Sun 14 Mar 11:22:33</serverTimePretty> - <pageId>10</pageId> + <pageId>11</pageId> <icon /> </row> </actionDetails> @@ -195,7 +206,7 @@ <pageTitle>A page view by new-user-id@one-weeklater</pageTitle> <pageIdAction>2</pageIdAction> <serverTimePretty>Sun 14 Mar 11:46:33</serverTimePretty> - <pageId>11</pageId> + <pageId>12</pageId> <icon /> </row> </actionDetails> diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml index 4a8baf7fc310decd23eb2dca0dcc88b35425d296..ca85e8dedebac3ef7da39382a1bcfe610b0f4632 100644 --- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml +++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml @@ -3,12 +3,12 @@ <nb_uniq_visitors>5</nb_uniq_visitors> <nb_users>2</nb_users> <nb_visits>6</nb_visits> - <nb_actions>9</nb_actions> + <nb_actions>10</nb_actions> <nb_visits_converted>1</nb_visits_converted> <bounce_count>3</bounce_count> - <sum_visit_length>1623</sum_visit_length> - <max_actions>2</max_actions> + <sum_visit_length>1983</sum_visit_length> + <max_actions>3</max_actions> <bounce_rate>50%</bounce_rate> - <nb_actions_per_visit>1.5</nb_actions_per_visit> - <avg_time_on_site>271</avg_time_on_site> + <nb_actions_per_visit>1.7</nb_actions_per_visit> + <avg_time_on_site>331</avg_time_on_site> </result> \ No newline at end of file diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml index 94ed6f59e05e0e08fff104dc8d2249d7c3df486e..7731535795426ac8e138dca89a94bbb58d26249b 100644 --- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml +++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml @@ -3,12 +3,12 @@ <nb_uniq_visitors>6</nb_uniq_visitors> <nb_users>3</nb_users> <nb_visits>8</nb_visits> - <nb_actions>11</nb_actions> + <nb_actions>12</nb_actions> <nb_visits_converted>1</nb_visits_converted> <bounce_count>5</bounce_count> - <sum_visit_length>1623</sum_visit_length> - <max_actions>2</max_actions> + <sum_visit_length>1983</sum_visit_length> + <max_actions>3</max_actions> <bounce_rate>63%</bounce_rate> - <nb_actions_per_visit>1.4</nb_actions_per_visit> - <avg_time_on_site>203</avg_time_on_site> + <nb_actions_per_visit>1.5</nb_actions_per_visit> + <avg_time_on_site>248</avg_time_on_site> </result> \ No newline at end of file diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml index 4a8baf7fc310decd23eb2dca0dcc88b35425d296..ca85e8dedebac3ef7da39382a1bcfe610b0f4632 100644 --- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml +++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml @@ -3,12 +3,12 @@ <nb_uniq_visitors>5</nb_uniq_visitors> <nb_users>2</nb_users> <nb_visits>6</nb_visits> - <nb_actions>9</nb_actions> + <nb_actions>10</nb_actions> <nb_visits_converted>1</nb_visits_converted> <bounce_count>3</bounce_count> - <sum_visit_length>1623</sum_visit_length> - <max_actions>2</max_actions> + <sum_visit_length>1983</sum_visit_length> + <max_actions>3</max_actions> <bounce_rate>50%</bounce_rate> - <nb_actions_per_visit>1.5</nb_actions_per_visit> - <avg_time_on_site>271</avg_time_on_site> + <nb_actions_per_visit>1.7</nb_actions_per_visit> + <avg_time_on_site>331</avg_time_on_site> </result> \ No newline at end of file diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml index 107fa6ae8aef2b799212ce9bb7caed9d7197bbbb..84850ee827e073c38ac01eb3d231498aaf72d41d 100644 --- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml +++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8" ?> <result> <nb_visits>8</nb_visits> - <nb_actions>11</nb_actions> + <nb_actions>12</nb_actions> <nb_visits_converted>1</nb_visits_converted> <bounce_count>5</bounce_count> - <sum_visit_length>1623</sum_visit_length> - <max_actions>2</max_actions> + <sum_visit_length>1983</sum_visit_length> + <max_actions>3</max_actions> <bounce_rate>63%</bounce_rate> - <nb_actions_per_visit>1.4</nb_actions_per_visit> - <avg_time_on_site>203</avg_time_on_site> + <nb_actions_per_visit>1.5</nb_actions_per_visit> + <avg_time_on_site>248</avg_time_on_site> </result> \ No newline at end of file diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml index df83d3ad4d9f64f8856f2b70c34e0cfc11eb5a48..f45437858dcc1b545ca283bbc269965cbb204e6f 100644 --- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml @@ -51,7 +51,7 @@ <searches>0</searches> <actions>0</actions> - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -262,7 +262,7 @@ <searches>0</searches> <actions>3</actions> - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml index db2945b307cac91708f4c406139823c874bc8167..37e6dd50a1dc4c482d4920c39969ff0520705055 100644 --- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml @@ -127,7 +127,7 @@ <searches>0</searches> <actions>3</actions> - <userId>0</userId> + <userId /> <visitorType>returningCustomer</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>0</visitConverted> @@ -457,7 +457,7 @@ <searches>0</searches> <actions>6</actions> - <userId>0</userId> + <userId /> <visitorType>returning</visitorType> <visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon> <visitConverted>1</visitConverted> @@ -677,7 +677,7 @@ <searches>0</searches> <actions>4</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html index b92f3087c5eea105509c70343ad844eda82392f1..401a3a99bbf6c5f07a6e1332732cb89f59792b54 100644 --- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html +++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html @@ -188,6 +188,16 @@ <a href="#Actions_getDownloads" style="text-decoration:none; color: rgb(68,68,68);"> Downloads </a> + </li> + <li> + <a href="#Contents_getContentNames" style="text-decoration:none; color: rgb(68,68,68);"> + Content Name + </a> + </li> + <li> + <a href="#Contents_getContentPieces" style="text-decoration:none; color: rgb(68,68,68);"> + Content Piece + </a> </li> <li> <a href="#Events_getCategory" style="text-decoration:none; color: rgb(68,68,68);"> @@ -498,7 +508,7 @@ <td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;"> Users </td> <td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;"> - 1 + 0 </td> </tr> @@ -4806,6 +4816,16 @@ Downloads </h2> + There is no data for this report. +<h2 id="Contents_getContentNames" style="color: rgb(126,115,99); font-size: 11pt;"> + Content Name +</h2> + + There is no data for this report. +<h2 id="Contents_getContentPieces" style="color: rgb(126,115,99); font-size: 11pt;"> + Content Piece +</h2> + There is no data for this report. <h2 id="Events_getCategory" style="color: rgb(126,115,99); font-size: 11pt;"> Event Categories diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf index 0bb4a30c1767307a2a0b817f1882bc9395092609..e8e37848fd2f107a6b9e3ad041a811634a3f2642 100644 Binary files a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf and b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf differ diff --git a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml index b5052f4f745d49c04e6f615880101afb597f371b..38191d26d91a0103f76d4d0270cf945b010b07a3 100644 --- a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml +++ b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml @@ -25,7 +25,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -140,7 +140,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> @@ -311,7 +311,7 @@ <searches>0</searches> <actions>3</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml index 0725631cd965055be6e6e210ea7a03b3ff5ef687..ff0e385988a3541085cc5e831467eb5aea8424ba 100644 --- a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml +++ b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml @@ -68,7 +68,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>0</visitConverted> @@ -184,7 +184,7 @@ <searches>0</searches> <actions>1</actions> - <userId>0</userId> + <userId /> <visitorType>new</visitorType> <visitorTypeIcon /> <visitConverted>1</visitConverted> diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php index ae8a674ed2e636d47ed754a1dbfa94639612cbaf..d5d78128a4552d64a401f45533c00e87f4bc11fd 100755 --- a/tests/PHPUnit/IntegrationTestCase.php +++ b/tests/PHPUnit/IntegrationTestCase.php @@ -414,10 +414,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase { $testConfig = new ApiTestConfig($params); - // make sure that the reports we process here are not directly deleted in ArchiveProcessor/PluginsArchiver - // (because we process reports in the past, they would sometimes be invalid, and would have been deleted) - \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives(); - $testName = 'test_' . static::getOutputPrefix(); $this->missingExpectedFiles = array(); $this->comparisonFailures = array(); @@ -440,9 +436,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase $this->_testApiUrl($testName . $testConfig->testSuffix, $apiId, $requestUrl, $testConfig->compareAgainst, $testConfig->xmlFieldsToRemove, $params); } - // Restore normal purge behavior - \Piwik\ArchiveProcessor\Rules::enablePurgeOutdatedArchives(); - // change the language back to en if ($this->lastLanguage != 'en') { $this->changeLanguage('en'); @@ -525,7 +518,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase */ protected static function restoreDbTables($tables) { - // truncate existing tables DbHelper::truncateAllTables(); // insert data diff --git a/tests/PHPUnit/TestingEnvironment.php b/tests/PHPUnit/TestingEnvironment.php index ba15855f61cbaf794d00ed13bd63285633007a72..0c5ddbc69e2fcb317e00478482438963f8d9520c 100644 --- a/tests/PHPUnit/TestingEnvironment.php +++ b/tests/PHPUnit/TestingEnvironment.php @@ -88,7 +88,7 @@ class Piwik_TestingEnvironment if (isset($_SERVER['QUERY_STRING']) && !$this->dontUseTestConfig ) { - \Piwik\Log::verbose("Test Environment Variables for (%s):\n%s", $_SERVER['QUERY_STRING'], print_r($this->behaviorOverrideProperties, true)); + @\Piwik\Log::verbose("Test Environment Variables for (%s):\n%s", $_SERVER['QUERY_STRING'], print_r($this->behaviorOverrideProperties, true)); } } catch (Exception $ex) { // ignore diff --git a/tests/PHPUnit/UI b/tests/PHPUnit/UI index d67cb0957de65891ab2e5b6fe12e04cdd35e1bfd..d98325f48a495a6d5d71cb1ffed231f6834f30bb 160000 --- a/tests/PHPUnit/UI +++ b/tests/PHPUnit/UI @@ -1 +1 @@ -Subproject commit d67cb0957de65891ab2e5b6fe12e04cdd35e1bfd +Subproject commit d98325f48a495a6d5d71cb1ffed231f6834f30bb diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index dc16386563641a42d73c8d9a8101a5317298c05c..2fd701a0e87e9728e0bed7abf37f76788ad6dba3 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -34,8 +34,9 @@ require_once file_exists(PIWIK_INCLUDE_PATH . '/vendor/autoload.php') require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php'; require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php'; require_once PIWIK_INCLUDE_PATH . '/core/FrontController.php'; -require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/DatabaseTestCase.php'; +require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixture.php'; require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/IntegrationTestCase.php'; +require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/DatabaseTestCase.php'; require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/ConsoleCommandTestCase.php'; require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/FakeAccess.php'; require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockPiwikOption.php'; @@ -49,8 +50,6 @@ if (getenv('PIWIK_USE_XHPROF') == 1) { } // require test fixtures -require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixture.php'; - $fixturesToLoad = array( '/tests/PHPUnit/Fixtures/*.php', '/tests/PHPUnit/UI/Fixtures/*.php', diff --git a/tests/PHPUnit/proxy/includes.php b/tests/PHPUnit/proxy/includes.php index 858ae485d20ae74fdca4e65dca9ad2581804fa85..b813fd21ad0575de52b4dec5631bd4c3e99d4c1b 100644 --- a/tests/PHPUnit/proxy/includes.php +++ b/tests/PHPUnit/proxy/includes.php @@ -28,7 +28,3 @@ require_once $vendorDirectory . '/piwik/device-detector/DeviceDetector.php'; \Piwik\SettingsServer::setMaxExecutionTime(0); -// Make sure Data processed in cron core:archive command is not being purged instantly (useful for: Integration/ArchiveCronTest) -if(\Piwik\SettingsServer::isArchivePhpTriggered()) { - \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives(); -} \ No newline at end of file diff --git a/tests/resources/staticFileServer.php b/tests/resources/staticFileServer.php index 5907c169e3e61f188a17f4bf0a432a51895d4b19..6ad277863e3bca23b28c966d5be743eba8d3c994 100644 --- a/tests/resources/staticFileServer.php +++ b/tests/resources/staticFileServer.php @@ -15,7 +15,7 @@ use Piwik\ProxyHttp; define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__).'/../../'); if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) { - require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php'; + require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php'; } error_reporting(E_ALL|E_NOTICE); @@ -25,11 +25,11 @@ error_reporting(E_ALL|E_NOTICE); if(!defined('PIWIK_USER_PATH')) { - define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT); + define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT); } if(!defined('PIWIK_INCLUDE_PATH')) { - define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT); + define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT); } require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';