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

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2018-12-06 08:44:01 +0300
committerdiosmosis <diosmosis@users.noreply.github.com>2018-12-06 08:44:01 +0300
commit7b6a14626960795b0acdf4a064cd3982d7704f2b (patch)
tree1f8b5fc7d7869c77e91a1f8dc2ab592b3f8de4ca /plugins
parent1fe8a6ee582c2f61dc209b7df856695378007c23 (diff)
Report tracking into wrong Site ID and missing token auth (#13493)
* log tracking failures * added page * tweak UI * use a db column instead of option table to simplify code * add system summary, notifiy super users by email, fixes, update, ... * more fixes, needs tests next * add widget for tracking failures * ensure to not log any failure when visit is excluded * some tests and fixes * added tests * added missing test * apply review feedback * fix tests * trying to fix test * fix tests * fix update names * fix tests * Fix another test.
Diffstat (limited to 'plugins')
-rw-r--r--plugins/BulkTracking/tests/Integration/RequestsTest.php2
-rw-r--r--plugins/CoreAdminHome/.gitignore1
-rw-r--r--plugins/CoreAdminHome/API.php59
-rw-r--r--plugins/CoreAdminHome/Controller.php9
-rw-r--r--plugins/CoreAdminHome/CoreAdminHome.php41
-rw-r--r--plugins/CoreAdminHome/Emails/TrackingFailuresEmail.php106
-rw-r--r--plugins/CoreAdminHome/Menu.php6
-rw-r--r--plugins/CoreAdminHome/Tasks.php46
-rw-r--r--plugins/CoreAdminHome/Widgets/GetTrackingFailures.php39
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js59
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.html59
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js25
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less9
-rw-r--r--plugins/CoreAdminHome/config/test.php30
-rw-r--r--plugins/CoreAdminHome/lang/en.json21
-rw-r--r--plugins/CoreAdminHome/templates/_trackingFailuresEmail.twig3
-rw-r--r--plugins/CoreAdminHome/templates/getTrackingFailures.twig13
-rw-r--r--plugins/CoreAdminHome/templates/home.twig7
-rw-r--r--plugins/CoreAdminHome/templates/trackingFailures.twig8
-rw-r--r--plugins/CoreAdminHome/tests/Fixture/TrackingFailures.php46
-rw-r--r--plugins/CoreAdminHome/tests/Fixtures/SimpleFixtureTrackFewVisits.php77
-rw-r--r--plugins/CoreAdminHome/tests/Integration/APITest.php134
-rw-r--r--plugins/CoreAdminHome/tests/Integration/TasksTest.php36
-rw-r--r--plugins/CoreAdminHome/tests/System/TrackingFailuresTest.php63
-rw-r--r--plugins/CoreAdminHome/tests/System/expected/test___CoreAdminHome.getTrackingFailures.xml27
-rw-r--r--plugins/CoreAdminHome/tests/UI/TrackingFailures_spec.js94
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_no_failures.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_ask_confirmation.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_confirmed.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_ask_confirmation.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_confirmed.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_no_failures.png3
-rw-r--r--plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_with_failures.png3
-rw-r--r--plugins/CoreHome/CoreHome.php2
-rw-r--r--plugins/CoreHome/Tracker/VisitRequestProcessor.php5
-rw-r--r--plugins/CustomVariables/tests/UI/expected-screenshots/CustomVariables_link_in_menu.png4
m---------plugins/QueuedTracking0
-rw-r--r--plugins/SitesManager/SitesManager.php15
-rw-r--r--plugins/Widgetize/tests/System/WidgetTest.php8
40 files changed, 1062 insertions, 16 deletions
diff --git a/plugins/BulkTracking/tests/Integration/RequestsTest.php b/plugins/BulkTracking/tests/Integration/RequestsTest.php
index 7f6236d82c..e0832770d7 100644
--- a/plugins/BulkTracking/tests/Integration/RequestsTest.php
+++ b/plugins/BulkTracking/tests/Integration/RequestsTest.php
@@ -33,6 +33,8 @@ class RequestsTest extends IntegrationTestCase
{
parent::setUp();
+ Fixture::createWebsite('2014-01-02 03:04:05');
+
$this->requests = new Requests();
}
diff --git a/plugins/CoreAdminHome/.gitignore b/plugins/CoreAdminHome/.gitignore
new file mode 100644
index 0000000000..c8c9480010
--- /dev/null
+++ b/plugins/CoreAdminHome/.gitignore
@@ -0,0 +1 @@
+tests/System/processed/*xml \ No newline at end of file
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index 6fc3313696..19eef3d946 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreAdminHome;
use Exception;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
+use Piwik\Access;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Config;
use Piwik\Container\StaticContainer;
@@ -22,6 +23,7 @@ use Piwik\Piwik;
use Piwik\Segment;
use Piwik\Scheduler\Scheduler;
use Piwik\Site;
+use Piwik\Tracker\Failures;
use Piwik\Url;
/**
@@ -39,10 +41,16 @@ class API extends \Piwik\Plugin\API
*/
private $invalidator;
- public function __construct(Scheduler $scheduler, ArchiveInvalidator $invalidator)
+ /**
+ * @var Failures
+ */
+ private $trackingFailures;
+
+ public function __construct(Scheduler $scheduler, ArchiveInvalidator $invalidator, Failures $trackingFailures)
{
$this->scheduler = $scheduler;
$this->invalidator = $invalidator;
+ $this->trackingFailures = $trackingFailures;
}
/**
@@ -180,6 +188,55 @@ class API extends \Piwik\Plugin\API
}
/**
+ * Deletes all tracking failures this user has at least admin access to.
+ * A super user will also delete tracking failures for sites that don't exist.
+ */
+ public function deleteAllTrackingFailures()
+ {
+ if (Piwik::hasUserSuperUserAccess()) {
+ $this->trackingFailures->deleteAllTrackingFailures();
+ } else {
+ Piwik::checkUserHasSomeAdminAccess();
+ $idSites = Access::getInstance()->getSitesIdWithAdminAccess();
+ Piwik::checkUserHasAdminAccess($idSites);
+ $this->trackingFailures->deleteTrackingFailures($idSites);
+ }
+ }
+
+ /**
+ * Deletes a specific tracking failure
+ * @param int $idSite
+ * @param int $idFailure
+ */
+ public function deleteTrackingFailure($idSite, $idFailure)
+ {
+ $idSite = (int) $idSite;
+ Piwik::checkUserHasAdminAccess($idSite);
+
+ $this->trackingFailures->deleteTrackingFailure($idSite, $idFailure);
+ }
+
+ /**
+ * Get all tracking failures. A user retrieves only tracking failures for sites with at least admin access.
+ * A super user will also retrieve failed requests for sites that don't exist.
+ * @return array
+ */
+ public function getTrackingFailures()
+ {
+ if (Piwik::hasUserSuperUserAccess()) {
+ $failures = $this->trackingFailures->getAllFailures();
+ } else {
+ Piwik::checkUserHasSomeAdminAccess();
+ $idSites = Access::getInstance()->getSitesIdWithAdminAccess();
+ Piwik::checkUserHasAdminAccess($idSites);
+
+ $failures = $this->trackingFailures->getFailuresForSites($idSites);
+ }
+
+ return $failures;
+ }
+
+ /**
* Ensure the specified dates are valid.
* Store invalid date so we can log them
* @param array $dates
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index bee4b57cac..32441e0d39 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -60,6 +60,7 @@ class Controller extends ControllerAdmin
$hasPremiumFeatures = $widgetsList->isDefined('Marketplace', 'getPremiumFeatures');
$hasNewPlugins = $widgetsList->isDefined('Marketplace', 'getNewPlugins');
$hasDiagnostics = $widgetsList->isDefined('Installation', 'getSystemCheck');
+ $hasTrackingFailures = $widgetsList->isDefined('CoreAdminHome', 'getTrackingFailures');
return $this->renderTemplate('home', array(
'isInternetEnabled' => $isInternetEnabled,
@@ -70,6 +71,7 @@ class Controller extends ControllerAdmin
'hasDonateForm' => $hasDonateForm,
'hasPiwikBlog' => $hasPiwikBlog,
'hasDiagnostics' => $hasDiagnostics,
+ 'hasTrackingFailures' => $hasTrackingFailures,
));
}
@@ -79,6 +81,13 @@ class Controller extends ControllerAdmin
return;
}
+ public function trackingFailures()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+
+ return $this->renderTemplate('trackingFailures');
+ }
+
public function generalSettings()
{
Piwik::checkUserHasSuperUserAccess();
diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php
index a87c6da948..3e0d0e4912 100644
--- a/plugins/CoreAdminHome/CoreAdminHome.php
+++ b/plugins/CoreAdminHome/CoreAdminHome.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\CoreAdminHome;
-use Piwik\Db;
+use Piwik\API\Request;
use Piwik\Piwik;
use Piwik\ProxyHttp;
-use Piwik\Settings\Plugin\UserSetting;
+use Piwik\Plugins\CoreHome\SystemSummary;
use Piwik\Settings\Storage\Backend\PluginSettingsTable;
/**
@@ -20,7 +20,7 @@ use Piwik\Settings\Storage\Backend\PluginSettingsTable;
class CoreAdminHome extends \Piwik\Plugin
{
/**
- * @see Piwik\Plugin::registerEvents
+ * @see \Piwik\Plugin::registerEvents
*/
public function registerEvents()
{
@@ -30,10 +30,24 @@ class CoreAdminHome extends \Piwik\Plugin
'UsersManager.deleteUser' => 'cleanupUser',
'API.DocumentationGenerator.@hideExceptForSuperUser' => 'displayOnlyForSuperUser',
'Template.jsGlobalVariables' => 'addJsGlobalVariables',
- 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
+ 'System.addSystemSummaryItems' => 'addSystemSummaryItems',
);
}
+ public function addSystemSummaryItems(&$systemSummary)
+ {
+ if (Piwik::isUserHasSomeAdminAccess()) {
+ $failures = Request::processRequest('CoreAdminHome.getTrackingFailures', [], []);
+ $numFailures = count($failures);
+ $icon = 'icon-error';
+ if ($numFailures === 0) {
+ $icon = 'icon-ok';
+ }
+ $systemSummary[] = new SystemSummary\Item($key = 'trackingfailures', Piwik::translate('CoreAdminHome_NTrackingFailures', $numFailures), $value = null, array('module' => 'CoreAdminHome', 'action' => 'trackingFailures'), $icon, $order = 9);
+ }
+ }
+
public function cleanupUser($userLogin)
{
PluginSettingsTable::removeAllUserSettingsForUser($userLogin);
@@ -45,6 +59,7 @@ class CoreAdminHome extends \Piwik\Plugin
$stylesheets[] = "plugins/Morpheus/stylesheets/base.less";
$stylesheets[] = "plugins/Morpheus/stylesheets/main.less";
$stylesheets[] = "plugins/CoreAdminHome/stylesheets/generalSettings.less";
+ $stylesheets[] = "plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less";
}
public function getJsFiles(&$jsFiles)
@@ -82,5 +97,23 @@ class CoreAdminHome extends \Piwik\Plugin
$translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectlySolution';
$translationKeys[] = 'CoreAdminHome_SettingsSaveSuccess';
$translationKeys[] = 'UserCountryMap_None';
+ $translationKeys[] = 'Actions_ColumnPageURL';
+ $translationKeys[] = 'General_Date';
+ $translationKeys[] = 'General_Measurable';
+ $translationKeys[] = 'General_Action';
+ $translationKeys[] = 'General_Delete';
+ $translationKeys[] = 'General_Id';
+ $translationKeys[] = 'CoreHome_ClickToSeeFullInformation';
+ $translationKeys[] = 'CoreAdminHome_LearnMore';
+ $translationKeys[] = 'CoreAdminHome_ConfirmDeleteAllTrackingFailures';
+ $translationKeys[] = 'CoreAdminHome_ConfirmDeleteThisTrackingFailure';
+ $translationKeys[] = 'CoreAdminHome_DeleteAllFailures';
+ $translationKeys[] = 'CoreAdminHome_NTrackingFailures';
+ $translationKeys[] = 'CoreAdminHome_Problem';
+ $translationKeys[] = 'CoreAdminHome_Solution';
+ $translationKeys[] = 'CoreAdminHome_TrackingFailures';
+ $translationKeys[] = 'CoreAdminHome_TrackingFailuresIntroduction';
+ $translationKeys[] = 'CoreAdminHome_TrackingURL';
+ $translationKeys[] = 'CoreAdminHome_NoKnownFailures';
}
}
diff --git a/plugins/CoreAdminHome/Emails/TrackingFailuresEmail.php b/plugins/CoreAdminHome/Emails/TrackingFailuresEmail.php
new file mode 100644
index 0000000000..92d3ee2c16
--- /dev/null
+++ b/plugins/CoreAdminHome/Emails/TrackingFailuresEmail.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Plugins\CoreAdminHome\Emails;
+
+use Piwik\Access;
+use Piwik\Mail;
+use Piwik\Piwik;
+use Piwik\SettingsPiwik;
+use Piwik\Url;
+use Piwik\View;
+
+class TrackingFailuresEmail extends Mail
+{
+ /**
+ * @var string
+ */
+ private $login;
+
+ /**
+ * @var string
+ */
+ private $emailAddress;
+
+ /**
+ * @var int
+ */
+ private $numFailures;
+
+ public function __construct($login, $emailAddress, $numFailures)
+ {
+ parent::__construct();
+
+ $this->login = $login;
+ $this->emailAddress = $emailAddress;
+ $this->numFailures = (int)$numFailures;
+
+ $this->setUpEmail();
+ }
+
+ /**
+ * @return string
+ */
+ public function getLogin()
+ {
+ return $this->login;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmailAddress()
+ {
+ return $this->emailAddress;
+ }
+
+ /**
+ * @return int
+ */
+ public function getNumFailures()
+ {
+ return $this->numFailures;
+ }
+
+ private function setUpEmail()
+ {
+ $this->setDefaultFromPiwik();
+ $this->addTo($this->emailAddress);
+ $this->setSubject($this->getDefaultSubject());
+ $this->setReplyTo($this->getFrom());
+ $this->setWrappedHtmlBody($this->getDefaultBodyView());
+ }
+
+ private function getDefaultSubject()
+ {
+ return Piwik::translate('CoreAdminHome_TrackingFailuresEmailSubject');
+ }
+
+ private function getDefaultBodyView()
+ {
+ $view = new View('@CoreAdminHome/_trackingFailuresEmail.twig');
+ $view->login = $this->login;
+ $view->emailAddress = $this->emailAddress;
+ $view->numFailures = $this->numFailures;
+
+ $sitesId = Access::getInstance()->getSitesIdWithAtLeastViewAccess();
+ $idSite = false;
+ if (!empty($sitesId)) {
+ $idSite = array_shift($sitesId);
+ }
+ $view->trackingFailuresUrl = SettingsPiwik::getPiwikUrl() . 'index.php?' . Url::getQueryStringFromParameters([
+ 'module' => 'CoreAdminHome',
+ 'action' => 'trackingFailures',
+ 'period' => 'day',
+ 'date' => 'yesterday',
+ 'idSite' => $idSite
+ ]);
+ return $view;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php
index 49a5579636..8ec288ec6c 100644
--- a/plugins/CoreAdminHome/Menu.php
+++ b/plugins/CoreAdminHome/Menu.php
@@ -36,6 +36,12 @@ class Menu extends \Piwik\Plugin\Menu
$this->urlForAction('trackingCodeGenerator'),
$order = 12);
}
+
+ if (Piwik::isUserHasSomeAdminAccess()) {
+ $menu->addDiagnosticItem('CoreAdminHome_TrackingFailures',
+ $this->urlForAction('trackingFailures'),
+ $order = 2);
+ }
}
public function configureTopMenu(MenuTop $menu)
diff --git a/plugins/CoreAdminHome/Tasks.php b/plugins/CoreAdminHome/Tasks.php
index 80560dde48..e6c7d1b242 100644
--- a/plugins/CoreAdminHome/Tasks.php
+++ b/plugins/CoreAdminHome/Tasks.php
@@ -18,13 +18,14 @@ use Piwik\Date;
use Piwik\Db;
use Piwik\Http;
use Piwik\Option;
+use Piwik\Piwik;
use Piwik\Plugins\CoreAdminHome\Emails\JsTrackingCodeMissingEmail;
+use Piwik\Plugins\CoreAdminHome\Emails\TrackingFailuresEmail;
use Piwik\Plugins\CoreAdminHome\Tasks\ArchivesToPurgeDistributedList;
use Piwik\Plugins\SitesManager\SitesManager;
-use Piwik\Scheduler\Schedule\Daily;
-use Piwik\Scheduler\Schedule\Monthly;
use Piwik\Scheduler\Schedule\SpecificTime;
use Piwik\Settings\Storage\Backend\MeasurableSettingsTable;
+use Piwik\Tracker\Failures;
use Piwik\Site;
use Piwik\Tracker\Visit\ReferrerSpamFilter;
use Psr\Log\LoggerInterface;
@@ -43,10 +44,16 @@ class Tasks extends \Piwik\Plugin\Tasks
*/
private $logger;
- public function __construct(ArchivePurger $archivePurger, LoggerInterface $logger)
+ /**
+ * @var Failures
+ */
+ private $trackingFailures;
+
+ public function __construct(ArchivePurger $archivePurger, LoggerInterface $logger, Failures $failures)
{
$this->archivePurger = $archivePurger;
$this->logger = $logger;
+ $this->trackingFailures = $failures;
}
public function schedule()
@@ -60,6 +67,9 @@ class Tasks extends \Piwik\Plugin\Tasks
// lowest priority since tables should be optimized after they are modified
$this->daily('optimizeArchiveTable', null, self::LOWEST_PRIORITY);
+ $this->daily('cleanupTrackingFailures', null, self::LOWEST_PRIORITY);
+ $this->weekly('notifyTrackingFailures', null, self::LOWEST_PRIORITY);
+
if(SettingsPiwik::isInternetEnabled() === true){
$this->weekly('updateSpammerBlacklist');
}
@@ -140,6 +150,36 @@ class Tasks extends \Piwik\Plugin\Tasks
}
/**
+ * To test execute the following command:
+ * `./console core:run-scheduled-tasks "Piwik\Plugins\CoreAdminHome\Tasks.cleanupTrackingFailures"`
+ *
+ * @throws \Exception
+ */
+ public function cleanupTrackingFailures()
+ {
+ // we remove possibly outdated/fixed tracking failures that have not occurred again recently
+ $this->trackingFailures->removeFailuresOlderThanDays(Failures::CLEANUP_OLD_FAILURES_DAYS);
+ }
+
+ /**
+ * To test execute the following command:
+ * `./console core:run-scheduled-tasks "Piwik\Plugins\CoreAdminHome\Tasks.notifyTrackingFailures"`
+ *
+ * @throws \Exception
+ */
+ public function notifyTrackingFailures()
+ {
+ $failures = $this->trackingFailures->getAllFailures();
+ if (!empty($failures)) {
+ $superUsers = Piwik::getAllSuperUserAccessEmailAddresses();
+ foreach ($superUsers as $login => $email) {
+ $email = new TrackingFailuresEmail($login, $email, count($failures));
+ $email->send();
+ }
+ }
+ }
+
+ /**
* @return bool `true` if the purge was executed, `false` if it was skipped.
* @throws \Exception
*/
diff --git a/plugins/CoreAdminHome/Widgets/GetTrackingFailures.php b/plugins/CoreAdminHome/Widgets/GetTrackingFailures.php
new file mode 100644
index 0000000000..c251c3c92f
--- /dev/null
+++ b/plugins/CoreAdminHome/Widgets/GetTrackingFailures.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreAdminHome\Widgets;
+
+use Piwik\API\Request;
+use Piwik\Piwik;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+
+class GetTrackingFailures extends Widget
+{
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('About Matomo');
+ $config->setName('CoreAdminHome_TrackingFailures');
+ $config->setOrder(5);
+
+ if (!Piwik::isUserHasSomeAdminAccess()) {
+ $config->disable();
+ }
+ }
+
+ public function render()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $failures = Request::processRequest('CoreAdminHome.getTrackingFailures');
+ $numFailures = count($failures);
+
+ return $this->renderTemplate('getTrackingFailures', array(
+ 'numFailures' => $numFailures
+ ));
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js
new file mode 100644
index 0000000000..e007fae6c3
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js
@@ -0,0 +1,59 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('TrackingFailuresController', TrackingFailuresController);
+
+ TrackingFailuresController.$inject = ['piwikApi', 'piwik'];
+
+ function TrackingFailuresController(piwikApi, piwik){
+ var self = this;
+ this.failures = [];
+ this.sortColumn = 'idsite';
+ this.sortReverse = false;
+ this.isLoading = false;
+
+ this.changeSortOrder = function (columnToSort) {
+ if (this.sortColumn === columnToSort) {
+ this.sortReverse = !this.sortReverse;
+ } else {
+ this.sortColumn = columnToSort;
+ }
+ };
+
+ this.fetchAll = function () {
+ this.failures = [];
+ this.isLoading = true;
+ piwikApi.fetch({method: 'CoreAdminHome.getTrackingFailures', filter_limit: '-1'}).then(function (failures) {
+ self.failures = failures;
+ self.isLoading = false;
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+
+ this.deleteAll = function () {
+ piwik.helper.modalConfirm('#confirmDeleteAllTrackingFailures', {yes: function () {
+ self.failures = [];
+ piwikApi.fetch({method: 'CoreAdminHome.deleteAllTrackingFailures'}).then(function () {
+ self.fetchAll();
+ });
+ }});
+ };
+
+ this.deleteFailure = function (idSite, idFailure) {
+ piwik.helper.modalConfirm('#confirmDeleteThisTrackingFailure', {yes: function () {
+ self.failures = [];
+ piwikApi.fetch({method: 'CoreAdminHome.deleteTrackingFailure', idSite: idSite, idFailure: idFailure}).then(function () {
+ self.fetchAll();
+ });
+ }});
+ };
+
+ this.fetchAll();
+ }
+
+})();
diff --git a/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.html b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.html
new file mode 100644
index 0000000000..07e4baa895
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.html
@@ -0,0 +1,59 @@
+<div piwik-content-block
+ content-title="{{ 'CoreAdminHome_TrackingFailures'|translate }}"
+ class="matomoTrackingFailures">
+ <p>
+ {{ 'CoreAdminHome_TrackingFailuresIntroduction'|translate:2 }}
+ <br /><br />
+ <input class="btn deleteAllFailures"
+ ng-show="!trackingFailures.isLoading && trackingFailures.failures.length > 0"
+ type="button" ng-click="trackingFailures.deleteAll();"
+ value="{{'CoreAdminHome_DeleteAllFailures'|translate}}">
+ </p>
+
+ <div piwik-activity-indicator loading="trackingFailures.isLoading"></div>
+
+ <table piwik-content-table>
+ <thead>
+ <tr>
+ <th ng-click="trackingFailures.changeSortOrder('idsite')">{{ 'General_Measurable'|translate }}</th>
+ <th ng-click="trackingFailures.changeSortOrder('problem')">{{ 'CoreAdminHome_Problem'|translate }}</th>
+ <th ng-click="trackingFailures.changeSortOrder('solution')">{{ 'CoreAdminHome_Solution'|translate }}</th>
+ <th ng-click="trackingFailures.changeSortOrder('date_first_occurred')">{{ 'General_Date'|translate }}</th>
+ <th ng-click="trackingFailures.changeSortOrder('url')">{{ 'Actions_ColumnPageURL'|translate }}</th>
+ <th ng-click="trackingFailures.changeSortOrder('request_url')">{{ 'CoreAdminHome_TrackingURL'|translate }}</th>
+ <th class="action">{{ 'General_Action'|translate }}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td colspan="7" ng-show="!trackingFailures.isLoading && trackingFailures.failures.length == 0">{{'CoreAdminHome_NoKnownFailures'|translate}} <span class="icon-ok"></span></td></tr>
+ <tr ng-repeat="failure in trackingFailures.failures | orderBy:trackingFailures.sortColumn:trackingFailures.sortReverse">
+ <td>{{ failure.site_name }} ({{'General_Id'|translate}} {{ failure.idsite }})</td>
+ <td>{{ failure.problem }}</td>
+ <td>{{ failure.solution }} <a ng-show="failure.solution_url" rel="noopener noreferrer" ng-href="{{ failure.solution_url }}">{{'CoreAdminHome_LearnMore'|translate }}</a></td>
+ <td class="datetime">{{ failure.pretty_date_first_occurred }}</td>
+ <td>{{ failure.url }}</td>
+ <td><span ng-show="!failure.showFullRequestUrl" title="{{'CoreHome_ClickToSeeFullInformation'|translate}}"
+ ng-click="failure.showFullRequestUrl = true">{{ failure.request_url|limitTo:100 }}...</span>
+ <span ng-show="failure.showFullRequestUrl">{{ failure.request_url }}</span></td>
+ <td><span class="table-action icon-delete"
+ title="{{'General_Delete'|translate}}"
+ ng-click="trackingFailures.deleteFailure(failure.idsite, failure.idfailure)"></span></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="ui-confirm" id="confirmDeleteAllTrackingFailures">
+ <h2>{{ 'CoreAdminHome_ConfirmDeleteAllTrackingFailures'|translate }}</h2>
+
+ <input type="button" value="{{ 'General_Yes'|translate }}" role="yes"/>
+ <input type="button" value="{{ 'General_No'|translate }}" role="no" />
+ </div>
+
+ <div class="ui-confirm" id="confirmDeleteThisTrackingFailure">
+ <h2>{{ 'CoreAdminHome_ConfirmDeleteThisTrackingFailure'|translate }}</h2>
+
+ <input type="button" value="{{ 'General_Yes'|translate }}" role="yes"/>
+ <input type="button" value="{{ 'General_No'|translate }}" role="no" />
+ </div>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js
new file mode 100644
index 0000000000..910e5278d7
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js
@@ -0,0 +1,25 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div matomo-tracking-failures>
+ */
+(function () {
+ angular.module('piwikApp').directive('matomoTrackingFailures', matomoTrackingFailures);
+
+ matomoTrackingFailures.$inject = ['piwik'];
+
+ function matomoTrackingFailures(piwik){
+ return {
+ restrict: 'A',
+ templateUrl: 'plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'TrackingFailuresController',
+ controllerAs: 'trackingFailures'
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less
new file mode 100644
index 0000000000..505b106431
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less
@@ -0,0 +1,9 @@
+.matomoTrackingFailures {
+ .icon-delete,
+ th:not(.action) {
+ cursor: pointer;
+ }
+ th.action {
+ width: 60px;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/config/test.php b/plugins/CoreAdminHome/config/test.php
new file mode 100644
index 0000000000..13a2080dae
--- /dev/null
+++ b/plugins/CoreAdminHome/config/test.php
@@ -0,0 +1,30 @@
+<?php
+use \Piwik\Tracker\Request;
+use \Piwik\Tracker\Failures;
+
+return array(
+
+ 'Piwik\Tracker\Failures' => DI\decorate(function ($previous) {
+ /** @var Failures $previous */
+
+ $generate = \Piwik\Container\StaticContainer::get('test.vars.generateTrackingFailures');
+ if ($generate) {
+ $previous->setNow(\Piwik\Date::factory('2018-07-07 01:02:03'));
+ $previous->logFailure(Failures::FAILURE_ID_INVALID_SITE, new Request(array(
+ 'idsite' => 998, 'rec' => '1'
+ )));
+ $previous->logFailure(Failures::FAILURE_ID_NOT_AUTHENTICATED, new Request(array(
+ 'idsite' => 1,
+ 'url' => 'https://www.example.com/foo/bar?x=1',
+ 'action_name' => 'foobar',
+ 'rec' => '1'
+ )));
+ $previous->logFailure(Failures::FAILURE_ID_INVALID_SITE, new Request(array(
+ 'idsite' => 999, 'rec' => '1'
+ )));
+ }
+
+ return $previous;
+ }),
+
+); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json
index 25ae8e094f..ac3328f395 100644
--- a/plugins/CoreAdminHome/lang/en.json
+++ b/plugins/CoreAdminHome/lang/en.json
@@ -82,6 +82,7 @@
"PluginSettingsValueNotAllowed": "The value for field \"%1$s\" in plugin \"%2$s\" is not allowed",
"PluginSettingsSaveFailed": "Failed to save plugin settings",
"PluginSettingsSaveSuccess": "Plugin settings updated.",
+ "TrackingFailures": "Tracking failures",
"SettingsSaveSuccess": "Settings updated.",
"SendPluginUpdateCommunication": "Send an email when a plugin update is available",
"SendPluginUpdateCommunicationHelp": "An email will be sent to Super Users when there is a new version available for a plugin.",
@@ -108,6 +109,24 @@
"MissingTrackingCodeEmailSubject": "No traffic for %s recorded in Matomo Analytics, get started now",
"JsTrackingCodeMissingEmail1": "A few days ago you added the website '%s' to your Matomo Analytics. We just checked and your Matomo doesn't seem to have any recorded traffic for this website.",
"JsTrackingCodeMissingEmail2": "To begin tracking data and getting insights into your users, you'll need to setup tracking in your website or mobile app. For websites simply embed the tracking code right before the %s tag.",
- "JsTrackingCodeMissingEmail3": "To find and customize your tracking code, %1$sclick here%2$s (or have a look at the %3$sJavaScript Tracking Client guide%4$s)."
+ "JsTrackingCodeMissingEmail3": "To find and customize your tracking code, %1$sclick here%2$s (or have a look at the %3$sJavaScript Tracking Client guide%4$s).",
+ "TrackingFailuresIntroduction": "This page lists tracking failures that happened during the last %s days. Please note that only the most common kind of tracking failures are recorded and not all of them.",
+ "NoKnownFailures": "There are no known tracking failures.",
+ "Problem": "Problem",
+ "Solution": "Solution",
+ "TrackingURL": "Tracking URL",
+ "LearnMore": "Learn more",
+ "DeleteAllFailures": "Delete all failures",
+ "NTrackingFailures": "%s tracking failures",
+ "ViewAllTrackingFailures": "View all tracking failures",
+ "TrackingFailureInvalidSiteProblem": "The site does not exist.",
+ "TrackingFailureInvalidSiteSolution": "Update the configured idSite in the tracker.",
+ "TrackingFailureAuthenticationProblem": "Request was not authenticated but authentication was required.",
+ "TrackingFailureAuthenticationSolution": "Set or correct a \"token_auth\" in your tracking request.",
+ "ConfirmDeleteAllTrackingFailures": "Are you sure you want to delete all tracking failures?",
+ "ConfirmDeleteThisTrackingFailure": "Are you sure you want to delete this tracking failure?",
+ "TrackingFailuresEmailSubject": "Tracking failures in your Matomo Analytics",
+ "TrackingFailuresEmail1": "This is just to let you know that %s different kinds of tracking failures have occurred in the last days.",
+ "TrackingFailuresEmail2": "To view all the failed tracking requests %1$sclick here%2$s."
}
}
diff --git a/plugins/CoreAdminHome/templates/_trackingFailuresEmail.twig b/plugins/CoreAdminHome/templates/_trackingFailuresEmail.twig
new file mode 100644
index 0000000000..8be51c0e0d
--- /dev/null
+++ b/plugins/CoreAdminHome/templates/_trackingFailuresEmail.twig
@@ -0,0 +1,3 @@
+<p>{{ 'General_HelloUser'|translate(login) }}</p>
+<p>{{ 'CoreAdminHome_TrackingFailuresEmail1'|translate('<strong>'~numFailures~'</strong>')|raw }}</p>
+<p>{{ 'CoreAdminHome_TrackingFailuresEmail2'|translate('<a href="'~trackingFailuresUrl~'">', '</a>')|raw }}</p>
diff --git a/plugins/CoreAdminHome/templates/getTrackingFailures.twig b/plugins/CoreAdminHome/templates/getTrackingFailures.twig
new file mode 100644
index 0000000000..3096e65c2f
--- /dev/null
+++ b/plugins/CoreAdminHome/templates/getTrackingFailures.twig
@@ -0,0 +1,13 @@
+<div class="widgetBody system-check">
+ {% if numFailures == 0 %}
+ <p class="system-success"><span class="icon-ok"></span> {{ 'CoreAdminHome_NoKnownFailures'|translate }}</p>
+ {% else %}
+ <p class="system-errors">
+ <span style="font-size: 16px;"><span class="icon-error"></span> {{ 'CoreAdminHome_NTrackingFailures'|translate(numFailures) }}</span>
+ </p>
+ <p>
+ <a href="{{ linkTo({'module': 'CoreAdminHome', 'action': 'trackingFailures'}) }}"
+ >{{ 'CoreAdminHome_ViewAllTrackingFailures'|translate }}</a>
+ </p>
+ {% endif %}
+</div> \ No newline at end of file
diff --git a/plugins/CoreAdminHome/templates/home.twig b/plugins/CoreAdminHome/templates/home.twig
index 58c3288c19..0c52fdf9c1 100644
--- a/plugins/CoreAdminHome/templates/home.twig
+++ b/plugins/CoreAdminHome/templates/home.twig
@@ -21,9 +21,14 @@
<div class="col s12 {% if isFeedbackEnabled %}m4{% else %}m6{% endif %}">
<div piwik-widget-loader='{"module":"CoreHome","action":"getSystemSummary"}'></div>
</div>
- {% if hasDiagnostics %}
+ {% if hasDiagnostics or hasTrackingFailures %}
<div class="col s12 {% if isFeedbackEnabled %}m4{% else %}m6{% endif %}">
+ {% if hasDiagnostics %}
<div piwik-widget-loader='{"module":"Installation","action":"getSystemCheck"}'></div>
+ {% endif %}
+ {% if hasTrackingFailures %}
+ <div piwik-widget-loader='{"module":"CoreAdminHome","action":"getTrackingFailures"}'></div>
+ {% endif %}
</div>
{% endif %}
{% if isFeedbackEnabled %}
diff --git a/plugins/CoreAdminHome/templates/trackingFailures.twig b/plugins/CoreAdminHome/templates/trackingFailures.twig
new file mode 100644
index 0000000000..36c2347e2e
--- /dev/null
+++ b/plugins/CoreAdminHome/templates/trackingFailures.twig
@@ -0,0 +1,8 @@
+{% extends 'admin.twig' %}
+
+{% set title %}{{ 'CoreAdminHome_TrackingFailures'|translate }}{% endset %}
+
+{% block content %}
+ <div matomo-tracking-failures>
+ </div>
+{% endblock %}
diff --git a/plugins/CoreAdminHome/tests/Fixture/TrackingFailures.php b/plugins/CoreAdminHome/tests/Fixture/TrackingFailures.php
new file mode 100644
index 0000000000..f8ab0cb808
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/Fixture/TrackingFailures.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\CoreAdminHome\tests\Fixture;
+
+use Piwik\Date;
+use Piwik\Tests\Framework\Fixture;
+
+class TrackingFailures extends Fixture
+{
+ public $idSite = 1;
+ public $dateTime = '2013-01-02 03:04:05';
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Fixture::createSuperUser();
+ if (!self::siteCreated($this->idSite)) {
+ Fixture::createWebsite('2014-01-02 03:04:05');
+ }
+ $this->trackData();
+ }
+
+ private function trackData()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ self::checkResponse($t->doTrackPageView('Valid Site'));
+
+ $t = self::getTracker(99999, Date::now()->getDatetime(), $defaultInit = true);
+
+ for ($i = 0; $i < 2; $i++) {
+ // we trigger it multiple times to test it will be inserted only once
+ $t->doTrackPageView('Invalid Site');
+ }
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('10.11.12.13');
+ $t->setTokenAuth('foobar'); // wrong token
+ $t->doTrackPageView('Invalid Token');
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/tests/Fixtures/SimpleFixtureTrackFewVisits.php b/plugins/CoreAdminHome/tests/Fixtures/SimpleFixtureTrackFewVisits.php
new file mode 100644
index 0000000000..b5c7fcf711
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/Fixtures/SimpleFixtureTrackFewVisits.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\CoreAdminHome\tests\Fixtures;
+
+use Piwik\Date;
+use Piwik\Tests\Framework\Fixture;
+
+/**
+ * Generates tracker testing data for our TrackingFailuresTest
+ *
+ * This Simple fixture adds one website and tracks one visit with couple pageviews and an ecommerce conversion
+ */
+class SimpleFixtureTrackFewVisits extends Fixture
+{
+ public $dateTime = '2013-01-23 01:23:45';
+ public $idSite = 1;
+
+ public function setUp()
+ {
+ $this->setUpWebsite();
+ $this->trackFirstVisit();
+ $this->trackSecondVisit();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function setUpWebsite()
+ {
+ if (!self::siteCreated($this->idSite)) {
+ $idSite = self::createWebsite($this->dateTime, $ecommerce = 1);
+ $this->assertSame($this->idSite, $idSite);
+ }
+ }
+
+ protected function trackFirstVisit()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrl('http://example.com/');
+ self::checkResponse($t->doTrackPageView('Viewing homepage'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $t->setUrl('http://example.com/sub/page');
+ self::checkResponse($t->doTrackPageView('Second page view'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
+ $t->addEcommerceItem($sku = 'SKU_ID', $name = 'Test item!', $category = 'Test & Category', $price = 777, $quantity = 33);
+ self::checkResponse($t->doTrackEcommerceOrder('TestingOrder', $grandTotal = 33 * 77));
+ }
+
+ protected function trackSecondVisit()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.73');
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrl('http://example.com/sub/page');
+ self::checkResponse($t->doTrackPageView('Viewing homepage'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $t->setUrl('http://example.com/?search=this is a site search query');
+ self::checkResponse($t->doTrackPageView('Site search query'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
+ $t->addEcommerceItem($sku = 'SKU_ID2', $name = 'A durable item', $category = 'Best seller', $price = 321);
+ self::checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 33 * 77));
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/tests/Integration/APITest.php b/plugins/CoreAdminHome/tests/Integration/APITest.php
new file mode 100644
index 0000000000..a3ddf2a182
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/Integration/APITest.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CoreAdminHome\tests\Integration;
+
+use Piwik\Plugins\CoreAdminHome\API;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\Mock\FakeAccess;
+
+/**
+ * @group CoreAdminHome
+ * @group APITest
+ * @group API
+ * @group Plugins
+ */
+class APITest extends \Piwik\Tests\Framework\TestCase\IntegrationTestCase
+{
+ /**
+ * @var int
+ */
+ private $idSite;
+
+ /**
+ * @var API
+ */
+ private $api;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->api = API::getInstance();
+ for ($i = 0; $i < 5; $i++) {
+ Fixture::createWebsite('2014-01-02 03:04:05');
+ }
+ }
+
+ /**
+ * @expectedException \Piwik\NoAccessException
+ * @expectedExceptionMessage checkUserHasSomeAdminAccess
+ */
+ public function test_getTrackingFailures_failsForViewUser()
+ {
+ $this->setUser();
+ $this->api->getTrackingFailures();
+ }
+
+ public function test_getTrackingFailures_WorksForAdminAndSuperuser()
+ {
+ $this->setAdminUser();
+ $this->assertSame(array(), $this->api->getTrackingFailures());
+ $this->setSuperUser();
+ $this->api->getTrackingFailures();
+ $this->assertSame(array(), $this->api->getTrackingFailures());
+ }
+
+ /**
+ * @expectedException \Piwik\NoAccessException
+ * @expectedExceptionMessage checkUserHasSomeAdminAccess
+ */
+ public function test_deleteAllTrackingFailures_failsForViewUser()
+ {
+ $this->setUser();
+ $this->api->deleteAllTrackingFailures();
+ }
+
+ public function test_deleteAllTrackingFailures_WorksForAdminAndSuperuser()
+ {
+ $this->setAdminUser();
+ $this->api->deleteAllTrackingFailures();
+ $this->setSuperUser();
+ $this->api->deleteAllTrackingFailures();
+ }
+
+ /**
+ * @expectedException \Piwik\NoAccessException
+ * @expectedExceptionMessage checkUserHasAdminAccess
+ */
+ public function test_deleteTrackingFailure_failsForViewUser()
+ {
+ $this->setUser();
+ $this->api->deleteTrackingFailure(1, 2);
+ }
+
+ /**
+ * @expectedException \Piwik\NoAccessException
+ * @expectedExceptionMessage checkUserHasAdminAccess
+ */
+ public function test_deleteTrackingFailure_failsForAdminUserIfNotAdminAccessToThatSite()
+ {
+ $this->setAdminUser();
+ $this->api->deleteTrackingFailure(2, 2);
+ }
+
+ public function test_deleteTrackingFailure_WorksForAdminAndSuperuser()
+ {
+ $this->setAdminUser();
+ $this->api->deleteTrackingFailure(1, 2);
+ $this->setSuperUser();
+ $this->api->deleteTrackingFailure(1, 2);
+ }
+
+ protected function setSuperUser()
+ {
+ FakeAccess::clearAccess(true);
+ }
+
+ protected function setUser()
+ {
+ FakeAccess::clearAccess(false);
+ FakeAccess::$identity = 'testUser';
+ FakeAccess::$idSitesView = array(1,3, $this->idSite);
+ FakeAccess::$idSitesAdmin = array();
+ }
+
+ protected function setAdminUser()
+ {
+ FakeAccess::clearAccess(false);
+ FakeAccess::$identity = 'testUser';
+ FakeAccess::$idSitesView = array();
+ FakeAccess::$idSitesAdmin = array(1,3, $this->idSite);
+ }
+
+ public function provideContainerConfig()
+ {
+ return array(
+ 'Piwik\Access' => new FakeAccess()
+ );
+ }
+}
diff --git a/plugins/CoreAdminHome/tests/Integration/TasksTest.php b/plugins/CoreAdminHome/tests/Integration/TasksTest.php
index d3ffc66518..ffab94c01d 100644
--- a/plugins/CoreAdminHome/tests/Integration/TasksTest.php
+++ b/plugins/CoreAdminHome/tests/Integration/TasksTest.php
@@ -14,12 +14,15 @@ use Piwik\Date;
use Piwik\Db;
use Piwik\Mail;
use Piwik\Plugins\CoreAdminHome\Emails\JsTrackingCodeMissingEmail;
+use Piwik\Plugins\CoreAdminHome\Emails\TrackingFailuresEmail;
use Piwik\Plugins\CoreAdminHome\Tasks;
use Piwik\Plugins\CoreAdminHome\Tasks\ArchivesToPurgeDistributedList;
use Piwik\Scheduler\Task;
use Piwik\Tests\Fixtures\RawArchiveDataWithTempAndInvalidated;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker\Failures;
+use Piwik\Tracker\Request;
use Psr\Log\NullLogger;
/**
@@ -66,7 +69,7 @@ class TasksTest extends IntegrationTestCase
$archivePurger->setYesterdayDate(Date::factory('2015-02-26'));
$archivePurger->setNow(Date::factory('2015-02-27 08:00:00')->getTimestamp());
- $this->tasks = new Tasks($archivePurger, new NullLogger());
+ $this->tasks = new Tasks($archivePurger, new NullLogger(), new Failures());
$this->mail = null;
}
@@ -129,6 +132,8 @@ class TasksTest extends IntegrationTestCase
'purgeOutdatedArchives.',
'purgeInvalidatedArchives.',
'optimizeArchiveTable.',
+ 'cleanupTrackingFailures.',
+ 'notifyTrackingFailures.',
'updateSpammerBlacklist.',
'checkSiteHasTrackedVisits.2',
'checkSiteHasTrackedVisits.3',
@@ -181,6 +186,35 @@ class TasksTest extends IntegrationTestCase
$this->assertEquals($mail->getIdSite(), $idSite);
}
+ public function test_cleanupTrackingFailures_doesNotCauseAnyException()
+ {
+ // it is only calling one method which is already tested... no need to write complex tests for it
+ $this->tasks->cleanupTrackingFailures();
+ $this->assertTrue(true);
+ }
+
+ public function test_notifyTrackingFailures_doesNotSendAnyMailWhenThereAreNoTrackingRequests()
+ {
+ $this->tasks->notifyTrackingFailures();
+ $this->assertNull($this->mail);
+ }
+
+ public function test_notifyTrackingFailures_sendsMailWhenThereAreTrackingFailures()
+ {
+ $failures = new Failures();
+ $failures->logFailure(1, new Request(array('idsite' => 9999, 'rec' => 1)));
+ $failures->logFailure(1, new Request(array('idsite' => 9998, 'rec' => 1)));
+ Fixture::createSuperUser(false);
+ $this->tasks->notifyTrackingFailures();
+
+ /** @var TrackingFailuresEmail $mail */
+ $mail = $this->mail;
+ $this->assertInstanceOf(TrackingFailuresEmail::class, $mail);
+ $this->assertEquals('superUserLogin', $mail->getLogin());
+ $this->assertEquals('hello@example.org', $mail->getEmailAddress());
+ $this->assertEquals(2, $mail->getNumFailures());
+ }
+
/**
* @param Date[] $dates
*/
diff --git a/plugins/CoreAdminHome/tests/System/TrackingFailuresTest.php b/plugins/CoreAdminHome/tests/System/TrackingFailuresTest.php
new file mode 100644
index 0000000000..061c819e60
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/System/TrackingFailuresTest.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CoreAdminHome\tests\System;
+
+use Piwik\Plugins\CoreAdminHome\tests\Fixture\TrackingFailures;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group CoreAdminHome
+ * @group TrackingFailuresTest
+ * @group Plugins
+ */
+class TrackingFailuresTest extends SystemTestCase
+{
+ /**
+ * @var TrackingFailures
+ */
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @dataProvider getApiForTesting
+ */
+ public function testApi($api, $params)
+ {
+ $params['xmlFieldsToRemove'] = array('date_first_occurred', 'pretty_date_first_occurred', 'request_url');
+ $this->runApiTests($api, $params);
+ }
+
+ public function getApiForTesting()
+ {
+ $api = array(
+ 'CoreAdminHome.getTrackingFailures',
+ );
+
+ $apiToTest = array();
+ $apiToTest[] = array($api,
+ array(
+ 'testSuffix' => ''
+ )
+ );
+
+ return $apiToTest;
+ }
+
+ public static function getOutputPrefix()
+ {
+ return '';
+ }
+
+ public static function getPathToTestDirectory()
+ {
+ return dirname(__FILE__);
+ }
+
+}
+
+TrackingFailuresTest::$fixture = new TrackingFailures(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/tests/System/expected/test___CoreAdminHome.getTrackingFailures.xml b/plugins/CoreAdminHome/tests/System/expected/test___CoreAdminHome.getTrackingFailures.xml
new file mode 100644
index 0000000000..511339901f
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/System/expected/test___CoreAdminHome.getTrackingFailures.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <idsite>1</idsite>
+ <idfailure>2</idfailure>
+
+
+ <site_name>Piwik test</site_name>
+
+ <url>http://example.com/piwik/</url>
+ <problem>Request was not authenticated but authentication was required.</problem>
+ <solution>Set or correct a &quot;token_auth&quot; in your tracking request.</solution>
+ <solution_url>https://matomo.org/faq/how-to/faq_30835/</solution_url>
+ </row>
+ <row>
+ <idsite>99999</idsite>
+ <idfailure>1</idfailure>
+
+
+ <site_name>Unknown</site_name>
+
+ <url>http://example.com/piwik/</url>
+ <problem>The site does not exist.</problem>
+ <solution>Update the configured idSite in the tracker.</solution>
+ <solution_url>https://matomo.org/faq/how-to/faq_30838/</solution_url>
+ </row>
+</result> \ No newline at end of file
diff --git a/plugins/CoreAdminHome/tests/UI/TrackingFailures_spec.js b/plugins/CoreAdminHome/tests/UI/TrackingFailures_spec.js
new file mode 100644
index 0000000000..f98f1c0646
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/TrackingFailures_spec.js
@@ -0,0 +1,94 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+describe("TrackingFailures", function () {
+ this.timeout(0);
+
+ var manageUrl = '?module=CoreAdminHome&action=trackingFailures&idSite=1&period=day&date=today';
+ var widgetUrl = '?module=Widgetize&action=iframe&moduleToWidgetize=CoreAdminHome&actionToWidgetize=getTrackingFailures&idSite=1&period=day&date=today&widget=1';
+
+ function captureScreen(done, screenshotName, theTest)
+ {
+ expect.screenshot(screenshotName).to.be.captureSelector('.matomoTrackingFailures', theTest, done);
+ }
+
+ function captureModal(done, screenshotName, theTest)
+ {
+ expect.screenshot(screenshotName).to.be.captureSelector('.modal.open', theTest, done);
+ }
+
+ function generateTrackingFailures()
+ {
+ testEnvironment.generateTrackingFailures = 1;
+ testEnvironment.save();
+ }
+
+ function confirmModal(page)
+ {
+ page.click('.modal.open .modal-footer a:contains(Yes)');
+ }
+
+ afterEach(function () {
+ delete testEnvironment.generateTrackingFailures;
+ testEnvironment.save();
+ });
+
+ it('should show widget with no failures', function (done) {
+ captureScreen(done, 'widget_no_failures', function (page) {
+ page.load(widgetUrl);
+ });
+ });
+
+ it('should show manage page with no failures', function (done) {
+ captureScreen(done, 'manage_no_failures', function (page) {
+ page.load(manageUrl);
+ });
+ });
+
+ it('should show widget with failures', function (done) {
+ generateTrackingFailures();
+ captureScreen(done, 'widget_with_failures', function (page) {
+ generateTrackingFailures();
+ page.load(widgetUrl);
+ });
+ });
+
+ it('should show manage page with failures', function (done) {
+ generateTrackingFailures();
+ captureScreen(done, 'manage_with_failures', function (page) {
+ generateTrackingFailures();
+ page.load(manageUrl);
+ });
+ });
+
+ it('should show ask to confirm delete one', function (done) {
+ captureModal(done, 'manage_with_failures_delete_one_ask_confirmation', function (page) {
+ page.evaluate(function () {
+ $('.matomoTrackingFailures table tbody tr:nth-child(2) .icon-delete').click()
+ });
+ });
+ });
+
+ it('should show delete when confirmed', function (done) {
+ captureScreen(done, 'manage_with_failures_delete_one_confirmed', function (page) {
+ confirmModal(page);
+ });
+ });
+
+ it('should show ask to confirm delete all', function (done) {
+ captureModal(done, 'manage_with_failures_delete_all_ask_confirmation', function (page) {
+ page.click('.matomoTrackingFailures .deleteAllFailures');
+ });
+ });
+
+ it('should show ask to confirm delete one', function (done) {
+ captureScreen(done, 'manage_with_failures_delete_all_confirmed', function (page) {
+ confirmModal(page);
+ });
+ });
+
+}); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_no_failures.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_no_failures.png
new file mode 100644
index 0000000000..3652d932ca
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_no_failures.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:555956386a3960079e98fd0ed955ccf836d5aef1e9883f330ef08e43c496f3ff
+size 25706
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures.png
new file mode 100644
index 0000000000..3920b896ea
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:164fc892e74d90ff316f76cc424dbb00e8af9f8ff5c487deaca4df67fb3b058d
+size 75105
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_ask_confirmation.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_ask_confirmation.png
new file mode 100644
index 0000000000..d087655199
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_ask_confirmation.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a8c736bf8efa2dde7c14609ae473fddd4c09f616cbfa1d794bddcc01d12b707
+size 9554
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_confirmed.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_confirmed.png
new file mode 100644
index 0000000000..3652d932ca
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_all_confirmed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:555956386a3960079e98fd0ed955ccf836d5aef1e9883f330ef08e43c496f3ff
+size 25706
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_ask_confirmation.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_ask_confirmation.png
new file mode 100644
index 0000000000..372005bf86
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_ask_confirmation.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a773a094fe4a81c031a71ad279ccda03cee6f3b8139ed6bbaac27c6ea8ff7c4
+size 9585
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_confirmed.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_confirmed.png
new file mode 100644
index 0000000000..237b4c9a7a
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_manage_with_failures_delete_one_confirmed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:32619750eb571199b8fb5b0761efe64d46f9db3dd090b7f8bbc7a1151aa5e295
+size 48513
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_no_failures.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_no_failures.png
new file mode 100644
index 0000000000..c610efccb2
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_no_failures.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8151ae81cc83466e763346f02b272dc7ec480832d49807f59cfb32a53630da6a
+size 11165
diff --git a/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_with_failures.png b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_with_failures.png
new file mode 100644
index 0000000000..fc40fbe9b1
--- /dev/null
+++ b/plugins/CoreAdminHome/tests/UI/expected-screenshots/TrackingFailures_widget_with_failures.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1a98049c33bb5cab09436985b5cf062a931560028014f8c9f8b1ccc0d50c3af8
+size 12805
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 76e933ae51..ab11a468eb 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -287,6 +287,8 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js";
$jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js";
$jsFiles[] = "plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js";
// we have to load these CorePluginsAdmin files here. If we loaded them in CorePluginsAdmin,
// there would be JS errors as CorePluginsAdmin is loaded first. Meaning it is loaded before
diff --git a/plugins/CoreHome/Tracker/VisitRequestProcessor.php b/plugins/CoreHome/Tracker/VisitRequestProcessor.php
index 033399e196..c2a88e93d4 100644
--- a/plugins/CoreHome/Tracker/VisitRequestProcessor.php
+++ b/plugins/CoreHome/Tracker/VisitRequestProcessor.php
@@ -88,7 +88,10 @@ class VisitRequestProcessor extends RequestProcessor
$visitProperties->setProperty('location_ip', $request->getIp());
$excluded = new VisitExcluded($request);
- if ($excluded->isExcluded()) {
+ $isExcluded = $excluded->isExcluded();
+ $request->setMetadata('CoreHome', 'isVisitExcluded', $isExcluded);
+
+ if ($isExcluded) {
return true;
}
diff --git a/plugins/CustomVariables/tests/UI/expected-screenshots/CustomVariables_link_in_menu.png b/plugins/CustomVariables/tests/UI/expected-screenshots/CustomVariables_link_in_menu.png
index 62e1cb3583..ea9a6fe395 100644
--- a/plugins/CustomVariables/tests/UI/expected-screenshots/CustomVariables_link_in_menu.png
+++ b/plugins/CustomVariables/tests/UI/expected-screenshots/CustomVariables_link_in_menu.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:08217344adea43733531372ee479b5f11dc6f67d7ac40f07284e8feaae5b3c8a
-size 14959
+oid sha256:f7798c4e443bd838de905511c77f82899e44d4d2e990e5623ee2fd0b2a09c79d
+size 16695
diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking
-Subproject 49e30794fcf0a77bac7cef9e5c779e855453549
+Subproject 7609e1bd46b5da8f8e638c01dc71fb782dffdb9
diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php
index 76576f84ed..054ecc2fce 100644
--- a/plugins/SitesManager/SitesManager.php
+++ b/plugins/SitesManager/SitesManager.php
@@ -8,9 +8,11 @@
*/
namespace Piwik\Plugins\SitesManager;
+use Piwik\Access;
use Piwik\API\Request;
use Piwik\Common;
use Piwik\Container\StaticContainer;
+use Piwik\Exception\UnexpectedWebsiteFoundException;
use Piwik\Piwik;
use Piwik\Plugins\CoreHome\SystemSummary;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
@@ -36,7 +38,8 @@ class SitesManager extends \Piwik\Plugin
return array(
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
- 'Tracker.Cache.getSiteAttributes' => 'recordWebsiteDataInCache',
+ 'Tracker.Cache.getSiteAttributes' => array('function' => 'recordWebsiteDataInCache', 'before' => true),
+ 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'SitesManager.deleteSite.end' => 'onSiteDeleted',
'System.addSystemSummaryItems' => 'addSystemSummaryItems',
@@ -145,13 +148,13 @@ class SitesManager extends \Piwik\Plugin
{
$idSite = (int) $idSite;
+ $website = API::getInstance()->getSiteFromId($idSite);
$urls = API::getInstance()->getSiteUrlsFromId($idSite);
// add the 'hosts' entry in the website array
$array['urls'] = $urls;
$array['hosts'] = $this->getTrackerHosts($urls);
- $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);
@@ -165,6 +168,14 @@ class SitesManager extends \Piwik\Plugin
$array['type'] = $website['type'];
}
+ public function setTrackerCacheGeneral(&$cache)
+ {
+ Access::doAsSuperUser(function () use (&$cache) {
+ $cache['global_excluded_user_agents'] = self::filterBlankFromCommaSepList(API::getInstance()->getExcludedUserAgentsGlobal());
+ $cache['global_excluded_ips'] = self::filterBlankFromCommaSepList(API::getInstance()->getExcludedIpsGlobal());
+ });
+ }
+
/**
* Returns whether we should keep URL fragments for a specific site.
*
diff --git a/plugins/Widgetize/tests/System/WidgetTest.php b/plugins/Widgetize/tests/System/WidgetTest.php
index cdf8fe54ff..96b78dd70b 100644
--- a/plugins/Widgetize/tests/System/WidgetTest.php
+++ b/plugins/Widgetize/tests/System/WidgetTest.php
@@ -1016,6 +1016,14 @@ class WidgetTest extends SystemTestCase
'action' => 'getSystemCheck',
),
), array (
+ 'name' => 'Tracking failures',
+ 'uniqueId' => 'widgetCoreAdminHomegetTrackingFailures',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreAdminHome',
+ 'action' => 'getTrackingFailures',
+ ),
+ ), array (
'name' => 'System Summary',
'uniqueId' => 'widgetCoreHomegetSystemSummary',
'parameters' =>