From f26f8a19d172f0055b26c63b0e7e0aaeadee7002 Mon Sep 17 00:00:00 2001 From: barbushin <barbushin@gmail.com> Date: Mon, 13 Jul 2015 23:43:33 +0300 Subject: [PATCH] Add option to track only website urls --- core/Db/Schema/Mysql.php | 1 + core/Tracker/VisitExcluded.php | 29 +++++++++++++ core/Updates/2.14.1-b2.php | 41 +++++++++++++++++++ plugins/SitesManager/API.php | 10 ++++- plugins/SitesManager/SitesManager.php | 3 ++ .../sites-manager-site.controller.js | 2 + plugins/SitesManager/lang/en.json | 2 + .../templates/sites-list/site-fields.html | 7 ++++ .../tests/Integration/ApiTest.php | 12 +++--- ...ger__SitesManager.getPatternMatchSites.xml | 11 +++++ .../PHPUnit/Integration/Tracker/VisitTest.php | 39 ++++++++++++++++++ 11 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 core/Updates/2.14.1-b2.php diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 8402dc5bd1..8b554e1ae4 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -92,6 +92,7 @@ class Mysql implements SchemaInterface sitesearch_category_parameters TEXT NOT NULL, timezone VARCHAR( 50 ) NOT NULL, currency CHAR( 3 ) NOT NULL, + exclude_unknown_urls TINYINT(1) DEFAULT 0, excluded_ips TEXT NOT NULL, excluded_parameters TEXT NOT NULL, excluded_user_agents TEXT NOT NULL, diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php index 028c44460c..ef0cd00b6d 100644 --- a/core/Tracker/VisitExcluded.php +++ b/core/Tracker/VisitExcluded.php @@ -128,6 +128,14 @@ class VisitExcluded } } + // Check if request URL is excluded + if (!$excluded) { + $excluded = $this->isUrlExcluded(); + if ($excluded) { + Common::printDebug("Unknown URL is not allowed to track."); + } + } + if (!$excluded) { if ($this->isPrefetchDetected()) { $excluded = true; @@ -274,6 +282,27 @@ class VisitExcluded return false; } + /** + * Checks if request URL is excluded + * @return bool + */ + protected function isUrlExcluded() + { + $site = Cache::getCacheWebsiteAttributes($this->idSite); + + if (!empty($site['exclude_unknown_urls']) && !empty($site['hosts'])) { + $trackingHost = parse_url($this->request->getParam('url'), PHP_URL_HOST); + foreach ($site['hosts'] as $siteHost) { + if ($trackingHost == $siteHost) { + return false; + } + } + return true; + } + + return false; + } + /** * Returns true if the specified user agent should be excluded for the current site or not. * diff --git a/core/Updates/2.14.1-b2.php b/core/Updates/2.14.1-b2.php new file mode 100644 index 0000000000..8762fedc18 --- /dev/null +++ b/core/Updates/2.14.1-b2.php @@ -0,0 +1,41 @@ +<?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\Common; +use Piwik\Updater; +use Piwik\Updates; + +/** + * Update for version 2.14.0. + */ +class Updates_2_14_1_b2 extends Updates +{ + /** + * Here you can define one or multiple SQL statements that should be executed during the update. + * @return array + */ + static function getSql() + { + $updateSql = array( + 'ALTER TABLE `' . Common::prefixTable('site') . '` ADD COLUMN `exclude_unknown_urls` TINYINT(1) DEFAULT 0 AFTER `currency`' => array(1060) + ); + return $updateSql; + } + + /** + * Here you can define any action that should be performed during the update. For instance executing SQL statements, + * renaming config entries, updating files, etc. + */ + static function update() + { + Updater::updateDatabase(__FILE__, self::getSql()); + } +} diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 8c182c47a5..8d13eb1e11 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -506,6 +506,7 @@ class API extends \Piwik\Plugin\API * @param array|null $settings JSON serialized settings eg {settingName: settingValue, ...} * @see getKeepURLFragmentsGlobal. * @param string $type The website type, defaults to "website" if not set. + * @param bool|null $excludeUnknownUrls Track only URL matching one of website URLs * * @return int the website ID created */ @@ -524,7 +525,8 @@ class API extends \Piwik\Plugin\API $excludedUserAgents = null, $keepURLFragments = null, $type = null, - $settings = null) + $settings = null, + $excludeUnknownUrls = null) { Piwik::checkUserHasSuperUserAccess(); @@ -555,6 +557,7 @@ class API extends \Piwik\Plugin\API $bind = array('name' => $siteName, 'main_url' => $url); + $bind['exclude_unknown_urls'] = (bool)$excludeUnknownUrls; $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents); @@ -1087,6 +1090,7 @@ class API extends \Piwik\Plugin\API * will be removed. If 0, the default global behavior will be used. * @param string $type The Website type, default value is "website" * @param array|null $settings JSON serialized settings eg {settingName: settingValue, ...} + * @param bool|null $excludeUnknownUrls Track only URL matching one of website URLs * @throws Exception * @see getKeepURLFragmentsGlobal. If null, the existing value will * not be modified. @@ -1109,7 +1113,8 @@ class API extends \Piwik\Plugin\API $excludedUserAgents = null, $keepURLFragments = null, $type = null, - $settings = null) + $settings = null, + $excludeUnknownUrls = null) { Piwik::checkUserHasAdminAccess($idSite); @@ -1156,6 +1161,7 @@ class API extends \Piwik\Plugin\API if (!is_null($startDate)) { $bind['ts_created'] = Date::factory($startDate)->getDatetime(); } + $bind['exclude_unknown_urls'] = (bool)$excludeUnknownUrls; $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents); diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index a298234640..16c330d6c1 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -117,6 +117,7 @@ class SitesManager extends \Piwik\Plugin $array['hosts'] = $this->getTrackerHosts($idSite); $website = API::getInstance()->getSiteFromId($idSite); + $array['exclude_unknown_urls'] = $website['exclude_unknown_urls']; $array['excluded_ips'] = $this->getTrackerExcludedIps($website); $array['excluded_parameters'] = self::getTrackerExcludedQueryParameters($website); $array['excluded_user_agents'] = self::getExcludedUserAgents($website); @@ -286,6 +287,8 @@ class SitesManager extends \Piwik\Plugin $translationKeys[] = "SitesManager_Currency"; $translationKeys[] = "SitesManager_ShowTrackingTag"; $translationKeys[] = "SitesManager_AliasUrlHelp"; + $translationKeys[] = "SitesManager_OnlyMatchedUrlsAllowed"; + $translationKeys[] = "SitesManager_OnlyMatchedUrlsAllowedHelp"; $translationKeys[] = "SitesManager_KeepURLFragmentsLong"; $translationKeys[] = "SitesManager_HelpExcludedIps"; $translationKeys[] = "SitesManager_ListOfQueryParametersToExclude"; diff --git a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js index 21dc868f7a..d759ec940f 100644 --- a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js +++ b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js @@ -111,6 +111,7 @@ searchKeywordParameters: sendSiteSearchKeywordParams ? $scope.site.sitesearch_keyword_parameters.join(',') : null, searchCategoryParameters: sendSearchCategoryParameters ? $scope.site.sitesearch_category_parameters.join(',') : null, urls: $scope.site.alias_urls, + excludeUnknownUrls: $scope.site.exclude_unknown_urls, settings: flatSettings }, 'POST'); @@ -133,6 +134,7 @@ "http://siteUrl.com/", "http://siteUrl2.com/" ]; + $scope.site.exclude_unknown_urls = 0; $scope.site.keep_url_fragment = "0"; $scope.site.excluded_ips = []; $scope.site.excluded_parameters = []; diff --git a/plugins/SitesManager/lang/en.json b/plugins/SitesManager/lang/en.json index 20977db504..218140c307 100644 --- a/plugins/SitesManager/lang/en.json +++ b/plugins/SitesManager/lang/en.json @@ -74,6 +74,8 @@ "TrackingTags": "Tracking code for %s", "Urls": "URLs", "UTCTimeIs": "UTC time is %s.", + "OnlyMatchedUrlsAllowed": "Only track visits for actions on any of the website URLs", + "OnlyMatchedUrlsAllowedHelp": "When enabled, Piwik will only track the data when the Page URL is one of the known URLs for your website.", "WebsitesManagement": "Websites Management", "XManagement": "Manage %s", "ChooseMeasurableTypeHeadline": "What would you like to measure?", diff --git a/plugins/SitesManager/templates/sites-list/site-fields.html b/plugins/SitesManager/templates/sites-list/site-fields.html index 888d9f7b1f..dada85cc22 100644 --- a/plugins/SitesManager/templates/sites-list/site-fields.html +++ b/plugins/SitesManager/templates/sites-list/site-fields.html @@ -82,6 +82,13 @@ {{ 'SitesManager_AliasUrlHelp' | translate }} </div> <div sites-manager-multiline-field field="site.alias_urls" cols="25" rows="3"></div> + + <div class="form-help"> + {{ 'SitesManager_OnlyMatchedUrlsAllowedHelp' | translate }} + </div> + <label class="checkbox"> + <input type="checkbox" ng-model="site.exclude_unknown_urls" ng-true-value="1" ng-false-value="0"> {{ 'SitesManager_OnlyMatchedUrlsAllowed' | translate:'':'' }} + </label> </div> <div class="form-group"> diff --git a/plugins/SitesManager/tests/Integration/ApiTest.php b/plugins/SitesManager/tests/Integration/ApiTest.php index 0bb70872bb..9d8542526d 100644 --- a/plugins/SitesManager/tests/Integration/ApiTest.php +++ b/plugins/SitesManager/tests/Integration/ApiTest.php @@ -550,8 +550,8 @@ class ApiTest extends IntegrationTestCase API::getInstance()->addSite("site3", array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), - 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), + 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), + 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), ); FakeAccess::setIdSitesAdmin(array(1, 3)); @@ -659,8 +659,8 @@ class ApiTest extends IntegrationTestCase API::getInstance()->addSite("site3", array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), - 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), + 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), + 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), ); FakeAccess::setIdSitesView(array(1, 3)); @@ -695,8 +695,8 @@ class ApiTest extends IntegrationTestCase API::getInstance()->addSite("site3", array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 1, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), - 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website'), + 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 1, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), + 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0, 'type' => 'website', 'exclude_unknown_urls' => 0), ); FakeAccess::setIdSitesView(array(1, 3)); diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getPatternMatchSites.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getPatternMatchSites.xml index 1fa55da06a..fbcef21650 100644 --- a/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getPatternMatchSites.xml +++ b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getPatternMatchSites.xml @@ -17,6 +17,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>10</idsite> @@ -35,6 +36,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>11</idsite> @@ -53,6 +55,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>12</idsite> @@ -71,6 +74,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>13</idsite> @@ -89,6 +93,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>14</idsite> @@ -107,6 +112,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>15</idsite> @@ -125,6 +131,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>16</idsite> @@ -143,6 +150,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>17</idsite> @@ -161,6 +169,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>18</idsite> @@ -179,6 +188,7 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> <row> <idsite>19</idsite> @@ -197,5 +207,6 @@ <group /> <type>website</type> <keep_url_fragment>0</keep_url_fragment> + <exclude_unknown_urls>0</exclude_unknown_urls> </row> </result> \ No newline at end of file diff --git a/tests/PHPUnit/Integration/Tracker/VisitTest.php b/tests/PHPUnit/Integration/Tracker/VisitTest.php index 78410327c5..107c16c083 100644 --- a/tests/PHPUnit/Integration/Tracker/VisitTest.php +++ b/tests/PHPUnit/Integration/Tracker/VisitTest.php @@ -99,6 +99,45 @@ class VisitTest extends IntegrationTestCase } } + public function getExcludeByUrlData() + { + return array( + array(array('http://test.com'), true, array( + 'http://test.com' => true, + 'https://test.com' => true, + 'http://test.com/uri' => true, + 'http://test.com/?query' => true, + 'http://xtest.com' => false, + 'http://x.test.com' => false, + 'http://x.com/test.com' => false, + )), + array(array('http://test.com', 'http://localhost'), true, array( + 'http://test.com' => true, + 'http://localhost' => true, + 'http://x.com' => false, + )), + array(array('http://test.com'), false, array( + 'http://x.com' => true, + )), + ); + } + + /** + * @dataProvider getExcludeByUrlData + */ + public function testExcludeByUrl($siteUrls, $excludeUnknownUrls, array $urlsTracked) + { + $siteId = API::getInstance()->addSite('name', $siteUrls, $ecommerce = null, $siteSearch = null, $searchKeywordParameters = null, $searchCategoryParameters = null, null, null, null, null, null, null, null, null, null, null, $excludeUnknownUrls); + foreach ($urlsTracked as $url => $isTracked) { + $visitExclude = new VisitExcluded(new Request(array( + 'idsite' => $siteId, + 'rec' => 1, + 'url' => $url + ))); + $this->assertEquals($isTracked, !$visitExclude->isExcluded()); + } + } + /** * @dataProvider getChromeDataSaverData */ -- GitLab