diff --git a/.travis.yml b/.travis.yml index 2b3e2a6ce759b8f3b3d3feb1adf8b18b17977607..f3a65a7f60038d1cee4833ce6ffff35578f9170d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -117,7 +117,7 @@ before_script: - mysql -e "SELECT VERSION();" # configure mysql - - mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION'" # Travis default + - mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES'" # Travis default # Uncomment to enable sql_mode STRICT_TRANS_TABLES (new default in Mysql 5.6) #- mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'" diff --git a/composer.json b/composer.json index ada86611922be463cd51d8e892c831a1fcedfad2..f68897fe60f38e95725f47ac7588b1eea2041431 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,18 @@ "wiki": "http://dev.piwik.org/", "source": "https://github.com/piwik/piwik" }, + "autoload": { + "psr-4": { + "Piwik\\Plugins\\": "plugins/", + "Piwik\\": "core/" + }, + "psr-0": { + "Zend_": "libs/", + "HTML_": "libs/", + "PEAR_": "libs/", + "Archive_": "libs/" + } + }, "require": { "php": ">=5.3.2", "twig/twig": "1.*", diff --git a/composer.lock b/composer.lock index 257eeb64a423b7554e369397230483e576e6876e..7f47a22d7fe291c9087a7bec56836f07b68b374e 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": "ba9388aa9f61e6a370364aadbf0316ad", + "hash": "aca9f46b026a9e629b4a11e3e57428c0", "packages": [ { "name": "leafo/lessphp", @@ -96,16 +96,16 @@ }, { "name": "piwik/device-detector", - "version": "2.3", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/piwik/device-detector.git", - "reference": "c881d3592b55253c4e8968245e7b162f453ca4b9" + "reference": "9cd0338be126aaf947f20fd08a6381c0c658dca5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/piwik/device-detector/zipball/c881d3592b55253c4e8968245e7b162f453ca4b9", - "reference": "c881d3592b55253c4e8968245e7b162f453ca4b9", + "url": "https://api.github.com/repos/piwik/device-detector/zipball/9cd0338be126aaf947f20fd08a6381c0c658dca5", + "reference": "9cd0338be126aaf947f20fd08a6381c0c658dca5", "shasum": "" }, "require": { @@ -123,7 +123,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-3.0+" + "LGPL-3.0+" ], "authors": [ { @@ -139,21 +139,21 @@ "parser", "useragent" ], - "time": "2014-07-29 10:06:51" + "time": "2014-08-11 20:21:18" }, { "name": "symfony/console", - "version": "v2.5.0", + "version": "v2.5.3", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c" + "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c", - "reference": "ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c", + "url": "https://api.github.com/repos/symfony/Console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63", + "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63", "shasum": "" }, "require": { @@ -183,20 +183,18 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-05-22 08:54:24" + "time": "2014-08-05 09:00:40" }, { "name": "tedivm/jshrink", @@ -241,16 +239,16 @@ }, { "name": "twig/twig", - "version": "v1.15.1", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/fabpot/Twig.git", - "reference": "1fb5784662f438d7d96a541e305e28b812e2eeed" + "reference": "8ce37115802e257a984a82d38254884085060024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fabpot/Twig/zipball/1fb5784662f438d7d96a541e305e28b812e2eeed", - "reference": "1fb5784662f438d7d96a541e305e28b812e2eeed", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/8ce37115802e257a984a82d38254884085060024", + "reference": "8ce37115802e257a984a82d38254884085060024", "shasum": "" }, "require": { @@ -259,7 +257,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.16-dev" } }, "autoload": { @@ -294,22 +292,173 @@ "keywords": [ "templating" ], - "time": "2014-02-13 10:19:29" + "time": "2014-07-05 12:19:05" } ], "packages-dev": [ + { + "name": "facebook/xhprof", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phacility/xhprof.git", + "reference": "0a7a5f69f5d762c34056164eb08dd13d41156015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phacility/xhprof/zipball/0a7a5f69f5d762c34056164eb08dd13d41156015", + "reference": "0a7a5f69f5d762c34056164eb08dd13d41156015", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "type": "library", + "autoload": { + "files": [ + "xhprof_lib/utils/xhprof_lib.php", + "xhprof_lib/utils/xhprof_runs.php" + ] + }, + "license": [ + "Apache-2.0" + ], + "description": "XHProf: A Hierarchical Profiler for PHP", + "homepage": "http://pecl.php.net/package/xhprof", + "keywords": [ + "performance", + "profiling" + ], + "support": { + "source": "https://github.com/phacility/xhprof/tree/master" + }, + "time": "2014-04-22 20:02:16" + }, + { + "name": "ocramius/instantiator", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/Instantiator.git", + "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/Instantiator/zipball/a7abbb5fc9df6e7126af741dd6c140d1a7369435", + "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435", + "shasum": "" + }, + "require": { + "ocramius/lazy-map": "1.0.*", + "php": "~5.3" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "2.0.*@ALPHA" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Instantiator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/Ocramius/Instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2014-08-14 15:10:55" + }, + { + "name": "ocramius/lazy-map", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/LazyMap.git", + "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/LazyMap/zipball/7fe3d347f5e618bcea7d39345ff83f3651d8b752", + "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "athletic/athletic": "~0.1.6", + "phpmd/phpmd": "1.5.*", + "phpunit/phpunit": ">=3.7", + "satooshi/php-coveralls": "~0.6", + "squizlabs/php_codesniffer": "1.4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "LazyMap\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/", + "role": "Developer" + } + ], + "description": "A library that provides lazy instantiation logic for a map of objects", + "homepage": "https://github.com/Ocramius/LazyMap", + "keywords": [ + "lazy", + "lazy instantiation", + "lazy loading", + "map", + "service location" + ], + "time": "2013-11-09 22:30:54" + }, { "name": "phpunit/php-code-coverage", - "version": "2.0.9", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f" + "reference": "6d196af48e8c100a3ae881940123e693da5a9217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ed8ac99ce38c3fd134128c898f7ca74665abef7f", - "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6d196af48e8c100a3ae881940123e693da5a9217", + "reference": "6d196af48e8c100a3ae881940123e693da5a9217", "shasum": "" }, "require": { @@ -361,7 +510,7 @@ "testing", "xunit" ], - "time": "2014-06-29 08:14:40" + "time": "2014-08-06 06:39:42" }, { "name": "phpunit/php-file-iterator", @@ -548,16 +697,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.1.3", + "version": "4.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91" + "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/939cb801b3b2aa253aedd0b279f40bb8f35cec91", - "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a33fa68ece9f8c68589bfc2da8d2794e27b820bc", + "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc", "shasum": "" }, "require": { @@ -571,7 +720,7 @@ "phpunit/php-file-iterator": "~1.3.1", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "~1.0.2", - "phpunit/phpunit-mock-objects": "~2.1", + "phpunit/phpunit-mock-objects": "~2.2", "sebastian/comparator": "~1.0", "sebastian/diff": "~1.1", "sebastian/environment": "~1.0", @@ -588,7 +737,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1.x-dev" + "dev-master": "4.2.x-dev" } }, "autoload": { @@ -618,28 +767,29 @@ "testing", "xunit" ], - "time": "2014-06-11 14:15:47" + "time": "2014-08-18 05:12:30" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.1.5", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a" + "reference": "42e589e08bc86e3e9bdf20d385e948347788505b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7878b9c41edb3afab92b85edf5f0981014a2713a", - "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/42e589e08bc86e3e9bdf20d385e948347788505b", + "reference": "42e589e08bc86e3e9bdf20d385e948347788505b", "shasum": "" }, "require": { + "ocramius/instantiator": "~1.0", "php": ">=5.3.3", "phpunit/php-text-template": "~1.2" }, "require-dev": { - "phpunit/phpunit": "~4.1" + "phpunit/phpunit": "4.2.*@dev" }, "suggest": { "ext-soap": "*" @@ -647,7 +797,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "2.2.x-dev" } }, "autoload": { @@ -675,7 +825,7 @@ "mock", "xunit" ], - "time": "2014-06-12 07:22:15" + "time": "2014-08-02 13:50:58" }, { "name": "sebastian/comparator", @@ -946,17 +1096,17 @@ }, { "name": "symfony/yaml", - "version": "v2.5.0", + "version": "v2.5.3", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "b4b09c68ec2f2727574544ef0173684281a5033c" + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/b4b09c68ec2f2727574544ef0173684281a5033c", - "reference": "b4b09c68ec2f2727574544ef0173684281a5033c", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", "shasum": "" }, "require": { @@ -978,29 +1128,28 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-05-16 14:25:18" + "time": "2014-08-05 09:00:40" } ], "aliases": [ ], "minimum-stability": "stable", - "stability-flags": [ - - ], + "stability-flags": { + "facebook/xhprof": 20 + }, + "prefer-stable": false, "platform": { "php": ">=5.3.2" }, diff --git a/console b/console index bb901dfed7867e05026a59953d0e4464b9c9fffc..41b4ec5835a6ed628f42086bca7d90afc3278b7d 100755 --- a/console +++ b/console @@ -17,7 +17,6 @@ require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php'; require_once file_exists(PIWIK_INCLUDE_PATH . '/vendor/autoload.php') ? PIWIK_INCLUDE_PATH . '/vendor/autoload.php' // Piwik is the main project : PIWIK_INCLUDE_PATH . '/../../autoload.php'; // Piwik is installed as a dependency -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php'; Piwik\Translate::loadEnglishTranslation(); diff --git a/core/Archive.php b/core/Archive.php index 63f0a61dc87799ade938d2d62b2ebfeaab41b5a4..65b5b915324871f3c8e212c261e798f38645bdff 100644 --- a/core/Archive.php +++ b/core/Archive.php @@ -11,7 +11,7 @@ namespace Piwik; use Piwik\Archive\Parameters; use Piwik\ArchiveProcessor\Rules; use Piwik\DataAccess\ArchiveSelector; -use Piwik\Period\Factory; +use Piwik\Period\Factory as PeriodFactory; /** * The **Archive** class is used to query cached analytics statistics @@ -200,10 +200,10 @@ class Archive $timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false; if (Period::isMultiplePeriod($strDate, $period)) { - $oPeriod = Factory::build($period, $strDate, $timezone); + $oPeriod = PeriodFactory::build($period, $strDate, $timezone); $allPeriods = $oPeriod->getSubperiods(); } else { - $oPeriod = Factory::makePeriodFromQueryParams($timezone, $period, $strDate); + $oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate); $allPeriods = array($oPeriod); } $segment = new Segment($segment, $websiteIds); diff --git a/core/Common.php b/core/Common.php index 3b49dc9bd9963e32af49077f52ee4238668cd7f2..ecfefabcec0e4d2cf3f9438192e2d6a0c5ca5e04 100644 --- a/core/Common.php +++ b/core/Common.php @@ -857,7 +857,7 @@ class Common } } - if (is_null($browserLang)) { + if (empty($browserLang)) { // a fallback might be to infer the language in HTTP_USER_AGENT (i.e., localized build) $browserLang = ""; } else { diff --git a/core/CronArchive.php b/core/CronArchive.php index b6a3dffdb0209425e880ecb097d0de42a6ce56bf..97a7f430108ed265cf74d8e99d5228d90b4e2f65 100644 --- a/core/CronArchive.php +++ b/core/CronArchive.php @@ -12,7 +12,7 @@ use Exception; use Piwik\ArchiveProcessor\Rules; use Piwik\CronArchive\FixedSiteIds; use Piwik\CronArchive\SharedSiteIds; -use Piwik\Period\Factory; +use Piwik\Period\Factory as PeriodFactory; use Piwik\Plugins\CoreAdminHome\API as APICoreAdminHome; use Piwik\Plugins\SitesManager\API as APISitesManager; @@ -1288,7 +1288,7 @@ class CronArchive private function getPeriodsToProcess() { $this->restrictToPeriods = array_intersect($this->restrictToPeriods, $this->getDefaultPeriodsToProcess()); - $this->restrictToPeriods = array_intersect($this->restrictToPeriods, Factory::getPeriodsEnabledForAPI()); + $this->restrictToPeriods = array_intersect($this->restrictToPeriods, PeriodFactory::getPeriodsEnabledForAPI()); return $this->restrictToPeriods; } diff --git a/core/DataTable/Renderer.php b/core/DataTable/Renderer.php index ddcde91cf39611cce27b675a4735ba327a2d19fa..aea71e4da863ae42bd4f49912db0911cb9e8d8c9 100644 --- a/core/DataTable/Renderer.php +++ b/core/DataTable/Renderer.php @@ -10,9 +10,9 @@ namespace Piwik\DataTable; use Exception; use Piwik\DataTable; -use Piwik\Loader; use Piwik\Metrics; use Piwik\Piwik; +use Piwik\Factory; /** * A DataTable Renderer can produce an output given a DataTable object. @@ -22,7 +22,7 @@ use Piwik\Piwik; * $render->setTable($dataTable); * echo $render; */ -abstract class Renderer +abstract class Renderer extends Factory { protected $table; @@ -156,25 +156,17 @@ abstract class Renderer return self::$availableRenderers; } - /** - * Returns the DataTable associated to the output format $name - * - * @param string $name - * @throws Exception If the renderer is unknown - * @return \Piwik\DataTable\Renderer - */ - public static function factory($name) + protected static function getClassNameFromClassId($id) { - $className = ucfirst(strtolower($name)); + $className = ucfirst(strtolower($id)); $className = 'Piwik\DataTable\Renderer\\' . $className; - try { - Loader::loadClass($className); - return new $className; - } catch (Exception $e) { - $availableRenderers = implode(', ', self::getRenderers()); - @header('Content-Type: text/plain; charset=utf-8'); - throw new Exception(Piwik::translate('General_ExceptionInvalidRendererFormat', array($className, $availableRenderers))); - } + return $className; + } + + protected static function getInvalidClassIdExceptionMessage($id) + { + $availableRenderers = implode(', ', self::getRenderers()); + return Piwik::translate('General_ExceptionInvalidRendererFormat', array(self::getClassNameFromClassId($id), $availableRenderers)); } /** diff --git a/core/Db/Adapter.php b/core/Db/Adapter.php index 6bafa3a653f779946dc9d0c0b764bc3b08f4db9f..342a320d723c38d58b29d7940521d9e92e7af7a2 100644 --- a/core/Db/Adapter.php +++ b/core/Db/Adapter.php @@ -8,7 +8,6 @@ */ namespace Piwik\Db; -use Piwik\Loader; use Zend_Db_Table; /** @@ -39,8 +38,6 @@ class Adapter } $className = self::getAdapterClassName($adapterName); - Loader::loadClass($className); - $adapter = new $className($dbInfos); if ($connect) { diff --git a/core/Factory.php b/core/Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..480f9313c17c80ae035044eb1eb9f7f45ba449f1 --- /dev/null +++ b/core/Factory.php @@ -0,0 +1,60 @@ +<?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; + +use Exception; + +/** + * Base class for all factory types. + * + * Factory types are base classes that contain a **factory** method. This method is used to instantiate + * concrete instances by a specified string ID. Fatal errors do not occur if a class does not exist. + * Instead an exception is thrown. + * + * Derived classes should override the **getClassNameFromClassId** and **getInvalidClassIdExceptionMessage** + * static methods. + */ +abstract class Factory +{ + /** + * Creates a new instance of a class using a string ID. + * + * @param string $classId The ID of the class. + * @return Factory + * @throws Exception if $classId is invalid. + */ + public static function factory($classId) + { + $className = static::getClassNameFromClassId($classId); + + if (!class_exists($className)) { + @header('Content-Type: text/plain; charset=utf-8'); + throw new Exception(static::getInvalidClassIdExceptionMessage($classId)); + } + + return new $className; + } + + /** + * Should return a class name based on the class's associated string ID. + */ + protected static function getClassNameFromClassId($id) + { + return $id; + } + + /** + * Should return a message to use in an Exception when an invalid class ID is supplied to + * {@link factory()}. + */ + protected static function getInvalidClassIdExceptionMessage($id) + { + return "Invalid class ID '$id' for " . get_called_class() . "::factory()."; + } +} \ No newline at end of file diff --git a/core/FrontController.php b/core/FrontController.php index dc10f72e94e186d8905de9b5b6273ad21b5c0d57..db6f1772dc13385d38c51b33f75eea1aac21138c 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -126,7 +126,7 @@ class FrontController extends Singleton } if ($action === false) { - $this->triggerControllerActionNotFoundError($controller, $controllerAction); + $this->triggerControllerActionNotFoundError($module, $controllerAction); } } diff --git a/core/Loader.php b/core/Loader.php deleted file mode 100644 index 6e33987f1932488c020d1bfdc4a059d2ace63546..0000000000000000000000000000000000000000 --- a/core/Loader.php +++ /dev/null @@ -1,126 +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; - -use Exception; - -/** - * Piwik auto loader - * - */ -class Loader -{ - // our class search path; current directory is intentionally excluded - protected static $dirs = array('/core/', '/plugins/'); - - /** - * Get class file name - * - * @param string $class Class name - * @return string Class file name - * @throws Exception if class name is invalid - */ - protected static function getClassFileName($class) - { - if (!preg_match('/^[A-Za-z0-9_\\\\]+$/D', $class)) { - throw new Exception("Invalid class name \"$class\"."); - } - - // prefixed class - $class = str_replace('_', '/', $class); - - // namespace \Piwik\Common - $class = str_replace('\\', '/', $class); - - if ($class == 'Piwik') { - return $class; - } - - $class = self::removeFirstMatchingPrefix($class, array('/Piwik/', 'Piwik/')); - $class = self::removeFirstMatchingPrefix($class, array('/Plugins/', 'Plugins/')); - - return $class; - } - - protected static function removeFirstMatchingPrefix($class, $vendorPrefixesToRemove) - { - foreach ($vendorPrefixesToRemove as $prefix) { - if (strpos($class, $prefix) === 0) { - return substr($class, strlen($prefix)); - } - } - - return $class; - } - - private static function isPluginClass($class) - { - return 0 === strpos($class, 'Piwik\Plugins') || 0 === strpos($class, '\Piwik\Plugins'); - } - - private static function usesPiwikNamespace($class) - { - return 0 === strpos($class, 'Piwik\\') || 0 === strpos($class, '\Piwik\\'); - } - - /** - * Load class by name - * - * @param string $class Class name - * @throws Exception if class not found - */ - public static function loadClass($class) - { - $classPath = self::getClassFileName($class); - - if (static::isPluginClass($class)) { - static::tryToLoadClass($class, '/plugins/', $classPath); - } elseif (static::usesPiwikNamespace($class)) { - static::tryToLoadClass($class, '/core/', $classPath); - } else { - // non-Piwik classes (e.g., Zend Framework) are in libs/ - static::tryToLoadClass($class, '/libs/', $classPath); - } - } - - private static function tryToLoadClass($class, $dir, $classPath) - { - $path = PIWIK_INCLUDE_PATH . $dir . $classPath . '.php'; - - if (file_exists($path)) { - require_once $path; // prefixed by PIWIK_INCLUDE_PATH - - return class_exists($class, false) || interface_exists($class, false); - } - - return false; - } - - /** - * Autoloader - * - * @param string $class Class name - */ - public static function autoload($class) - { - try { - self::loadClass($class); - } catch (Exception $e) { - } - } -} - -// use the SPL autoload stack -spl_autoload_register(array('Piwik\Loader', 'autoload')); - -// preserve any existing __autoload -if (function_exists('__autoload')) { - spl_autoload_register('__autoload'); -} diff --git a/core/Period.php b/core/Period.php index 868ead7f13838a9a4c41b446eb3db01a26af0761..6898020ce704ca4447a79d9dc01105e41f67aba8 100644 --- a/core/Period.php +++ b/core/Period.php @@ -8,7 +8,7 @@ */ namespace Piwik; -use Piwik\Period\Factory; +use Piwik\Period\Factory as PeriodFactory; use Piwik\Period\Range; /** @@ -64,7 +64,7 @@ abstract class Period */ public static function factory($period, $date) { - return Factory::build($period, $date); + return PeriodFactory::build($period, $date); } /** diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index c7d0164d5a2ee842ec1c58659a535b03e34fe5fa..114d79a1d60e8462b396faaf53a72296ba44cc0c 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -22,6 +22,7 @@ use Piwik\Theme; use Piwik\Tracker; use Piwik\Translate; use Piwik\Updater; +use Piwik\SettingsServer; use Piwik\Plugin\Dimension\ActionDimension; use Piwik\Plugin\Dimension\ConversionDimension; use Piwik\Plugin\Dimension\VisitDimension; @@ -957,7 +958,7 @@ class Manager extends Singleton private function loadTranslation($plugin, $langCode) { // we are in Tracker mode if Loader is not (yet) loaded - if (!class_exists('Piwik\\Loader', false)) { + if (SettingsServer::isTrackerApiRequest()) { return false; } diff --git a/core/ReportRenderer.php b/core/ReportRenderer.php index 44fc77f6326bc8afb971e8755a8902e579465705..e6ccc530ed818679f956d8eda91ddccbed0caa17 100644 --- a/core/ReportRenderer.php +++ b/core/ReportRenderer.php @@ -14,12 +14,13 @@ use Piwik\DataTable\Row; use Piwik\DataTable\Simple; use Piwik\DataTable; use Piwik\Plugins\ImageGraph\API; +use Piwik\Factory; /** * A Report Renderer produces user friendly renderings of any given Piwik report. * All new Renderers must be copied in ReportRenderer and added to the $availableReportRenderers. */ -abstract class ReportRenderer +abstract class ReportRenderer extends Factory { const DEFAULT_REPORT_FONT = 'dejavusans'; const REPORT_TEXT_COLOR = "68,68,68"; @@ -39,32 +40,22 @@ abstract class ReportRenderer self::CSV_FORMAT, ); - /** - * Return the ReportRenderer associated to the renderer type $rendererType - * - * @throws exception If the renderer is unknown - * @param string $rendererType - * @return \Piwik\ReportRenderer - */ - public static function factory($rendererType) + protected static function getClassNameFromClassId($rendererType) { - $name = ucfirst(strtolower($rendererType)); - $className = 'Piwik\ReportRenderer\\' . $name; - - try { - Loader::loadClass($className); - return new $className; - } catch (Exception $e) { + return 'Piwik\ReportRenderer\\' . self::normalizeRendererType($rendererType); + } - @header('Content-Type: text/html; charset=utf-8'); + protected static function getInvalidClassIdExceptionMessage($rendererType) + { + return Piwik::translate( + 'General_ExceptionInvalidReportRendererFormat', + array(self::normalizeRendererType($rendererType), implode(', ', self::$availableReportRenderers)) + ); + } - throw new Exception( - Piwik::translate( - 'General_ExceptionInvalidReportRendererFormat', - array($name, implode(', ', self::$availableReportRenderers)) - ) - ); - } + protected static function normalizeRendererType($rendererType) + { + return ucfirst(strtolower($rendererType)); } /** diff --git a/core/Tracker.php b/core/Tracker.php index a6ab0225ac02ec71df9852233945af39f789df38..d0502e3ca3e4b4a3bf03bd6d2509059433008924 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -407,7 +407,6 @@ class Tracker && self::$initTrackerMode === false ) { self::$initTrackerMode = true; - require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; require_once PIWIK_INCLUDE_PATH . '/core/Option.php'; Access::getInstance(); diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index fbc73381aa2ca28286237ad80615bacf037a8d9b..12e3865d2c183eb72f395cfc536a24dacd01ab21 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -138,15 +138,29 @@ class GoalManager || ($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; } $url = $decodedActionUrl; - // Matching on Page Title - if ($attribute == 'title') { - $url = $action->getActionName(); + + switch ($attribute) { + case 'title': + // Matching on Page Title + $url = $action->getActionName(); + break; + case 'event_action': + $url = $action->getEventAction(); + break; + case 'event_name': + $url = $action->getEventName(); + break; + case 'event_category': + $url = $action->getEventCategory(); + break; } + $pattern_type = $goal['pattern_type']; $match = $this->isUrlMatchingGoal($goal, $pattern_type, $url); diff --git a/index.php b/index.php index cc6780eb9bb013314d8dbc03cda2e9274c603cb8..baee05066c4f636d2f193b673eba3d781e0f53df 100644 --- a/index.php +++ b/index.php @@ -38,10 +38,9 @@ session_cache_limiter('nocache'); require_once file_exists(PIWIK_INCLUDE_PATH . '/vendor/autoload.php') ? PIWIK_INCLUDE_PATH . '/vendor/autoload.php' // Piwik is the main project : PIWIK_INCLUDE_PATH . '/../../autoload.php'; // Piwik is installed as a dependency -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; if(!defined('PIWIK_PRINT_ERROR_BACKTRACE')) { define('PIWIK_PRINT_ERROR_BACKTRACE', false); } -require_once PIWIK_INCLUDE_PATH . '/core/dispatch.php'; +require_once PIWIK_INCLUDE_PATH . '/core/dispatch.php'; \ No newline at end of file diff --git a/js/tracker.php b/js/tracker.php index 0a842b8c0fdcfefdb752442ebdce68a8bc978ff0..a8d768e6bcd2447160c70fd172109d4de73b0de6 100644 --- a/js/tracker.php +++ b/js/tracker.php @@ -27,7 +27,7 @@ define('PIWIK_DOCUMENT_ROOT', '..'); define('PIWIK_USER_PATH', '..'); require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php'; -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; +require_once PIWIK_INCLUDE_PATH . '/vendor/autoload.php'; $file = '../piwik.js'; diff --git a/lang/en.json b/lang/en.json index abd9d25e9268ca273a2c35bb95ba7e2206553abc..b7dad2a8b2eea0dc05f82830d7c6247dc94c9c8c 100644 --- a/lang/en.json +++ b/lang/en.json @@ -930,6 +930,7 @@ "CaseSensitive": "Case sensitive match", "ChooseGoal": "Choose Goal", "ClickOutlink": "Click on a Link to an external website", + "SendEvent": "Send an event", "ColumnAverageOrderRevenueDocumentation": "Average Order Value (AOV) is the total revenue from all Ecommerce Orders divided by the number of orders.", "ColumnAveragePriceDocumentation": "The average revenue for this %s.", "ColumnAverageQuantityDocumentation": "The average quantity of this %s sold in Ecommerce orders.", diff --git a/misc/log-analytics/import_logs.py b/misc/log-analytics/import_logs.py index 2328a56ca41ddd4930367d8b08236f6002801f41..a52b581c8c7a3f3cc6a741b6ce47578db0e326e1 100755 --- a/misc/log-analytics/import_logs.py +++ b/misc/log-analytics/import_logs.py @@ -360,11 +360,11 @@ class Configuration(object): ) option_parser.add_option( '--exclude-path', dest='excluded_paths', action='append', default=[], - help="Paths to exclude. Can be specified multiple times" + help="Any URL path matching this exclude-path will not be imported in Piwik. Can be specified multiple times" ) option_parser.add_option( '--exclude-path-from', dest='exclude_path_from', - help="Each line from this file is a path to exclude" + help="Each line from this file is a path to exclude (see: --exclude-path)" ) option_parser.add_option( '--include-path', dest='included_paths', action='append', default=[], diff --git a/misc/others/cli-script-bootstrap.php b/misc/others/cli-script-bootstrap.php index a5c200f30cdce2c59df60a560ac9ad5e28a83a9b..991b531a943789fb1f5a53b4da390c7712a28758 100644 --- a/misc/others/cli-script-bootstrap.php +++ b/misc/others/cli-script-bootstrap.php @@ -28,7 +28,6 @@ set_time_limit(0); require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php'; require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php'; -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; $GLOBALS['PIWIK_TRACKER_DEBUG'] = false; define('PIWIK_ENABLE_DISPATCH', false); diff --git a/piwik.php b/piwik.php index d992ef56a51f892415c197603fbc4dfa482b4f55..f5278f5d47ac1d97081368dbceeff1103d168924 100644 --- a/piwik.php +++ b/piwik.php @@ -83,7 +83,6 @@ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/VisitorNotFoundInDb.php'; require_once PIWIK_INCLUDE_PATH . '/core/CacheFile.php'; require_once PIWIK_INCLUDE_PATH . '/core/Filesystem.php'; require_once PIWIK_INCLUDE_PATH . '/core/Cookie.php'; -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; session_cache_limiter('nocache'); @date_default_timezone_set('UTC'); @@ -96,8 +95,6 @@ if (!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING) { $GLOBALS['PIWIK_TRACKER_DEBUG'] = (bool) \Piwik\Config::getInstance()->Tracker['debug']; if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) { - require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; - require_once PIWIK_INCLUDE_PATH . '/core/Error.php'; \Piwik\Error::setErrorHandler(); require_once PIWIK_INCLUDE_PATH . '/core/ExceptionHandler.php'; diff --git a/plugins/CoreConsole/templates/travis.yml.twig b/plugins/CoreConsole/templates/travis.yml.twig index 553f5ae7b44021f48eef711f150debee48d3d5d9..935eb2cd2f1b3311a5eea8540eca408f443623c8 100644 --- a/plugins/CoreConsole/templates/travis.yml.twig +++ b/plugins/CoreConsole/templates/travis.yml.twig @@ -107,7 +107,7 @@ before_script: - mysql -e "SELECT VERSION();" # configure mysql - - mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION'" # Travis default + - mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES'" # Travis default # Uncomment to enable sql_mode STRICT_TRANS_TABLES (new default in Mysql 5.6) #- mysql -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'" @@ -171,4 +171,4 @@ after_success: {{ extraSections|trim|raw }} -{%- endif -%} \ No newline at end of file +{%- endif -%} diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts index 825e948c4e3a7b5f8800639d3617998aea88e1e1..c467c059942e0c4331e79d547c4e3acb4bbd0fa1 160000 --- a/plugins/CustomAlerts +++ b/plugins/CustomAlerts @@ -1 +1 @@ -Subproject commit 825e948c4e3a7b5f8800639d3617998aea88e1e1 +Subproject commit c467c059942e0c4331e79d547c4e3acb4bbd0fa1 diff --git a/plugins/Events/Actions/ActionEvent.php b/plugins/Events/Actions/ActionEvent.php index 3791de794d418e336af58acbbfb9042329beddd2..9921c8d39bd6a2628f8744abf2787558139764c0 100644 --- a/plugins/Events/Actions/ActionEvent.php +++ b/plugins/Events/Actions/ActionEvent.php @@ -38,6 +38,21 @@ class ActionEvent extends Action return (strlen($eventCategory) > 0 && strlen($eventAction) > 0); } + public function getEventAction() + { + return $this->request->getParam('e_a'); + } + + public function getEventCategory() + { + return $this->request->getParam('e_c'); + } + + public function getEventName() + { + return $this->request->getParam('e_n'); + } + public function getCustomFloatValue() { return $this->eventValue; diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php index 088eb213a1310f52c4defae7c8a6f10addbe7af8..b3a413ad5ff54ddd14e90d13d6abb033c07916f5 100644 --- a/plugins/Events/Events.php +++ b/plugins/Events/Events.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\Events; use Piwik\Common; use Piwik\Piwik; +use Piwik\Plugin\ViewDataTable; class Events extends \Piwik\Plugin { @@ -20,7 +21,8 @@ class Events extends \Piwik\Plugin { return array( 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations', - 'Metrics.getDefaultMetricDocumentationTranslations' => 'addMetricDocumentationTranslations' + 'Metrics.getDefaultMetricDocumentationTranslations' => 'addMetricDocumentationTranslations', + 'ViewDataTable.configure' => 'configureViewDataTable', ); } @@ -142,6 +144,94 @@ class Events extends \Piwik\Plugin throw new \Exception("Translation not found for report $apiMethod"); } + public function configureViewDataTable(ViewDataTable $view) + { + if ($view->requestConfig->getApiModuleToRequest() != 'Events') { + return; + } + + // eg. 'Events.getCategory' + $apiMethod = $view->requestConfig->getApiMethodToRequest(); + + $secondaryDimension = $this->getSecondaryDimensionFromRequest(); + $view->config->subtable_controller_action = API::getInstance()->getActionToLoadSubtables($apiMethod, $secondaryDimension); + $view->config->columns_to_display = array('label', 'nb_events', 'sum_event_value'); + $view->config->show_flatten_table = true; + $view->config->show_table_all_columns = false; + $view->requestConfig->filter_sort_column = 'nb_events'; + + $labelTranslation = $this->getColumnTranslation($apiMethod); + $view->config->addTranslation('label', $labelTranslation); + $view->config->addTranslations($this->getMetricTranslations()); + $this->addRelatedReports($view, $secondaryDimension); + $this->addTooltipEventValue($view); + } + + private function addRelatedReports($view, $secondaryDimension) + { + if(empty($secondaryDimension)) { + // eg. Row Evolution + return; + } + + $view->config->show_related_reports = true; + + $apiMethod = $view->requestConfig->getApiMethodToRequest(); + $secondaryDimensions = API::getInstance()->getSecondaryDimensions($apiMethod); + + if(empty($secondaryDimensions)) { + return; + } + + $secondaryDimensionTranslation = $this->getDimensionLabel($secondaryDimension); + $view->config->related_reports_title = + Piwik::translate('Events_SecondaryDimension', $secondaryDimensionTranslation) + . "<br/>" + . Piwik::translate('Events_SwitchToSecondaryDimension', ''); + + foreach($secondaryDimensions as $dimension) { + if($dimension == $secondaryDimension) { + // don't show as related report the currently selected dimension + continue; + } + + $dimensionTranslation = $this->getDimensionLabel($dimension); + $view->config->addRelatedReport( + $view->requestConfig->apiMethodToRequestDataTable, + $dimensionTranslation, + array('secondaryDimension' => $dimension) + ); + } + + } + + private function addTooltipEventValue($view) + { + // Creates the tooltip message for Event Value column + $tooltipCallback = function ($hits, $min, $max, $avg) { + if (!$hits) { + return false; + } + $msgEventMinMax = Piwik::translate("Events_EventValueTooltip", array($hits, "<br />", $min, $max)); + $msgEventAvg = Piwik::translate("Events_AvgEventValue", $avg); + return $msgEventMinMax . "<br/>" . $msgEventAvg; + }; + + // Add tooltip metadata column to the DataTable + $view->config->filters[] = array('ColumnCallbackAddMetadata', + array( + array( + 'nb_events', + 'min_event_value', + 'max_event_value', + 'avg_event_value' + ), + 'sum_event_value_tooltip', + $tooltipCallback + ) + ); + } + /** * @return mixed */ diff --git a/plugins/Events/Reports/Base.php b/plugins/Events/Reports/Base.php index 20441109ca204066f47d28740564b2bbfbc9c83d..c452120dc67e27178befc08afc174abeebadc001 100644 --- a/plugins/Events/Reports/Base.php +++ b/plugins/Events/Reports/Base.php @@ -25,93 +25,4 @@ abstract class Base extends \Piwik\Plugin\Report ); } - public function configureView(ViewDataTable $view) - { - if ($view->requestConfig->getApiModuleToRequest() != 'Events') { - return; - } - - // eg. 'Events.getCategory' - $apiMethod = $view->requestConfig->getApiMethodToRequest(); - - $events = new Events(); - $secondaryDimension = $events->getSecondaryDimensionFromRequest(); - $view->config->subtable_controller_action = API::getInstance()->getActionToLoadSubtables($apiMethod, $secondaryDimension); - $view->config->columns_to_display = array('label', 'nb_events', 'sum_event_value'); - $view->config->show_flatten_table = true; - $view->config->show_table_all_columns = false; - $view->requestConfig->filter_sort_column = 'nb_events'; - - $labelTranslation = $events->getColumnTranslation($apiMethod); - $view->config->addTranslation('label', $labelTranslation); - $view->config->addTranslations($events->getMetricTranslations()); - $this->addRelatedReports($view, $secondaryDimension); - $this->addTooltipEventValue($view); - } - - private function addRelatedReports($view, $secondaryDimension) - { - if(empty($secondaryDimension)) { - // eg. Row Evolution - return; - } - - $events = new Events(); - $view->config->show_related_reports = true; - - $apiMethod = $view->requestConfig->getApiMethodToRequest(); - $secondaryDimensions = API::getInstance()->getSecondaryDimensions($apiMethod); - - if(empty($secondaryDimensions)) { - return; - } - - $secondaryDimensionTranslation = $events->getDimensionLabel($secondaryDimension); - $view->config->related_reports_title = - Piwik::translate('Events_SecondaryDimension', $secondaryDimensionTranslation) - . "<br/>" - . Piwik::translate('Events_SwitchToSecondaryDimension', ''); - - foreach($secondaryDimensions as $dimension) { - if($dimension == $secondaryDimension) { - // don't show as related report the currently selected dimension - continue; - } - - $dimensionTranslation = $events->getDimensionLabel($dimension); - $view->config->addRelatedReport( - $view->requestConfig->apiMethodToRequestDataTable, - $dimensionTranslation, - array('secondaryDimension' => $dimension) - ); - } - - } - - private function addTooltipEventValue($view) - { - // Creates the tooltip message for Event Value column - $tooltipCallback = function ($hits, $min, $max, $avg) { - if (!$hits) { - return false; - } - $msgEventMinMax = Piwik::translate("Events_EventValueTooltip", array($hits, "<br />", $min, $max)); - $msgEventAvg = Piwik::translate("Events_AvgEventValue", $avg); - return $msgEventMinMax . "<br/>" . $msgEventAvg; - }; - - // Add tooltip metadata column to the DataTable - $view->config->filters[] = array('ColumnCallbackAddMetadata', - array( - array( - 'nb_events', - 'min_event_value', - 'max_event_value', - 'avg_event_value' - ), - 'sum_event_value_tooltip', - $tooltipCallback - ) - ); - } } diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index 43fff0bfdb9007f86cd5b95fea07e278127469a4..176cafad24d3400c8de0832a16d20e177da6e477 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -79,7 +79,7 @@ class API extends \Piwik\Plugin\API * * @param int $idSite * @param string $name - * @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually' + * @param string $matchAttribute 'url', 'title', 'file', 'external_website', 'manually', 'event_action', 'event_category' or 'event_name' * @param string $pattern eg. purchase-confirmation.htm * @param string $patternType 'regex', 'contains', 'exact' * @param bool $caseSensitive @@ -91,7 +91,7 @@ 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); + $this->checkPatternIsValid($patternType, $pattern, $matchAttribute); $name = $this->checkName($name); $pattern = $this->checkPattern($pattern); @@ -141,7 +141,7 @@ class API extends \Piwik\Plugin\API Piwik::checkUserHasAdminAccess($idSite); $name = $this->checkName($name); $pattern = $this->checkPattern($pattern); - $this->checkPatternIsValid($patternType, $pattern); + $this->checkPatternIsValid($patternType, $pattern, $matchAttribute); Db::get()->update(Common::prefixTable('goal'), array( 'name' => $name, @@ -157,10 +157,11 @@ class API extends \Piwik\Plugin\API Cache::regenerateCacheWebsiteAttributes($idSite); } - private function checkPatternIsValid($patternType, $pattern) + private function checkPatternIsValid($patternType, $pattern, $matchAttribute) { if ($patternType == 'exact' && substr($pattern, 0, 4) != 'http' + && substr($matchAttribute, 0, 6) != 'event_' ) { throw new Exception(Piwik::translate('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html"))); } diff --git a/plugins/Goals/javascripts/goalsForm.js b/plugins/Goals/javascripts/goalsForm.js index 643a9ce3af4130d551d77dd041a9a81a37e58713..400f3813c273bf7053fef1239d96ec3542349262 100644 --- a/plugins/Goals/javascripts/goalsForm.js +++ b/plugins/Goals/javascripts/goalsForm.js @@ -34,6 +34,25 @@ function showCancel() { }); } +function onMatchAttributeChange(matchAttribute) +{ + if ('event' === matchAttribute) { + $('.entityAddContainer .whereEvent').show(); + $('.entityAddContainer .whereUrl').hide(); + } else { + $('.entityAddContainer .whereEvent').hide(); + $('.entityAddContainer .whereUrl').show(); + } + + $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]); + $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]); +} + +function updateMatchAttribute () { + var matchTypeId = $(this).val(); + onMatchAttributeChange(matchTypeId); +} + // init the goal form with existing goal value, if any function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId) { $('#goal_name').val(goalName); @@ -46,6 +65,14 @@ function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, patte } else { $('select[name=trigger_type] option[value=visitors]').prop('selected', true); } + + if (0 === matchAttribute.indexOf('event')) { + $('select[name=event_type] option[value=' + matchAttribute + ']').prop('selected', true); + matchAttribute = 'event'; + } + + onMatchAttributeChange(matchAttribute); + $('input[name=match_attribute][value=' + matchAttribute + ']').prop('checked', true); $('input[name=allow_multiple][value=' + allowMultiple + ']').prop('checked', true); $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]); @@ -66,6 +93,7 @@ function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, patte } function bindGoalForm() { + $('select[name=trigger_type]').click(function () { var triggerTypeId = $(this).val(); if (triggerTypeId == "manually") { @@ -79,10 +107,9 @@ function bindGoalForm() { } }); - $('input[name=match_attribute]').click(function () { - var matchTypeId = $(this).val(); - $('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]); - $('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]); + $(document).bind('Goals.edit', function () { + $('input[name=match_attribute]').off('change', updateMatchAttribute); + $('input[name=match_attribute]').change(updateMatchAttribute); }); $('#goal_submit').click(function () { @@ -126,6 +153,11 @@ function ajaxAddGoal() { parameters.caseSensitive = 0; } else { parameters.matchAttribute = $('input[name=match_attribute]:checked').val(); + + if (parameters.matchAttribute === 'event') { + parameters.matchAttribute = $('select[name=event_type]').val(); + } + parameters.patternType = $('[name=pattern_type]').val(); parameters.pattern = encodeURIComponent($('input[name=pattern]').val()); parameters.caseSensitive = $('#case_sensitive').prop('checked') == true ? 1 : 0; diff --git a/plugins/Goals/templates/_addEditGoal.twig b/plugins/Goals/templates/_addEditGoal.twig index c8feb87bc50a2e5c2e66e25b03ac542d32cd94be..f9449d8ce98abaa4728baffa52b3913189f5c617 100644 --- a/plugins/Goals/templates/_addEditGoal.twig +++ b/plugins/Goals/templates/_addEditGoal.twig @@ -55,7 +55,8 @@ "url": "{{ 'Goals_URL'|translate }}", "title": "{{ 'Goals_PageTitle'|translate }}", "file": "{{ 'Goals_Filename'|translate }}", - "external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}" + "external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}", + "event": "{{ 'Events_Event'|translate }}" }; var mappingMatchTypeExamples = { "url": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'checkout/confirmation'") }} \ @@ -67,7 +68,10 @@ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\.zip'") }}", "external_website": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'amazon.com'") }} \ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://mypartner.com/landing.html'") }} \ - <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}" + <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}", + "event": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'video'") }} \ + <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'click'") }} \ + <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)_banner'") }}" }; bindGoalForm(); diff --git a/plugins/Goals/templates/_formAddGoal.twig b/plugins/Goals/templates/_formAddGoal.twig index 002df4954b649676f34747cc3df13963cd553b20..7bff75f8cb45e6e7e928872c94984c387a327f21 100644 --- a/plugins/Goals/templates/_formAddGoal.twig +++ b/plugins/Goals/templates/_formAddGoal.twig @@ -25,6 +25,9 @@ <input type="radio" id="match_attribute_title" value="title" name="match_attribute"/> <label for="match_attribute_title">{{ 'Goals_VisitPageTitle'|translate }}</label> <br/> + <input type="radio" id="match_attribute_event" value="event" name="match_attribute"/> + <label for="match_attribute_event">{{ 'Goals_SendEvent'|translate }}</label> + <br/> <input type="radio" id="match_attribute_file" value="file" name="match_attribute"/> <label for="match_attribute_file">{{ 'Goals_Download'|translate }}</label> <br/> @@ -35,7 +38,11 @@ </tbody> <tbody id="match_attribute_section"> <tr> - <td class="first">{{ 'Goals_WhereThe'|translate }} <span id="match_attribute_name"></span></td> + <td class="first">{{ 'Goals_WhereThe'|translate }} <span class="whereUrl" id="match_attribute_name"></span><select name="event_type" class="whereEvent inp"> + <option value="event_category">{{ 'Events_EventCategory'|translate }}</option> + <option value="event_action">{{ 'Events_EventAction'|translate }}</option> + <option value="event_name">{{ 'Events_EventName'|translate }}</option> + </select></td> <td> <select name="pattern_type" class="inp"> <option value="contains">{{ 'Goals_Contains'|translate("") }}</option> diff --git a/plugins/Goals/templates/_listGoalEdit.twig b/plugins/Goals/templates/_listGoalEdit.twig index 11c1172f905bc81c0be36a77ff9cc1e7b37d2b2d..5118cfb72ac29033c096b1690b0c4e137c0332d8 100644 --- a/plugins/Goals/templates/_listGoalEdit.twig +++ b/plugins/Goals/templates/_listGoalEdit.twig @@ -50,7 +50,10 @@ "file": "{{ 'Goals_Download'|translate }}", "url": "{{ 'Goals_VisitUrl'|translate }}", "title": "{{ 'Goals_VisitPageTitle'|translate }}", - "external_website": "{{ 'Goals_ClickOutlink'|translate }}" + "external_website": "{{ 'Goals_ClickOutlink'|translate }}", + "event_action": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventAction'|translate }})", + "event_category": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventCategory'|translate }})", + "event_name": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventName'|translate }})" }; $(document).ready(function () { diff --git a/plugins/Goals/tests/APITest.php b/plugins/Goals/tests/APITest.php new file mode 100644 index 0000000000000000000000000000000000000000..010449e41ff5c29bfb0bf92ef2afd8817842e74e --- /dev/null +++ b/plugins/Goals/tests/APITest.php @@ -0,0 +1,228 @@ +<?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\tests; +use Piwik\Access; +use Piwik\Plugins\Goals\API; +use Piwik\Tests\Fixture; + +/** + * @group Goals + * @group Plugins + * @group APITest + * @group Database + */ +class APITest extends \DatabaseTestCase +{ + /** + * @var API + */ + private $api; + + private $idSite = 1; + + public function setUp() + { + parent::setUp(); + $this->api = API::getInstance(); + + Fixture::createWebsite('2014-01-01 00:00:00'); + Fixture::createWebsite('2014-01-01 00:00:00'); + } + + public function test_addGoal_shouldReturnGoalId_IfCreationIsSuccessful() + { + $idGoal = $this->createAnyGoal(); + + $this->assertSame(1, $idGoal); + } + + public function test_addGoal_shouldSucceed_IfOnlyMinimumFieldsGiven() + { + $idGoal = $this->api->addGoal($this->idSite, 'MyName', 'url', 'http://www.test.de', 'exact'); + + $this->assertGoal($idGoal, 'MyName', 'url', 'http://www.test.de', 'exact', 0, 0, 0); + } + + public function test_addGoal_ShouldSucceed_IfAllFieldsGiven() + { + $idGoal = $this->api->addGoal($this->idSite, 'MyName', 'url', 'http://www.test.de', 'exact', true, 50, true); + + $this->assertGoal($idGoal, 'MyName', 'url', 'http://www.test.de', 'exact', 1, 50, 1); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString + */ + public function test_addGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent() + { + $this->api->addGoal($this->idSite, 'MyName', 'url', 'www.test.de', 'exact'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString + */ + public function test_addGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent2() + { + $this->api->addGoal($this->idSite, 'MyName', 'external_website', 'www.test.de', 'exact'); + } + + public function test_addGoal_shouldNotThrowException_IfPatternTypeIsExactAndMatchAttributeIsEvent() + { + $this->api->addGoal($this->idSite, 'MyName1', 'event_action', 'test', 'exact'); + $this->api->addGoal($this->idSite, 'MyName2', 'event_name', 'test', 'exact'); + $idGoal = $this->api->addGoal($this->idSite, 'MyName3', 'event_category', 'test', 'exact'); + + $this->assertSame('3', $idGoal); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage checkUserHasAdminAccess Fake exception + */ + public function test_addGoal_shouldThrowException_IfNotEnoughPermission() + { + $this->setNonAdminUser(); + $this->createAnyGoal(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage checkUserHasAdminAccess Fake exception + */ + public function test_updateGoal_shouldThrowException_IfNotEnoughPermission() + { + $idGoal = $this->createAnyGoal(); + $this->assertSame(1, $idGoal); // make sure goal is created and does not already fail here + $this->setNonAdminUser(); + $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'url', 'www.test.de', 'exact'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString + */ + public function test_updateGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent() + { + $idGoal = $this->createAnyGoal(); + $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'url', 'www.test.de', 'exact'); + } + + public function test_updateGoal_shouldNotThrowException_IfPatternTypeIsExactAndMatchAttributeIsEvent() + { + $idGoal = $this->createAnyGoal(); + $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_action', 'www.test.de', 'exact'); + $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_category', 'www.test.de', 'exact'); + $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_name', 'www.test.de', 'exact'); + + $this->assertSame(1, $idGoal); + } + + public function test_updateGoal_shouldUpdateAllGivenFields() + { + $idGoal = $this->createAnyGoal(); + $this->api->updateGoal($this->idSite, $idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', true, 999, true); + + $this->assertGoal($idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', 1, 999, 1); + } + + public function test_updateGoal_shouldUpdateMinimalFields_ShouldLeaveOtherFieldsUntouched() + { + $idGoal = $this->createAnyGoal(); + $this->api->updateGoal($this->idSite, $idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains'); + + $this->assertGoal($idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', 0, 0, 0); + } + + public function test_deleteGoal_shouldNotDeleteAGoal_IfGoalIdDoesNotExist() + { + $this->assertHasNoGoals(); + + $this->createAnyGoal(); + $this->assertHasGoals(); + + $this->api->deleteGoal($this->idSite, 999); + $this->assertHasGoals(); + } + + public function test_deleteGoal_shouldNotDeleteAGoal_IfSiteDoesNotMatchGoalId() + { + $this->assertHasNoGoals(); + + $idGoal = $this->createAnyGoal(); + $this->assertHasGoals(); + + $this->api->deleteGoal($idSite = 2, $idGoal); + $this->assertHasGoals(); + } + + public function test_deleteGoal_shouldDeleteAGoal_IfGoalAndSiteMatches() + { + $this->assertHasNoGoals(); + + $idGoal = $this->createAnyGoal(); + $this->assertHasGoals(); + + $this->api->deleteGoal($this->idSite, $idGoal); + $this->assertHasNoGoals(); + } + + private function assertHasGoals() + { + $goals = $this->getGoals(); + $this->assertNotEmpty($goals); + } + + private function assertHasNoGoals() + { + $goals = $this->getGoals(); + $this->assertEmpty($goals); + } + + private function assertGoal($idGoal, $name, $url, $pattern, $patternType, $caseSenstive = 0, $revenue = 0, $allowMultiple = 0) + { + $expected = array($idGoal => array( + 'idsite' => $this->idSite, + 'idgoal' => $idGoal, + 'name' => $name, + 'match_attribute' => $url, + 'pattern' => $pattern, + 'pattern_type' => $patternType, + 'case_sensitive' => $caseSenstive, + 'allow_multiple' => $allowMultiple, + 'revenue' => $revenue, + 'deleted' => 0 + )); + + $goals = $this->getGoals(); + + $this->assertEquals($expected, $goals); + } + + private function getGoals() + { + return $this->api->getGoals($this->idSite); + } + + private function createAnyGoal() + { + return $this->api->addGoal($this->idSite, 'MyName1', 'event_action', 'test', 'exact'); + } + + protected function setNonAdminUser() + { + $pseudoMockAccess = new \FakeAccess; + \FakeAccess::setSuperUserAccess(false); + \FakeAccess::$idSitesView = array(99); + \FakeAccess::$identity = 'aUser'; + Access::setSingletonInstance($pseudoMockAccess); + } + +} diff --git a/plugins/ImageGraph/StaticGraph.php b/plugins/ImageGraph/StaticGraph.php index 0ece9a173d0d2388959b5322ad45e356b572a1cd..8adef6e41177b9fcbd0c1428fc33ed6ed34a001c 100644 --- a/plugins/ImageGraph/StaticGraph.php +++ b/plugins/ImageGraph/StaticGraph.php @@ -12,9 +12,9 @@ namespace Piwik\Plugins\ImageGraph; use Exception; use pData; use pImage; -use Piwik\Loader; use Piwik\Piwik; use Piwik\SettingsPiwik; +use Piwik\Factory; require_once PIWIK_INCLUDE_PATH . "/libs/pChart2.1.3/class/pDraw.class.php"; require_once PIWIK_INCLUDE_PATH . "/libs/pChart2.1.3/class/pImage.class.php"; @@ -24,7 +24,7 @@ require_once PIWIK_INCLUDE_PATH . "/libs/pChart2.1.3/class/pData.class.php"; * The StaticGraph abstract class is used as a base class for different types of static graphs. * */ -abstract class StaticGraph +abstract class StaticGraph extends Factory { const GRAPH_TYPE_BASIC_LINE = "evolution"; const GRAPH_TYPE_VERTICAL_BAR = "verticalBar"; @@ -73,29 +73,19 @@ abstract class StaticGraph abstract public function renderGraph(); - /** - * Return the StaticGraph according to the static graph type $graphType - * - * @throws Exception If the static graph type is unknown - * @param string $graphType - * @return \Piwik\Plugins\ImageGraph\StaticGraph - */ - public static function factory($graphType) + protected static function getClassNameFromClassId($graphType) { - if (isset(self::$availableStaticGraphTypes[$graphType])) { + $className = self::$availableStaticGraphTypes[$graphType]; + $className = __NAMESPACE__ . "\\StaticGraph\\" . $className; + return new $className; + } - $className = self::$availableStaticGraphTypes[$graphType]; - $className = __NAMESPACE__ . "\\StaticGraph\\" . $className; - Loader::loadClass($className); - return new $className; - } else { - throw new Exception( - Piwik::translate( - 'General_ExceptionInvalidStaticGraphType', - array($graphType, implode(', ', self::getAvailableStaticGraphTypes())) - ) - ); - } + protected static function getInvalidClassIdExceptionMessage($graphType) + { + return Piwik::translate( + 'General_ExceptionInvalidStaticGraphType', + array($graphType, implode(', ', self::getAvailableStaticGraphTypes())) + ); } public static function getAvailableStaticGraphTypes() diff --git a/plugins/MobileMessaging/SMSProvider.php b/plugins/MobileMessaging/SMSProvider.php index 9bbf0758ed000fac3b4224dbfad10f9a4ecc78a5..0019a1ad582084e0a86cd52b2ff669bbfc251b52 100644 --- a/plugins/MobileMessaging/SMSProvider.php +++ b/plugins/MobileMessaging/SMSProvider.php @@ -9,14 +9,14 @@ namespace Piwik\Plugins\MobileMessaging; use Exception; -use Piwik\Loader; use Piwik\Piwik; +use Piwik\Factory; /** * The SMSProvider abstract class is used as a base class for SMS provider implementations. * */ -abstract class SMSProvider +abstract class SMSProvider extends Factory { const MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS = 160; const MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS = 153; @@ -38,6 +38,18 @@ abstract class SMSProvider ', ); + protected static function getClassNameFromClassId($id) + { + return __NAMESPACE__ . '\\SMSProvider\\' . $id; + } + + protected static function getInvalidClassIdExceptionMessage($id) + { + return Piwik::translate('MobileMessaging_Exception_UnknownProvider', + array($id, implode(', ', array_keys(self::$availableSMSProviders))) + ); + } + /** * Return the SMSProvider associated to the provider name $providerName * @@ -49,10 +61,7 @@ abstract class SMSProvider { $className = __NAMESPACE__ . '\\SMSProvider\\' . $providerName; - try { - Loader::loadClass($className); - return new $className; - } catch (Exception $e) { + if (!class_exists($className)) { throw new Exception( Piwik::translate( 'MobileMessaging_Exception_UnknownProvider', @@ -60,6 +69,8 @@ abstract class SMSProvider ) ); } + + return new $className; } /** diff --git a/plugins/Morpheus/stylesheets/general/_forms.less b/plugins/Morpheus/stylesheets/general/_forms.less index bac73f074ada2851428e77e1ec631d859888fc81..ea75691a781a142e44da34bac0729b68c4d493e7 100644 --- a/plugins/Morpheus/stylesheets/general/_forms.less +++ b/plugins/Morpheus/stylesheets/general/_forms.less @@ -41,6 +41,12 @@ button[type="button"], } } +.top_bar_sites_selector { + .sites_autocomplete .custom_select { + z-index: 139; + } +} + .sites_autocomplete { input { min-height: 0; diff --git a/plugins/UserSettings/Columns/Language.php b/plugins/UserSettings/Columns/Language.php index 9c0bcadfd37799efbd91706bd330fda85be0f550..f61154c7c42cddf66c751dbd7011f5d2b5e3894d 100644 --- a/plugins/UserSettings/Columns/Language.php +++ b/plugins/UserSettings/Columns/Language.php @@ -32,6 +32,12 @@ class Language extends VisitDimension */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - return substr($request->getBrowserLanguage(), 0, 20); + $language = $request->getBrowserLanguage(); + + if (empty($language)) { + return ''; + } + + return substr($language, 0, 20); } } \ No newline at end of file diff --git a/tests/PHPUnit/Core/FactoryTest.php b/tests/PHPUnit/Core/FactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..dfa6cbdb7a6c083abce3611b3b2a2e75910c7525 --- /dev/null +++ b/tests/PHPUnit/Core/FactoryTest.php @@ -0,0 +1,33 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +use Piwik\Factory; + +/** + * @group Core + * @group Core_FactoryTest + */ +class FactoryTest extends PHPUnit_Framework_TestCase +{ + public function testCreatingExistingClassSucceeds() + { + $instance = Factory::factory("Piwik\\Timer"); + + $this->assertNotNull($instance); + $this->assertInstanceOf("Piwik\\Timer", $instance); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Invalid class ID + */ + public function testCreatingInvalidClassThrows() + { + Factory::factory("This\\Class\\Does\\Not\\Exist"); + } +} \ No newline at end of file diff --git a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php index 74254a586e9d9e03730817804e03a13299167523..185fc594a889be3fdb4d1a88831a188a12238719 100644 --- a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php +++ b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php @@ -53,6 +53,18 @@ class SomeVisitsAllConversions extends Fixture $revenue = 10, $allowMultipleConversions = true ); } + + if (!self::goalExists($idSite = 1, $idGoal = 3)) { + API::getInstance()->addGoal($this->idSite, 'click event', 'event_action', 'click', 'contains'); + } + + if (!self::goalExists($idSite = 1, $idGoal = 4)) { + API::getInstance()->addGoal($this->idSite, 'category event', 'event_category', 'The_Category', 'exact', true); + } + + if (!self::goalExists($idSite = 1, $idGoal = 5)) { + API::getInstance()->addGoal($this->idSite, 'name event', 'event_name', 'the_name', 'exact'); + } } private function trackVisits() @@ -93,5 +105,17 @@ class SomeVisitsAllConversions extends Fixture $t->setTokenAuth($this->getTokenAuth()); $t->setForceNewVisit(); $t->doTrackPageView('This is tracked in a new visit.'); + + // should trigger two goals at once (event_category, event_action) + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime()); + self::checkResponse($t->doTrackEvent('The_Category', 'click_action', 'name')); + + // should not trigger a goal (the_category is case senstive goal) + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.4)->getDatetime()); + self::checkResponse($t->doTrackEvent('the_category', 'click_action', 'name')); + + // should trigger a goal for event_name + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.4)->getDatetime()); + self::checkResponse($t->doTrackEvent('other_category', 'other_action', 'the_name')); } } \ No newline at end of file diff --git a/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php b/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php index 6ed0f41a4655bdd91fa735b6766bfd0230f58f1b..cac31449c2c6ca17bc12d03874191c010f150bfd 100755 --- a/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php +++ b/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php @@ -39,11 +39,11 @@ class TrackGoalsAllowMultipleConversionsPerVisitTest extends IntegrationTestCase // test delete is working as expected $goals = API::getInstance()->getGoals($idSite); - $this->assertTrue(2 == count($goals)); + $this->assertTrue(5 == count($goals)); API::getInstance()->deleteGoal($idSite, self::$fixture->idGoal_OneConversionPerVisit); API::getInstance()->deleteGoal($idSite, self::$fixture->idGoal_MultipleConversionPerVisit); $goals = API::getInstance()->getGoals($idSite); - $this->assertTrue(empty($goals)); + $this->assertTrue(3 == count($goals)); } public function getApiForTesting() diff --git a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml index 38b1c4d24766c71323011f19995b93e77db119c0..9c6eba0c0cb5407a27e99218e9e0e8e270cc82a4 100644 --- a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml +++ b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml @@ -4,10 +4,10 @@ <label>0h</label> <nb_uniq_visitors>1</nb_uniq_visitors> <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - <max_actions>1</max_actions> - <sum_visit_length>1120</sum_visit_length> - <bounce_count>2</bounce_count> + <nb_actions>5</nb_actions> + <max_actions>3</max_actions> + <sum_visit_length>363</sum_visit_length> + <bounce_count>0</bounce_count> <goals> <row idgoal='1'> <nb_conversions>2</nb_conversions> @@ -19,8 +19,23 @@ <nb_visits_converted>1</nb_visits_converted> <revenue>666</revenue> </row> + <row idgoal='3'> + <nb_conversions>2</nb_conversions> + <nb_visits_converted>2</nb_visits_converted> + <revenue>0</revenue> + </row> + <row idgoal='4'> + <nb_conversions>1</nb_conversions> + <nb_visits_converted>1</nb_visits_converted> + <revenue>0</revenue> + </row> + <row idgoal='5'> + <nb_conversions>1</nb_conversions> + <nb_visits_converted>1</nb_visits_converted> + <revenue>0</revenue> + </row> </goals> - <nb_conversions>4</nb_conversions> + <nb_conversions>8</nb_conversions> <revenue>1332</revenue> </row> <row> diff --git a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml index 6a1390c2e5be8e2f3ec7a144922c41f97135d3f2..76b20063bff495e90d0ecedd64102f3be2647a6e 100644 --- a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml +++ b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml @@ -2,12 +2,12 @@ <result> <nb_uniq_visitors>1</nb_uniq_visitors> <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - <nb_visits_converted>1</nb_visits_converted> - <bounce_count>2</bounce_count> - <sum_visit_length>1120</sum_visit_length> - <max_actions>1</max_actions> - <bounce_rate>100%</bounce_rate> - <nb_actions_per_visit>1</nb_actions_per_visit> - <avg_time_on_site>560</avg_time_on_site> + <nb_actions>5</nb_actions> + <nb_visits_converted>2</nb_visits_converted> + <bounce_count>0</bounce_count> + <sum_visit_length>363</sum_visit_length> + <max_actions>3</max_actions> + <bounce_rate>0%</bounce_rate> + <nb_actions_per_visit>2.5</nb_actions_per_visit> + <avg_time_on_site>182</avg_time_on_site> </result> \ No newline at end of file diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index d32c604026d002fc203b56b0eb1cf2f6d7c4f9cf..6a80c72755d0b20d50fe6d2779578d80760629d5 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -33,7 +33,6 @@ 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/Loader.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/IntegrationTestCase.php'; diff --git a/tests/PHPUnit/proxy/includes.php b/tests/PHPUnit/proxy/includes.php index 6e3acc8af52ba8fedf5eb3adf650eeb40e7f2902..858ae485d20ae74fdca4e65dca9ad2581804fa85 100644 --- a/tests/PHPUnit/proxy/includes.php +++ b/tests/PHPUnit/proxy/includes.php @@ -13,7 +13,6 @@ require_once file_exists(PIWIK_INCLUDE_PATH . '/vendor/autoload.php') ? PIWIK_INCLUDE_PATH . '/vendor/autoload.php' // Piwik is the main project : PIWIK_INCLUDE_PATH . '/../../autoload.php'; // Piwik is installed as a dependency -require_once PIWIK_INCLUDE_PATH . '/core/Loader.php'; require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php'; require_once PIWIK_INCLUDE_PATH . '/core/Piwik.php'; require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php'; diff --git a/tests/resources/staticFileServer.php b/tests/resources/staticFileServer.php index 237cf0af59adbc26cffa25847d4dbac44554747f..5907c169e3e61f188a17f4bf0a432a51895d4b19 100644 --- a/tests/resources/staticFileServer.php +++ b/tests/resources/staticFileServer.php @@ -39,7 +39,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php'; session_cache_limiter('nocache'); @date_default_timezone_set('UTC'); -require_once PIWIK_INCLUDE_PATH .'/core/Loader.php'; +require_once PIWIK_INCLUDE_PATH .'/vendor/autoload.php'; // This is Piwik logo, the static file used in this test suit define("TEST_FILE_LOCATION", dirname(__FILE__) . "/lipsum.txt"); diff --git a/tests/travis/travis.sh b/tests/travis/travis.sh index 2d1d9ebac1e9d74998a204901cae677c43c76398..fa783472dc7a708d0a637390f9397860c0be8560 100755 --- a/tests/travis/travis.sh +++ b/tests/travis/travis.sh @@ -12,7 +12,7 @@ if [ "$TEST_SUITE" != "UITests" ] && [ "$TEST_SUITE" != "AngularJSTests" ] then if [ `phpunit --group __nogroup__ | grep "No tests executed" | wc -l` -ne 1 ] then - echo "=====> There are some tests functions which do not have a @group set. " + echo "=====> There are some tests functions which do not have a @group set or have no tests. " echo " Please add the @group phpdoc comment to the following tests: <=====" phpunit --group __nogroup__ --testdox | grep "[x]" exit 1