Version Description
- Update Matomo core to 3.13.4
- Fix the website's timezone may be set to UTC instead of the WP timezone
- Improve compatibility with PHP 7.4 by fixing more notices
- Add a review link to the About page
- Add a newsletter signup possibility to the About page.
- Support MaxMind geolocation database
- Better support for hiding login URLs eg with WPS plugin
- Show header icon images
- Update GeoIP DB monthly instead of weekly
- Ask for a review every 90 days unless dismissed
- Possibility to configure proxy client header
See changelog for all versions.
Download this release
Release Info
Developer | matomoteam |
Plugin | Matomo Analytics – Ethical Stats. Powerful Insights. |
Version | 1.0.4 |
Comparing to | |
See all releases |
Code changes from version 1.0.3 to 1.0.4
- app/bootstrap.php +26 -7
- app/core/Access.php +10 -0
- app/core/Archive.php +4 -72
- app/core/Archive/ArchiveInvalidator.php +16 -7
- app/core/ArchiveProcessor/Loader.php +84 -13
- app/core/ArchiveProcessor/Parameters.php +5 -0
- app/core/Config/IniFileChain.php +6 -4
- app/core/Cookie.php +12 -10
- app/core/CronArchive.php +63 -25
- app/core/DataAccess/ArchiveSelector.php +92 -60
- app/core/DataAccess/LogAggregator.php +28 -20
- app/core/DataAccess/Model.php +13 -4
- app/core/Db/Schema/Mysql.php +1 -1
- app/core/DbHelper.php +42 -0
- app/core/FrontController.php +7 -0
- app/core/Http.php +6 -3
- app/core/Period.php +15 -0
- app/core/Period/Factory.php +16 -0
- app/core/Plugin/ControllerAdmin.php +3 -21
- app/core/RankingQuery.php +4 -1
- app/core/Session.php +1 -1
- app/core/Tracker/GoalManager.php +6 -1
- app/core/Tracker/Model.php +11 -4
- app/core/Tracker/Request.php +7 -1
- app/core/Tracker/Visit.php +4 -4
- app/core/Twig.php +1 -1
- app/core/Updates/3.13.4-b1.php +26 -0
- app/core/Version.php +1 -1
- app/core/View.php +3 -0
- app/lang/en.json +4 -1
- app/plugins/CoreAdminHome/Tasks.php +18 -0
- app/plugins/CoreAdminHome/templates/optOut.twig +8 -1
- app/plugins/CoreHome/CoreHome.php +6 -0
- app/plugins/Dashboard/Dashboard.php +1 -5
- app/plugins/Goals/Controller.php +0 -9
- app/plugins/Installation/ServerFilesGenerator.php +7 -1
- app/plugins/Live/Model.php +35 -54
- app/plugins/Marketplace/Api/Client.php +5 -1
- app/plugins/Marketplace/templates/getPremiumFeatures.twig +3 -0
- app/plugins/Monolog/Processor/ExceptionToTextProcessor.php +4 -0
- app/plugins/Proxy/Controller.php +6 -1
- app/plugins/TagManager/API/PreviewCookie.php +2 -2
- app/plugins/TagManager/Context/BaseContext.php +5 -4
- app/plugins/TagManager/Dao/VariablesDao.php +1 -1
- app/plugins/Transitions/API.php +39 -19
- app/plugins/UserCountry/templates/_updaterManage.twig +3 -2
- app/plugins/UserCountry/templates/adminIndex.twig +11 -2
- app/vendor/autoload.php +1 -1
- app/vendor/composer/autoload_classmap.php +1 -0
- app/vendor/composer/autoload_real.php +10 -7
- app/vendor/composer/autoload_static.php +7 -6
- assets/css/admin-style.css +5 -0
- assets/img/activity_log.jpg +0 -0
- assets/img/cohorts.png +0 -0
- assets/img/custom_reports.png +0 -0
- assets/img/form_analytics.jpg +0 -0
- assets/img/funnels.png +0 -0
- assets/img/heatmap.jpg +0 -0
- assets/img/logo-full.png +0 -0
- assets/img/logo.png +0 -0
- assets/img/media_analytics.jpg +0 -0
- assets/img/multi_attribution.png +0 -0
- assets/img/premiumbundle.png +0 -0
- assets/img/search_engine_keywords.png +0 -0
- assets/img/users_flow.png +0 -0
- assets/js/admin.js +7 -0
- assets/js/asset_manager_core_js.js +1 -1
- classes/WpMatomo.php +6 -17
- classes/WpMatomo/Admin/Admin.php +3 -2
- classes/WpMatomo/Admin/AdminSettings.php +10 -4
- classes/WpMatomo/Admin/AdvancedSettings.php +98 -0
- classes/WpMatomo/Admin/GeolocationSettings.php +71 -0
- classes/WpMatomo/Admin/Info.php +43 -2
- classes/WpMatomo/Admin/Menu.php +2 -2
- classes/WpMatomo/Admin/SystemReport.php +17 -2
- classes/WpMatomo/Admin/views/access.php +3 -2
- classes/WpMatomo/Admin/views/advanced_settings.php +59 -0
- classes/WpMatomo/Admin/views/exclusion_settings.php +5 -10
- classes/WpMatomo/Admin/views/geolocation_settings.php +74 -0
- classes/WpMatomo/Admin/views/get_started.php +1 -1
- classes/WpMatomo/Admin/views/info.php +6 -4
- classes/WpMatomo/Admin/views/info_help.php +1 -1
- classes/WpMatomo/Admin/views/info_multisite.php +1 -1
- classes/WpMatomo/Admin/views/info_newsletter.php +46 -0
- classes/WpMatomo/Admin/views/info_shared.php +1 -1
- classes/WpMatomo/Admin/views/marketplace.php +23 -16
- classes/WpMatomo/Admin/views/settings.php +1 -0
- classes/WpMatomo/Admin/views/summary.php +2 -2
- classes/WpMatomo/Admin/views/systemreport.php +2 -0
- classes/WpMatomo/Installer.php +4 -0
- classes/WpMatomo/Referral.php +125 -0
- classes/WpMatomo/ScheduledTasks.php +13 -5
- classes/WpMatomo/Settings.php +1 -0
- classes/WpMatomo/Site/Sync.php +5 -2
- classes/WpMatomo/TrackingCode.php +4 -2
- classes/WpMatomo/Updater.php +1 -1
- classes/WpMatomo/views/referral.php +22 -0
- matomo.php +50 -4
- plugins/WordPress/Controller.php +2 -1
- plugins/WordPress/WordPress.php +18 -1
- plugins/WordPress/WpAssetManager.php +2 -1
- readme.txt +20 -1
app/bootstrap.php
CHANGED
@@ -22,8 +22,7 @@ if ( ! defined( 'PIWIK_ENABLE_ERROR_HANDLER' ) ) {
|
|
22 |
}
|
23 |
}
|
24 |
|
25 |
-
$
|
26 |
-
|
27 |
|
28 |
function matomo_log_message_no_display($message)
|
29 |
{
|
@@ -52,7 +51,8 @@ function matomo_log_message_no_display($message)
|
|
52 |
}
|
53 |
}
|
54 |
|
55 |
-
if ( $
|
|
|
56 |
// prevent from loading twice
|
57 |
$matomo_wpload_base = '../../../../wp-load.php';
|
58 |
$matomo_wpload_full = dirname( __FILE__ ) . '/' . $matomo_wpload_base;
|
@@ -76,8 +76,8 @@ if ( $matomo_was_wp_loaded_directly ) {
|
|
76 |
});
|
77 |
}
|
78 |
|
79 |
-
if (!empty($
|
80 |
-
require_once rtrim($
|
81 |
} elseif ( file_exists($matomo_wpload_full ) ) {
|
82 |
require_once $matomo_wpload_full;
|
83 |
} elseif (realpath( $matomo_wpload_full ) && file_exists(realpath( $matomo_wpload_full ))) {
|
@@ -94,6 +94,25 @@ if ( $matomo_was_wp_loaded_directly ) {
|
|
94 |
}
|
95 |
}
|
96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
if ($matomo_is_archive_request) {
|
98 |
restore_error_handler();
|
99 |
if (ob_get_level()) {
|
@@ -106,7 +125,7 @@ if ( $matomo_was_wp_loaded_directly ) {
|
|
106 |
}
|
107 |
|
108 |
if ( ! defined( 'ABSPATH' ) ) {
|
109 |
-
echo 'Could not find wp-load. If your server uses symlinks or a custom content directory, Matomo may not work for you as we cannot detect the paths correctly. For more information see https://matomo.org/faq/wordpress/
|
110 |
exit; // if accessed directly
|
111 |
}
|
112 |
|
@@ -115,7 +134,7 @@ if ( !is_plugin_active('matomo/matomo.php')
|
|
115 |
exit;
|
116 |
}
|
117 |
|
118 |
-
if ($
|
119 |
// see https://github.com/matomo-org/wp-matomo/issues/190
|
120 |
// wp-external-links plugin would register an ob_start(function () {...}) and manipulate any of our API output
|
121 |
// and in some cases the output would get completely lost causing blank pages.
|
22 |
}
|
23 |
}
|
24 |
|
25 |
+
$GLOBALS['MATOMO_LOADED_DIRECTLY'] = ! defined( 'ABSPATH' );
|
|
|
26 |
|
27 |
function matomo_log_message_no_display($message)
|
28 |
{
|
51 |
}
|
52 |
}
|
53 |
|
54 |
+
if ( $GLOBALS['MATOMO_LOADED_DIRECTLY'] ) {
|
55 |
+
|
56 |
// prevent from loading twice
|
57 |
$matomo_wpload_base = '../../../../wp-load.php';
|
58 |
$matomo_wpload_full = dirname( __FILE__ ) . '/' . $matomo_wpload_base;
|
76 |
});
|
77 |
}
|
78 |
|
79 |
+
if (!empty($_SERVER['MATOMO_WP_ROOT_PATH']) && file_exists( rtrim($_SERVER['MATOMO_WP_ROOT_PATH'], '/') . '/wp-load.php')) {
|
80 |
+
require_once rtrim($_SERVER['MATOMO_WP_ROOT_PATH'], '/') . '/wp-load.php';
|
81 |
} elseif ( file_exists($matomo_wpload_full ) ) {
|
82 |
require_once $matomo_wpload_full;
|
83 |
} elseif (realpath( $matomo_wpload_full ) && file_exists(realpath( $matomo_wpload_full ))) {
|
94 |
}
|
95 |
}
|
96 |
|
97 |
+
if (!defined( 'ABSPATH')) {
|
98 |
+
// still not loaded... look in plugins directory if there is a config file for us.
|
99 |
+
$matomo_wpload_config = dirname(__FILE__) . '/../../matomo.wpload_dir.php';
|
100 |
+
if (file_exists( $matomo_wpload_config) && is_readable($matomo_wpload_config)) {
|
101 |
+
$matomo_wpload_content = @file_get_contents($matomo_wpload_config); // we do not include that file for security reasons
|
102 |
+
if (!empty($matomo_wpload_content)) {
|
103 |
+
$matomo_wpload_content = str_replace(array('<?php', 'exit;', 'wp-load.php'), '', $matomo_wpload_content);
|
104 |
+
$matomo_wpload_content = preg_replace('/\s/', '', $matomo_wpload_content);
|
105 |
+
$matomo_wpload_content = trim(ltrim(trim($matomo_wpload_content), '#')); // the path may be commented out # /abs/path
|
106 |
+
if (strpos($matomo_wpload_content, DIRECTORY_SEPARATOR) === 0) {
|
107 |
+
$matomo_wpload_file = rtrim($matomo_wpload_content, DIRECTORY_SEPARATOR) . '/wp-load.php';
|
108 |
+
if (file_exists($matomo_wpload_file) && is_readable($matomo_wpload_file)) {
|
109 |
+
require_once $matomo_wpload_file;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
}
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
if ($matomo_is_archive_request) {
|
117 |
restore_error_handler();
|
118 |
if (ob_get_level()) {
|
125 |
}
|
126 |
|
127 |
if ( ! defined( 'ABSPATH' ) ) {
|
128 |
+
echo 'Could not find wp-load. If your server uses symlinks or a custom content directory, Matomo may not work for you as we cannot detect the paths correctly. For more information see https://matomo.org/faq/wordpress/how-do-i-make-matomo-for-wordpress-work-when-i-have-a-custom-content-directory/';
|
129 |
exit; // if accessed directly
|
130 |
}
|
131 |
|
134 |
exit;
|
135 |
}
|
136 |
|
137 |
+
if ( $GLOBALS['MATOMO_LOADED_DIRECTLY'] ) {
|
138 |
// see https://github.com/matomo-org/wp-matomo/issues/190
|
139 |
// wp-external-links plugin would register an ob_start(function () {...}) and manipulate any of our API output
|
140 |
// and in some cases the output would get completely lost causing blank pages.
|
app/core/Access.php
CHANGED
@@ -727,6 +727,16 @@ class Access
|
|
727 |
|
728 |
throw new NoAccessException($message);
|
729 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
730 |
}
|
731 |
|
732 |
/**
|
727 |
|
728 |
throw new NoAccessException($message);
|
729 |
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Returns true if the current user is logged in or not.
|
733 |
+
*
|
734 |
+
* @return bool
|
735 |
+
*/
|
736 |
+
public function isUserLoggedIn()
|
737 |
+
{
|
738 |
+
return !empty($this->login);
|
739 |
+
}
|
740 |
}
|
741 |
|
742 |
/**
|
app/core/Archive.php
CHANGED
@@ -12,7 +12,6 @@ use Piwik\Archive\ArchiveQuery;
|
|
12 |
use Piwik\Archive\ArchiveQueryFactory;
|
13 |
use Piwik\Archive\Parameters;
|
14 |
use Piwik\ArchiveProcessor\Rules;
|
15 |
-
use Piwik\Archive\ArchiveInvalidator;
|
16 |
use Piwik\Container\StaticContainer;
|
17 |
use Piwik\DataAccess\ArchiveSelector;
|
18 |
|
@@ -167,11 +166,6 @@ class Archive implements ArchiveQuery
|
|
167 |
*/
|
168 |
private static $cache;
|
169 |
|
170 |
-
/**
|
171 |
-
* @var ArchiveInvalidator
|
172 |
-
*/
|
173 |
-
private $invalidator;
|
174 |
-
|
175 |
/**
|
176 |
* @param Parameters $params
|
177 |
* @param bool $forceIndexedBySite Whether to force index the result of a query by site ID.
|
@@ -183,8 +177,6 @@ class Archive implements ArchiveQuery
|
|
183 |
$this->params = $params;
|
184 |
$this->forceIndexedBySite = $forceIndexedBySite;
|
185 |
$this->forceIndexedByDate = $forceIndexedByDate;
|
186 |
-
|
187 |
-
$this->invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator');
|
188 |
}
|
189 |
|
190 |
/**
|
@@ -453,67 +445,6 @@ class Archive implements ArchiveQuery
|
|
453 |
return $dataTable;
|
454 |
}
|
455 |
|
456 |
-
private function getSiteIdsThatAreRequestedInThisArchiveButWereNotInvalidatedYet()
|
457 |
-
{
|
458 |
-
if (is_null(self::$cache)) {
|
459 |
-
self::$cache = Cache::getTransientCache();
|
460 |
-
}
|
461 |
-
|
462 |
-
$id = 'Archive.SiteIdsOfRememberedReportsInvalidated';
|
463 |
-
|
464 |
-
if (!self::$cache->contains($id)) {
|
465 |
-
self::$cache->save($id, array());
|
466 |
-
}
|
467 |
-
|
468 |
-
$siteIdsAlreadyHandled = self::$cache->fetch($id);
|
469 |
-
$siteIdsRequested = $this->params->getIdSites();
|
470 |
-
|
471 |
-
foreach ($siteIdsRequested as $index => $siteIdRequested) {
|
472 |
-
$siteIdRequested = (int) $siteIdRequested;
|
473 |
-
|
474 |
-
if (in_array($siteIdRequested, $siteIdsAlreadyHandled)) {
|
475 |
-
unset($siteIdsRequested[$index]); // was already handled previously, do not do it again
|
476 |
-
} else {
|
477 |
-
$siteIdsAlreadyHandled[] = $siteIdRequested; // we will handle this id this time
|
478 |
-
}
|
479 |
-
}
|
480 |
-
|
481 |
-
self::$cache->save($id, $siteIdsAlreadyHandled);
|
482 |
-
|
483 |
-
return $siteIdsRequested;
|
484 |
-
}
|
485 |
-
|
486 |
-
private function invalidatedReportsIfNeeded()
|
487 |
-
{
|
488 |
-
$siteIdsRequested = $this->getSiteIdsThatAreRequestedInThisArchiveButWereNotInvalidatedYet();
|
489 |
-
|
490 |
-
if (empty($siteIdsRequested)) {
|
491 |
-
return; // all requested site ids were already handled
|
492 |
-
}
|
493 |
-
|
494 |
-
$sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated();
|
495 |
-
|
496 |
-
foreach ($sitesPerDays as $date => $siteIds) {
|
497 |
-
if (empty($siteIds)) {
|
498 |
-
continue;
|
499 |
-
}
|
500 |
-
|
501 |
-
$siteIdsToActuallyInvalidate = array_intersect($siteIds, $siteIdsRequested);
|
502 |
-
|
503 |
-
if (empty($siteIdsToActuallyInvalidate)) {
|
504 |
-
continue; // all site ids that should be handled are already handled
|
505 |
-
}
|
506 |
-
|
507 |
-
try {
|
508 |
-
$this->invalidator->markArchivesAsInvalidated($siteIdsToActuallyInvalidate, array(Date::factory($date)), false);
|
509 |
-
} catch (\Exception $e) {
|
510 |
-
Site::clearCache();
|
511 |
-
throw $e;
|
512 |
-
}
|
513 |
-
}
|
514 |
-
|
515 |
-
Site::clearCache();
|
516 |
-
}
|
517 |
|
518 |
/**
|
519 |
* Queries archive tables for data and returns the result.
|
@@ -638,8 +569,6 @@ class Archive implements ArchiveQuery
|
|
638 |
*/
|
639 |
private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins)
|
640 |
{
|
641 |
-
$this->invalidatedReportsIfNeeded();
|
642 |
-
|
643 |
$today = Date::today();
|
644 |
|
645 |
foreach ($this->params->getPeriods() as $period) {
|
@@ -856,8 +785,11 @@ class Archive implements ArchiveQuery
|
|
856 |
*/
|
857 |
private function prepareArchive(array $archiveGroups, Site $site, Period $period)
|
858 |
{
|
|
|
|
|
|
|
859 |
$parameters = new ArchiveProcessor\Parameters($site, $period, $this->params->getSegment());
|
860 |
-
$archiveLoader = new ArchiveProcessor\Loader($parameters);
|
861 |
|
862 |
$periodString = $period->getRangeString();
|
863 |
|
12 |
use Piwik\Archive\ArchiveQueryFactory;
|
13 |
use Piwik\Archive\Parameters;
|
14 |
use Piwik\ArchiveProcessor\Rules;
|
|
|
15 |
use Piwik\Container\StaticContainer;
|
16 |
use Piwik\DataAccess\ArchiveSelector;
|
17 |
|
166 |
*/
|
167 |
private static $cache;
|
168 |
|
|
|
|
|
|
|
|
|
|
|
169 |
/**
|
170 |
* @param Parameters $params
|
171 |
* @param bool $forceIndexedBySite Whether to force index the result of a query by site ID.
|
177 |
$this->params = $params;
|
178 |
$this->forceIndexedBySite = $forceIndexedBySite;
|
179 |
$this->forceIndexedByDate = $forceIndexedByDate;
|
|
|
|
|
180 |
}
|
181 |
|
182 |
/**
|
445 |
return $dataTable;
|
446 |
}
|
447 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
448 |
|
449 |
/**
|
450 |
* Queries archive tables for data and returns the result.
|
569 |
*/
|
570 |
private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins)
|
571 |
{
|
|
|
|
|
572 |
$today = Date::today();
|
573 |
|
574 |
foreach ($this->params->getPeriods() as $period) {
|
785 |
*/
|
786 |
private function prepareArchive(array $archiveGroups, Site $site, Period $period)
|
787 |
{
|
788 |
+
// if cron archiving is running, we will invalidate in CronArchive, not here
|
789 |
+
$invalidateBeforeArchiving = !SettingsServer::isArchivePhpTriggered();
|
790 |
+
|
791 |
$parameters = new ArchiveProcessor\Parameters($site, $period, $this->params->getSegment());
|
792 |
+
$archiveLoader = new ArchiveProcessor\Loader($parameters, $invalidateBeforeArchiving);
|
793 |
|
794 |
$periodString = $period->getRangeString();
|
795 |
|
app/core/Archive/ArchiveInvalidator.php
CHANGED
@@ -78,11 +78,12 @@ class ArchiveInvalidator
|
|
78 |
// we do not really have to get the value first. we could simply always try to call set() and it would update or
|
79 |
// insert the record if needed but we do not want to lock the table (especially since there are still some
|
80 |
// MyISAM installations)
|
81 |
-
$values = Option::getLike($this->rememberArchivedReportIdStart . '%');
|
82 |
|
83 |
$all = [];
|
84 |
foreach ($values as $name => $value) {
|
85 |
-
$suffix = substr($name,
|
|
|
86 |
list($idSite, $dateStr) = explode('_', $suffix);
|
87 |
|
88 |
$all[$idSite][$dateStr] = $value;
|
@@ -102,7 +103,7 @@ class ArchiveInvalidator
|
|
102 |
// we do not really have to get the value first. we could simply always try to call set() and it would update or
|
103 |
// insert the record if needed but we do not want to lock the table (especially since there are still some
|
104 |
// MyISAM installations)
|
105 |
-
$value = Option::getLike($key . '%');
|
106 |
}
|
107 |
|
108 |
// getLike() returns an empty array rather than 'false'
|
@@ -117,6 +118,7 @@ class ArchiveInvalidator
|
|
117 |
$mykey = $this->buildRememberArchivedReportIdProcessSafe($idSite, $date->toString());
|
118 |
Option::set($mykey, '1');
|
119 |
Cache::clearCacheGeneral();
|
|
|
120 |
}
|
121 |
}
|
122 |
|
@@ -134,16 +136,21 @@ class ArchiveInvalidator
|
|
134 |
|
135 |
public function getRememberedArchivedReportsThatShouldBeInvalidated()
|
136 |
{
|
137 |
-
$reports = Option::getLike($this->rememberArchivedReportIdStart . '%_%');
|
138 |
|
139 |
$sitesPerDay = array();
|
140 |
|
141 |
foreach ($reports as $report => $value) {
|
|
|
142 |
$report = str_replace($this->rememberArchivedReportIdStart, '', $report);
|
143 |
$report = explode('_', $report);
|
144 |
$siteId = (int) $report[0];
|
145 |
$date = $report[1];
|
146 |
|
|
|
|
|
|
|
|
|
147 |
if (empty($sitesPerDay[$date])) {
|
148 |
$sitesPerDay[$date] = array();
|
149 |
}
|
@@ -170,14 +177,16 @@ class ArchiveInvalidator
|
|
170 |
// This version is multi process safe on the insert of a new date to invalidate.
|
171 |
private function buildRememberArchivedReportIdProcessSafe($idSite, $date)
|
172 |
{
|
173 |
-
$id
|
|
|
174 |
$id .= '_' . Common::getProcessId();
|
|
|
175 |
return $id;
|
176 |
}
|
177 |
|
178 |
public function forgetRememberedArchivedReportsToInvalidateForSite($idSite)
|
179 |
{
|
180 |
-
$id = $this->buildRememberArchivedReportIdForSite($idSite)
|
181 |
$this->deleteOptionLike($id);
|
182 |
Cache::clearCacheGeneral();
|
183 |
}
|
@@ -201,7 +210,7 @@ class ArchiveInvalidator
|
|
201 |
{
|
202 |
// we're not using deleteLike since it maybe could cause deadlocks see https://github.com/matomo-org/matomo/issues/15545
|
203 |
// we want to reduce number of rows scanned and only delete specific primary key
|
204 |
-
$keys = Option::getLike($id . '%');
|
205 |
|
206 |
if (empty($keys)) {
|
207 |
return;
|
78 |
// we do not really have to get the value first. we could simply always try to call set() and it would update or
|
79 |
// insert the record if needed but we do not want to lock the table (especially since there are still some
|
80 |
// MyISAM installations)
|
81 |
+
$values = Option::getLike('%' . $this->rememberArchivedReportIdStart . '%');
|
82 |
|
83 |
$all = [];
|
84 |
foreach ($values as $name => $value) {
|
85 |
+
$suffix = substr($name, strpos($name, $this->rememberArchivedReportIdStart));
|
86 |
+
$suffix = str_replace($this->rememberArchivedReportIdStart, '', $suffix);
|
87 |
list($idSite, $dateStr) = explode('_', $suffix);
|
88 |
|
89 |
$all[$idSite][$dateStr] = $value;
|
103 |
// we do not really have to get the value first. we could simply always try to call set() and it would update or
|
104 |
// insert the record if needed but we do not want to lock the table (especially since there are still some
|
105 |
// MyISAM installations)
|
106 |
+
$value = Option::getLike('%' . $key . '%');
|
107 |
}
|
108 |
|
109 |
// getLike() returns an empty array rather than 'false'
|
118 |
$mykey = $this->buildRememberArchivedReportIdProcessSafe($idSite, $date->toString());
|
119 |
Option::set($mykey, '1');
|
120 |
Cache::clearCacheGeneral();
|
121 |
+
return $mykey;
|
122 |
}
|
123 |
}
|
124 |
|
136 |
|
137 |
public function getRememberedArchivedReportsThatShouldBeInvalidated()
|
138 |
{
|
139 |
+
$reports = Option::getLike('%' . $this->rememberArchivedReportIdStart . '%_%');
|
140 |
|
141 |
$sitesPerDay = array();
|
142 |
|
143 |
foreach ($reports as $report => $value) {
|
144 |
+
$report = substr($report, strpos($report, $this->rememberArchivedReportIdStart));
|
145 |
$report = str_replace($this->rememberArchivedReportIdStart, '', $report);
|
146 |
$report = explode('_', $report);
|
147 |
$siteId = (int) $report[0];
|
148 |
$date = $report[1];
|
149 |
|
150 |
+
if (empty($siteId)) {
|
151 |
+
continue;
|
152 |
+
}
|
153 |
+
|
154 |
if (empty($sitesPerDay[$date])) {
|
155 |
$sitesPerDay[$date] = array();
|
156 |
}
|
177 |
// This version is multi process safe on the insert of a new date to invalidate.
|
178 |
private function buildRememberArchivedReportIdProcessSafe($idSite, $date)
|
179 |
{
|
180 |
+
$id = Common::getRandomString(4, 'abcdefghijklmnoprstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') . '_';
|
181 |
+
$id .= $this->buildRememberArchivedReportIdForSiteAndDate($idSite, $date);
|
182 |
$id .= '_' . Common::getProcessId();
|
183 |
+
|
184 |
return $id;
|
185 |
}
|
186 |
|
187 |
public function forgetRememberedArchivedReportsToInvalidateForSite($idSite)
|
188 |
{
|
189 |
+
$id = $this->buildRememberArchivedReportIdForSite($idSite);
|
190 |
$this->deleteOptionLike($id);
|
191 |
Cache::clearCacheGeneral();
|
192 |
}
|
210 |
{
|
211 |
// we're not using deleteLike since it maybe could cause deadlocks see https://github.com/matomo-org/matomo/issues/15545
|
212 |
// we want to reduce number of rows scanned and only delete specific primary key
|
213 |
+
$keys = Option::getLike('%' . $id . '%');
|
214 |
|
215 |
if (empty($keys)) {
|
216 |
return;
|
app/core/ArchiveProcessor/Loader.php
CHANGED
@@ -8,13 +8,18 @@
|
|
8 |
*/
|
9 |
namespace Piwik\ArchiveProcessor;
|
10 |
|
|
|
11 |
use Piwik\Cache;
|
12 |
use Piwik\Config;
|
13 |
use Piwik\Container\StaticContainer;
|
14 |
use Piwik\Context;
|
15 |
use Piwik\DataAccess\ArchiveSelector;
|
|
|
16 |
use Piwik\Date;
|
|
|
17 |
use Piwik\Piwik;
|
|
|
|
|
18 |
|
19 |
/**
|
20 |
* This class uses PluginsArchiver class to trigger data aggregation and create archives.
|
@@ -33,9 +38,28 @@ class Loader
|
|
33 |
*/
|
34 |
protected $params;
|
35 |
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
{
|
38 |
$this->params = $params;
|
|
|
|
|
|
|
|
|
39 |
}
|
40 |
|
41 |
/**
|
@@ -65,11 +89,19 @@ class Loader
|
|
65 |
{
|
66 |
$this->params->setRequestedPlugin($pluginName);
|
67 |
|
68 |
-
list($idArchive, $visits, $visitsConverted) = $this->loadExistingArchiveIdFromDb();
|
69 |
-
if (!empty($idArchive)) {
|
70 |
return $idArchive;
|
71 |
}
|
72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
/** @var ArchivingStatus $archivingStatus */
|
74 |
$archivingStatus = StaticContainer::get(ArchivingStatus::class);
|
75 |
$archivingStatus->archiveStarted($this->params);
|
@@ -170,20 +202,17 @@ class Loader
|
|
170 |
*/
|
171 |
public function loadExistingArchiveIdFromDb()
|
172 |
{
|
173 |
-
$noArchiveFound = array(false, false, false);
|
174 |
-
|
175 |
if ($this->isArchivingForcedToTrigger()) {
|
176 |
-
|
177 |
-
}
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
if (!$idAndVisits) {
|
183 |
-
return $noArchiveFound;
|
184 |
}
|
185 |
|
186 |
-
|
|
|
|
|
187 |
}
|
188 |
|
189 |
/**
|
@@ -242,4 +271,46 @@ class Loader
|
|
242 |
|
243 |
return $cache->fetch($cacheKey);
|
244 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
}
|
8 |
*/
|
9 |
namespace Piwik\ArchiveProcessor;
|
10 |
|
11 |
+
use Piwik\Archive\ArchiveInvalidator;
|
12 |
use Piwik\Cache;
|
13 |
use Piwik\Config;
|
14 |
use Piwik\Container\StaticContainer;
|
15 |
use Piwik\Context;
|
16 |
use Piwik\DataAccess\ArchiveSelector;
|
17 |
+
use Piwik\DataAccess\ArchiveTableCreator;
|
18 |
use Piwik\Date;
|
19 |
+
use Piwik\Db;
|
20 |
use Piwik\Piwik;
|
21 |
+
use Piwik\Site;
|
22 |
+
use Psr\Log\LoggerInterface;
|
23 |
|
24 |
/**
|
25 |
* This class uses PluginsArchiver class to trigger data aggregation and create archives.
|
38 |
*/
|
39 |
protected $params;
|
40 |
|
41 |
+
/**
|
42 |
+
* @var ArchiveInvalidator
|
43 |
+
*/
|
44 |
+
private $invalidator;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var \Piwik\Cache\Cache
|
48 |
+
*/
|
49 |
+
private $cache;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var LoggerInterface
|
53 |
+
*/
|
54 |
+
private $logger;
|
55 |
+
|
56 |
+
public function __construct(Parameters $params, $invalidateBeforeArchiving = false)
|
57 |
{
|
58 |
$this->params = $params;
|
59 |
+
$this->invalidateBeforeArchiving = $invalidateBeforeArchiving;
|
60 |
+
$this->invalidator = StaticContainer::get(ArchiveInvalidator::class);
|
61 |
+
$this->cache = Cache::getTransientCache();
|
62 |
+
$this->logger = StaticContainer::get(LoggerInterface::class);
|
63 |
}
|
64 |
|
65 |
/**
|
89 |
{
|
90 |
$this->params->setRequestedPlugin($pluginName);
|
91 |
|
92 |
+
list($idArchive, $visits, $visitsConverted, $isAnyArchiveExists) = $this->loadExistingArchiveIdFromDb();
|
93 |
+
if (!empty($idArchive)) { // we have a usable idarchive (it's not invalidated and it's new enough)
|
94 |
return $idArchive;
|
95 |
}
|
96 |
|
97 |
+
// if there is an archive, but we can't use it for some reason, invalidate existing archives before
|
98 |
+
// we start archiving. if the archive is made invalid, we will correctly re-archive below.
|
99 |
+
if ($this->invalidateBeforeArchiving
|
100 |
+
&& $isAnyArchiveExists
|
101 |
+
) {
|
102 |
+
$this->invalidatedReportsIfNeeded();
|
103 |
+
}
|
104 |
+
|
105 |
/** @var ArchivingStatus $archivingStatus */
|
106 |
$archivingStatus = StaticContainer::get(ArchivingStatus::class);
|
107 |
$archivingStatus->archiveStarted($this->params);
|
202 |
*/
|
203 |
public function loadExistingArchiveIdFromDb()
|
204 |
{
|
|
|
|
|
205 |
if ($this->isArchivingForcedToTrigger()) {
|
206 |
+
$this->logger->debug("Archiving forced to trigger for {$this->params}.");
|
|
|
207 |
|
208 |
+
// return no usable archive found, and no existing archive. this will skip invalidation, which should
|
209 |
+
// be fine since we just force archiving.
|
210 |
+
return [false, false, false, false];
|
|
|
|
|
211 |
}
|
212 |
|
213 |
+
$minDatetimeArchiveProcessedUTC = $this->getMinTimeArchiveProcessed();
|
214 |
+
$result = ArchiveSelector::getArchiveIdAndVisits($this->params, $minDatetimeArchiveProcessedUTC);
|
215 |
+
return $result;
|
216 |
}
|
217 |
|
218 |
/**
|
271 |
|
272 |
return $cache->fetch($cacheKey);
|
273 |
}
|
274 |
+
|
275 |
+
// public for tests
|
276 |
+
public function getReportsToInvalidate()
|
277 |
+
{
|
278 |
+
$sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated();
|
279 |
+
|
280 |
+
foreach ($sitesPerDays as $dateStr => $siteIds) {
|
281 |
+
if (empty($siteIds)
|
282 |
+
|| !in_array($this->params->getSite()->getId(), $siteIds)
|
283 |
+
) {
|
284 |
+
unset($sitesPerDays[$dateStr]);
|
285 |
+
}
|
286 |
+
|
287 |
+
$date = Date::factory($dateStr);
|
288 |
+
if ($date->isEarlier($this->params->getPeriod()->getDateStart())
|
289 |
+
|| $date->isLater($this->params->getPeriod()->getDateEnd())
|
290 |
+
) { // date in list is not the current date, so ignore it
|
291 |
+
unset($sitesPerDays[$dateStr]);
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
return $sitesPerDays;
|
296 |
+
}
|
297 |
+
|
298 |
+
private function invalidatedReportsIfNeeded()
|
299 |
+
{
|
300 |
+
$sitesPerDays = $this->getReportsToInvalidate();
|
301 |
+
if (empty($sitesPerDays)) {
|
302 |
+
return;
|
303 |
+
}
|
304 |
+
|
305 |
+
foreach ($sitesPerDays as $date => $siteIds) {
|
306 |
+
try {
|
307 |
+
$this->invalidator->markArchivesAsInvalidated([$this->params->getSite()->getId()], array(Date::factory($date)), false, $this->params->getSegment());
|
308 |
+
} catch (\Exception $e) {
|
309 |
+
Site::clearCache();
|
310 |
+
throw $e;
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
Site::clearCache();
|
315 |
+
}
|
316 |
}
|
app/core/ArchiveProcessor/Parameters.php
CHANGED
@@ -258,4 +258,9 @@ class Parameters
|
|
258 |
{
|
259 |
$this->isRootArchiveRequest = $isRootArchiveRequest;
|
260 |
}
|
|
|
|
|
|
|
|
|
|
|
261 |
}
|
258 |
{
|
259 |
$this->isRootArchiveRequest = $isRootArchiveRequest;
|
260 |
}
|
261 |
+
|
262 |
+
public function __toString()
|
263 |
+
{
|
264 |
+
return "[idSite = {$this->getSite()->getId()}, period = {$this->getPeriod()->getLabel()} {$this->getPeriod()->getRangeString()}, segment = {$this->getSegment()->getString()}]";
|
265 |
+
}
|
266 |
}
|
app/core/Config/IniFileChain.php
CHANGED
@@ -210,13 +210,16 @@ class IniFileChain
|
|
210 |
$this->resetSettingsChain($defaultSettingsFiles, $userSettingsFile);
|
211 |
}
|
212 |
|
213 |
-
|
|
|
|
|
|
|
214 |
$cache = new Cache();
|
215 |
$values = $cache->doFetch(self::CONFIG_CACHE_KEY);
|
216 |
|
217 |
if (!empty($values)
|
218 |
&& isset($values['mergedSettings'])
|
219 |
-
&& isset($values['settingsChain'])) {
|
220 |
$this->mergedSettings = $values['mergedSettings'];
|
221 |
$this->settingsChain = $values['settingsChain'];
|
222 |
return;
|
@@ -246,8 +249,7 @@ class IniFileChain
|
|
246 |
$this->mergedSettings = call_user_func($GLOBALS['MATOMO_MODIFY_CONFIG_SETTINGS'], $this->mergedSettings);
|
247 |
}
|
248 |
|
249 |
-
if (
|
250 |
-
&& !empty($userSettingsFile)
|
251 |
&& !empty($this->mergedSettings)
|
252 |
&& !empty($this->settingsChain)) {
|
253 |
|
210 |
$this->resetSettingsChain($defaultSettingsFiles, $userSettingsFile);
|
211 |
}
|
212 |
|
213 |
+
$hasAbsoluteConfigFile = !empty($userSettingsFile) && strpos($userSettingsFile, DIRECTORY_SEPARATOR) === 0;
|
214 |
+
$useConfigCache = !empty($GLOBALS['ENABLE_CONFIG_PHP_CACHE']) && $hasAbsoluteConfigFile;
|
215 |
+
|
216 |
+
if ($useConfigCache) {
|
217 |
$cache = new Cache();
|
218 |
$values = $cache->doFetch(self::CONFIG_CACHE_KEY);
|
219 |
|
220 |
if (!empty($values)
|
221 |
&& isset($values['mergedSettings'])
|
222 |
+
&& isset($values['settingsChain'][$userSettingsFile])) {
|
223 |
$this->mergedSettings = $values['mergedSettings'];
|
224 |
$this->settingsChain = $values['settingsChain'];
|
225 |
return;
|
249 |
$this->mergedSettings = call_user_func($GLOBALS['MATOMO_MODIFY_CONFIG_SETTINGS'], $this->mergedSettings);
|
250 |
}
|
251 |
|
252 |
+
if ($useConfigCache
|
|
|
253 |
&& !empty($this->mergedSettings)
|
254 |
&& !empty($this->settingsChain)) {
|
255 |
|
app/core/Cookie.php
CHANGED
@@ -441,16 +441,18 @@ class Cookie
|
|
441 |
$sameSite = ucfirst(strtolower($default));
|
442 |
|
443 |
if ($sameSite == 'None') {
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
$
|
|
|
|
|
454 |
}
|
455 |
}
|
456 |
|
441 |
$sameSite = ucfirst(strtolower($default));
|
442 |
|
443 |
if ($sameSite == 'None') {
|
444 |
+
if ((!ProxyHttp::isHttps())) {
|
445 |
+
$sameSite = 'Lax'; // None can be only used when secure flag will be set
|
446 |
+
} else {
|
447 |
+
$userAgent = Http::getUserAgent();
|
448 |
+
$ddFactory = StaticContainer::get(\Piwik\DeviceDetector\DeviceDetectorFactory::class);
|
449 |
+
$deviceDetector = $ddFactory->makeInstance($userAgent);
|
450 |
+
$deviceDetector->parse();
|
451 |
+
|
452 |
+
$browserFamily = \DeviceDetector\Parser\Client\Browser::getBrowserFamily($deviceDetector->getClient('short_name'));
|
453 |
+
if ($browserFamily === 'Safari') {
|
454 |
+
$sameSite = '';
|
455 |
+
}
|
456 |
}
|
457 |
}
|
458 |
|
app/core/CronArchive.php
CHANGED
@@ -898,6 +898,8 @@ class CronArchive
|
|
898 |
|
899 |
$visitsLastDays = 0;
|
900 |
|
|
|
|
|
901 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, 'day', $date, $segment = '');
|
902 |
if ($isThereArchive) {
|
903 |
$visitsToday = Archive::build($idSite, 'day', $date)->getNumeric('nb_visits');
|
@@ -972,7 +974,8 @@ class CronArchive
|
|
972 |
return $dayArchiveWasSuccessful;
|
973 |
}
|
974 |
|
975 |
-
|
|
|
976 |
{
|
977 |
if (Range::isMultiplePeriod($date, $period)) {
|
978 |
$rangePeriod = Factory::build($period, $date, Site::getTimezoneFor($idSite));
|
@@ -981,9 +984,17 @@ class CronArchive
|
|
981 |
$periodsToCheck = [Factory::build($period, $date, Site::getTimezoneFor($idSite))];
|
982 |
}
|
983 |
|
984 |
-
$
|
|
|
985 |
|
986 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
987 |
|
988 |
$archiveIds = ArchiveSelector::getArchiveIds(
|
989 |
[$idSite], $periodsToCheck, new Segment($segment, [$idSite]), $plugins = [], // empty plugins param since we only check for an 'all' archive
|
@@ -1003,36 +1014,59 @@ class CronArchive
|
|
1003 |
// if there is an invalidated archive within the range, find out the oldest one and how far it is from today,
|
1004 |
// and change the lastN $date to be value so it is correctly re-processed.
|
1005 |
$newDate = $date;
|
1006 |
-
if (
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
}
|
1022 |
-
});
|
1023 |
|
1024 |
-
|
1025 |
-
|
1026 |
|
1027 |
-
|
1028 |
-
|
1029 |
|
1030 |
-
|
|
|
|
|
|
|
|
|
1031 |
}
|
1032 |
|
1033 |
return [$isThereArchiveForAllPeriods, $newDate];
|
1034 |
}
|
1035 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1036 |
/**
|
1037 |
* @param $idSite
|
1038 |
* @return array
|
@@ -1102,6 +1136,8 @@ class CronArchive
|
|
1102 |
return Request::ABORT;
|
1103 |
}
|
1104 |
|
|
|
|
|
1105 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, $period, $date, $segment);
|
1106 |
if ($isThereArchive) {
|
1107 |
$this->logArchiveWebsiteSkippedValidArchiveExists($idSite, $period, $date);
|
@@ -1979,6 +2015,8 @@ class CronArchive
|
|
1979 |
return Request::ABORT;
|
1980 |
}
|
1981 |
|
|
|
|
|
1982 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, $period, $date, $segment);
|
1983 |
if ($isThereArchive) {
|
1984 |
$this->logArchiveWebsiteSkippedValidArchiveExists($idSite, $period, $date, $segment);
|
898 |
|
899 |
$visitsLastDays = 0;
|
900 |
|
901 |
+
$this->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain();
|
902 |
+
|
903 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, 'day', $date, $segment = '');
|
904 |
if ($isThereArchive) {
|
905 |
$visitsToday = Archive::build($idSite, 'day', $date)->getNumeric('nb_visits');
|
974 |
return $dayArchiveWasSuccessful;
|
975 |
}
|
976 |
|
977 |
+
// public for tests
|
978 |
+
public function isThereAValidArchiveForPeriod($idSite, $period, $date, $segment = '')
|
979 |
{
|
980 |
if (Range::isMultiplePeriod($date, $period)) {
|
981 |
$rangePeriod = Factory::build($period, $date, Site::getTimezoneFor($idSite));
|
984 |
$periodsToCheck = [Factory::build($period, $date, Site::getTimezoneFor($idSite))];
|
985 |
}
|
986 |
|
987 |
+
$isTodayIncluded = $this->isTodayIncludedInPeriod($idSite, $periodsToCheck);
|
988 |
+
$isLast = preg_match('/^last([0-9]+)/', $date, $matches);
|
989 |
|
990 |
+
// don't do this check for a single period that includes today
|
991 |
+
if ($isTodayIncluded
|
992 |
+
&& !$isLast
|
993 |
+
) {
|
994 |
+
return [false, $date];
|
995 |
+
}
|
996 |
+
|
997 |
+
$periodsToCheckRanges = array_map(function (Period $p) { return $p->getRangeString(); }, $periodsToCheck);
|
998 |
|
999 |
$archiveIds = ArchiveSelector::getArchiveIds(
|
1000 |
[$idSite], $periodsToCheck, new Segment($segment, [$idSite]), $plugins = [], // empty plugins param since we only check for an 'all' archive
|
1014 |
// if there is an invalidated archive within the range, find out the oldest one and how far it is from today,
|
1015 |
// and change the lastN $date to be value so it is correctly re-processed.
|
1016 |
$newDate = $date;
|
1017 |
+
if ($isLast) {
|
1018 |
+
if (!$isThereArchiveForAllPeriods) {
|
1019 |
+
$lastNValue = (int)$matches[1];
|
1020 |
+
|
1021 |
+
usort($diff, function ($lhs, $rhs) {
|
1022 |
+
$lhsDate = explode(',', $lhs)[0];
|
1023 |
+
$rhsDate = explode(',', $rhs)[0];
|
1024 |
+
|
1025 |
+
if ($lhsDate == $rhsDate) {
|
1026 |
+
return 1;
|
1027 |
+
} else if (Date::factory($lhsDate)->isEarlier(Date::factory($rhsDate))) {
|
1028 |
+
return -1;
|
1029 |
+
} else {
|
1030 |
+
return 1;
|
1031 |
+
}
|
1032 |
+
});
|
|
|
1033 |
|
1034 |
+
$oldestDateWithoutArchive = explode(',', reset($diff))[0];
|
1035 |
+
$todayInTimezone = Date::factoryInTimezone('today', Site::getTimezoneFor($idSite));
|
1036 |
|
1037 |
+
/** @var Range $newRangePeriod */
|
1038 |
+
$newRangePeriod = PeriodFactory::build($period, $oldestDateWithoutArchive . ',' . $todayInTimezone);
|
1039 |
|
1040 |
+
$newDate = 'last' . max(min($lastNValue, $newRangePeriod->getNumberOfSubperiods()), 2);
|
1041 |
+
} else if ($isTodayIncluded) {
|
1042 |
+
$isThereArchiveForAllPeriods = false;
|
1043 |
+
$newDate = 'last2';
|
1044 |
+
}
|
1045 |
}
|
1046 |
|
1047 |
return [$isThereArchiveForAllPeriods, $newDate];
|
1048 |
}
|
1049 |
|
1050 |
+
/**
|
1051 |
+
* @param int $idSite
|
1052 |
+
* @param Period[] $periods
|
1053 |
+
* @return bool
|
1054 |
+
* @throws Exception
|
1055 |
+
*/
|
1056 |
+
private function isTodayIncludedInPeriod($idSite, $periods)
|
1057 |
+
{
|
1058 |
+
$timezone = Site::getTimezoneFor($idSite);
|
1059 |
+
$today = Date::factoryInTimezone('today', $timezone);
|
1060 |
+
|
1061 |
+
foreach ($periods as $period) {
|
1062 |
+
if ($period->isDateInPeriod($today)) {
|
1063 |
+
return true;
|
1064 |
+
}
|
1065 |
+
}
|
1066 |
+
|
1067 |
+
return false;
|
1068 |
+
}
|
1069 |
+
|
1070 |
/**
|
1071 |
* @param $idSite
|
1072 |
* @return array
|
1136 |
return Request::ABORT;
|
1137 |
}
|
1138 |
|
1139 |
+
$this->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain();
|
1140 |
+
|
1141 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, $period, $date, $segment);
|
1142 |
if ($isThereArchive) {
|
1143 |
$this->logArchiveWebsiteSkippedValidArchiveExists($idSite, $period, $date);
|
2015 |
return Request::ABORT;
|
2016 |
}
|
2017 |
|
2018 |
+
$this->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain();
|
2019 |
+
|
2020 |
list($isThereArchive, $newDate) = $this->isThereAValidArchiveForPeriod($idSite, $period, $date, $segment);
|
2021 |
if ($isThereArchive) {
|
2022 |
$this->logArchiveWebsiteSkippedValidArchiveExists($idSite, $period, $date, $segment);
|
app/core/DataAccess/ArchiveSelector.php
CHANGED
@@ -48,7 +48,12 @@ class ArchiveSelector
|
|
48 |
/**
|
49 |
* @param ArchiveProcessor\Parameters $params
|
50 |
* @param bool $minDatetimeArchiveProcessedUTC deprecated. will be removed in Matomo 4.
|
51 |
-
* @return array
|
|
|
|
|
|
|
|
|
|
|
52 |
* @throws Exception
|
53 |
*/
|
54 |
public static function getArchiveIdAndVisits(ArchiveProcessor\Parameters $params, $minDatetimeArchiveProcessedUTC = false, $includeInvalidated = true)
|
@@ -61,80 +66,41 @@ class ArchiveSelector
|
|
61 |
|
62 |
$numericTable = ArchiveTableCreator::getNumericTable($dateStart);
|
63 |
|
64 |
-
$minDatetimeIsoArchiveProcessedUTC = null;
|
65 |
-
if ($minDatetimeArchiveProcessedUTC) {
|
66 |
-
$minDatetimeIsoArchiveProcessedUTC = Date::factory($minDatetimeArchiveProcessedUTC)->getDatetime();
|
67 |
-
}
|
68 |
-
|
69 |
$requestedPlugin = $params->getRequestedPlugin();
|
70 |
$segment = $params->getSegment();
|
71 |
$plugins = array("VisitsSummary", $requestedPlugin);
|
72 |
|
73 |
$doneFlags = Rules::getDoneFlags($plugins, $segment);
|
|
|
74 |
$doneFlagValues = Rules::getSelectableDoneFlagValues($includeInvalidated, $params);
|
75 |
|
76 |
-
$results = self::getModel()->getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso,
|
77 |
-
|
78 |
-
|
79 |
-
return false;
|
80 |
-
}
|
81 |
-
|
82 |
-
$idArchive = self::getMostRecentIdArchiveFromResults($segment, $requestedPlugin, $results);
|
83 |
-
|
84 |
-
$idArchiveVisitsSummary = self::getMostRecentIdArchiveFromResults($segment, "VisitsSummary", $results);
|
85 |
-
|
86 |
-
list($visits, $visitsConverted) = self::getVisitsMetricsFromResults($idArchive, $idArchiveVisitsSummary, $results);
|
87 |
-
|
88 |
-
if (false === $visits && false === $idArchive) {
|
89 |
-
return false;
|
90 |
}
|
91 |
|
92 |
-
|
93 |
-
}
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
$visits = $visitsConverted = false;
|
98 |
-
$archiveWithVisitsMetricsWasFound = ($idArchiveVisitsSummary !== false);
|
99 |
|
100 |
-
if ($
|
101 |
-
$
|
|
|
|
|
102 |
}
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
$visits = $value;
|
111 |
-
}
|
112 |
-
if (empty($visitsConverted)
|
113 |
-
&& $result['name'] == self::NB_VISITS_CONVERTED_RECORD_LOOKED_UP
|
114 |
-
) {
|
115 |
-
$visitsConverted = $value;
|
116 |
-
}
|
117 |
-
}
|
118 |
}
|
119 |
|
120 |
-
|
121 |
-
}
|
122 |
-
|
123 |
-
protected static function getMostRecentIdArchiveFromResults(Segment $segment, $requestedPlugin, $results)
|
124 |
-
{
|
125 |
-
$idArchive = false;
|
126 |
-
$namesRequestedPlugin = Rules::getDoneFlags(array($requestedPlugin), $segment);
|
127 |
-
|
128 |
-
foreach ($results as $result) {
|
129 |
-
if ($idArchive === false
|
130 |
-
&& in_array($result['name'], $namesRequestedPlugin)
|
131 |
-
) {
|
132 |
-
$idArchive = $result['idarchive'];
|
133 |
-
break;
|
134 |
-
}
|
135 |
-
}
|
136 |
|
137 |
-
return $idArchive;
|
138 |
}
|
139 |
|
140 |
/**
|
@@ -213,7 +179,6 @@ class ArchiveSelector
|
|
213 |
|
214 |
$sql = sprintf($getArchiveIdsSql, $table, $dateCondition);
|
215 |
|
216 |
-
|
217 |
$archiveIds = $db->fetchAll($sql, $bind);
|
218 |
|
219 |
// get the archive IDs
|
@@ -381,4 +346,71 @@ class ArchiveSelector
|
|
381 |
// create the SQL to find archives that are DONE
|
382 |
return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))";
|
383 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
384 |
}
|
48 |
/**
|
49 |
* @param ArchiveProcessor\Parameters $params
|
50 |
* @param bool $minDatetimeArchiveProcessedUTC deprecated. will be removed in Matomo 4.
|
51 |
+
* @return array An array with four values: \
|
52 |
+
* - the latest archive ID or false if none
|
53 |
+
* - the latest visits value for the latest archive, regardless of whether the archive is invalidated or not
|
54 |
+
* - the latest visits converted value for the latest archive, regardless of whether the archive is invalidated or not
|
55 |
+
* - whether there is an archive that exists or not. if this is true and the latest archive is false, it means
|
56 |
+
* the archive found was not usable (for example, it was invalidated and we are not looking for invalidated archives)
|
57 |
* @throws Exception
|
58 |
*/
|
59 |
public static function getArchiveIdAndVisits(ArchiveProcessor\Parameters $params, $minDatetimeArchiveProcessedUTC = false, $includeInvalidated = true)
|
66 |
|
67 |
$numericTable = ArchiveTableCreator::getNumericTable($dateStart);
|
68 |
|
|
|
|
|
|
|
|
|
|
|
69 |
$requestedPlugin = $params->getRequestedPlugin();
|
70 |
$segment = $params->getSegment();
|
71 |
$plugins = array("VisitsSummary", $requestedPlugin);
|
72 |
|
73 |
$doneFlags = Rules::getDoneFlags($plugins, $segment);
|
74 |
+
$requestedPluginDoneFlags = Rules::getDoneFlags([$requestedPlugin], $segment);
|
75 |
$doneFlagValues = Rules::getSelectableDoneFlagValues($includeInvalidated, $params);
|
76 |
|
77 |
+
$results = self::getModel()->getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, null, $doneFlags);
|
78 |
+
if (empty($results)) { // no archive found
|
79 |
+
return [false, false, false, false];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
}
|
81 |
|
82 |
+
$result = self::findArchiveDataWithLatestTsArchived($results, $requestedPluginDoneFlags);
|
|
|
83 |
|
84 |
+
$visits = isset($result['nb_visits']) ? $result['nb_visits'] : false;
|
85 |
+
$visitsConverted = isset($result['nb_visits_converted']) ? $result['nb_visits_converted'] : false;
|
|
|
|
|
86 |
|
87 |
+
if (isset($result['value'])
|
88 |
+
&& !in_array($result['value'], $doneFlagValues)
|
89 |
+
) { // the archive cannot be considered valid for this request (has wrong done flag value)
|
90 |
+
return [false, $visits, $visitsConverted, true];
|
91 |
}
|
92 |
|
93 |
+
// the archive is too old
|
94 |
+
if ($minDatetimeArchiveProcessedUTC
|
95 |
+
&& isset($result['idarchive'])
|
96 |
+
&& Date::factory($result['ts_archived'])->isEarlier(Date::factory($minDatetimeArchiveProcessedUTC))
|
97 |
+
) {
|
98 |
+
return [false, $visits, $visitsConverted, true];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
}
|
100 |
|
101 |
+
$idArchive = isset($result['idarchive']) ? $result['idarchive'] : false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
|
103 |
+
return array($idArchive, $visits, $visitsConverted, true);
|
104 |
}
|
105 |
|
106 |
/**
|
179 |
|
180 |
$sql = sprintf($getArchiveIdsSql, $table, $dateCondition);
|
181 |
|
|
|
182 |
$archiveIds = $db->fetchAll($sql, $bind);
|
183 |
|
184 |
// get the archive IDs
|
346 |
// create the SQL to find archives that are DONE
|
347 |
return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))";
|
348 |
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* This method takes the output of Model::getArchiveIdAndVisits() and selects data from the
|
352 |
+
* latest archives.
|
353 |
+
*
|
354 |
+
* This includes:
|
355 |
+
* - the idarchive with the latest ts_archived ($results will be ordered by ts_archived desc)
|
356 |
+
* - the visits/converted visits of the latest archive, which includes archives for VisitsSummary alone
|
357 |
+
* ($requestedPluginDoneFlags will have the done flag for the overall archive plus a done flag for
|
358 |
+
* VisitsSummary by itself)
|
359 |
+
* - the ts_archived for the latest idarchive
|
360 |
+
* - the doneFlag value for the latest archive
|
361 |
+
*
|
362 |
+
* @param $results
|
363 |
+
* @param $requestedPluginDoneFlags
|
364 |
+
* @return array
|
365 |
+
*/
|
366 |
+
private static function findArchiveDataWithLatestTsArchived($results, $requestedPluginDoneFlags)
|
367 |
+
{
|
368 |
+
// find latest idarchive for each done flag
|
369 |
+
$idArchives = [];
|
370 |
+
foreach ($results as $row) {
|
371 |
+
$doneFlag = $row['name'];
|
372 |
+
if (preg_match('/^done/', $doneFlag)
|
373 |
+
&& !isset($idArchives[$doneFlag])
|
374 |
+
) {
|
375 |
+
$idArchives[$doneFlag] = $row['idarchive'];
|
376 |
+
}
|
377 |
+
}
|
378 |
+
|
379 |
+
$archiveData = [];
|
380 |
+
|
381 |
+
// gather the latest visits/visits_converted metrics
|
382 |
+
foreach ($results as $row) {
|
383 |
+
$name = $row['name'];
|
384 |
+
if (!isset($archiveData[$name])
|
385 |
+
&& in_array($name, [self::NB_VISITS_RECORD_LOOKED_UP, self::NB_VISITS_CONVERTED_RECORD_LOOKED_UP])
|
386 |
+
&& in_array($row['idarchive'], $idArchives)
|
387 |
+
) {
|
388 |
+
$archiveData[$name] = $row['value'];
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
// if an archive is found, but the metric data isn't found, we set the value to 0,
|
393 |
+
// so it won't get returned as false. this is here because the code used to do this before this change
|
394 |
+
// and we didn't want to introduce any side effects. it may be removable in the future.
|
395 |
+
foreach ([self::NB_VISITS_RECORD_LOOKED_UP, self::NB_VISITS_CONVERTED_RECORD_LOOKED_UP] as $metric) {
|
396 |
+
if (!empty($idArchives)
|
397 |
+
&& !isset($archiveData[$metric])
|
398 |
+
) {
|
399 |
+
$archiveData[$metric] = 0;
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
// set the idarchive & ts_archived for the archive we're looking for
|
404 |
+
foreach ($results as $row) {
|
405 |
+
$name = $row['name'];
|
406 |
+
if (in_array($name, $requestedPluginDoneFlags)) {
|
407 |
+
$archiveData['idarchive'] = $row['idarchive'];
|
408 |
+
$archiveData['ts_archived'] = $row['ts_archived'];
|
409 |
+
$archiveData['value'] = $row['value'];
|
410 |
+
break;
|
411 |
+
}
|
412 |
+
}
|
413 |
+
|
414 |
+
return $archiveData;
|
415 |
+
}
|
416 |
}
|
app/core/DataAccess/LogAggregator.php
CHANGED
@@ -467,18 +467,18 @@ class LogAggregator
|
|
467 |
*
|
468 |
* The following columns are in each row of the result set:
|
469 |
*
|
470 |
-
* - **{@link Piwik\Metrics::INDEX_NB_UNIQ_VISITORS}**: The total number of unique visitors in this group
|
471 |
* of aggregated visits.
|
472 |
-
* - **{@link Piwik\Metrics::INDEX_NB_VISITS}**: The total number of visits aggregated.
|
473 |
-
* - **{@link Piwik\Metrics::INDEX_NB_ACTIONS}**: The total number of actions performed in this group of
|
474 |
* aggregated visits.
|
475 |
-
* - **{@link Piwik\Metrics::INDEX_MAX_ACTIONS}**: The maximum actions perfomred in one visit for this group of
|
476 |
* visits.
|
477 |
-
* - **{@link Piwik\Metrics::INDEX_SUM_VISIT_LENGTH}**: The total amount of time spent on the site for this
|
478 |
* group of visits.
|
479 |
-
* - **{@link Piwik\Metrics::INDEX_BOUNCE_COUNT}**: The total number of bounced visits in this group of
|
480 |
* visits.
|
481 |
-
* - **{@link Piwik\Metrics::INDEX_NB_VISITS_CONVERTED}**: The total number of visits for which at least one
|
482 |
* conversion occurred, for this group of visits.
|
483 |
*
|
484 |
* Additional data can be selected by setting the `$additionalSelects` parameter.
|
@@ -498,24 +498,26 @@ class LogAggregator
|
|
498 |
* @param bool|array $metrics The set of metrics to calculate and return. If false, the query will select
|
499 |
* all of them. The following values can be used:
|
500 |
*
|
501 |
-
* - {@link Piwik\Metrics::INDEX_NB_UNIQ_VISITORS}
|
502 |
-
* - {@link Piwik\Metrics::INDEX_NB_VISITS}
|
503 |
-
* - {@link Piwik\Metrics::INDEX_NB_ACTIONS}
|
504 |
-
* - {@link Piwik\Metrics::INDEX_MAX_ACTIONS}
|
505 |
-
* - {@link Piwik\Metrics::INDEX_SUM_VISIT_LENGTH}
|
506 |
-
* - {@link Piwik\Metrics::INDEX_BOUNCE_COUNT}
|
507 |
-
* - {@link Piwik\Metrics::INDEX_NB_VISITS_CONVERTED}
|
508 |
* @param bool|\Piwik\RankingQuery $rankingQuery
|
509 |
* A pre-configured ranking query instance that will be used to limit the result.
|
510 |
-
* If set, the return value is the array returned by {@link Piwik\RankingQuery::execute()}.
|
|
|
|
|
511 |
*
|
512 |
* @return mixed A Zend_Db_Statement if `$rankingQuery` isn't supplied, otherwise the result of
|
513 |
-
* {@link Piwik\RankingQuery::execute()}. Read {@link queryVisitsByDimension() this}
|
514 |
* to see what aggregate data is calculated by the query.
|
515 |
* @api
|
516 |
*/
|
517 |
public function queryVisitsByDimension(array $dimensions = array(), $where = false, array $additionalSelects = array(),
|
518 |
-
$metrics = false, $rankingQuery = false, $orderBy = false)
|
519 |
{
|
520 |
$tableName = self::LOG_VISIT_TABLE;
|
521 |
$availableMetrics = $this->getVisitsMetricFields();
|
@@ -551,9 +553,11 @@ class LogAggregator
|
|
551 |
$rankingQuery->addColumn(Metrics::INDEX_MAX_ACTIONS, 'max');
|
552 |
}
|
553 |
|
554 |
-
return $rankingQuery->execute($query['sql'], $query['bind']);
|
555 |
}
|
556 |
|
|
|
|
|
557 |
return $this->getDb()->query($query['sql'], $query['bind']);
|
558 |
}
|
559 |
|
@@ -878,6 +882,7 @@ class LogAggregator
|
|
878 |
* If a string is used for this parameter, the table alias is not
|
879 |
* suffixed (since there is only one column).
|
880 |
* @param string $secondaryOrderBy A secondary order by clause for the ranking query
|
|
|
881 |
* @return mixed A Zend_Db_Statement if `$rankingQuery` isn't supplied, otherwise the result of
|
882 |
* {@link Piwik\RankingQuery::execute()}. Read [this](#queryEcommerceItems-result-set)
|
883 |
* to see what aggregate data is calculated by the query.
|
@@ -890,7 +895,8 @@ class LogAggregator
|
|
890 |
$metrics = false,
|
891 |
$rankingQuery = null,
|
892 |
$joinLogActionOnColumn = false,
|
893 |
-
$secondaryOrderBy = null
|
|
|
894 |
) {
|
895 |
$tableName = self::LOG_ACTIONS_TABLE;
|
896 |
$availableMetrics = $this->getActionsMetricFields();
|
@@ -942,9 +948,11 @@ class LogAggregator
|
|
942 |
|
943 |
$rankingQuery->addColumn($sumColumns, 'sum');
|
944 |
|
945 |
-
return $rankingQuery->execute($query['sql'], $query['bind']);
|
946 |
}
|
947 |
|
|
|
|
|
948 |
return $this->getDb()->query($query['sql'], $query['bind']);
|
949 |
}
|
950 |
|
467 |
*
|
468 |
* The following columns are in each row of the result set:
|
469 |
*
|
470 |
+
* - **{@link \Piwik\Metrics::INDEX_NB_UNIQ_VISITORS}**: The total number of unique visitors in this group
|
471 |
* of aggregated visits.
|
472 |
+
* - **{@link \Piwik\Metrics::INDEX_NB_VISITS}**: The total number of visits aggregated.
|
473 |
+
* - **{@link \Piwik\Metrics::INDEX_NB_ACTIONS}**: The total number of actions performed in this group of
|
474 |
* aggregated visits.
|
475 |
+
* - **{@link \Piwik\Metrics::INDEX_MAX_ACTIONS}**: The maximum actions perfomred in one visit for this group of
|
476 |
* visits.
|
477 |
+
* - **{@link \Piwik\Metrics::INDEX_SUM_VISIT_LENGTH}**: The total amount of time spent on the site for this
|
478 |
* group of visits.
|
479 |
+
* - **{@link \Piwik\Metrics::INDEX_BOUNCE_COUNT}**: The total number of bounced visits in this group of
|
480 |
* visits.
|
481 |
+
* - **{@link \Piwik\Metrics::INDEX_NB_VISITS_CONVERTED}**: The total number of visits for which at least one
|
482 |
* conversion occurred, for this group of visits.
|
483 |
*
|
484 |
* Additional data can be selected by setting the `$additionalSelects` parameter.
|
498 |
* @param bool|array $metrics The set of metrics to calculate and return. If false, the query will select
|
499 |
* all of them. The following values can be used:
|
500 |
*
|
501 |
+
* - {@link \Piwik\Metrics::INDEX_NB_UNIQ_VISITORS}
|
502 |
+
* - {@link \Piwik\Metrics::INDEX_NB_VISITS}
|
503 |
+
* - {@link \Piwik\Metrics::INDEX_NB_ACTIONS}
|
504 |
+
* - {@link \Piwik\Metrics::INDEX_MAX_ACTIONS}
|
505 |
+
* - {@link \Piwik\Metrics::INDEX_SUM_VISIT_LENGTH}
|
506 |
+
* - {@link \Piwik\Metrics::INDEX_BOUNCE_COUNT}
|
507 |
+
* - {@link \Piwik\Metrics::INDEX_NB_VISITS_CONVERTED}
|
508 |
* @param bool|\Piwik\RankingQuery $rankingQuery
|
509 |
* A pre-configured ranking query instance that will be used to limit the result.
|
510 |
+
* If set, the return value is the array returned by {@link \Piwik\RankingQuery::execute()}.
|
511 |
+
* @param bool|string $orderBy Order By clause to add (e.g. user_id ASC)
|
512 |
+
* @param int $timeLimitInMs Adds a MAX_EXECUTION_TIME query hint to the query if $timeLimitInMs > 0
|
513 |
*
|
514 |
* @return mixed A Zend_Db_Statement if `$rankingQuery` isn't supplied, otherwise the result of
|
515 |
+
* {@link \Piwik\RankingQuery::execute()}. Read {@link queryVisitsByDimension() this}
|
516 |
* to see what aggregate data is calculated by the query.
|
517 |
* @api
|
518 |
*/
|
519 |
public function queryVisitsByDimension(array $dimensions = array(), $where = false, array $additionalSelects = array(),
|
520 |
+
$metrics = false, $rankingQuery = false, $orderBy = false, $timeLimitInMs = -1)
|
521 |
{
|
522 |
$tableName = self::LOG_VISIT_TABLE;
|
523 |
$availableMetrics = $this->getVisitsMetricFields();
|
553 |
$rankingQuery->addColumn(Metrics::INDEX_MAX_ACTIONS, 'max');
|
554 |
}
|
555 |
|
556 |
+
return $rankingQuery->execute($query['sql'], $query['bind'], $timeLimitInMs);
|
557 |
}
|
558 |
|
559 |
+
$query['sql'] = DbHelper::addMaxExecutionTimeHintToQuery($query['sql'], $timeLimitInMs);
|
560 |
+
|
561 |
return $this->getDb()->query($query['sql'], $query['bind']);
|
562 |
}
|
563 |
|
882 |
* If a string is used for this parameter, the table alias is not
|
883 |
* suffixed (since there is only one column).
|
884 |
* @param string $secondaryOrderBy A secondary order by clause for the ranking query
|
885 |
+
* @param int $timeLimitInMs Adds a MAX_EXECUTION_TIME hint to the query if $timeLimitInMs > 0
|
886 |
* @return mixed A Zend_Db_Statement if `$rankingQuery` isn't supplied, otherwise the result of
|
887 |
* {@link Piwik\RankingQuery::execute()}. Read [this](#queryEcommerceItems-result-set)
|
888 |
* to see what aggregate data is calculated by the query.
|
895 |
$metrics = false,
|
896 |
$rankingQuery = null,
|
897 |
$joinLogActionOnColumn = false,
|
898 |
+
$secondaryOrderBy = null,
|
899 |
+
$timeLimitInMs = -1
|
900 |
) {
|
901 |
$tableName = self::LOG_ACTIONS_TABLE;
|
902 |
$availableMetrics = $this->getActionsMetricFields();
|
948 |
|
949 |
$rankingQuery->addColumn($sumColumns, 'sum');
|
950 |
|
951 |
+
return $rankingQuery->execute($query['sql'], $query['bind'], $timeLimitInMs);
|
952 |
}
|
953 |
|
954 |
+
$query['sql'] = DbHelper::addMaxExecutionTimeHintToQuery($query['sql'], $timeLimitInMs);
|
955 |
+
|
956 |
return $this->getDb()->query($query['sql'], $query['bind']);
|
957 |
}
|
958 |
|
app/core/DataAccess/Model.php
CHANGED
@@ -215,7 +215,8 @@ class Model
|
|
215 |
return $deletedRows;
|
216 |
}
|
217 |
|
218 |
-
public function getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC,
|
|
|
219 |
{
|
220 |
$bindSQL = array($idSite,
|
221 |
$dateStartIso,
|
@@ -231,7 +232,8 @@ class Model
|
|
231 |
$bindSQL[] = $minDatetimeIsoArchiveProcessedUTC;
|
232 |
}
|
233 |
|
234 |
-
|
|
|
235 |
WHERE idsite = ?
|
236 |
AND date1 = ?
|
237 |
AND date2 = ?
|
@@ -240,7 +242,7 @@ class Model
|
|
240 |
OR name = '" . ArchiveSelector::NB_VISITS_RECORD_LOOKED_UP . "'
|
241 |
OR name = '" . ArchiveSelector::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "')
|
242 |
$timeStampWhere
|
243 |
-
ORDER BY idarchive DESC";
|
244 |
$results = Db::fetchAll($sqlQuery, $bindSQL);
|
245 |
|
246 |
return $results;
|
@@ -428,7 +430,14 @@ class Model
|
|
428 |
$allDoneFlags = "'" . implode("','", $doneFlags) . "'";
|
429 |
|
430 |
// create the SQL to find archives that are DONE
|
431 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
432 |
}
|
433 |
|
434 |
}
|
215 |
return $deletedRows;
|
216 |
}
|
217 |
|
218 |
+
public function getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC,
|
219 |
+
$doneFlags, $doneFlagValues = null)
|
220 |
{
|
221 |
$bindSQL = array($idSite,
|
222 |
$dateStartIso,
|
232 |
$bindSQL[] = $minDatetimeIsoArchiveProcessedUTC;
|
233 |
}
|
234 |
|
235 |
+
// NOTE: we can't predict how many segments there will be so there could be lots of nb_visits/nb_visits_converted rows... have to select everything.
|
236 |
+
$sqlQuery = "SELECT idarchive, value, name, ts_archived, date1 as startDate FROM $numericTable
|
237 |
WHERE idsite = ?
|
238 |
AND date1 = ?
|
239 |
AND date2 = ?
|
242 |
OR name = '" . ArchiveSelector::NB_VISITS_RECORD_LOOKED_UP . "'
|
243 |
OR name = '" . ArchiveSelector::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "')
|
244 |
$timeStampWhere
|
245 |
+
ORDER BY ts_archived DESC, idarchive DESC";
|
246 |
$results = Db::fetchAll($sqlQuery, $bindSQL);
|
247 |
|
248 |
return $results;
|
430 |
$allDoneFlags = "'" . implode("','", $doneFlags) . "'";
|
431 |
|
432 |
// create the SQL to find archives that are DONE
|
433 |
+
$result = "((name IN ($allDoneFlags))";
|
434 |
+
|
435 |
+
if (!empty($possibleValues)) {
|
436 |
+
$result .= " AND (value IN (" . implode(',', $possibleValues) . ")))";
|
437 |
+
}
|
438 |
+
$result .= ')';
|
439 |
+
|
440 |
+
return $result;
|
441 |
}
|
442 |
|
443 |
}
|
app/core/Db/Schema/Mysql.php
CHANGED
@@ -225,7 +225,7 @@ class Mysql implements SchemaInterface
|
|
225 |
idvisit BIGINT(10) UNSIGNED NOT NULL,
|
226 |
idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
|
227 |
idaction_name_ref INTEGER(10) UNSIGNED NULL,
|
228 |
-
custom_float
|
229 |
PRIMARY KEY(idlink_va),
|
230 |
INDEX index_idvisit(idvisit)
|
231 |
) ENGINE=$engine DEFAULT CHARSET=utf8
|
225 |
idvisit BIGINT(10) UNSIGNED NOT NULL,
|
226 |
idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
|
227 |
idaction_name_ref INTEGER(10) UNSIGNED NULL,
|
228 |
+
custom_float DOUBLE NULL DEFAULT NULL,
|
229 |
PRIMARY KEY(idlink_va),
|
230 |
INDEX index_idvisit(idvisit)
|
231 |
) ENGINE=$engine DEFAULT CHARSET=utf8
|
app/core/DbHelper.php
CHANGED
@@ -177,6 +177,21 @@ class DbHelper
|
|
177 |
Schema::getInstance()->createDatabase($dbName);
|
178 |
}
|
179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
/**
|
181 |
* Get the SQL to create Piwik tables
|
182 |
*
|
@@ -212,6 +227,33 @@ class DbHelper
|
|
212 |
ArchiveTableCreator::refreshTableList($forceReload = true);
|
213 |
}
|
214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
/**
|
216 |
* Returns true if the string is a valid database name for MySQL. MySQL allows + in the database names.
|
217 |
* Database names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), plus(+), and dot(.) will be accepted.
|
177 |
Schema::getInstance()->createDatabase($dbName);
|
178 |
}
|
179 |
|
180 |
+
/**
|
181 |
+
* Returns if the given table has an index with the given name
|
182 |
+
*
|
183 |
+
* @param string $table
|
184 |
+
* @param string $indexName
|
185 |
+
*
|
186 |
+
* @return bool
|
187 |
+
* @throws Exception
|
188 |
+
*/
|
189 |
+
public static function tableHasIndex($table, $indexName)
|
190 |
+
{
|
191 |
+
$result = Db::get()->fetchOne('SHOW INDEX FROM '.$table.' WHERE Key_name = ?', [$indexName]);
|
192 |
+
return !empty($result);
|
193 |
+
}
|
194 |
+
|
195 |
/**
|
196 |
* Get the SQL to create Piwik tables
|
197 |
*
|
227 |
ArchiveTableCreator::refreshTableList($forceReload = true);
|
228 |
}
|
229 |
|
230 |
+
/**
|
231 |
+
* Adds a MAX_EXECUTION_TIME hint into a SELECT query if $limit is bigger than 1
|
232 |
+
*
|
233 |
+
* @param string $sql query to add hint to
|
234 |
+
* @param int $limit time limit in seconds
|
235 |
+
* @return string
|
236 |
+
*/
|
237 |
+
public static function addMaxExecutionTimeHintToQuery($sql, $limit)
|
238 |
+
{
|
239 |
+
if ($limit <= 0) {
|
240 |
+
return $sql;
|
241 |
+
}
|
242 |
+
|
243 |
+
$sql = trim($sql);
|
244 |
+
$pos = stripos($sql, 'SELECT');
|
245 |
+
if ($pos !== false) {
|
246 |
+
|
247 |
+
$timeInMs = $limit * 1000;
|
248 |
+
$timeInMs = (int) $timeInMs;
|
249 |
+
$maxExecutionTimeHint = ' /*+ MAX_EXECUTION_TIME('.$timeInMs.') */ ';
|
250 |
+
|
251 |
+
$sql = substr_replace($sql, 'SELECT ' . $maxExecutionTimeHint, $pos, strlen('SELECT'));
|
252 |
+
}
|
253 |
+
|
254 |
+
return $sql;
|
255 |
+
}
|
256 |
+
|
257 |
/**
|
258 |
* Returns true if the string is a valid database name for MySQL. MySQL allows + in the database names.
|
259 |
* Database names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), plus(+), and dot(.) will be accepted.
|
app/core/FrontController.php
CHANGED
@@ -392,6 +392,7 @@ class FrontController extends Singleton
|
|
392 |
|
393 |
$loggedIn = false;
|
394 |
|
|
|
395 |
// try authenticating w/ session first...
|
396 |
$sessionAuth = $this->makeSessionAuthenticator();
|
397 |
if ($sessionAuth) {
|
@@ -647,6 +648,12 @@ class FrontController extends Singleton
|
|
647 |
|
648 |
private function makeSessionAuthenticator()
|
649 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
650 |
$module = Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
|
651 |
$action = Common::getRequestVar('action', false);
|
652 |
|
392 |
|
393 |
$loggedIn = false;
|
394 |
|
395 |
+
// don't use sessionauth in cli mode
|
396 |
// try authenticating w/ session first...
|
397 |
$sessionAuth = $this->makeSessionAuthenticator();
|
398 |
if ($sessionAuth) {
|
648 |
|
649 |
private function makeSessionAuthenticator()
|
650 |
{
|
651 |
+
if (Common::isPhpClimode()
|
652 |
+
&& !defined('PIWIK_TEST_MODE')
|
653 |
+
) { // don't use the session auth during CLI requests
|
654 |
+
return null;
|
655 |
+
}
|
656 |
+
|
657 |
$module = Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
|
658 |
$action = Common::getRequestVar('action', false);
|
659 |
|
app/core/Http.php
CHANGED
@@ -934,9 +934,12 @@ class Http
|
|
934 |
* With HTTP/2 Cloudflare is passing headers in lowercase (e.g. 'content-type' instead of 'Content-Type')
|
935 |
* which breaks any code which uses the header data.
|
936 |
*/
|
937 |
-
|
938 |
-
|
939 |
-
$
|
|
|
|
|
|
|
940 |
}
|
941 |
}
|
942 |
|
934 |
* With HTTP/2 Cloudflare is passing headers in lowercase (e.g. 'content-type' instead of 'Content-Type')
|
935 |
* which breaks any code which uses the header data.
|
936 |
*/
|
937 |
+
if (version_compare(PHP_VERSION, '5.5.16', '>=')) {
|
938 |
+
// Passing a second arg to ucwords is not supported by older versions of PHP
|
939 |
+
$camelName = ucwords($name, '-');
|
940 |
+
if ($camelName !== $name) {
|
941 |
+
$headers[$camelName] = trim($value);
|
942 |
+
}
|
943 |
}
|
944 |
}
|
945 |
|
app/core/Period.php
CHANGED
@@ -247,6 +247,21 @@ abstract class Period
|
|
247 |
return $this->subperiods;
|
248 |
}
|
249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
/**
|
251 |
* Add a date to the period.
|
252 |
*
|
247 |
return $this->subperiods;
|
248 |
}
|
249 |
|
250 |
+
/**
|
251 |
+
* Returns whether the date `$date` is within the current period or not.
|
252 |
+
*
|
253 |
+
* Note: the time component of the period's dates and `$date` is ignored.
|
254 |
+
*
|
255 |
+
* @param Date $today
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function isDateInPeriod(Date $date)
|
259 |
+
{
|
260 |
+
$ts = $date->getStartOfDay()->getTimestamp();
|
261 |
+
return $ts >= $this->getDateStart()->getStartOfDay()->getTimestamp()
|
262 |
+
&& $ts < $this->getDateEnd()->addDay(1)->getStartOfDay()->getTimestamp();
|
263 |
+
}
|
264 |
+
|
265 |
/**
|
266 |
* Add a date to the period.
|
267 |
*
|
app/core/Period/Factory.php
CHANGED
@@ -74,6 +74,7 @@ abstract class Factory
|
|
74 |
self::checkPeriodIsEnabled($period);
|
75 |
|
76 |
if (is_string($date)) {
|
|
|
77 |
if (Period::isMultiplePeriod($date, $period)
|
78 |
|| $period == 'range'
|
79 |
) {
|
@@ -130,6 +131,19 @@ abstract class Factory
|
|
130 |
throw new Exception($message);
|
131 |
}
|
132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
/**
|
134 |
* Creates a Period instance using a period, date and timezone.
|
135 |
*
|
@@ -146,6 +160,8 @@ abstract class Factory
|
|
146 |
$timezone = 'UTC';
|
147 |
}
|
148 |
|
|
|
|
|
149 |
if ($period == 'range') {
|
150 |
self::checkPeriodIsEnabled('range');
|
151 |
$oPeriod = new Range('range', $date, $timezone, Date::factory('today', $timezone));
|
74 |
self::checkPeriodIsEnabled($period);
|
75 |
|
76 |
if (is_string($date)) {
|
77 |
+
list($period, $date) = self::convertRangeToDateIfNeeded($period, $date);
|
78 |
if (Period::isMultiplePeriod($date, $period)
|
79 |
|| $period == 'range'
|
80 |
) {
|
131 |
throw new Exception($message);
|
132 |
}
|
133 |
|
134 |
+
private static function convertRangeToDateIfNeeded($period, $date)
|
135 |
+
{
|
136 |
+
if (is_string($period) && is_string($date) && $period === 'range') {
|
137 |
+
$dates = explode(',', $date);
|
138 |
+
if (count($dates) === 2 && $dates[0] === $dates[1]) {
|
139 |
+
$period = 'day';
|
140 |
+
$date = $dates[0];
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
return array($period, $date);
|
145 |
+
}
|
146 |
+
|
147 |
/**
|
148 |
* Creates a Period instance using a period, date and timezone.
|
149 |
*
|
160 |
$timezone = 'UTC';
|
161 |
}
|
162 |
|
163 |
+
list($period, $date) = self::convertRangeToDateIfNeeded($period, $date);
|
164 |
+
|
165 |
if ($period == 'range') {
|
166 |
self::checkPeriodIsEnabled('range');
|
167 |
$oPeriod = new Range('range', $date, $timezone, Date::factory('today', $timezone));
|
app/core/Plugin/ControllerAdmin.php
CHANGED
@@ -207,12 +207,12 @@ abstract class ControllerAdmin extends Controller
|
|
207 |
}
|
208 |
|
209 |
/**
|
210 |
-
* PHP Version required by the next major
|
211 |
* @return string
|
212 |
*/
|
213 |
private static function getNextRequiredMinimumPHP()
|
214 |
{
|
215 |
-
return '7.
|
216 |
}
|
217 |
|
218 |
private static function isUsingPhpVersionCompatibleWithNextPiwik()
|
@@ -222,25 +222,7 @@ abstract class ControllerAdmin extends Controller
|
|
222 |
|
223 |
private static function notifyWhenPhpVersionIsNotCompatibleWithNextMajorPiwik()
|
224 |
{
|
225 |
-
|
226 |
-
|
227 |
-
if (self::isUsingPhpVersionCompatibleWithNextPiwik()) {
|
228 |
-
return;
|
229 |
-
}
|
230 |
-
|
231 |
-
$youMustUpgradePHP = Piwik::translate('General_YouMustUpgradePhpVersionToReceiveLatestPiwik');
|
232 |
-
$message = Piwik::translate('General_PiwikCannotBeUpgradedBecausePhpIsTooOld')
|
233 |
-
. ' '
|
234 |
-
. sprintf(Piwik::translate('General_PleaseUpgradeYourPhpVersionSoYourPiwikDataStaysSecure'), self::getNextRequiredMinimumPHP())
|
235 |
-
;
|
236 |
-
|
237 |
-
$notification = new Notification($message);
|
238 |
-
$notification->title = $youMustUpgradePHP;
|
239 |
-
$notification->priority = Notification::PRIORITY_LOW;
|
240 |
-
$notification->context = Notification::CONTEXT_WARNING;
|
241 |
-
$notification->type = Notification::TYPE_TRANSIENT;
|
242 |
-
$notification->flags = Notification::FLAG_NO_CLEAR;
|
243 |
-
NotificationManager::notify('PHPVersionTooOldForNewestPiwikCheck', $notification);
|
244 |
}
|
245 |
|
246 |
private static function notifyWhenPhpVersionIsEOL()
|
207 |
}
|
208 |
|
209 |
/**
|
210 |
+
* PHP Version required by the next major Matomo version
|
211 |
* @return string
|
212 |
*/
|
213 |
private static function getNextRequiredMinimumPHP()
|
214 |
{
|
215 |
+
return '7.2';
|
216 |
}
|
217 |
|
218 |
private static function isUsingPhpVersionCompatibleWithNextPiwik()
|
222 |
|
223 |
private static function notifyWhenPhpVersionIsNotCompatibleWithNextMajorPiwik()
|
224 |
{
|
225 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
}
|
227 |
|
228 |
private static function notifyWhenPhpVersionIsEOL()
|
app/core/RankingQuery.php
CHANGED
@@ -220,12 +220,15 @@ class RankingQuery
|
|
220 |
* has to be specified in this query. {@link RankingQuery} cannot apply ordering
|
221 |
* itself.
|
222 |
* @param $bind array Bindings for the inner query.
|
|
|
223 |
* @return array The format depends on which methods have been used
|
224 |
* to configure the ranking query.
|
225 |
*/
|
226 |
-
public function execute($innerQuery, $bind = array())
|
227 |
{
|
228 |
$query = $this->generateRankingQuery($innerQuery);
|
|
|
|
|
229 |
$data = Db::getReader()->fetchAll($query, $bind);
|
230 |
|
231 |
if ($this->columnToMarkExcludedRows !== false) {
|
220 |
* has to be specified in this query. {@link RankingQuery} cannot apply ordering
|
221 |
* itself.
|
222 |
* @param $bind array Bindings for the inner query.
|
223 |
+
* @param int $timeLimitInMs Adds a MAX_EXECUTION_TIME query hint to the query if $timeLimitInMs > 0
|
224 |
* @return array The format depends on which methods have been used
|
225 |
* to configure the ranking query.
|
226 |
*/
|
227 |
+
public function execute($innerQuery, $bind = array(), $timeLimitInMs = 0)
|
228 |
{
|
229 |
$query = $this->generateRankingQuery($innerQuery);
|
230 |
+
$query = DbHelper::addMaxExecutionTimeHintToQuery($query, $timeLimitInMs);
|
231 |
+
|
232 |
$data = Db::getReader()->fetchAll($query, $bind);
|
233 |
|
234 |
if ($this->columnToMarkExcludedRows !== false) {
|
app/core/Session.php
CHANGED
@@ -222,7 +222,7 @@ class Session extends Zend_Session
|
|
222 |
{
|
223 |
$headerStr = 'Set-Cookie: ' . rawurlencode($name) . '=' . rawurlencode($value);
|
224 |
if ($expires) {
|
225 |
-
$headerStr .= '; expires=' . $expires;
|
226 |
}
|
227 |
if ($path) {
|
228 |
$headerStr .= '; path=' . $path;
|
222 |
{
|
223 |
$headerStr = 'Set-Cookie: ' . rawurlencode($name) . '=' . rawurlencode($value);
|
224 |
if ($expires) {
|
225 |
+
$headerStr .= '; expires=' . gmdate('D, d-M-Y H:i:s', $expires) . ' GMT';
|
226 |
}
|
227 |
if ($path) {
|
228 |
$headerStr .= '; path=' . $path;
|
app/core/Tracker/GoalManager.php
CHANGED
@@ -665,6 +665,10 @@ class GoalManager
|
|
665 |
|
666 |
protected function getItemRowEnriched($goal, $item)
|
667 |
{
|
|
|
|
|
|
|
|
|
668 |
$newRow = array(
|
669 |
'idaction_sku' => (int)$item[self::INTERNAL_ITEM_SKU],
|
670 |
'idaction_name' => (int)$item[self::INTERNAL_ITEM_NAME],
|
@@ -879,8 +883,9 @@ class GoalManager
|
|
879 |
{
|
880 |
$lastVisitTime = $visitProperties->getProperty('visit_last_action_time');
|
881 |
if (!$lastVisitTime) {
|
882 |
-
|
883 |
}
|
|
|
884 |
$goal = array(
|
885 |
'idvisit' => $visitProperties->getProperty('idvisit'),
|
886 |
'idvisitor' => $visitProperties->getProperty('idvisitor'),
|
665 |
|
666 |
protected function getItemRowEnriched($goal, $item)
|
667 |
{
|
668 |
+
if (empty($goal['server_time'])) {
|
669 |
+
// hack to test #215
|
670 |
+
$goal['server_time'] = Date::getDatetimeFromTimestamp(time());
|
671 |
+
}
|
672 |
$newRow = array(
|
673 |
'idaction_sku' => (int)$item[self::INTERNAL_ITEM_SKU],
|
674 |
'idaction_name' => (int)$item[self::INTERNAL_ITEM_NAME],
|
883 |
{
|
884 |
$lastVisitTime = $visitProperties->getProperty('visit_last_action_time');
|
885 |
if (!$lastVisitTime) {
|
886 |
+
$lastVisitTime = $request->getCurrentTimestamp();
|
887 |
}
|
888 |
+
|
889 |
$goal = array(
|
890 |
'idvisit' => $visitProperties->getProperty('idvisit'),
|
891 |
'idvisitor' => $visitProperties->getProperty('idvisitor'),
|
app/core/Tracker/Model.php
CHANGED
@@ -16,6 +16,7 @@ use Psr\Log\LoggerInterface;
|
|
16 |
|
17 |
class Model
|
18 |
{
|
|
|
19 |
|
20 |
public function createAction($visitAction)
|
21 |
{
|
@@ -70,10 +71,10 @@ class Model
|
|
70 |
$sqlBind[] = $value;
|
71 |
}
|
72 |
|
73 |
-
$parts = implode(
|
74 |
$table = Common::prefixTable('log_conversion');
|
75 |
|
76 |
-
$sql = "UPDATE $table SET $parts WHERE " . implode(
|
77 |
|
78 |
try {
|
79 |
$this->getDb()->query($sql, $sqlBind);
|
@@ -285,7 +286,7 @@ class Model
|
|
285 |
$sqlBind[] = $value;
|
286 |
}
|
287 |
|
288 |
-
$parts = implode(
|
289 |
$table = Common::prefixTable('log_conversion_item');
|
290 |
|
291 |
$sql = "UPDATE $table SET $parts WHERE idvisit = ? AND idorder = ? AND idaction_sku = ?";
|
@@ -418,7 +419,13 @@ class Model
|
|
418 |
|
419 |
private function findVisitorByVisitorId($idVisitor, $select, $from, $where, $bindSql)
|
420 |
{
|
421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
$where .= ' AND idvisitor = ?';
|
423 |
$bindSql[] = $idVisitor;
|
424 |
|
16 |
|
17 |
class Model
|
18 |
{
|
19 |
+
const CACHE_KEY_INDEX_IDSITE_IDVISITOR = 'log_visit_has_index_idsite_idvisitor';
|
20 |
|
21 |
public function createAction($visitAction)
|
22 |
{
|
71 |
$sqlBind[] = $value;
|
72 |
}
|
73 |
|
74 |
+
$parts = implode(', ', $updateParts);
|
75 |
$table = Common::prefixTable('log_conversion');
|
76 |
|
77 |
+
$sql = "UPDATE $table SET $parts WHERE " . implode(' AND ', $updateWhereParts);
|
78 |
|
79 |
try {
|
80 |
$this->getDb()->query($sql, $sqlBind);
|
286 |
$sqlBind[] = $value;
|
287 |
}
|
288 |
|
289 |
+
$parts = implode(', ', $updateParts);
|
290 |
$table = Common::prefixTable('log_conversion_item');
|
291 |
|
292 |
$sql = "UPDATE $table SET $parts WHERE idvisit = ? AND idorder = ? AND idaction_sku = ?";
|
419 |
|
420 |
private function findVisitorByVisitorId($idVisitor, $select, $from, $where, $bindSql)
|
421 |
{
|
422 |
+
$cache = Cache::getCacheGeneral();
|
423 |
+
|
424 |
+
// use INDEX index_idsite_idvisitor (idsite, idvisitor) if available
|
425 |
+
if (array_key_exists(self::CACHE_KEY_INDEX_IDSITE_IDVISITOR, $cache) && true === $cache[self::CACHE_KEY_INDEX_IDSITE_IDVISITOR]) {
|
426 |
+
$from .= ' FORCE INDEX (index_idsite_idvisitor) ';
|
427 |
+
}
|
428 |
+
|
429 |
$where .= ' AND idvisitor = ?';
|
430 |
$bindSql[] = $idVisitor;
|
431 |
|
app/core/Tracker/Request.php
CHANGED
@@ -20,6 +20,7 @@ use Piwik\Network\IPUtils;
|
|
20 |
use Piwik\Piwik;
|
21 |
use Piwik\Plugins\CustomVariables\CustomVariables;
|
22 |
use Piwik\Plugins\UsersManager\UsersManager;
|
|
|
23 |
use Piwik\Tracker;
|
24 |
use Piwik\Cache as PiwikCache;
|
25 |
|
@@ -686,7 +687,12 @@ class Request
|
|
686 |
$cookie = $this->makeThirdPartyCookieUID();
|
687 |
$idVisitor = bin2hex($idVisitor);
|
688 |
$cookie->set(0, $idVisitor);
|
689 |
-
|
|
|
|
|
|
|
|
|
|
|
690 |
|
691 |
Common::printDebug(sprintf("We set the visitor ID to %s in the 3rd party cookie...", $idVisitor));
|
692 |
}
|
20 |
use Piwik\Piwik;
|
21 |
use Piwik\Plugins\CustomVariables\CustomVariables;
|
22 |
use Piwik\Plugins\UsersManager\UsersManager;
|
23 |
+
use Piwik\ProxyHttp;
|
24 |
use Piwik\Tracker;
|
25 |
use Piwik\Cache as PiwikCache;
|
26 |
|
687 |
$cookie = $this->makeThirdPartyCookieUID();
|
688 |
$idVisitor = bin2hex($idVisitor);
|
689 |
$cookie->set(0, $idVisitor);
|
690 |
+
if (ProxyHttp::isHttps()) {
|
691 |
+
$cookie->setSecure(true);
|
692 |
+
$cookie->save('None');
|
693 |
+
} else {
|
694 |
+
$cookie->save('Lax');
|
695 |
+
}
|
696 |
|
697 |
Common::printDebug(sprintf("We set the visitor ID to %s in the 3rd party cookie...", $idVisitor));
|
698 |
}
|
app/core/Tracker/Visit.php
CHANGED
@@ -572,10 +572,10 @@ class Visit implements VisitInterface
|
|
572 |
$date = Date::factory((int)$time, $timezone);
|
573 |
|
574 |
// $date->isToday() is buggy when server and website timezones don't match - so we'll do our own checking
|
575 |
-
$
|
576 |
-
$
|
577 |
-
if ($
|
578 |
-
return;
|
579 |
}
|
580 |
|
581 |
$this->invalidator->rememberToInvalidateArchivedReportsLater($idSite, $date);
|
572 |
$date = Date::factory((int)$time, $timezone);
|
573 |
|
574 |
// $date->isToday() is buggy when server and website timezones don't match - so we'll do our own checking
|
575 |
+
$startOfToday = Date::factoryInTimezone('yesterday', $timezone)->addDay(1);
|
576 |
+
$isLaterThanYesterday = $date->getTimestamp() >= $startOfToday->getTimestamp();
|
577 |
+
if ($isLaterThanYesterday) {
|
578 |
+
return; // don't try to invalidate archives for today or later
|
579 |
}
|
580 |
|
581 |
$this->invalidator->rememberToInvalidateArchivedReportsLater($idSite, $date);
|
app/core/Twig.php
CHANGED
@@ -362,7 +362,7 @@ class Twig
|
|
362 |
if (!empty($options['raw'])) {
|
363 |
$template .= piwik_fix_lbrace($message);
|
364 |
} else {
|
365 |
-
$template .=
|
366 |
}
|
367 |
|
368 |
$template .= '</div>';
|
362 |
if (!empty($options['raw'])) {
|
363 |
$template .= piwik_fix_lbrace($message);
|
364 |
} else {
|
365 |
+
$template .= piwik_escape_filter($twigEnv, $message, 'html');
|
366 |
}
|
367 |
|
368 |
$template .= '</div>';
|
app/core/Updates/3.13.4-b1.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Piwik - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
|
10 |
+
namespace Piwik\Updates;
|
11 |
+
|
12 |
+
use Piwik\Plugins\Installation\ServerFilesGenerator;
|
13 |
+
use Piwik\SettingsServer;
|
14 |
+
use Piwik\Updater;
|
15 |
+
use Piwik\Updates as PiwikUpdates;
|
16 |
+
|
17 |
+
class Updates_3_13_4_b1 extends PiwikUpdates
|
18 |
+
{
|
19 |
+
public function doUpdate(Updater $updater)
|
20 |
+
{
|
21 |
+
// Fix issue with HeatmapSessionRecording on IIS (https://github.com/matomo-org/matomo/issues/15651)
|
22 |
+
if (SettingsServer::isIIS()) {
|
23 |
+
ServerFilesGenerator::createFilesForSecurity();
|
24 |
+
}
|
25 |
+
}
|
26 |
+
}
|
app/core/Version.php
CHANGED
@@ -20,7 +20,7 @@ final class Version
|
|
20 |
* The current Matomo version.
|
21 |
* @var string
|
22 |
*/
|
23 |
-
const VERSION = '3.13.
|
24 |
|
25 |
public function isStableVersion($version)
|
26 |
{
|
20 |
* The current Matomo version.
|
21 |
* @var string
|
22 |
*/
|
23 |
+
const VERSION = '3.13.4';
|
24 |
|
25 |
public function isStableVersion($version)
|
26 |
{
|
app/core/View.php
CHANGED
@@ -287,6 +287,9 @@ class View implements ViewInterface
|
|
287 |
// don't send Referer-Header for outgoing links
|
288 |
if (!empty($this->useStrictReferrerPolicy)) {
|
289 |
Common::sendHeader('Referrer-Policy: same-origin');
|
|
|
|
|
|
|
290 |
}
|
291 |
|
292 |
return $this->renderTwigTemplate();
|
287 |
// don't send Referer-Header for outgoing links
|
288 |
if (!empty($this->useStrictReferrerPolicy)) {
|
289 |
Common::sendHeader('Referrer-Policy: same-origin');
|
290 |
+
} else {
|
291 |
+
// always send explicit default header
|
292 |
+
Common::sendHeader('Referrer-Policy: no-referrer-when-downgrade');
|
293 |
}
|
294 |
|
295 |
return $this->renderTwigTemplate();
|
app/lang/en.json
CHANGED
@@ -2983,6 +2983,7 @@
|
|
2983 |
"AllowedUploadFormats": "You may upload a plugin or theme in .zip format via this page.",
|
2984 |
"Authors": "Authors",
|
2985 |
"Browse": "Browse",
|
|
|
2986 |
"LatestMarketplaceUpdates": "Latest Marketplace Updates",
|
2987 |
"BackToMarketplace": "Back to Marketplace",
|
2988 |
"BrowseMarketplace": "Browse Marketplace",
|
@@ -4377,7 +4378,9 @@
|
|
4377 |
"UpdaterWasLastRun": "The updater was last run on %s.",
|
4378 |
"UpdaterWillRunNext": "It is next scheduled to run on %s.",
|
4379 |
"WidgetLocation": "Visitor Location",
|
4380 |
-
"InstallGeoIp2": "Matomo recommends using the %1$sdbip%2$s databases, but this requires using the GeoIp2 plugin. If you install and activate it (which will require a possibly long running update), you will be able to get started using %1$sdbip%2$s databases. (Ignore this if you are using a third party plugin that provides it's own geolocation functionality.)"
|
|
|
|
|
4381 |
},
|
4382 |
"UserCountryMap": {
|
4383 |
"PluginDescription": "This plugin provides the widgets Visitor Map and Real-time Map. Note: Requires the UserCountry plugin enabled.",
|
2983 |
"AllowedUploadFormats": "You may upload a plugin or theme in .zip format via this page.",
|
2984 |
"Authors": "Authors",
|
2985 |
"Browse": "Browse",
|
2986 |
+
"SupportMatomoThankYou": "Any purchase will help fund the future of the Matomo open-source project. Thank you for your support!",
|
2987 |
"LatestMarketplaceUpdates": "Latest Marketplace Updates",
|
2988 |
"BackToMarketplace": "Back to Marketplace",
|
2989 |
"BrowseMarketplace": "Browse Marketplace",
|
4378 |
"UpdaterWasLastRun": "The updater was last run on %s.",
|
4379 |
"UpdaterWillRunNext": "It is next scheduled to run on %s.",
|
4380 |
"WidgetLocation": "Visitor Location",
|
4381 |
+
"InstallGeoIp2": "Matomo recommends using the %1$sdbip%2$s databases, but this requires using the GeoIp2 plugin. If you install and activate it (which will require a possibly long running update), you will be able to get started using %1$sdbip%2$s databases. (Ignore this if you are using a third party plugin that provides it's own geolocation functionality.)",
|
4382 |
+
"GeoIpDbIpAccuracyNote": "Note: the DBIP databases are free and can be downloaded automatically, but geolocation results (specifically city results) are not as accurate as MaxMind's. MaxMind, however, requires that you create an account even for the free database. If you want to use MaxMind's geolocation database, you can start the process %1$shere%2$s",
|
4383 |
+
"MaxMindLinkExplanation": "If you are using MaxMind's geolocation databases and you do not already know how to generate your download URL, %1$sclick here to learn how%2$s."
|
4384 |
},
|
4385 |
"UserCountryMap": {
|
4386 |
"PluginDescription": "This plugin provides the widgets Visitor Map and Real-time Map. Note: Requires the UserCountry plugin enabled.",
|
app/plugins/CoreAdminHome/Tasks.php
CHANGED
@@ -9,11 +9,14 @@
|
|
9 |
namespace Piwik\Plugins\CoreAdminHome;
|
10 |
|
11 |
use Piwik\API\Request;
|
|
|
|
|
12 |
use Piwik\ArchiveProcessor\Rules;
|
13 |
use Piwik\Archive\ArchivePurger;
|
14 |
use Piwik\Common;
|
15 |
use Piwik\Config;
|
16 |
use Piwik\Container\StaticContainer;
|
|
|
17 |
use Piwik\DataAccess\ArchiveTableCreator;
|
18 |
use Piwik\Date;
|
19 |
use Piwik\Db;
|
@@ -60,6 +63,10 @@ class Tasks extends \Piwik\Plugin\Tasks
|
|
60 |
|
61 |
public function schedule()
|
62 |
{
|
|
|
|
|
|
|
|
|
63 |
// general data purge on older archive tables, executed daily
|
64 |
$this->daily('purgeOutdatedArchives', null, self::HIGH_PRIORITY);
|
65 |
|
@@ -81,6 +88,17 @@ class Tasks extends \Piwik\Plugin\Tasks
|
|
81 |
$this->scheduleTrackingCodeReminderChecks();
|
82 |
}
|
83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
private function scheduleTrackingCodeReminderChecks()
|
85 |
{
|
86 |
$daysToTrackedVisitsCheck = (int) Config::getInstance()->General['num_days_before_tracking_code_reminder'];
|
9 |
namespace Piwik\Plugins\CoreAdminHome;
|
10 |
|
11 |
use Piwik\API\Request;
|
12 |
+
use Piwik\Archive;
|
13 |
+
use Piwik\Archive\ArchiveInvalidator;
|
14 |
use Piwik\ArchiveProcessor\Rules;
|
15 |
use Piwik\Archive\ArchivePurger;
|
16 |
use Piwik\Common;
|
17 |
use Piwik\Config;
|
18 |
use Piwik\Container\StaticContainer;
|
19 |
+
use Piwik\CronArchive;
|
20 |
use Piwik\DataAccess\ArchiveTableCreator;
|
21 |
use Piwik\Date;
|
22 |
use Piwik\Db;
|
63 |
|
64 |
public function schedule()
|
65 |
{
|
66 |
+
// for browser triggered archiving, make sure we invalidate archives once a day just to make
|
67 |
+
// sure all archives that need to be invalidated get invalidated
|
68 |
+
$this->daily('invalidateOutdatedArchives', null, self::HIGH_PRIORITY);
|
69 |
+
|
70 |
// general data purge on older archive tables, executed daily
|
71 |
$this->daily('purgeOutdatedArchives', null, self::HIGH_PRIORITY);
|
72 |
|
88 |
$this->scheduleTrackingCodeReminderChecks();
|
89 |
}
|
90 |
|
91 |
+
public function invalidateOutdatedArchives()
|
92 |
+
{
|
93 |
+
if (!Rules::isBrowserTriggerEnabled()) {
|
94 |
+
$this->logger->info("Browser triggered archiving disabled, archives will be invalidated during core:archive.");
|
95 |
+
return;
|
96 |
+
}
|
97 |
+
|
98 |
+
$cronArchive = new CronArchive();
|
99 |
+
$cronArchive->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain();
|
100 |
+
}
|
101 |
+
|
102 |
private function scheduleTrackingCodeReminderChecks()
|
103 |
{
|
104 |
$daysToTrackedVisitsCheck = (int) Config::getInstance()->General['num_days_before_tracking_code_reminder'];
|
app/plugins/CoreAdminHome/templates/optOut.twig
CHANGED
@@ -33,7 +33,14 @@
|
|
33 |
#}
|
34 |
{% if showConfirmOnly %}
|
35 |
<p>{{ 'CoreAdminHome_OptingYouOut'|translate }}</p>
|
36 |
-
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
<noscript>
|
38 |
{% endif %}
|
39 |
|
33 |
#}
|
34 |
{% if showConfirmOnly %}
|
35 |
<p>{{ 'CoreAdminHome_OptingYouOut'|translate }}</p>
|
36 |
+
<script>
|
37 |
+
{# try to update nonce in iframe, so sending it a second time works #}
|
38 |
+
try {
|
39 |
+
window.opener.document.querySelector('[name="nonce"]').value = '{{ nonce }}';
|
40 |
+
window.opener.document.querySelector('form').action = window.opener.document.querySelector('form').action.replace(/nonce=[0-9a-z]+/, 'nonce={{ nonce }}');
|
41 |
+
} catch (e) {}
|
42 |
+
window.close();
|
43 |
+
</script>
|
44 |
<noscript>
|
45 |
{% endif %}
|
46 |
|
app/plugins/CoreHome/CoreHome.php
CHANGED
@@ -11,13 +11,16 @@ namespace Piwik\Plugins\CoreHome;
|
|
11 |
use Piwik\Archive\ArchiveInvalidator;
|
12 |
use Piwik\Columns\ComputedMetricFactory;
|
13 |
use Piwik\Columns\MetricsList;
|
|
|
14 |
use Piwik\Container\StaticContainer;
|
|
|
15 |
use Piwik\IP;
|
16 |
use Piwik\Piwik;
|
17 |
use Piwik\Plugin\ArchivedMetric;
|
18 |
use Piwik\Plugin\ComputedMetric;
|
19 |
use Piwik\Plugin\ThemeStyles;
|
20 |
use Piwik\SettingsServer;
|
|
|
21 |
|
22 |
/**
|
23 |
*
|
@@ -59,6 +62,9 @@ class CoreHome extends \Piwik\Plugin
|
|
59 |
/** @var ArchiveInvalidator $archiveInvalidator */
|
60 |
$archiveInvalidator = StaticContainer::get(ArchiveInvalidator::class);
|
61 |
$cacheGeneral[ArchiveInvalidator::TRACKER_CACHE_KEY] = $archiveInvalidator->getAllRememberToInvalidateArchivedReportsLater();
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
public function addStylesheets(&$mergedContent)
|
11 |
use Piwik\Archive\ArchiveInvalidator;
|
12 |
use Piwik\Columns\ComputedMetricFactory;
|
13 |
use Piwik\Columns\MetricsList;
|
14 |
+
use Piwik\Common;
|
15 |
use Piwik\Container\StaticContainer;
|
16 |
+
use Piwik\DbHelper;
|
17 |
use Piwik\IP;
|
18 |
use Piwik\Piwik;
|
19 |
use Piwik\Plugin\ArchivedMetric;
|
20 |
use Piwik\Plugin\ComputedMetric;
|
21 |
use Piwik\Plugin\ThemeStyles;
|
22 |
use Piwik\SettingsServer;
|
23 |
+
use Piwik\Tracker\Model as TrackerModel;
|
24 |
|
25 |
/**
|
26 |
*
|
62 |
/** @var ArchiveInvalidator $archiveInvalidator */
|
63 |
$archiveInvalidator = StaticContainer::get(ArchiveInvalidator::class);
|
64 |
$cacheGeneral[ArchiveInvalidator::TRACKER_CACHE_KEY] = $archiveInvalidator->getAllRememberToInvalidateArchivedReportsLater();
|
65 |
+
|
66 |
+
$hasIndex = DbHelper::tableHasIndex(Common::prefixTable('log_visit'), 'index_idsite_idvisitor');
|
67 |
+
$cacheGeneral[TrackerModel::CACHE_KEY_INDEX_IDSITE_IDVISITOR] = $hasIndex;
|
68 |
}
|
69 |
|
70 |
public function addStylesheets(&$mergedContent)
|
app/plugins/Dashboard/Dashboard.php
CHANGED
@@ -155,11 +155,7 @@ class Dashboard extends \Piwik\Plugin
|
|
155 |
if ($advertising->areAdsForProfessionalServicesEnabled() && Plugin\Manager::getInstance()->isPluginActivated('ProfessionalServices')) {
|
156 |
$advertisingWidget = '{"uniqueId":"widgetProfessionalServicespromoServices","parameters":{"module":"ProfessionalServices","action":"promoServices"}},';
|
157 |
}
|
158 |
-
|
159 |
-
$piwikPromoWidget = '{"uniqueId":"widgetCoreHomegetDonateForm","parameters":{"module":"CoreHome","action":"getDonateForm"}}';
|
160 |
-
} else {
|
161 |
-
$piwikPromoWidget = '{"uniqueId":"widgetCoreHomegetPromoVideo","parameters":{"module":"CoreHome","action":"getPromoVideo"}}';
|
162 |
-
}
|
163 |
$defaultLayout = '[
|
164 |
[
|
165 |
{"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}},
|
155 |
if ($advertising->areAdsForProfessionalServicesEnabled() && Plugin\Manager::getInstance()->isPluginActivated('ProfessionalServices')) {
|
156 |
$advertisingWidget = '{"uniqueId":"widgetProfessionalServicespromoServices","parameters":{"module":"ProfessionalServices","action":"promoServices"}},';
|
157 |
}
|
158 |
+
$piwikPromoWidget = '{"uniqueId":"widgetCoreHomegetPromoVideo","parameters":{"module":"CoreHome","action":"getPromoVideo"}}';
|
|
|
|
|
|
|
|
|
159 |
$defaultLayout = '[
|
160 |
[
|
161 |
{"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}},
|
app/plugins/Goals/Controller.php
CHANGED
@@ -24,17 +24,12 @@ use Piwik\View;
|
|
24 |
*/
|
25 |
class Controller extends \Piwik\Plugin\Controller
|
26 |
{
|
27 |
-
const CONVERSION_RATE_PRECISION = 1;
|
28 |
-
|
29 |
/**
|
30 |
* Number of "Your top converting keywords/etc are" to display in the per Goal overview page
|
31 |
* @var int
|
32 |
*/
|
33 |
const COUNT_TOP_ROWS_TO_DISPLAY = 3;
|
34 |
|
35 |
-
const ECOMMERCE_LOG_SHOW_ORDERS = 1;
|
36 |
-
const ECOMMERCE_LOG_SHOW_ABANDONED_CARTS = 2;
|
37 |
-
|
38 |
protected $goalColumnNameToLabel = array(
|
39 |
'avg_order_revenue' => 'General_AverageOrderValue',
|
40 |
'nb_conversions' => 'Goals_ColumnConversions',
|
@@ -59,10 +54,6 @@ class Controller extends \Piwik\Plugin\Controller
|
|
59 |
}
|
60 |
}
|
61 |
|
62 |
-
if (!is_numeric($conversionRate)) {
|
63 |
-
$conversionRate = sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
|
64 |
-
}
|
65 |
-
|
66 |
return $conversionRate;
|
67 |
}
|
68 |
|
24 |
*/
|
25 |
class Controller extends \Piwik\Plugin\Controller
|
26 |
{
|
|
|
|
|
27 |
/**
|
28 |
* Number of "Your top converting keywords/etc are" to display in the per Goal overview page
|
29 |
* @var int
|
30 |
*/
|
31 |
const COUNT_TOP_ROWS_TO_DISPLAY = 3;
|
32 |
|
|
|
|
|
|
|
33 |
protected $goalColumnNameToLabel = array(
|
34 |
'avg_order_revenue' => 'General_AverageOrderValue',
|
35 |
'nb_conversions' => 'Goals_ColumnConversions',
|
54 |
}
|
55 |
}
|
56 |
|
|
|
|
|
|
|
|
|
57 |
return $conversionRate;
|
58 |
}
|
59 |
|
app/plugins/Installation/ServerFilesGenerator.php
CHANGED
@@ -167,6 +167,12 @@ Header set Cache-Control \"Cache-Control: private, no-cache, no-store\"
|
|
167 |
'/vendor',
|
168 |
'/plugins',
|
169 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
foreach ($directoriesToProtect as $directoryToProtect) {
|
171 |
@file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
|
172 |
'<?xml version="1.0" encoding="UTF-8"?>
|
@@ -176,7 +182,7 @@ Header set Cache-Control \"Cache-Control: private, no-cache, no-store\"
|
|
176 |
<requestFiltering>
|
177 |
<denyUrlSequences>
|
178 |
<add sequence=".php" />
|
179 |
-
</denyUrlSequences>
|
180 |
</requestFiltering>
|
181 |
</security>
|
182 |
</system.webServer>
|
167 |
'/vendor',
|
168 |
'/plugins',
|
169 |
);
|
170 |
+
|
171 |
+
$additionForPlugins = '
|
172 |
+
<alwaysAllowedUrls>
|
173 |
+
<add url="/plugins/HeatmapSessionRecording/configs.php" />
|
174 |
+
</alwaysAllowedUrls>';
|
175 |
+
|
176 |
foreach ($directoriesToProtect as $directoryToProtect) {
|
177 |
@file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
|
178 |
'<?xml version="1.0" encoding="UTF-8"?>
|
182 |
<requestFiltering>
|
183 |
<denyUrlSequences>
|
184 |
<add sequence=".php" />
|
185 |
+
</denyUrlSequences>' . ($directoryToProtect === '/plugins' ? $additionForPlugins : '') . '
|
186 |
</requestFiltering>
|
187 |
</security>
|
188 |
</system.webServer>
|
app/plugins/Live/Model.php
CHANGED
@@ -16,6 +16,7 @@ use Piwik\Config;
|
|
16 |
use Piwik\Container\StaticContainer;
|
17 |
use Piwik\Date;
|
18 |
use Piwik\Db;
|
|
|
19 |
use Piwik\Period;
|
20 |
use Piwik\Period\Range;
|
21 |
use Piwik\Piwik;
|
@@ -121,7 +122,7 @@ class Model
|
|
121 |
try {
|
122 |
$visits = $readerDb->fetchAll($sql, $bind);
|
123 |
} catch (Exception $e) {
|
124 |
-
$this->handleMaxExecutionTimeError($readerDb, $e, $
|
125 |
throw $e;
|
126 |
}
|
127 |
return $visits;
|
@@ -130,17 +131,16 @@ class Model
|
|
130 |
/**
|
131 |
* @param \Piwik\Tracker\Db|\Piwik\Db\AdapterInterface|\Piwik\Db $readerDb
|
132 |
* @param Exception $e
|
133 |
-
* @param $sql
|
134 |
-
* @param array $bind
|
135 |
* @param $segment
|
136 |
* @param $dateStart
|
137 |
* @param $dateEnd
|
138 |
* @param $minTimestamp
|
139 |
* @param $limit
|
|
|
140 |
*
|
141 |
* @throws MaxExecutionTimeExceededException
|
142 |
*/
|
143 |
-
public function handleMaxExecutionTimeError($readerDb, $e, $
|
144 |
{
|
145 |
// we also need to check for the 'maximum statement execution time exceeded' text as the query might be
|
146 |
// aborted at different stages and we can't really know all the possible codes at which it may be aborted etc
|
@@ -148,39 +148,41 @@ class Model
|
|
148 |
|| $readerDb->isErrNo($e, DbMigration::ERROR_CODE_MAX_EXECUTION_TIME_EXCEEDED_SORT_ABORTED)
|
149 |
|| strpos($e->getMessage(), 'maximum statement execution time exceeded') !== false;
|
150 |
|
151 |
-
if ($isMaxExecutionTimeError) {
|
152 |
-
|
|
|
|
|
|
|
153 |
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
|
171 |
-
|
172 |
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
}
|
184 |
}
|
185 |
|
186 |
/**
|
@@ -190,7 +192,7 @@ class Model
|
|
190 |
* @return bool
|
191 |
* @throws Exception
|
192 |
*/
|
193 |
-
public function isLookingAtMoreThanOneDay($dateStart, $dateEnd, $minTimestamp)
|
194 |
{
|
195 |
if (!$dateStart) {
|
196 |
if (!$minTimestamp) {
|
@@ -493,35 +495,14 @@ class Model
|
|
493 |
|
494 |
$bind = $innerQuery['bind'];
|
495 |
|
496 |
-
|
497 |
-
if ($visitorId) {
|
498 |
// for now let's not apply when looking for a specific visitor
|
499 |
-
$
|
500 |
-
}
|
501 |
-
if ($maxExecutionTimeHint) {
|
502 |
-
$innerQuery['sql'] = trim($innerQuery['sql']);
|
503 |
-
$pos = stripos($innerQuery['sql'], 'SELECT');
|
504 |
-
if ($pos !== false) {
|
505 |
-
$innerQuery['sql'] = substr_replace($innerQuery['sql'], 'SELECT ' . $maxExecutionTimeHint, $pos, strlen('SELECT'));
|
506 |
-
}
|
507 |
}
|
508 |
|
509 |
return array($innerQuery['sql'], $bind);
|
510 |
}
|
511 |
|
512 |
-
private function getMaxExecutionTimeMySQLHint()
|
513 |
-
{
|
514 |
-
$general = Config::getInstance()->General;
|
515 |
-
$maxExecutionTime = $general['live_query_max_execution_time'];
|
516 |
-
$maxExecutionTimeHint = '';
|
517 |
-
if (is_numeric($maxExecutionTime) && $maxExecutionTime > 0) {
|
518 |
-
$timeInMs = $maxExecutionTime * 1000;
|
519 |
-
$timeInMs = (int) $timeInMs;
|
520 |
-
$maxExecutionTimeHint = ' /*+ MAX_EXECUTION_TIME('.$timeInMs.') */ ';
|
521 |
-
}
|
522 |
-
return $maxExecutionTimeHint;
|
523 |
-
}
|
524 |
-
|
525 |
/**
|
526 |
* @param $idSite
|
527 |
* @return Site
|
16 |
use Piwik\Container\StaticContainer;
|
17 |
use Piwik\Date;
|
18 |
use Piwik\Db;
|
19 |
+
use Piwik\DbHelper;
|
20 |
use Piwik\Period;
|
21 |
use Piwik\Period\Range;
|
22 |
use Piwik\Piwik;
|
122 |
try {
|
123 |
$visits = $readerDb->fetchAll($sql, $bind);
|
124 |
} catch (Exception $e) {
|
125 |
+
$this->handleMaxExecutionTimeError($readerDb, $e, $segment, $dateStart, $dateEnd, $minTimestamp, $limit, ['sql' => $sql, 'bind' => $bind,]);
|
126 |
throw $e;
|
127 |
}
|
128 |
return $visits;
|
131 |
/**
|
132 |
* @param \Piwik\Tracker\Db|\Piwik\Db\AdapterInterface|\Piwik\Db $readerDb
|
133 |
* @param Exception $e
|
|
|
|
|
134 |
* @param $segment
|
135 |
* @param $dateStart
|
136 |
* @param $dateEnd
|
137 |
* @param $minTimestamp
|
138 |
* @param $limit
|
139 |
+
* @param $parameters
|
140 |
*
|
141 |
* @throws MaxExecutionTimeExceededException
|
142 |
*/
|
143 |
+
public static function handleMaxExecutionTimeError($readerDb, $e, $segment, $dateStart, $dateEnd, $minTimestamp, $limit, $parameters)
|
144 |
{
|
145 |
// we also need to check for the 'maximum statement execution time exceeded' text as the query might be
|
146 |
// aborted at different stages and we can't really know all the possible codes at which it may be aborted etc
|
148 |
|| $readerDb->isErrNo($e, DbMigration::ERROR_CODE_MAX_EXECUTION_TIME_EXCEEDED_SORT_ABORTED)
|
149 |
|| strpos($e->getMessage(), 'maximum statement execution time exceeded') !== false;
|
150 |
|
151 |
+
if (false === $isMaxExecutionTimeError) {
|
152 |
+
return;
|
153 |
+
}
|
154 |
+
|
155 |
+
$message = '';
|
156 |
|
157 |
+
if (self::isLookingAtMoreThanOneDay($dateStart, $dateEnd, $minTimestamp)) {
|
158 |
+
$message .= ' ' . Piwik::translate('Live_QueryMaxExecutionTimeExceededReasonDateRange');
|
159 |
+
}
|
160 |
|
161 |
+
if (!empty($segment)) {
|
162 |
+
$message .= ' ' . Piwik::translate('Live_QueryMaxExecutionTimeExceededReasonSegment');
|
163 |
+
}
|
164 |
|
165 |
+
$limitThatCannotBeSelectedInUiButOnlyApi = 550;
|
166 |
+
if ($limit > $limitThatCannotBeSelectedInUiButOnlyApi) {
|
167 |
+
$message .= ' ' . Piwik::translate('Live_QueryMaxExecutionTimeExceededLimit');
|
168 |
+
}
|
169 |
|
170 |
+
if (empty($message)) {
|
171 |
+
$message .= ' ' . Piwik::translate('Live_QueryMaxExecutionTimeExceededReasonUnknown');
|
172 |
+
}
|
173 |
|
174 |
+
$message = Piwik::translate('Live_QueryMaxExecutionTimeExceeded') . ' ' . $message;
|
175 |
|
176 |
+
$params = array_merge($parameters, [
|
177 |
+
'segment' => $segment, 'limit' => $limit
|
178 |
+
]);
|
179 |
|
180 |
+
/**
|
181 |
+
* @ignore
|
182 |
+
* @internal
|
183 |
+
*/
|
184 |
+
Piwik::postEvent('Live.queryMaxExecutionTimeExceeded', array($params));
|
185 |
+
throw new MaxExecutionTimeExceededException($message);
|
|
|
186 |
}
|
187 |
|
188 |
/**
|
192 |
* @return bool
|
193 |
* @throws Exception
|
194 |
*/
|
195 |
+
public static function isLookingAtMoreThanOneDay($dateStart, $dateEnd, $minTimestamp)
|
196 |
{
|
197 |
if (!$dateStart) {
|
198 |
if (!$minTimestamp) {
|
495 |
|
496 |
$bind = $innerQuery['bind'];
|
497 |
|
498 |
+
if (!$visitorId) {
|
|
|
499 |
// for now let's not apply when looking for a specific visitor
|
500 |
+
$innerQuery['sql'] = DbHelper::addMaxExecutionTimeHintToQuery($innerQuery['sql'], Config::getInstance()->General['live_query_max_execution_time']);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
501 |
}
|
502 |
|
503 |
return array($innerQuery['sql'], $bind);
|
504 |
}
|
505 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
506 |
/**
|
507 |
* @param $idSite
|
508 |
* @return Site
|
app/plugins/Marketplace/Api/Client.php
CHANGED
@@ -8,11 +8,14 @@
|
|
8 |
*/
|
9 |
namespace Piwik\Plugins\Marketplace\Api;
|
10 |
|
|
|
11 |
use Piwik\Cache;
|
12 |
use Piwik\Common;
|
13 |
use Piwik\Container\StaticContainer;
|
|
|
14 |
use Piwik\Filesystem;
|
15 |
use Piwik\Http;
|
|
|
16 |
use Piwik\Plugin;
|
17 |
use Piwik\Plugins\Marketplace\Environment;
|
18 |
use Piwik\Plugins\Marketplace\Api\Service;
|
@@ -177,8 +180,9 @@ class Client
|
|
177 |
}
|
178 |
|
179 |
$params = array('plugins' => $params);
|
|
|
180 |
|
181 |
-
$hasUpdates = $this->fetch('plugins/checkUpdates',
|
182 |
|
183 |
if (empty($hasUpdates)) {
|
184 |
return array();
|
8 |
*/
|
9 |
namespace Piwik\Plugins\Marketplace\Api;
|
10 |
|
11 |
+
use Piwik\API\Request;
|
12 |
use Piwik\Cache;
|
13 |
use Piwik\Common;
|
14 |
use Piwik\Container\StaticContainer;
|
15 |
+
use Piwik\DataTable;
|
16 |
use Piwik\Filesystem;
|
17 |
use Piwik\Http;
|
18 |
+
use Piwik\Piwik;
|
19 |
use Piwik\Plugin;
|
20 |
use Piwik\Plugins\Marketplace\Environment;
|
21 |
use Piwik\Plugins\Marketplace\Api\Service;
|
180 |
}
|
181 |
|
182 |
$params = array('plugins' => $params);
|
183 |
+
$params = array('plugins' => json_encode($params));
|
184 |
|
185 |
+
$hasUpdates = $this->fetch('plugins/checkUpdates', $params);
|
186 |
|
187 |
if (empty($hasUpdates)) {
|
188 |
return array();
|
app/plugins/Marketplace/templates/getPremiumFeatures.twig
CHANGED
@@ -1,5 +1,8 @@
|
|
1 |
<div class="getNewPlugins getPremiumFeatures widgetBody">
|
2 |
<div class="row">
|
|
|
|
|
|
|
3 |
{% for plugin in plugins %}
|
4 |
<div class="col s12 m4">
|
5 |
|
1 |
<div class="getNewPlugins getPremiumFeatures widgetBody">
|
2 |
<div class="row">
|
3 |
+
<div class="col s12 m12">
|
4 |
+
<h3 style="margin-bottom: 28px;">{{ 'Marketplace_SupportMatomoThankYou'|translate }}</h3></div>
|
5 |
+
|
6 |
{% for plugin in plugins %}
|
7 |
<div class="col s12 m4">
|
8 |
|
app/plugins/Monolog/Processor/ExceptionToTextProcessor.php
CHANGED
@@ -85,6 +85,10 @@ class ExceptionToTextProcessor
|
|
85 |
|
86 |
public static function getWholeBacktrace(\Exception $exception, $shouldPrintBacktrace = true)
|
87 |
{
|
|
|
|
|
|
|
|
|
88 |
$message = "";
|
89 |
|
90 |
$e = $exception;
|
85 |
|
86 |
public static function getWholeBacktrace(\Exception $exception, $shouldPrintBacktrace = true)
|
87 |
{
|
88 |
+
if (!$shouldPrintBacktrace) {
|
89 |
+
return $exception->getMessage();
|
90 |
+
}
|
91 |
+
|
92 |
$message = "";
|
93 |
|
94 |
$e = $exception;
|
app/plugins/Proxy/Controller.php
CHANGED
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\Proxy;
|
|
11 |
use Piwik\AssetManager;
|
12 |
use Piwik\AssetManager\UIAsset;
|
13 |
use Piwik\Common;
|
|
|
14 |
use Piwik\Piwik;
|
15 |
use Piwik\ProxyHttp;
|
16 |
use Piwik\Url;
|
@@ -33,7 +34,11 @@ class Controller extends \Piwik\Plugin\Controller
|
|
33 |
*/
|
34 |
public function getCss()
|
35 |
{
|
36 |
-
|
|
|
|
|
|
|
|
|
37 |
ProxyHttp::serverStaticFile($cssMergedFile->getAbsoluteLocation(), "text/css");
|
38 |
}
|
39 |
|
11 |
use Piwik\AssetManager;
|
12 |
use Piwik\AssetManager\UIAsset;
|
13 |
use Piwik\Common;
|
14 |
+
use Piwik\Exception\StylesheetLessCompileException;
|
15 |
use Piwik\Piwik;
|
16 |
use Piwik\ProxyHttp;
|
17 |
use Piwik\Url;
|
34 |
*/
|
35 |
public function getCss()
|
36 |
{
|
37 |
+
try {
|
38 |
+
$cssMergedFile = AssetManager::getInstance()->getMergedStylesheet();
|
39 |
+
} catch (StylesheetLessCompileException $exception) {
|
40 |
+
$cssMergedFile = AssetManager::getInstance()->getMergedStylesheet();
|
41 |
+
}
|
42 |
ProxyHttp::serverStaticFile($cssMergedFile->getAbsoluteLocation(), "text/css");
|
43 |
}
|
44 |
|
app/plugins/TagManager/API/PreviewCookie.php
CHANGED
@@ -28,13 +28,13 @@ class PreviewCookie extends Cookie
|
|
28 |
public function enable($idSite, $idContainer)
|
29 |
{
|
30 |
$this->set($this->getCookieValueName($idSite, $idContainer), '1');
|
31 |
-
$this->save();
|
32 |
}
|
33 |
|
34 |
public function disable($idSite, $idContainer)
|
35 |
{
|
36 |
$this->set($this->getCookieValueName($idSite, $idContainer), null);
|
37 |
-
$this->save();
|
38 |
}
|
39 |
|
40 |
}
|
28 |
public function enable($idSite, $idContainer)
|
29 |
{
|
30 |
$this->set($this->getCookieValueName($idSite, $idContainer), '1');
|
31 |
+
$this->save('Lax');
|
32 |
}
|
33 |
|
34 |
public function disable($idSite, $idContainer)
|
35 |
{
|
36 |
$this->set($this->getCookieValueName($idSite, $idContainer), null);
|
37 |
+
$this->save('Lax');
|
38 |
}
|
39 |
|
40 |
}
|
app/plugins/TagManager/Context/BaseContext.php
CHANGED
@@ -228,7 +228,8 @@ abstract class BaseContext
|
|
228 |
return strrpos($haystack, $needle, $offset);
|
229 |
}
|
230 |
|
231 |
-
|
|
|
232 |
{
|
233 |
if (is_scalar($value) && preg_match_all('/{{.+?}}/', $value, $matches)) {
|
234 |
$multiVars = [];
|
@@ -236,18 +237,18 @@ abstract class BaseContext
|
|
236 |
$pos = 0;
|
237 |
|
238 |
do {
|
239 |
-
|
240 |
|
241 |
$end = false;
|
242 |
if ($start !== false) {
|
243 |
// only if string contains a {{ we need to look to see if we find a matching end string
|
244 |
-
|
245 |
}
|
246 |
|
247 |
if ($end !== false) {
|
248 |
// now this might seem random, but it is basically to detect if there are the brackets two times there
|
249 |
// like "foo{{notExisting{{PageUrl}}" then we still detect "{{PageUrl}}"
|
250 |
-
|
251 |
}
|
252 |
|
253 |
if ($start === false || $end === false) {
|
228 |
return strrpos($haystack, $needle, $offset);
|
229 |
}
|
230 |
|
231 |
+
|
232 |
+
protected function parameterToVariableJs($value, $container)
|
233 |
{
|
234 |
if (is_scalar($value) && preg_match_all('/{{.+?}}/', $value, $matches)) {
|
235 |
$multiVars = [];
|
237 |
$pos = 0;
|
238 |
|
239 |
do {
|
240 |
+
$start = $this->mb_strpos($value, '{{', $pos);
|
241 |
|
242 |
$end = false;
|
243 |
if ($start !== false) {
|
244 |
// only if string contains a {{ we need to look to see if we find a matching end string
|
245 |
+
$end = $this->mb_strpos($value, '}}', $start);
|
246 |
}
|
247 |
|
248 |
if ($end !== false) {
|
249 |
// now this might seem random, but it is basically to detect if there are the brackets two times there
|
250 |
// like "foo{{notExisting{{PageUrl}}" then we still detect "{{PageUrl}}"
|
251 |
+
$start = $this->mb_strpos(Common::mb_substr($value, 0, $end), '{{', $pos);
|
252 |
}
|
253 |
|
254 |
if ($start === false || $end === false) {
|
app/plugins/TagManager/Dao/VariablesDao.php
CHANGED
@@ -33,7 +33,7 @@ class VariablesDao extends BaseDao implements TagManagerDao
|
|
33 |
`created_date` DATETIME NOT NULL,
|
34 |
`updated_date` DATETIME NOT NULL,
|
35 |
`deleted_date` DATETIME NULL,
|
36 |
-
PRIMARY KEY(`idvariable`), KEY
|
37 |
// we cannot set a unique key on (`idsite`, `idcontainerversion`, `name`) because we soft delete tags and want to make sure names can be used again after deleting an entry
|
38 |
}
|
39 |
|
33 |
`created_date` DATETIME NOT NULL,
|
34 |
`updated_date` DATETIME NOT NULL,
|
35 |
`deleted_date` DATETIME NULL,
|
36 |
+
PRIMARY KEY(`idvariable`), KEY (`idsite`, `idcontainerversion`)");
|
37 |
// we cannot set a unique key on (`idsite`, `idcontainerversion`, `name`) because we soft delete tags and want to make sure names can be used again after deleting an entry
|
38 |
}
|
39 |
|
app/plugins/Transitions/API.php
CHANGED
@@ -12,15 +12,18 @@ namespace Piwik\Plugins\Transitions;
|
|
12 |
use Exception;
|
13 |
use Piwik\ArchiveProcessor;
|
14 |
use Piwik\Common;
|
|
|
15 |
use Piwik\DataAccess\LogAggregator;
|
16 |
use Piwik\DataArray;
|
17 |
use Piwik\DataTable;
|
18 |
use Piwik\DataTable\Row;
|
|
|
19 |
use Piwik\Metrics;
|
20 |
use Piwik\Period;
|
21 |
use Piwik\Piwik;
|
22 |
use Piwik\Plugins\Actions\Actions;
|
23 |
use Piwik\Plugins\Actions\ArchivingHelper;
|
|
|
24 |
use Piwik\RankingQuery;
|
25 |
use Piwik\Segment;
|
26 |
use Piwik\Segment\SegmentExpression;
|
@@ -81,25 +84,40 @@ class API extends \Piwik\Plugin\API
|
|
81 |
'date' => Period\Factory::build($period->getLabel(), $date)->getLocalizedShortString()
|
82 |
);
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
}
|
102 |
|
|
|
103 |
// replace column names in the data tables
|
104 |
$reportNames = array(
|
105 |
'previousPages' => true,
|
@@ -290,7 +308,8 @@ class API extends \Piwik\Plugin\API
|
|
290 |
$metrics,
|
291 |
$rankingQuery,
|
292 |
$joinLogActionColumn,
|
293 |
-
$secondaryOrderBy = "`name`"
|
|
|
294 |
);
|
295 |
|
296 |
$dataTables = $this->makeDataTablesFollowingActions($types, $data);
|
@@ -342,7 +361,7 @@ class API extends \Piwik\Plugin\API
|
|
342 |
$where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
|
343 |
|
344 |
$metrics = array(Metrics::INDEX_NB_VISITS);
|
345 |
-
$data = $logAggregator->queryVisitsByDimension($dimensions, $where, $selects, $metrics, $rankingQuery);
|
346 |
|
347 |
$referrerData = array();
|
348 |
$referrerSubData = array();
|
@@ -433,7 +452,8 @@ class API extends \Piwik\Plugin\API
|
|
433 |
$metrics,
|
434 |
$rankingQuery,
|
435 |
$joinLogActionOn,
|
436 |
-
$secondaryOrderBy = "`name`"
|
|
|
437 |
);
|
438 |
|
439 |
$loops = 0;
|
12 |
use Exception;
|
13 |
use Piwik\ArchiveProcessor;
|
14 |
use Piwik\Common;
|
15 |
+
use Piwik\Config;
|
16 |
use Piwik\DataAccess\LogAggregator;
|
17 |
use Piwik\DataArray;
|
18 |
use Piwik\DataTable;
|
19 |
use Piwik\DataTable\Row;
|
20 |
+
use Piwik\Db;
|
21 |
use Piwik\Metrics;
|
22 |
use Piwik\Period;
|
23 |
use Piwik\Piwik;
|
24 |
use Piwik\Plugins\Actions\Actions;
|
25 |
use Piwik\Plugins\Actions\ArchivingHelper;
|
26 |
+
use Piwik\Plugins\Live\Model;
|
27 |
use Piwik\RankingQuery;
|
28 |
use Piwik\Segment;
|
29 |
use Piwik\Segment\SegmentExpression;
|
84 |
'date' => Period\Factory::build($period->getLabel(), $date)->getLocalizedShortString()
|
85 |
);
|
86 |
|
87 |
+
try {
|
88 |
+
$partsArray = explode(',', $parts);
|
89 |
+
if ($parts == 'all' || in_array('internalReferrers', $partsArray)) {
|
90 |
+
$this->addInternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
|
91 |
+
}
|
92 |
+
if ($parts == 'all' || in_array('followingActions', $partsArray)) {
|
93 |
+
$includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray);
|
94 |
+
$this->addFollowingActions($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops);
|
95 |
+
}
|
96 |
+
if ($parts == 'all' || in_array('externalReferrers', $partsArray)) {
|
97 |
+
$this->addExternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
|
98 |
+
}
|
99 |
|
100 |
+
// derive the number of exits from the other metrics
|
101 |
+
if ($parts == 'all') {
|
102 |
+
$report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews']
|
103 |
+
- $this->getTotalTransitionsToFollowingActions()
|
104 |
+
- $report['pageMetrics']['loops'];
|
105 |
+
}
|
106 |
+
} catch (\Exception $e) {
|
107 |
+
Model::handleMaxExecutionTimeError(
|
108 |
+
Db::getReader(),
|
109 |
+
$e,
|
110 |
+
$segment->getString(),
|
111 |
+
$period->getDateStart(),
|
112 |
+
$period->getDateEnd(),
|
113 |
+
0,
|
114 |
+
Config::getInstance()->General['live_query_max_execution_time'],
|
115 |
+
['method' => 'Transitions.getTransitionsForAction', 'actionName' => $actionName, 'actionType' => $actionType]
|
116 |
+
);
|
117 |
+
throw $e;
|
118 |
}
|
119 |
|
120 |
+
|
121 |
// replace column names in the data tables
|
122 |
$reportNames = array(
|
123 |
'previousPages' => true,
|
308 |
$metrics,
|
309 |
$rankingQuery,
|
310 |
$joinLogActionColumn,
|
311 |
+
$secondaryOrderBy = "`name`",
|
312 |
+
Config::getInstance()->General['live_query_max_execution_time']
|
313 |
);
|
314 |
|
315 |
$dataTables = $this->makeDataTablesFollowingActions($types, $data);
|
361 |
$where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
|
362 |
|
363 |
$metrics = array(Metrics::INDEX_NB_VISITS);
|
364 |
+
$data = $logAggregator->queryVisitsByDimension($dimensions, $where, $selects, $metrics, $rankingQuery, false, Config::getInstance()->General['live_query_max_execution_time']);
|
365 |
|
366 |
$referrerData = array();
|
367 |
$referrerSubData = array();
|
452 |
$metrics,
|
453 |
$rankingQuery,
|
454 |
$joinLogActionOn,
|
455 |
+
$secondaryOrderBy = "`name`",
|
456 |
+
Config::getInstance()->General['live_query_max_execution_time']
|
457 |
);
|
458 |
|
459 |
$loops = 0;
|
app/plugins/UserCountry/templates/_updaterManage.twig
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
<div ng-show="locationUpdater.geoipDatabaseInstalled" id="geoipdb-update-info">
|
2 |
<p>
|
3 |
-
{{ 'UserCountry_GeoIPUpdaterInstructions'|translate('<a href="http://www.maxmind.com/?rId=piwik">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/?refid=mtm">','</a>')|raw }}
|
4 |
<br/><br/>
|
5 |
-
{% if dbipLiteUrl|default is not empty %}{{ 'UserCountry_GeoLiteCityLink'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl|e('html_attr')~'">',dbipLiteUrl|e('html'),'</a>')|raw }}{% endif %}
|
|
|
6 |
|
7 |
<span ng-show="locationUpdater.geoipDatabaseInstalled">
|
8 |
<br/><br/>{{ 'UserCountry_GeoIPUpdaterIntro'|translate }}:
|
1 |
<div ng-show="locationUpdater.geoipDatabaseInstalled" id="geoipdb-update-info">
|
2 |
<p>
|
3 |
+
{{ 'UserCountry_GeoIPUpdaterInstructions'|translate('<a href="http://www.maxmind.com/?rId=piwik" rel="noreferrer noopener">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/?refid=mtm">','</a>')|raw }}
|
4 |
<br/><br/>
|
5 |
+
{% if dbipLiteUrl|default is not empty %}{{ 'UserCountry_GeoLiteCityLink'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl|e('html_attr')~'">',dbipLiteUrl|e('html'),'</a>')|raw }}<br/><br/>{% endif %}
|
6 |
+
{{ 'UserCountry_MaxMindLinkExplanation'|translate('<a href="https://matomo.org/faq/how-to/how-do-i-get-the-geolocation-download-url-for-the-free-maxmind-db/" rel="noreferrer noopener" target="_blank">', '</a>')|raw }}
|
7 |
|
8 |
<span ng-show="locationUpdater.geoipDatabaseInstalled">
|
9 |
<br/><br/>{{ 'UserCountry_GeoIPUpdaterIntro'|translate }}:
|
app/plugins/UserCountry/templates/adminIndex.twig
CHANGED
@@ -19,7 +19,7 @@
|
|
19 |
{% if dbipLiteUrl|default is not empty %}
|
20 |
<p>{{ 'UserCountry_HowToSetupGeoIPIntro'|translate }}</p>
|
21 |
<ul style="list-style:disc !important;margin-left:2em;">
|
22 |
-
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step1'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl~'">','</a>','<a rel="noreferrer noopener" target="_blank" href="http://db-ip.com/?refid=mtm">','</a>')|raw }}
|
23 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step2'|translate("'"~dbipLiteFilename~"'",'<strong>','</strong>','<strong>'~dbipLiteDesiredFilename~'</strong>')|raw }}</li>
|
24 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
|
25 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step4'|translate }}</li>
|
@@ -30,6 +30,12 @@
|
|
30 |
<p> </p>
|
31 |
{% endif %}
|
32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
<div class="row">
|
34 |
<div class="col s12 push-m9 m3">{{ 'General_InfoFor'|translate(thisIP) }}</div>
|
35 |
</div>
|
@@ -147,7 +153,7 @@
|
|
147 |
<div id="manage-geoip-dbs">
|
148 |
<div class="row" id="geoipdb-screen1">
|
149 |
<div class="geoipdb-column-1 col s6">
|
150 |
-
<p>{{ 'UserCountry_IWantToDownloadFreeGeoIP'|translate|raw }}
|
151 |
</div>
|
152 |
<div class="geoipdb-column-2 col s6">
|
153 |
<p>{{ 'UserCountry_IPurchasedGeoIPDBs'|translate('<a rel="noreferrer noopener" href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/db/?refid=mtm">','</a>')|raw }}</p>
|
@@ -163,6 +169,9 @@
|
|
163 |
value="{{ 'General_GetStarted'|translate }}..." id="start-automatic-update-geoip"/>
|
164 |
</div>
|
165 |
</div>
|
|
|
|
|
|
|
166 |
</div>
|
167 |
</div>
|
168 |
{% if dbipLiteUrl|default is not empty %}
|
19 |
{% if dbipLiteUrl|default is not empty %}
|
20 |
<p>{{ 'UserCountry_HowToSetupGeoIPIntro'|translate }}</p>
|
21 |
<ul style="list-style:disc !important;margin-left:2em;">
|
22 |
+
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step1'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl~'">','</a>','<a rel="noreferrer noopener" target="_blank" href="http://db-ip.com/?refid=mtm">','</a>')|raw }}<sup>*</sup></li>
|
23 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step2'|translate("'"~dbipLiteFilename~"'",'<strong>','</strong>','<strong>'~dbipLiteDesiredFilename~'</strong>')|raw }}</li>
|
24 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
|
25 |
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step4'|translate }}</li>
|
30 |
<p> </p>
|
31 |
{% endif %}
|
32 |
|
33 |
+
<div class="row">
|
34 |
+
<div class="col s12">
|
35 |
+
<p><sup>{% if not isThereWorkingProvider %}*{% endif %} <small>{{ 'UserCountry_GeoIpDbIpAccuracyNote'|translate('<a href="https://dev.maxmind.com/geoip/geoip2/geolite2/?rId=piwik" rel="noreferrer noopener" target="_blank">', '</a>')|raw }}.</small></sup></p>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
|
39 |
<div class="row">
|
40 |
<div class="col s12 push-m9 m3">{{ 'General_InfoFor'|translate(thisIP) }}</div>
|
41 |
</div>
|
153 |
<div id="manage-geoip-dbs">
|
154 |
<div class="row" id="geoipdb-screen1">
|
155 |
<div class="geoipdb-column-1 col s6">
|
156 |
+
<p>{{ 'UserCountry_IWantToDownloadFreeGeoIP'|translate|raw }}<sup><small>*</small></sup></p>
|
157 |
</div>
|
158 |
<div class="geoipdb-column-2 col s6">
|
159 |
<p>{{ 'UserCountry_IPurchasedGeoIPDBs'|translate('<a rel="noreferrer noopener" href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/db/?refid=mtm">','</a>')|raw }}</p>
|
169 |
value="{{ 'General_GetStarted'|translate }}..." id="start-automatic-update-geoip"/>
|
170 |
</div>
|
171 |
</div>
|
172 |
+
<div class="row">
|
173 |
+
<p><sup>* <small>{{ 'UserCountry_GeoIpDbIpAccuracyNote'|translate('<a href="https://dev.maxmind.com/geoip/geoip2/geolite2/?rId=piwik" rel="noreferrer noopener" target="_blank">', '</a>')|raw }}.</small></sup></p>
|
174 |
+
</div>
|
175 |
</div>
|
176 |
</div>
|
177 |
{% if dbipLiteUrl|default is not empty %}
|
app/vendor/autoload.php
CHANGED
@@ -4,4 +4,4 @@
|
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
-
return
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
+
return ComposerAutoloaderInitd94bcb60246118fb0b76072b15fbc1cf::getLoader();
|
app/vendor/composer/autoload_classmap.php
CHANGED
@@ -2603,6 +2603,7 @@ return array(
|
|
2603 |
'Piwik\\Updates\\Updates_3_12_0_b1' => $baseDir . '/core/Updates/3.12.0-b1.php',
|
2604 |
'Piwik\\Updates\\Updates_3_12_0_b7' => $baseDir . '/core/Updates/3.12.0-b7.php',
|
2605 |
'Piwik\\Updates\\Updates_3_13_1_b2' => $baseDir . '/core/Updates/3.13.1-b2.php',
|
|
|
2606 |
'Piwik\\Updates\\Updates_3_5_0_b2' => $baseDir . '/core/Updates/3.5.0-b2.php',
|
2607 |
'Piwik\\Updates\\Updates_3_5_0_b4' => $baseDir . '/core/Updates/3.5.0-b4.php',
|
2608 |
'Piwik\\Updates\\Updates_3_5_0_rc2' => $baseDir . '/core/Updates/3.5.0-rc2.php',
|
2603 |
'Piwik\\Updates\\Updates_3_12_0_b1' => $baseDir . '/core/Updates/3.12.0-b1.php',
|
2604 |
'Piwik\\Updates\\Updates_3_12_0_b7' => $baseDir . '/core/Updates/3.12.0-b7.php',
|
2605 |
'Piwik\\Updates\\Updates_3_13_1_b2' => $baseDir . '/core/Updates/3.13.1-b2.php',
|
2606 |
+
'Piwik\\Updates\\Updates_3_13_4_b1' => $baseDir . '/core/Updates/3.13.4-b1.php',
|
2607 |
'Piwik\\Updates\\Updates_3_5_0_b2' => $baseDir . '/core/Updates/3.5.0-b2.php',
|
2608 |
'Piwik\\Updates\\Updates_3_5_0_b4' => $baseDir . '/core/Updates/3.5.0-b4.php',
|
2609 |
'Piwik\\Updates\\Updates_3_5_0_rc2' => $baseDir . '/core/Updates/3.5.0-rc2.php',
|
app/vendor/composer/autoload_real.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
-
class
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
@@ -13,15 +13,18 @@ class ComposerAutoloaderInite67cb932b779452f67cda37373f10cc8
|
|
13 |
}
|
14 |
}
|
15 |
|
|
|
|
|
|
|
16 |
public static function getLoader()
|
17 |
{
|
18 |
if (null !== self::$loader) {
|
19 |
return self::$loader;
|
20 |
}
|
21 |
|
22 |
-
spl_autoload_register(array('
|
23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
-
spl_autoload_unregister(array('
|
25 |
|
26 |
$includePaths = require __DIR__ . '/include_paths.php';
|
27 |
$includePaths[] = get_include_path();
|
@@ -31,7 +34,7 @@ class ComposerAutoloaderInite67cb932b779452f67cda37373f10cc8
|
|
31 |
if ($useStaticLoader) {
|
32 |
require_once __DIR__ . '/autoload_static.php';
|
33 |
|
34 |
-
call_user_func(\Composer\Autoload\
|
35 |
} else {
|
36 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
37 |
foreach ($map as $namespace => $path) {
|
@@ -52,19 +55,19 @@ class ComposerAutoloaderInite67cb932b779452f67cda37373f10cc8
|
|
52 |
$loader->register(false);
|
53 |
|
54 |
if ($useStaticLoader) {
|
55 |
-
$includeFiles = Composer\Autoload\
|
56 |
} else {
|
57 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
58 |
}
|
59 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
60 |
-
|
61 |
}
|
62 |
|
63 |
return $loader;
|
64 |
}
|
65 |
}
|
66 |
|
67 |
-
function
|
68 |
{
|
69 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
70 |
require $file;
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
+
class ComposerAutoloaderInitd94bcb60246118fb0b76072b15fbc1cf
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
13 |
}
|
14 |
}
|
15 |
|
16 |
+
/**
|
17 |
+
* @return \Composer\Autoload\ClassLoader
|
18 |
+
*/
|
19 |
public static function getLoader()
|
20 |
{
|
21 |
if (null !== self::$loader) {
|
22 |
return self::$loader;
|
23 |
}
|
24 |
|
25 |
+
spl_autoload_register(array('ComposerAutoloaderInitd94bcb60246118fb0b76072b15fbc1cf', 'loadClassLoader'), true, false);
|
26 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
27 |
+
spl_autoload_unregister(array('ComposerAutoloaderInitd94bcb60246118fb0b76072b15fbc1cf', 'loadClassLoader'));
|
28 |
|
29 |
$includePaths = require __DIR__ . '/include_paths.php';
|
30 |
$includePaths[] = get_include_path();
|
34 |
if ($useStaticLoader) {
|
35 |
require_once __DIR__ . '/autoload_static.php';
|
36 |
|
37 |
+
call_user_func(\Composer\Autoload\ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::getInitializer($loader));
|
38 |
} else {
|
39 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
40 |
foreach ($map as $namespace => $path) {
|
55 |
$loader->register(false);
|
56 |
|
57 |
if ($useStaticLoader) {
|
58 |
+
$includeFiles = Composer\Autoload\ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$files;
|
59 |
} else {
|
60 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
61 |
}
|
62 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
63 |
+
composerRequired94bcb60246118fb0b76072b15fbc1cf($fileIdentifier, $file);
|
64 |
}
|
65 |
|
66 |
return $loader;
|
67 |
}
|
68 |
}
|
69 |
|
70 |
+
function composerRequired94bcb60246118fb0b76072b15fbc1cf($fileIdentifier, $file)
|
71 |
{
|
72 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
73 |
require $file;
|
app/vendor/composer/autoload_static.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
-
class
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
@@ -2845,6 +2845,7 @@ class ComposerStaticInite67cb932b779452f67cda37373f10cc8
|
|
2845 |
'Piwik\\Updates\\Updates_3_12_0_b1' => __DIR__ . '/../..' . '/core/Updates/3.12.0-b1.php',
|
2846 |
'Piwik\\Updates\\Updates_3_12_0_b7' => __DIR__ . '/../..' . '/core/Updates/3.12.0-b7.php',
|
2847 |
'Piwik\\Updates\\Updates_3_13_1_b2' => __DIR__ . '/../..' . '/core/Updates/3.13.1-b2.php',
|
|
|
2848 |
'Piwik\\Updates\\Updates_3_5_0_b2' => __DIR__ . '/../..' . '/core/Updates/3.5.0-b2.php',
|
2849 |
'Piwik\\Updates\\Updates_3_5_0_b4' => __DIR__ . '/../..' . '/core/Updates/3.5.0-b4.php',
|
2850 |
'Piwik\\Updates\\Updates_3_5_0_rc2' => __DIR__ . '/../..' . '/core/Updates/3.5.0-rc2.php',
|
@@ -3603,11 +3604,11 @@ class ComposerStaticInite67cb932b779452f67cda37373f10cc8
|
|
3603 |
public static function getInitializer(ClassLoader $loader)
|
3604 |
{
|
3605 |
return \Closure::bind(function () use ($loader) {
|
3606 |
-
$loader->prefixLengthsPsr4 =
|
3607 |
-
$loader->prefixDirsPsr4 =
|
3608 |
-
$loader->prefixesPsr0 =
|
3609 |
-
$loader->fallbackDirsPsr0 =
|
3610 |
-
$loader->classMap =
|
3611 |
|
3612 |
}, null, ClassLoader::class);
|
3613 |
}
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
+
class ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
2845 |
'Piwik\\Updates\\Updates_3_12_0_b1' => __DIR__ . '/../..' . '/core/Updates/3.12.0-b1.php',
|
2846 |
'Piwik\\Updates\\Updates_3_12_0_b7' => __DIR__ . '/../..' . '/core/Updates/3.12.0-b7.php',
|
2847 |
'Piwik\\Updates\\Updates_3_13_1_b2' => __DIR__ . '/../..' . '/core/Updates/3.13.1-b2.php',
|
2848 |
+
'Piwik\\Updates\\Updates_3_13_4_b1' => __DIR__ . '/../..' . '/core/Updates/3.13.4-b1.php',
|
2849 |
'Piwik\\Updates\\Updates_3_5_0_b2' => __DIR__ . '/../..' . '/core/Updates/3.5.0-b2.php',
|
2850 |
'Piwik\\Updates\\Updates_3_5_0_b4' => __DIR__ . '/../..' . '/core/Updates/3.5.0-b4.php',
|
2851 |
'Piwik\\Updates\\Updates_3_5_0_rc2' => __DIR__ . '/../..' . '/core/Updates/3.5.0-rc2.php',
|
3604 |
public static function getInitializer(ClassLoader $loader)
|
3605 |
{
|
3606 |
return \Closure::bind(function () use ($loader) {
|
3607 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$prefixLengthsPsr4;
|
3608 |
+
$loader->prefixDirsPsr4 = ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$prefixDirsPsr4;
|
3609 |
+
$loader->prefixesPsr0 = ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$prefixesPsr0;
|
3610 |
+
$loader->fallbackDirsPsr0 = ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$fallbackDirsPsr0;
|
3611 |
+
$loader->classMap = ComposerStaticInitd94bcb60246118fb0b76072b15fbc1cf::$classMap;
|
3612 |
|
3613 |
}, null, ClassLoader::class);
|
3614 |
}
|
assets/css/admin-style.css
CHANGED
@@ -67,6 +67,11 @@ ol.matomo-list {
|
|
67 |
margin: 0 20px 10px;
|
68 |
}
|
69 |
|
|
|
|
|
|
|
|
|
|
|
70 |
.matomo-footer ul a {
|
71 |
color: #37474f;
|
72 |
}
|
67 |
margin: 0 20px 10px;
|
68 |
}
|
69 |
|
70 |
+
.matomo-header-icon {
|
71 |
+
height: 32px;
|
72 |
+
vertical-align: top;
|
73 |
+
}
|
74 |
+
|
75 |
.matomo-footer ul a {
|
76 |
color: #37474f;
|
77 |
}
|
assets/img/activity_log.jpg
DELETED
Binary file
|
assets/img/cohorts.png
DELETED
Binary file
|
assets/img/custom_reports.png
DELETED
Binary file
|
assets/img/form_analytics.jpg
DELETED
Binary file
|
assets/img/funnels.png
DELETED
Binary file
|
assets/img/heatmap.jpg
DELETED
Binary file
|
assets/img/logo-full.png
ADDED
Binary file
|
assets/img/logo.png
ADDED
Binary file
|
assets/img/media_analytics.jpg
DELETED
Binary file
|
assets/img/multi_attribution.png
DELETED
Binary file
|
assets/img/premiumbundle.png
DELETED
Binary file
|
assets/img/search_engine_keywords.png
DELETED
Binary file
|
assets/img/users_flow.png
DELETED
Binary file
|
assets/js/admin.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(document).on( 'click', '#matomo-referral .notice-dismiss', function () {
|
2 |
+
var data = {'action': 'matomo_referral_dismiss_admin_notice'};
|
3 |
+
jQuery.post( ajaxurl, data );
|
4 |
+
});jQuery(document).on( 'click', '#matomo-referral .matomo-dismiss-forever', function () {
|
5 |
+
var data = {'action': 'matomo_referral_dismiss_admin_notice', forever: '1'};
|
6 |
+
jQuery.post( ajaxurl, data );
|
7 |
+
});
|
assets/js/asset_manager_core_js.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
/* Matomo Javascript - cb=
|
2 |
|
3 |
/*! jQuery Browser - v0.1.0 - 3/23/2012
|
4 |
* https://github.com/jquery/jquery-browser
|
1 |
+
/* Matomo Javascript - cb=e4651ae98a7539215063c712af63f642*/
|
2 |
|
3 |
/*! jQuery Browser - v0.1.0 - 3/23/2012
|
4 |
* https://github.com/jquery/jquery-browser
|
classes/WpMatomo.php
CHANGED
@@ -86,6 +86,11 @@ class WpMatomo {
|
|
86 |
$site_sync->register_hooks();
|
87 |
$user_sync = new UserSync();
|
88 |
$user_sync->register_hooks();
|
|
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
|
91 |
$tracking_code = new TrackingCode( self::$settings );
|
@@ -126,7 +131,7 @@ class WpMatomo {
|
|
126 |
add_action(
|
127 |
'admin_notices',
|
128 |
function () use ( $upload_path ) {
|
129 |
-
echo '<div class="error"><p>' . __( 'Matomo Analytics requires the uploads directory
|
130 |
}
|
131 |
);
|
132 |
}
|
@@ -136,22 +141,6 @@ class WpMatomo {
|
|
136 |
return false;
|
137 |
}
|
138 |
|
139 |
-
if ( ! matomo_has_compatible_content_dir() ) {
|
140 |
-
add_action(
|
141 |
-
'init',
|
142 |
-
function () {
|
143 |
-
if ( self::is_admin_user() ) {
|
144 |
-
add_action(
|
145 |
-
'admin_notices',
|
146 |
-
function () {
|
147 |
-
echo '<div class="error"><p>' . __( 'It looks like you are maybe using a custom WordPress content directory. The Matomo reporting/admin pages might not work. You may be able to workaround this.', 'matomo' ) . ' <a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/wordpress/what-are-the-requirements-for-matomo-for-wordpress/">' . esc_html__( 'Learn more', 'matomo' ) . '</a>.</p></div>';
|
148 |
-
}
|
149 |
-
);
|
150 |
-
}
|
151 |
-
}
|
152 |
-
);
|
153 |
-
}
|
154 |
-
|
155 |
return true;
|
156 |
}
|
157 |
|
86 |
$site_sync->register_hooks();
|
87 |
$user_sync = new UserSync();
|
88 |
$user_sync->register_hooks();
|
89 |
+
|
90 |
+
$referral = new \WpMatomo\Referral();
|
91 |
+
if ($referral->should_show()) {
|
92 |
+
$referral->register_hooks();
|
93 |
+
}
|
94 |
}
|
95 |
|
96 |
$tracking_code = new TrackingCode( self::$settings );
|
131 |
add_action(
|
132 |
'admin_notices',
|
133 |
function () use ( $upload_path ) {
|
134 |
+
echo '<div class="error"><p>' . sprintf(__( 'Matomo Analytics requires the uploads directory %s to be writable. Please make the directory writable for it to work.', 'matomo' ), '(' . esc_html( dirname( $upload_path ) ) . ')') . '</p></div>';
|
135 |
}
|
136 |
);
|
137 |
}
|
141 |
return false;
|
142 |
}
|
143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
return true;
|
145 |
}
|
146 |
|
classes/WpMatomo/Admin/Admin.php
CHANGED
@@ -22,11 +22,12 @@ class Admin {
|
|
22 |
public function __construct( $settings ) {
|
23 |
new Menu( $settings );
|
24 |
|
25 |
-
add_action( 'admin_enqueue_scripts', array( $this, '
|
26 |
}
|
27 |
|
28 |
-
public function
|
29 |
wp_enqueue_style( 'matomo_admin_css', plugins_url( 'assets/css/admin-style.css', MATOMO_ANALYTICS_FILE ), false, '1.0.0' );
|
|
|
30 |
}
|
31 |
|
32 |
}
|
22 |
public function __construct( $settings ) {
|
23 |
new Menu( $settings );
|
24 |
|
25 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts' ) );
|
26 |
}
|
27 |
|
28 |
+
public function load_scripts() {
|
29 |
wp_enqueue_style( 'matomo_admin_css', plugins_url( 'assets/css/admin-style.css', MATOMO_ANALYTICS_FILE ), false, '1.0.0' );
|
30 |
+
wp_enqueue_script( 'matomo_admin_js', plugins_url( 'assets/js/admin.js', MATOMO_ANALYTICS_FILE ), array( 'jquery' ), '1.0', true );
|
31 |
}
|
32 |
|
33 |
}
|
classes/WpMatomo/Admin/AdminSettings.php
CHANGED
@@ -17,10 +17,12 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
17 |
}
|
18 |
|
19 |
class AdminSettings {
|
20 |
-
const TAB_TRACKING
|
21 |
-
const TAB_ACCESS
|
22 |
-
const TAB_EXCLUSIONS
|
23 |
-
const TAB_PRIVACY
|
|
|
|
|
24 |
|
25 |
/**
|
26 |
* @var Settings
|
@@ -40,12 +42,16 @@ class AdminSettings {
|
|
40 |
$access_settings = new AccessSettings( $access, $this->settings );
|
41 |
$tracking = new TrackingSettings( $this->settings );
|
42 |
$exclusions = new ExclusionSettings( $this->settings );
|
|
|
43 |
$privacy = new PrivacySettings();
|
|
|
44 |
$setting_tabs = array(
|
45 |
self::TAB_TRACKING => $tracking,
|
46 |
self::TAB_ACCESS => $access_settings,
|
47 |
self::TAB_PRIVACY => $privacy,
|
48 |
self::TAB_EXCLUSIONS => $exclusions,
|
|
|
|
|
49 |
);
|
50 |
|
51 |
$setting_tabs = apply_filters( 'matomo_setting_tabs', $setting_tabs, $this->settings );
|
17 |
}
|
18 |
|
19 |
class AdminSettings {
|
20 |
+
const TAB_TRACKING = 'tracking';
|
21 |
+
const TAB_ACCESS = 'access';
|
22 |
+
const TAB_EXCLUSIONS = 'exlusions';
|
23 |
+
const TAB_PRIVACY = 'privacy';
|
24 |
+
const TAB_GEOLOCATION = 'geolocation';
|
25 |
+
const TAB_ADVANCED = 'advanced';
|
26 |
|
27 |
/**
|
28 |
* @var Settings
|
42 |
$access_settings = new AccessSettings( $access, $this->settings );
|
43 |
$tracking = new TrackingSettings( $this->settings );
|
44 |
$exclusions = new ExclusionSettings( $this->settings );
|
45 |
+
$geolocation = new GeolocationSettings( $this->settings );
|
46 |
$privacy = new PrivacySettings();
|
47 |
+
$advanced = new AdvancedSettings();
|
48 |
$setting_tabs = array(
|
49 |
self::TAB_TRACKING => $tracking,
|
50 |
self::TAB_ACCESS => $access_settings,
|
51 |
self::TAB_PRIVACY => $privacy,
|
52 |
self::TAB_EXCLUSIONS => $exclusions,
|
53 |
+
self::TAB_GEOLOCATION => $geolocation,
|
54 |
+
self::TAB_ADVANCED => $advanced,
|
55 |
);
|
56 |
|
57 |
$setting_tabs = apply_filters( 'matomo_setting_tabs', $setting_tabs, $this->settings );
|
classes/WpMatomo/Admin/AdvancedSettings.php
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
*/
|
9 |
+
|
10 |
+
namespace WpMatomo\Admin;
|
11 |
+
|
12 |
+
use Piwik\Config;
|
13 |
+
use Piwik\IP;
|
14 |
+
use WpMatomo\Bootstrap;
|
15 |
+
use WpMatomo\Capabilities;
|
16 |
+
use WpMatomo\Settings;
|
17 |
+
use WpMatomo\Site;
|
18 |
+
use WpMatomo\TrackingCode\TrackingCodeGenerator;
|
19 |
+
|
20 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
21 |
+
exit; // if accessed directly
|
22 |
+
}
|
23 |
+
|
24 |
+
class AdvancedSettings implements AdminSettingsInterface {
|
25 |
+
const FORM_NAME = 'matomo';
|
26 |
+
const NONCE_NAME = 'matomo_advanced';
|
27 |
+
|
28 |
+
public static $valid_host_headers = array(
|
29 |
+
'HTTP_CLIENT_IP',
|
30 |
+
'HTTP_X_REAL_IP',
|
31 |
+
'HTTP_X_FORWARDED_FOR',
|
32 |
+
'HTTP_X_FORWARDED',
|
33 |
+
'HTTP_FORWARDED_FOR',
|
34 |
+
'HTTP_FORWARDED',
|
35 |
+
'HTTP_CF_CONNECTING_IP',
|
36 |
+
'HTTP_TRUE_CLIENT_IP',
|
37 |
+
'HTTP_X_CLUSTER_CLIENT_IP',
|
38 |
+
);
|
39 |
+
|
40 |
+
public function get_title() {
|
41 |
+
return esc_html__( 'Advanced', 'matomo' );
|
42 |
+
}
|
43 |
+
|
44 |
+
private function update_if_submitted() {
|
45 |
+
if ( isset( $_POST )
|
46 |
+
&& ! empty( $_POST[ self::FORM_NAME ] )
|
47 |
+
&& is_admin()
|
48 |
+
&& check_admin_referer( self::NONCE_NAME )
|
49 |
+
&& $this->can_user_manage() ) {
|
50 |
+
$this->apply_settings();
|
51 |
+
|
52 |
+
return true;
|
53 |
+
}
|
54 |
+
|
55 |
+
return false;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function can_user_manage() {
|
59 |
+
return current_user_can( Capabilities::KEY_SUPERUSER );
|
60 |
+
}
|
61 |
+
|
62 |
+
private function apply_settings() {
|
63 |
+
Bootstrap::do_bootstrap();
|
64 |
+
$config = Config::getInstance();
|
65 |
+
$general = $config->General;
|
66 |
+
$general['proxy_client_headers'] = array();
|
67 |
+
|
68 |
+
if (!empty($_POST[ self::FORM_NAME ]['proxy_client_header'])) {
|
69 |
+
$client_header = $_POST[ self::FORM_NAME ]['proxy_client_header'];
|
70 |
+
if (in_array($client_header, self::$valid_host_headers, true)) {
|
71 |
+
$general['proxy_client_headers'][] = $client_header;
|
72 |
+
}
|
73 |
+
}
|
74 |
+
$config->General = $general;
|
75 |
+
$config->forceSave();
|
76 |
+
|
77 |
+
return true;
|
78 |
+
}
|
79 |
+
|
80 |
+
public function show_settings() {
|
81 |
+
$was_updated = $this->update_if_submitted();
|
82 |
+
|
83 |
+
$matomo_client_headers = array();
|
84 |
+
Bootstrap::do_bootstrap();
|
85 |
+
$config = Config::getInstance();
|
86 |
+
$general = $config->General;
|
87 |
+
if (!empty($general['proxy_client_headers']) && is_array($general['proxy_client_headers'])) {
|
88 |
+
$matomo_client_headers = $general['proxy_client_headers'];
|
89 |
+
}
|
90 |
+
|
91 |
+
$matomo_detected_ip = IP::getIpFromHeader();
|
92 |
+
|
93 |
+
include dirname( __FILE__ ) . '/views/advanced_settings.php';
|
94 |
+
}
|
95 |
+
|
96 |
+
|
97 |
+
|
98 |
+
}
|
classes/WpMatomo/Admin/GeolocationSettings.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
*/
|
9 |
+
|
10 |
+
namespace WpMatomo\Admin;
|
11 |
+
|
12 |
+
use WpMatomo\Capabilities;
|
13 |
+
use WpMatomo\ScheduledTasks;
|
14 |
+
use WpMatomo\Settings;
|
15 |
+
|
16 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
17 |
+
exit; // if accessed directly
|
18 |
+
}
|
19 |
+
|
20 |
+
class GeolocationSettings implements AdminSettingsInterface {
|
21 |
+
const NONCE_NAME = 'matomo_geolocation';
|
22 |
+
const FORM_NAME = 'matomo_maxmind_license';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var Settings
|
26 |
+
*/
|
27 |
+
private $settings;
|
28 |
+
|
29 |
+
public function __construct( Settings $settings ) {
|
30 |
+
$this->settings = $settings;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function get_title() {
|
34 |
+
return esc_html__( 'Geolocation', 'matomo' );
|
35 |
+
}
|
36 |
+
|
37 |
+
private function update_if_submitted() {
|
38 |
+
if ( isset( $_POST )
|
39 |
+
&& isset( $_POST[ self::FORM_NAME ] )
|
40 |
+
&& is_admin()
|
41 |
+
&& check_admin_referer( self::NONCE_NAME )
|
42 |
+
&& current_user_can( Capabilities::KEY_SUPERUSER ) ) {
|
43 |
+
|
44 |
+
$maxmind_license = stripslashes($_POST[ self::FORM_NAME ]);
|
45 |
+
|
46 |
+
if (empty($maxmind_license)) {
|
47 |
+
$maxmind_license = '';
|
48 |
+
} elseif (strlen($maxmind_license) > 20 || strlen($maxmind_license) < 7 || !ctype_graph($maxmind_license)) {
|
49 |
+
return false;
|
50 |
+
}
|
51 |
+
|
52 |
+
$this->settings->apply_changes(array(
|
53 |
+
'maxmind_license_key' => $maxmind_license
|
54 |
+
));
|
55 |
+
|
56 |
+
// update geoip in the backgronud
|
57 |
+
wp_schedule_single_event( time() + 10, ScheduledTasks::EVENT_GEOIP );
|
58 |
+
|
59 |
+
return true;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
public function show_settings() {
|
64 |
+
$invalid_format = $this->update_if_submitted() === false;
|
65 |
+
|
66 |
+
$current_maxmind_license = $this->settings->get_global_option('maxmind_license_key');
|
67 |
+
|
68 |
+
include dirname( __FILE__ ) . '/views/geolocation_settings.php';
|
69 |
+
}
|
70 |
+
|
71 |
+
}
|
classes/WpMatomo/Admin/Info.php
CHANGED
@@ -9,18 +9,59 @@
|
|
9 |
|
10 |
namespace WpMatomo\Admin;
|
11 |
|
|
|
|
|
12 |
if ( ! defined( 'ABSPATH' ) ) {
|
13 |
exit; // if accessed directly
|
14 |
}
|
15 |
|
16 |
class Info {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
public function show() {
|
19 |
-
|
20 |
}
|
21 |
|
22 |
public function show_multisite() {
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
|
9 |
|
10 |
namespace WpMatomo\Admin;
|
11 |
|
12 |
+
use WpMatomo\Capabilities;
|
13 |
+
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
exit; // if accessed directly
|
16 |
}
|
17 |
|
18 |
class Info {
|
19 |
+
const NONCE_NAME = 'matomo_newsletter';
|
20 |
+
const FORM_NAME = 'matomo_newsletter_signup';
|
21 |
+
|
22 |
+
private function update_if_submitted() {
|
23 |
+
if ( isset( $_POST )
|
24 |
+
&& !empty( $_POST[ self::FORM_NAME ] )
|
25 |
+
&& is_admin()
|
26 |
+
&& check_admin_referer( self::NONCE_NAME )
|
27 |
+
&& $this->show_newsletter_signup()
|
28 |
+
&& current_user_can( Capabilities::KEY_VIEW ) ) {
|
29 |
+
|
30 |
+
$user = wp_get_current_user();
|
31 |
+
$locale = explode('_', get_user_locale($user->ID));
|
32 |
+
wp_remote_get('https://api.matomo.org/1.0/subscribeNewsletter/?' . http_build_query(array(
|
33 |
+
'email' => $user->user_email,
|
34 |
+
'wordpress' => 1,
|
35 |
+
'language' => $locale[0],
|
36 |
+
)));
|
37 |
+
update_user_meta($user->ID, self::FORM_NAME, '1');
|
38 |
+
|
39 |
+
return true;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
private function show_newsletter_signup() {
|
44 |
+
if (!is_user_logged_in()) {
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
|
48 |
+
$user = wp_get_current_user();
|
49 |
+
return !get_user_meta($user->ID, self::FORM_NAME, true);
|
50 |
+
}
|
51 |
|
52 |
public function show() {
|
53 |
+
$this->render('info');
|
54 |
}
|
55 |
|
56 |
public function show_multisite() {
|
57 |
+
$this->render('info_multisite');
|
58 |
+
}
|
59 |
+
|
60 |
+
private function render($template) {
|
61 |
+
$signedup_newsletter = $this->update_if_submitted();
|
62 |
+
$show_newsletter = $this->show_newsletter_signup();
|
63 |
+
|
64 |
+
include dirname( __FILE__ ) . '/views/' . $template . '.php';
|
65 |
}
|
66 |
|
67 |
|
classes/WpMatomo/Admin/Menu.php
CHANGED
@@ -178,8 +178,8 @@ class Menu {
|
|
178 |
if ( $can_matomo_be_managed ) {
|
179 |
add_submenu_page(
|
180 |
self::$parent_slug,
|
181 |
-
__( '
|
182 |
-
__( '
|
183 |
Capabilities::KEY_SUPERUSER,
|
184 |
self::SLUG_SYSTEM_REPORT,
|
185 |
array(
|
178 |
if ( $can_matomo_be_managed ) {
|
179 |
add_submenu_page(
|
180 |
self::$parent_slug,
|
181 |
+
__( 'Diagnostics', 'matomo' ),
|
182 |
+
__( 'Diagnostics', 'matomo' ),
|
183 |
Capabilities::KEY_SUPERUSER,
|
184 |
self::SLUG_SYSTEM_REPORT,
|
185 |
array(
|
classes/WpMatomo/Admin/SystemReport.php
CHANGED
@@ -283,8 +283,8 @@ class SystemReport {
|
|
283 |
'comment' => $tmp_dir,
|
284 |
);
|
285 |
|
286 |
-
if ( ! empty( $
|
287 |
-
$custom_path = rtrim( $
|
288 |
$path_exists = file_exists( $custom_path );
|
289 |
$comment = '';
|
290 |
if ( ! $path_exists ) {
|
@@ -690,6 +690,21 @@ class SystemReport {
|
|
690 |
}
|
691 |
}
|
692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
693 |
return $rows;
|
694 |
}
|
695 |
|
283 |
'comment' => $tmp_dir,
|
284 |
);
|
285 |
|
286 |
+
if ( ! empty( $_SERVER['MATOMO_WP_ROOT_PATH'] ) ) {
|
287 |
+
$custom_path = rtrim( $_SERVER['MATOMO_WP_ROOT_PATH'], '/' ) . '/wp-load.php';
|
288 |
$path_exists = file_exists( $custom_path );
|
289 |
$comment = '';
|
290 |
if ( ! $path_exists ) {
|
690 |
}
|
691 |
}
|
692 |
|
693 |
+
$compatible_content_dir = matomo_has_compatible_content_dir();
|
694 |
+
if ($compatible_content_dir === true) {
|
695 |
+
$rows[] = array(
|
696 |
+
'name' => 'Compatible content directory',
|
697 |
+
'value' => true,
|
698 |
+
);
|
699 |
+
} else {
|
700 |
+
$rows[] = array(
|
701 |
+
'name' => 'Compatible content directory',
|
702 |
+
'value' => $compatible_content_dir,
|
703 |
+
'is_warning' => true,
|
704 |
+
'comment' => __( 'It looks like you are maybe using a custom WordPress content directory. The Matomo reporting/admin pages might not work. You may be able to workaround this.', 'matomo' ) . ' ' . __( 'Learn more', 'matomo' ) . ': https://matomo.org/faq/wordpress/how-do-i-make-matomo-for-wordpress-work-when-i-have-a-custom-content-directory/'
|
705 |
+
);
|
706 |
+
}
|
707 |
+
|
708 |
return $rows;
|
709 |
}
|
710 |
|
classes/WpMatomo/Admin/views/access.php
CHANGED
@@ -58,7 +58,8 @@ use WpMatomo\Admin\AccessSettings;
|
|
58 |
rel="noopener"><?php esc_html_e( 'Write', 'matomo' ); ?></a>,
|
59 |
<a href="https://matomo.org/faq/general/faq_69/" target="_blank" rel="noopener"><?php esc_html_e( 'Admin', 'matomo' ); ?></a>,
|
60 |
<a href="https://matomo.org/faq/general/faq_35/" target="_blank"
|
61 |
-
rel="noopener"><?php esc_html_e( 'Super User', 'matomo' ); ?></a
|
|
|
62 |
</p>
|
63 |
|
64 |
<h2><?php esc_html_e( 'Roles', 'matomo' ); ?></h2>
|
@@ -93,4 +94,4 @@ esc_html_e(
|
|
93 |
?>
|
94 |
<li><?php echo esc_html( $matomo_cap_name ); ?></li>
|
95 |
<?php } ?>
|
96 |
-
</ul>
|
58 |
rel="noopener"><?php esc_html_e( 'Write', 'matomo' ); ?></a>,
|
59 |
<a href="https://matomo.org/faq/general/faq_69/" target="_blank" rel="noopener"><?php esc_html_e( 'Admin', 'matomo' ); ?></a>,
|
60 |
<a href="https://matomo.org/faq/general/faq_35/" target="_blank"
|
61 |
+
rel="noopener"><?php esc_html_e( 'Super User', 'matomo' ); ?></a><br/>
|
62 |
+
<?php esc_html_e( 'Want to redirect to the home page when not logged in?', 'matomo' ); ?> <a href="https://matomo.org/faq/wordpress/how-do-i-hide-my-wordpress-login-url-when-someone-accesses-a-matomo-report-directly/" target="_blank" rel="noreferrer noopener"><?php esc_html_e( 'Learn more', 'matomo' ); ?></a>
|
63 |
</p>
|
64 |
|
65 |
<h2><?php esc_html_e( 'Roles', 'matomo' ); ?></h2>
|
94 |
?>
|
95 |
<li><?php echo esc_html( $matomo_cap_name ); ?></li>
|
96 |
<?php } ?>
|
97 |
+
</ul>
|
classes/WpMatomo/Admin/views/advanced_settings.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
* Code Based on
|
9 |
+
* @author André Bräkling
|
10 |
+
* https://github.com/braekling/WP-Matomo
|
11 |
+
*
|
12 |
+
*/
|
13 |
+
|
14 |
+
use WpMatomo\Admin\AdvancedSettings;
|
15 |
+
|
16 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
17 |
+
exit;
|
18 |
+
}
|
19 |
+
/** @var bool $was_updated */
|
20 |
+
/** @var string $matomo_detected_ip */
|
21 |
+
/** @var array $matomo_client_headers */
|
22 |
+
?>
|
23 |
+
|
24 |
+
<?php
|
25 |
+
if ( $was_updated ) {
|
26 |
+
include 'update_notice_clear_cache.php';
|
27 |
+
}
|
28 |
+
?>
|
29 |
+
<form method="post">
|
30 |
+
<?php wp_nonce_field( AdvancedSettings::NONCE_NAME ); ?>
|
31 |
+
|
32 |
+
<p><?php esc_html_e( 'Advanced settings', 'matomo' ); ?></p>
|
33 |
+
<table class="matomo-tracking-form widefat">
|
34 |
+
<tbody>
|
35 |
+
<tr>
|
36 |
+
<th width="20%" scope="row"><label for="matomo[proxy_client_header]"><?php esc_html_e( 'Proxy IP headers', 'matomo' ) ?>:</label>
|
37 |
+
</th>
|
38 |
+
<td>
|
39 |
+
<?php
|
40 |
+
echo '<span style="white-space: nowrap;display: inline-block;"><input type="radio" ' . ( empty($matomo_client_headers) ? 'checked="checked" ' : '' ) . ' value="REMOTE_ADDR" name="matomo[proxy_client_header]" /> <code>REMOTE_ADDR</code> ' . ( ! empty( $_SERVER[ 'REMOTE_ADDR' ] ) ? esc_html( $_SERVER[ 'REMOTE_ADDR' ] ) : esc_html__( 'No value found', 'matomo' ) ) . ' (' . esc_html__( 'Default', 'matomo' ) .')</span>';
|
41 |
+
foreach ( AdvancedSettings::$valid_host_headers as $host_header ) {
|
42 |
+
echo '<span style="white-space: nowrap;display: inline-block;"><input type="radio" ' . ( in_array( $host_header, $matomo_client_headers, true ) ? 'checked="checked" ' : '' ) . 'value="'. esc_attr($host_header).'" name="matomo[proxy_client_header]" /> <code>' . $host_header . '</code> ' . ( ! empty( $_SERVER[ $host_header ] ) ? esc_html( $_SERVER[ $host_header ] ) : esc_html__( 'No value found', 'matomo' ) ) . ' </span>';
|
43 |
+
}
|
44 |
+
?>
|
45 |
+
</td>
|
46 |
+
<td width="50%">
|
47 |
+
<?php esc_html_e( 'We detected you have the following IP address:', 'matomo' ) ?>
|
48 |
+
<?php echo esc_html( $matomo_detected_ip ) ?> <br>
|
49 |
+
<?php echo sprintf(esc_html__( 'To compare this value with your actual IP address %1$splease click here%2$s.', 'matomo' ), '<a rel="noreferrer noopener" target="_blank" href="https://matomo.org/ip.php">', '</a>') ?><br><br>
|
50 |
+
<?php esc_html_e( 'Should your IP address not match the above value, your WordPress might be behind a proxy and you may need to select a different HTTP header depending on which of the values on the left shows your correct IP address.', 'matomo' ) ?>
|
51 |
+
</td>
|
52 |
+
</tr>
|
53 |
+
<tr>
|
54 |
+
<td colspan="3"><p class="submit"><input name="Submit" type="submit" class="button-primary"
|
55 |
+
value="<?php esc_attr_e( 'Save Changes', 'matomo' ) ?>"/></p></td>
|
56 |
+
</tr>
|
57 |
+
</tbody>
|
58 |
+
</table>
|
59 |
+
</form>
|
classes/WpMatomo/Admin/views/exclusion_settings.php
CHANGED
@@ -42,8 +42,7 @@ if ( $was_updated ) {
|
|
42 |
<tbody>
|
43 |
|
44 |
<tr>
|
45 |
-
<th width="20%" scope="row"><label
|
46 |
-
for="%2$s"><?php esc_html_e( 'Tracking filter', 'matomo' ); ?></label>:
|
47 |
</th>
|
48 |
<td>
|
49 |
<?php
|
@@ -59,8 +58,7 @@ if ( $was_updated ) {
|
|
59 |
</td>
|
60 |
</tr>
|
61 |
<tr>
|
62 |
-
<th width="20%" scope="row"><label
|
63 |
-
for="%2$s"><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedIps' ) ); ?></label>:
|
64 |
</th>
|
65 |
<td width="30%">
|
66 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_ips', esc_html( $excluded_ips ) ); ?>
|
@@ -81,8 +79,7 @@ if ( $was_updated ) {
|
|
81 |
</td>
|
82 |
</tr>
|
83 |
<tr>
|
84 |
-
<th scope="row"><label
|
85 |
-
for="%2$s"><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedQueryParameters' ) ); ?></label>:
|
86 |
</th>
|
87 |
<td>
|
88 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_query_parameters', esc_html( $excluded_query_params ) ); ?>
|
@@ -93,8 +90,7 @@ if ( $was_updated ) {
|
|
93 |
</td>
|
94 |
</tr>
|
95 |
<tr>
|
96 |
-
<th scope="row"><label
|
97 |
-
for="%2$s"><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedUserAgents' ) ); ?></label>:
|
98 |
</th>
|
99 |
<td>
|
100 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_user_agents', esc_html( $excluded_user_agents ) ); ?>
|
@@ -109,8 +105,7 @@ if ( $was_updated ) {
|
|
109 |
</td>
|
110 |
</tr>
|
111 |
<tr>
|
112 |
-
<th scope="row"><label
|
113 |
-
for="%2$s"><?php echo esc_html( Piwik::translate( 'SitesManager_KeepURLFragmentsLong' ) ); ?></label>:
|
114 |
</th>
|
115 |
<td>
|
116 |
<?php echo sprintf( '<input type="checkbox" value="1" %2$s name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">', 'keep_url_fragments', $keep_url_fragments ? ' checked="checked"' : '' ); ?>
|
42 |
<tbody>
|
43 |
|
44 |
<tr>
|
45 |
+
<th width="20%" scope="row"><label><?php esc_html_e( 'Tracking filter', 'matomo' ); ?></label>:
|
|
|
46 |
</th>
|
47 |
<td>
|
48 |
<?php
|
58 |
</td>
|
59 |
</tr>
|
60 |
<tr>
|
61 |
+
<th width="20%" scope="row"><label><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedIps' ) ); ?></label>:
|
|
|
62 |
</th>
|
63 |
<td width="30%">
|
64 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_ips', esc_html( $excluded_ips ) ); ?>
|
79 |
</td>
|
80 |
</tr>
|
81 |
<tr>
|
82 |
+
<th scope="row"><label><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedQueryParameters' ) ); ?></label>:
|
|
|
83 |
</th>
|
84 |
<td>
|
85 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_query_parameters', esc_html( $excluded_query_params ) ); ?>
|
90 |
</td>
|
91 |
</tr>
|
92 |
<tr>
|
93 |
+
<th scope="row"><label><?php echo esc_html( Piwik::translate( 'SitesManager_GlobalListExcludedUserAgents' ) ); ?></label>:
|
|
|
94 |
</th>
|
95 |
<td>
|
96 |
<?php echo sprintf( '<textarea cols="40" rows="4" id="%1$s" name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">%2$s</textarea>', 'excluded_user_agents', esc_html( $excluded_user_agents ) ); ?>
|
105 |
</td>
|
106 |
</tr>
|
107 |
<tr>
|
108 |
+
<th scope="row"><label><?php echo esc_html( Piwik::translate( 'SitesManager_KeepURLFragmentsLong' ) ); ?></label>:
|
|
|
109 |
</th>
|
110 |
<td>
|
111 |
<?php echo sprintf( '<input type="checkbox" value="1" %2$s name="' . esc_attr( ExclusionSettings::FORM_NAME ) . '[%1$s]">', 'keep_url_fragments', $keep_url_fragments ? ' checked="checked"' : '' ); ?>
|
classes/WpMatomo/Admin/views/geolocation_settings.php
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
* Code Based on
|
9 |
+
* @author André Bräkling
|
10 |
+
* @package WP_Matomo
|
11 |
+
* https://github.com/braekling/matomo
|
12 |
+
*
|
13 |
+
*/
|
14 |
+
|
15 |
+
use WpMatomo\Admin\GeolocationSettings;
|
16 |
+
|
17 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
+
/** @var bool $was_updated */
|
21 |
+
/** @var bool $invalid_format */
|
22 |
+
/** @var string $current_maxmind_license */
|
23 |
+
|
24 |
+
if ($invalid_format) { ?>
|
25 |
+
<div class="updated notice error">
|
26 |
+
<p><?php esc_html_e( 'It looks like the MaxMind license key has a wrong format.', 'matomo' ); ?></p>
|
27 |
+
</div>
|
28 |
+
<?php
|
29 |
+
}
|
30 |
+
?>
|
31 |
+
|
32 |
+
<form method="post">
|
33 |
+
<?php wp_nonce_field( GeolocationSettings::NONCE_NAME ); ?>
|
34 |
+
|
35 |
+
<p>
|
36 |
+
<?php esc_html_e( 'On this page you can configure how Matomo detects the locations of your visitors.', 'matomo' ); ?>
|
37 |
+
</p>
|
38 |
+
<p>
|
39 |
+
<?php esc_html_e('To detect the location of a visitor, the IP address of a visitor is looked up in a so called geolocation database. This is automatically taken care of for you. However, the freely available database DB-IP we are using is sometimes less accurate than other freely available geolocation databases. This applies to the free and paid version of DB-IP. An alternative geolocation database is called MaxMind which has a free and a paid version as well. Because of GDPR we cannot configure this database automatically for you.', 'matomo'); ?>
|
40 |
+
<br><br>
|
41 |
+
<?php
|
42 |
+
echo sprintf(
|
43 |
+
__( 'To use MaxMind instead of the default DB-IP geolocation database %1$s get a MaxMind license key%2$s and then configure this key below.', 'matomo' ),
|
44 |
+
'<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to/how-do-i-get-a-license-key-for-the-maxmind-geolocation-database/">', '</a>'
|
45 |
+
);
|
46 |
+
?>
|
47 |
+
</p>
|
48 |
+
|
49 |
+
<table class="matomo-tracking-form widefat">
|
50 |
+
<tbody>
|
51 |
+
<tr>
|
52 |
+
<th scope="row" style="vertical-align: top;">
|
53 |
+
<label for="<?php echo esc_attr( GeolocationSettings::FORM_NAME ) ?>"><?php esc_html_e( 'MaxMind License Key', 'matomo' ); ?></label>:
|
54 |
+
</th>
|
55 |
+
<td>
|
56 |
+
<input size="20" type="text" maxlength="20"
|
57 |
+
id="<?php echo esc_attr( GeolocationSettings::FORM_NAME ) ?>"
|
58 |
+
name="<?php echo esc_attr( GeolocationSettings::FORM_NAME ) ?>" value="<?php echo esc_attr($current_maxmind_license) ?>">
|
59 |
+
</td>
|
60 |
+
<td>
|
61 |
+
<?php esc_html_e('Leave the field empty and click on "Save Changes" to configure the default DB-IP database.', 'matomo') ?>
|
62 |
+
<?php esc_html_e('When configured, your WordPress will send an HTTP request to a MaxMind server to download an approx. 60MB database and store it in your "wp-content/uploads/matomo" directory.', 'matomo') ?>
|
63 |
+
</td>
|
64 |
+
</tr>
|
65 |
+
<tr>
|
66 |
+
<td colspan="3">
|
67 |
+
<p class="submit"><input name="Submit" type="submit" class="button-primary"
|
68 |
+
value="<?php echo esc_attr__( 'Save Changes', 'matomo' ); ?>"/></p>
|
69 |
+
</td>
|
70 |
+
</tr>
|
71 |
+
|
72 |
+
</tbody>
|
73 |
+
</table>
|
74 |
+
</form>
|
classes/WpMatomo/Admin/views/get_started.php
CHANGED
@@ -38,7 +38,7 @@ if ( empty( $show_this_page ) ) {
|
|
38 |
?>
|
39 |
|
40 |
<?php if ( $settings->is_tracking_enabled() ) { ?>
|
41 |
-
<h2>1. <?php esc_html_e( 'Tracking is enabled', 'matomo' ); ?> <span class="dashicons dashicons-yes"></span></h2>
|
42 |
<p><a href="<?php echo AdminSettings::make_url( AdminSettings::TAB_TRACKING ); ?>"><?php esc_html_e( 'Click here to configure your tracking code.', 'matomo' ); ?></a></p>
|
43 |
|
44 |
<?php } else { ?>
|
38 |
?>
|
39 |
|
40 |
<?php if ( $settings->is_tracking_enabled() ) { ?>
|
41 |
+
<h2>1. <?php esc_html_e( 'Tracking is enabled', 'matomo' ); ?> <span class="dashicons dashicons-yes" style="color: green;"></span></h2>
|
42 |
<p><a href="<?php echo AdminSettings::make_url( AdminSettings::TAB_TRACKING ); ?>"><?php esc_html_e( 'Click here to configure your tracking code.', 'matomo' ); ?></a></p>
|
43 |
|
44 |
<?php } else { ?>
|
classes/WpMatomo/Admin/views/info.php
CHANGED
@@ -29,14 +29,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
29 |
<?php
|
30 |
echo sprintf(
|
31 |
esc_html__(
|
32 |
-
'Matomo is a collaborative project brought to you by %1$sMatomo team%2$s members as well as many other contributors around the globe. If you
|
33 |
-
%3$
|
34 |
'matomo'
|
35 |
),
|
36 |
'<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/team/">',
|
37 |
'</a>',
|
38 |
-
'<a target="_blank" rel="noreferrer noopener" href="https://
|
39 |
-
'
|
40 |
);
|
41 |
?>
|
42 |
<br/><br/>
|
@@ -55,6 +55,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
55 |
?>
|
56 |
</p>
|
57 |
|
|
|
|
|
58 |
<h2><?php esc_html_e( 'High traffic websites', 'matomo' ); ?></h2>
|
59 |
<?php require 'info_high_traffic.php'; ?>
|
60 |
|
29 |
<?php
|
30 |
echo sprintf(
|
31 |
esc_html__(
|
32 |
+
'Matomo is a collaborative project brought to you by %1$sMatomo team%2$s members as well as many other contributors around the globe. If you like Matomo,
|
33 |
+
%3$splease give us a review%4$s and spread the word about us.',
|
34 |
'matomo'
|
35 |
),
|
36 |
'<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/team/">',
|
37 |
'</a>',
|
38 |
+
'<a target="_blank" rel="noreferrer noopener" href="https://wordpress.org/support/plugin/matomo/reviews/?rate=5#new-post">',
|
39 |
+
'<span class="dashicons-before dashicons-star-filled" style="color:gold;"></span><span class="dashicons-before dashicons-star-filled" style="color:gold;"></span><span class="dashicons-before dashicons-star-filled" style="color:gold;"></span><span class="dashicons-before dashicons-star-filled" style="color:gold;"></span><span class="dashicons-before dashicons-star-filled" style="color:gold;"></span></a>'
|
40 |
);
|
41 |
?>
|
42 |
<br/><br/>
|
55 |
?>
|
56 |
</p>
|
57 |
|
58 |
+
<?php require 'info_newsletter.php'; ?>
|
59 |
+
|
60 |
<h2><?php esc_html_e( 'High traffic websites', 'matomo' ); ?></h2>
|
61 |
<?php require 'info_high_traffic.php'; ?>
|
62 |
|
classes/WpMatomo/Admin/views/info_help.php
CHANGED
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
13 |
?>
|
14 |
<h2><?php esc_html_e( 'How can we help?', 'matomo' ); ?></h2>
|
15 |
|
16 |
-
<form method="get" action="https://matomo.org" target="_blank">
|
17 |
<input type="text" name="s" style="width:300px;"><input type="submit" class="button-secondary"
|
18 |
value="Search on matomo.org">
|
19 |
</form>
|
13 |
?>
|
14 |
<h2><?php esc_html_e( 'How can we help?', 'matomo' ); ?></h2>
|
15 |
|
16 |
+
<form method="get" action="https://matomo.org" target="_blank" rel="noreferrer noopener">
|
17 |
<input type="text" name="s" style="width:300px;"><input type="submit" class="button-secondary"
|
18 |
value="Search on matomo.org">
|
19 |
</form>
|
classes/WpMatomo/Admin/views/info_multisite.php
CHANGED
@@ -11,12 +11,12 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
11 |
}
|
12 |
|
13 |
/** @var \WpMatomo\Settings $settings */
|
14 |
-
/** @var bool $canUserEdit */
|
15 |
?>
|
16 |
|
17 |
<div class="wrap">
|
18 |
<div id="icon-plugins" class="icon32"></div>
|
19 |
<h1><?php esc_html_e( 'Matomo Analytics in Multi Site mode', 'matomo' ); ?></h1>
|
|
|
20 |
<p><?php esc_html_e( 'You are seeing this page as you are viewing the network admin. Matomo differentiates between two different multi site modes:', 'matomo' ); ?></p>
|
21 |
<h2><?php esc_html_e( 'Matomo is network enabled', 'matomo' ); ?></h2>
|
22 |
<p><?php esc_html_e( 'In this mode, the tracking and access settings are managed in the network admin in one place and apply to all blogs.', 'matomo' ); ?>
|
11 |
}
|
12 |
|
13 |
/** @var \WpMatomo\Settings $settings */
|
|
|
14 |
?>
|
15 |
|
16 |
<div class="wrap">
|
17 |
<div id="icon-plugins" class="icon32"></div>
|
18 |
<h1><?php esc_html_e( 'Matomo Analytics in Multi Site mode', 'matomo' ); ?></h1>
|
19 |
+
<?php require 'info_newsletter.php'; ?>
|
20 |
<p><?php esc_html_e( 'You are seeing this page as you are viewing the network admin. Matomo differentiates between two different multi site modes:', 'matomo' ); ?></p>
|
21 |
<h2><?php esc_html_e( 'Matomo is network enabled', 'matomo' ); ?></h2>
|
22 |
<p><?php esc_html_e( 'In this mode, the tracking and access settings are managed in the network admin in one place and apply to all blogs.', 'matomo' ); ?>
|
classes/WpMatomo/Admin/views/info_newsletter.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
*/
|
9 |
+
|
10 |
+
use \WpMatomo\Admin\Info;
|
11 |
+
|
12 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
13 |
+
exit;
|
14 |
+
}
|
15 |
+
/** @var bool $signedup_newsletter */
|
16 |
+
/** @var bool $show_newsletter */
|
17 |
+
|
18 |
+
if ($signedup_newsletter) {
|
19 |
+
?>
|
20 |
+
<div class="notice notice-success is-dismissible">
|
21 |
+
<p><?php esc_html_e('Thank you for signing up to our newsletter.', 'matomo'); ?></p>
|
22 |
+
</div>
|
23 |
+
<?php
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
if (!$show_newsletter) {
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
?>
|
30 |
+
|
31 |
+
<div class="notice notice-success">
|
32 |
+
<h2><?php esc_html_e( 'Newsletter', 'matomo' ); ?></h2>
|
33 |
+
<form method="post">
|
34 |
+
<p>
|
35 |
+
<?php wp_nonce_field( Info::NONCE_NAME ); ?>
|
36 |
+
<input type="checkbox" id="<?php echo Info::FORM_NAME ?>" name="<?php echo Info::FORM_NAME ?>" value="1">
|
37 |
+
<label for="<?php echo Info::FORM_NAME ?>">
|
38 |
+
<?php esc_html_e('Subscribe to our newsletter to receive regular information about Matomo, web analytics, and privacy. You can unsubscribe from it any time.', 'matomo'); ?>
|
39 |
+
<?php esc_html_e('This service uses MadMimi.', 'matomo'); ?>
|
40 |
+
<?php echo sprintf(esc_html__('Learn more about it on our %1$sPrivacy Policy page%2$s.', 'matomo'), '<a href="https://matomo.org/privacy-policy/" target="_blank" rel="noreferrer noopener">', '</a>'); ?>
|
41 |
+
</label>
|
42 |
+
<br><br>
|
43 |
+
<input type="submit" class="button-secondary" value="<?php esc_attr_e('Subscribe', 'matomo');?>">
|
44 |
+
</p>
|
45 |
+
</form>
|
46 |
+
</div>
|
classes/WpMatomo/Admin/views/info_shared.php
CHANGED
@@ -11,7 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
11 |
exit; // if accessed directly
|
12 |
}
|
13 |
?>
|
14 |
-
<h1><?php esc_html_e( 'About
|
15 |
|
16 |
<p>
|
17 |
<?php
|
11 |
exit; // if accessed directly
|
12 |
}
|
13 |
?>
|
14 |
+
<h1><?php esc_html_e( 'About', 'matomo' ); ?> <?php matomo_header_icon(true); ?> </h1>
|
15 |
|
16 |
<p>
|
17 |
<?php
|
classes/WpMatomo/Admin/views/marketplace.php
CHANGED
@@ -30,7 +30,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
30 |
|
31 |
<div id="icon-plugins" class="icon32"></div>
|
32 |
|
33 |
-
<h1><?php esc_html_e( 'Discover new functionality for your Matomo', 'matomo' ); ?></h1>
|
34 |
<p>
|
35 |
<?php esc_html_e( 'Take your Matomo (formerly Piwik) to the next level and drive your conversions & revenue with these premium features. All features are fully hosted on your WordPress and come with 100% data ownership and no limitations.', 'matomo' ); ?>
|
36 |
<?php if ( is_plugin_active( MATOMO_MARKETPLACE_PLUGIN_NAME ) ) { ?>
|
@@ -43,7 +43,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
43 |
<h2><?php echo sprintf( esc_html__( 'Easily install over 100 free plugins & %1$spremium features%2$s for Matomo with just a click' ), '<span style="white-space: nowrap;">', '</span>' ); ?></h2>
|
44 |
<a href="https://builds.matomo.org/matomo-marketplace-for-wordpress-latest.zip" rel="noreferrer noopener" class="button matomo-cta-button"><?php esc_html_e( 'Download Matomo Marketplace for WordPress', 'matomo' ); ?></a>
|
45 |
<br>
|
46 |
-
|
47 |
<a target="_blank" href="https://plugins.matomo.org/?wp=1" rel="noreferrer noopener" class="matomo-next-link"><?php esc_html_e( 'Browse Marketplace', 'matomo' ); ?></a>
|
48 |
</div>
|
49 |
<?php } ?>
|
@@ -52,6 +52,8 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
52 |
$matomo_feature_sections = array(
|
53 |
array(
|
54 |
'title' => 'Top free plugins',
|
|
|
|
|
55 |
'features' =>
|
56 |
array(
|
57 |
array(
|
@@ -89,14 +91,14 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
89 |
'description' => 'Truly understand your visitors by seeing where they click, hover, type and scroll. Replay their actions in a video and ultimately increase conversions.',
|
90 |
'price' => '99EUR / 119USD',
|
91 |
'url' => 'https://plugins.matomo.org/HeatmapSessionRecording?wp=1',
|
92 |
-
'image' =>
|
93 |
),
|
94 |
array(
|
95 |
'name' => 'Custom Reports',
|
96 |
'description' => 'Pull out the information you need in order to be successful. Develop your custom strategy to meet your individualized goals while saving money & time.',
|
97 |
'price' => '99EUR / 119USD',
|
98 |
'url' => 'https://plugins.matomo.org/CustomReports?wp=1',
|
99 |
-
'image' =>
|
100 |
),
|
101 |
|
102 |
array(
|
@@ -104,7 +106,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
104 |
'description' => 'All premium features in one bundle, make the most out of your Matomo for WordPress and enjoy discounts of over 20%!',
|
105 |
'price' => '499EUR / 579USD',
|
106 |
'url' => 'https://plugins.matomo.org/WpPremiumBundle?wp=1',
|
107 |
-
'image' =>
|
108 |
),
|
109 |
),
|
110 |
),
|
@@ -117,21 +119,21 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
117 |
'description' => 'Increase conversions on your online forms and lose less visitors by learning everything about your users behavior and their pain points on your forms.',
|
118 |
'price' => '79EUR / 89USD',
|
119 |
'url' => 'https://plugins.matomo.org/FormAnalytics?wp=1',
|
120 |
-
'image' =>
|
121 |
),
|
122 |
array(
|
123 |
'name' => 'Media Analytics',
|
124 |
'description' => 'Grow your business with advanced video & audio analytics. Get powerful insights into how your audience watches your videos and listens to your audio.',
|
125 |
'price' => '79EUR / 89USD',
|
126 |
'url' => 'https://plugins.matomo.org/MediaAnalytics?wp=1',
|
127 |
-
'image' =>
|
128 |
),
|
129 |
array(
|
130 |
'name' => 'Users Flow',
|
131 |
'description' => 'Users Flow is a visual representation of the most popular paths your users take through your website & app which lets you understand your users needs.',
|
132 |
'price' => '39EUR / 39USD',
|
133 |
'url' => 'https://plugins.matomo.org/UsersFlow?wp=1',
|
134 |
-
'image' =>
|
135 |
),
|
136 |
),
|
137 |
),
|
@@ -144,14 +146,14 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
144 |
'description' => 'Identify and understand where your visitors drop off to increase your conversions, sales and revenue with your existing traffic.',
|
145 |
'price' => '89EUR / 99USD',
|
146 |
'url' => 'https://plugins.matomo.org/Funnels?wp=1',
|
147 |
-
'image' =>
|
148 |
),
|
149 |
array(
|
150 |
'name' => 'Multi Attribution',
|
151 |
'description' => 'Get a clear understanding of how much credit each of your marketing channel is actually responsible for to shift your marketing efforts wisely.',
|
152 |
'price' => '39EUR / 39USD',
|
153 |
'url' => 'https://plugins.matomo.org/MultiChannelConversionAttribution?wp=1',
|
154 |
-
'image' =>
|
155 |
),
|
156 |
),
|
157 |
),
|
@@ -164,14 +166,14 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
164 |
'description' => 'Track your retention efforts over time and keep your visitors engaged and coming back for more.',
|
165 |
'price' => '49EUR / 59USD',
|
166 |
'url' => 'https://plugins.matomo.org/Cohorts?wp=1',
|
167 |
-
'image' =>
|
168 |
),
|
169 |
array(
|
170 |
'name' => 'Search Engine Keywords Performance',
|
171 |
'description' => 'All keywords searched by your users on search engines are now visible into your Referrers reports! The ultimate solution to \'Keyword not defined\'.',
|
172 |
'price' => '69EUR / 79USD',
|
173 |
'url' => 'https://plugins.matomo.org/SearchEngineKeywordsPerformance?wp=1',
|
174 |
-
'image' =>
|
175 |
),
|
176 |
/*
|
177 |
array(
|
@@ -179,7 +181,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
179 |
'description' => 'Truly understand your visitors by seeing where they click, hover, type and scroll. Replay their actions in a video and ultimately increase conversions',
|
180 |
'price' => '19EUR / 19USD',
|
181 |
'url' => 'https://plugins.matomo.org/ActivityLog?wp=1',
|
182 |
-
'image' =>
|
183 |
),*/
|
184 |
),
|
185 |
),
|
@@ -213,7 +215,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
213 |
<?php
|
214 |
if ( ! $matomo_is_3_columns ) {
|
215 |
?>
|
216 |
-
name column-name<?php } ?>" style="margin-right: 0">
|
217 |
<h3>
|
218 |
<a href="<?php echo esc_url( $matomo_feature['url'] ); ?>"
|
219 |
rel="noreferrer noopener" target="_blank"
|
@@ -237,7 +239,7 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
237 |
if ( ! $matomo_is_3_columns ) {
|
238 |
?>
|
239 |
desc column-description<?php } ?>"
|
240 |
-
style="margin-right: 0">
|
241 |
<p class="matomo-description"><?php echo esc_html( $matomo_feature['description'] ); ?></p>
|
242 |
<p class="authors"><a class="button-primary"
|
243 |
rel="noreferrer noopener" target="_blank"
|
@@ -256,7 +258,12 @@ $matomo_extra_url_params = '&' . http_build_query(
|
|
256 |
</div>
|
257 |
<?php
|
258 |
}
|
259 |
-
echo '<div style="clear:both;"></div
|
|
|
|
|
|
|
|
|
|
|
260 |
}
|
261 |
?>
|
262 |
</div>
|
30 |
|
31 |
<div id="icon-plugins" class="icon32"></div>
|
32 |
|
33 |
+
<h1><?php matomo_header_icon(); ?> <?php esc_html_e( 'Discover new functionality for your Matomo', 'matomo' ); ?></h1>
|
34 |
<p>
|
35 |
<?php esc_html_e( 'Take your Matomo (formerly Piwik) to the next level and drive your conversions & revenue with these premium features. All features are fully hosted on your WordPress and come with 100% data ownership and no limitations.', 'matomo' ); ?>
|
36 |
<?php if ( is_plugin_active( MATOMO_MARKETPLACE_PLUGIN_NAME ) ) { ?>
|
43 |
<h2><?php echo sprintf( esc_html__( 'Easily install over 100 free plugins & %1$spremium features%2$s for Matomo with just a click' ), '<span style="white-space: nowrap;">', '</span>' ); ?></h2>
|
44 |
<a href="https://builds.matomo.org/matomo-marketplace-for-wordpress-latest.zip" rel="noreferrer noopener" class="button matomo-cta-button"><?php esc_html_e( 'Download Matomo Marketplace for WordPress', 'matomo' ); ?></a>
|
45 |
<br>
|
46 |
+
<a target="_blank" href="https://matomo.org/faq/wordpress/how-do-i-install-a-matomo-marketplace-plugin-in-matomo-for-wordpress/"><span class="dashicons-before dashicons-video-alt3"></span></a> <a target="_blank" href="https://matomo.org/faq/wordpress/how-do-i-install-a-matomo-marketplace-plugin-in-matomo-for-wordpress/"><?php esc_html_e( 'Install instructions', 'matomo' ); ?></a>
|
47 |
<a target="_blank" href="https://plugins.matomo.org/?wp=1" rel="noreferrer noopener" class="matomo-next-link"><?php esc_html_e( 'Browse Marketplace', 'matomo' ); ?></a>
|
48 |
</div>
|
49 |
<?php } ?>
|
52 |
$matomo_feature_sections = array(
|
53 |
array(
|
54 |
'title' => 'Top free plugins',
|
55 |
+
'more_url' => 'https://plugins.matomo.org/free?wp=1',
|
56 |
+
'more_text' => 'Browse all free plugins',
|
57 |
'features' =>
|
58 |
array(
|
59 |
array(
|
91 |
'description' => 'Truly understand your visitors by seeing where they click, hover, type and scroll. Replay their actions in a video and ultimately increase conversions.',
|
92 |
'price' => '99EUR / 119USD',
|
93 |
'url' => 'https://plugins.matomo.org/HeatmapSessionRecording?wp=1',
|
94 |
+
'image' => '',
|
95 |
),
|
96 |
array(
|
97 |
'name' => 'Custom Reports',
|
98 |
'description' => 'Pull out the information you need in order to be successful. Develop your custom strategy to meet your individualized goals while saving money & time.',
|
99 |
'price' => '99EUR / 119USD',
|
100 |
'url' => 'https://plugins.matomo.org/CustomReports?wp=1',
|
101 |
+
'image' => '',
|
102 |
),
|
103 |
|
104 |
array(
|
106 |
'description' => 'All premium features in one bundle, make the most out of your Matomo for WordPress and enjoy discounts of over 20%!',
|
107 |
'price' => '499EUR / 579USD',
|
108 |
'url' => 'https://plugins.matomo.org/WpPremiumBundle?wp=1',
|
109 |
+
'image' => '',
|
110 |
),
|
111 |
),
|
112 |
),
|
119 |
'description' => 'Increase conversions on your online forms and lose less visitors by learning everything about your users behavior and their pain points on your forms.',
|
120 |
'price' => '79EUR / 89USD',
|
121 |
'url' => 'https://plugins.matomo.org/FormAnalytics?wp=1',
|
122 |
+
'image' => '',
|
123 |
),
|
124 |
array(
|
125 |
'name' => 'Media Analytics',
|
126 |
'description' => 'Grow your business with advanced video & audio analytics. Get powerful insights into how your audience watches your videos and listens to your audio.',
|
127 |
'price' => '79EUR / 89USD',
|
128 |
'url' => 'https://plugins.matomo.org/MediaAnalytics?wp=1',
|
129 |
+
'image' => '',
|
130 |
),
|
131 |
array(
|
132 |
'name' => 'Users Flow',
|
133 |
'description' => 'Users Flow is a visual representation of the most popular paths your users take through your website & app which lets you understand your users needs.',
|
134 |
'price' => '39EUR / 39USD',
|
135 |
'url' => 'https://plugins.matomo.org/UsersFlow?wp=1',
|
136 |
+
'image' => '',
|
137 |
),
|
138 |
),
|
139 |
),
|
146 |
'description' => 'Identify and understand where your visitors drop off to increase your conversions, sales and revenue with your existing traffic.',
|
147 |
'price' => '89EUR / 99USD',
|
148 |
'url' => 'https://plugins.matomo.org/Funnels?wp=1',
|
149 |
+
'image' => '',
|
150 |
),
|
151 |
array(
|
152 |
'name' => 'Multi Attribution',
|
153 |
'description' => 'Get a clear understanding of how much credit each of your marketing channel is actually responsible for to shift your marketing efforts wisely.',
|
154 |
'price' => '39EUR / 39USD',
|
155 |
'url' => 'https://plugins.matomo.org/MultiChannelConversionAttribution?wp=1',
|
156 |
+
'image' => '',
|
157 |
),
|
158 |
),
|
159 |
),
|
166 |
'description' => 'Track your retention efforts over time and keep your visitors engaged and coming back for more.',
|
167 |
'price' => '49EUR / 59USD',
|
168 |
'url' => 'https://plugins.matomo.org/Cohorts?wp=1',
|
169 |
+
'image' => '',
|
170 |
),
|
171 |
array(
|
172 |
'name' => 'Search Engine Keywords Performance',
|
173 |
'description' => 'All keywords searched by your users on search engines are now visible into your Referrers reports! The ultimate solution to \'Keyword not defined\'.',
|
174 |
'price' => '69EUR / 79USD',
|
175 |
'url' => 'https://plugins.matomo.org/SearchEngineKeywordsPerformance?wp=1',
|
176 |
+
'image' => '',
|
177 |
),
|
178 |
/*
|
179 |
array(
|
181 |
'description' => 'Truly understand your visitors by seeing where they click, hover, type and scroll. Replay their actions in a video and ultimately increase conversions',
|
182 |
'price' => '19EUR / 19USD',
|
183 |
'url' => 'https://plugins.matomo.org/ActivityLog?wp=1',
|
184 |
+
'image' => '',
|
185 |
),*/
|
186 |
),
|
187 |
),
|
215 |
<?php
|
216 |
if ( ! $matomo_is_3_columns ) {
|
217 |
?>
|
218 |
+
name column-name<?php } ?>" style="margin-right: 0;<?php if ( empty( $matomo_feature['image'] )) { echo 'margin-left: 0;'; } ?>">
|
219 |
<h3>
|
220 |
<a href="<?php echo esc_url( $matomo_feature['url'] ); ?>"
|
221 |
rel="noreferrer noopener" target="_blank"
|
239 |
if ( ! $matomo_is_3_columns ) {
|
240 |
?>
|
241 |
desc column-description<?php } ?>"
|
242 |
+
style="margin-right: 0;<?php if ( empty( $matomo_feature['image'] )) { echo 'margin-left: 0;'; } ?>">
|
243 |
<p class="matomo-description"><?php echo esc_html( $matomo_feature['description'] ); ?></p>
|
244 |
<p class="authors"><a class="button-primary"
|
245 |
rel="noreferrer noopener" target="_blank"
|
258 |
</div>
|
259 |
<?php
|
260 |
}
|
261 |
+
echo '<div style="clear:both;"></div>';
|
262 |
+
echo '</div>';
|
263 |
+
if (!empty($matomo_feature_section['more_url'])) {
|
264 |
+
echo '<a target="_blank" rel="noreferrer noopener" href="'.esc_attr($matomo_feature_section['more_url']).'"><span class="dashicons dashicons-arrow-right-alt2"></span>'. esc_html($matomo_feature_section['more_text']).'</a>';
|
265 |
+
}
|
266 |
+
echo '</div>';
|
267 |
}
|
268 |
?>
|
269 |
</div>
|
classes/WpMatomo/Admin/views/settings.php
CHANGED
@@ -21,6 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
21 |
?>
|
22 |
<div class="wrap">
|
23 |
<div id="icon-plugins" class="icon32"></div>
|
|
|
24 |
<h2 class="nav-tab-wrapper">
|
25 |
<?php foreach ( $setting_tabs as $matomo_setting_slug => $matomo_setting_tab ) { ?>
|
26 |
<a href="<?php echo AdminSettings::make_url( $matomo_setting_slug ); ?>"
|
21 |
?>
|
22 |
<div class="wrap">
|
23 |
<div id="icon-plugins" class="icon32"></div>
|
24 |
+
<h1><?php matomo_header_icon(); ?> <?php esc_html_e( 'Settings', 'matomo' ); ?></h1>
|
25 |
<h2 class="nav-tab-wrapper">
|
26 |
<?php foreach ( $setting_tabs as $matomo_setting_slug => $matomo_setting_tab ) { ?>
|
27 |
<a href="<?php echo AdminSettings::make_url( $matomo_setting_slug ); ?>"
|
classes/WpMatomo/Admin/views/summary.php
CHANGED
@@ -28,7 +28,7 @@ global $wp;
|
|
28 |
<?php } ?>
|
29 |
<div class="wrap">
|
30 |
<div id="icon-plugins" class="icon32"></div>
|
31 |
-
<h1><?php esc_html_e( 'Summary', 'matomo' ); ?></h1>
|
32 |
<?php
|
33 |
if ( Dates::TODAY === $report_date ) {
|
34 |
echo '<div class="notice notice-info" style="padding:8px;">' . esc_html__( 'Reports for today are only refreshed approximately every hour through the WordPress cronjob.', 'matomo' ) . '</div>';
|
@@ -79,7 +79,7 @@ global $wp;
|
|
79 |
)
|
80 |
);
|
81 |
?>
|
82 |
-
" style="color: inherit;" target="_blank" rel="noreferrer noopener"
|
83 |
class="dashicons-before dashicons-external" aria-hidden="true"></a></button>
|
84 |
<?php } ?>
|
85 |
<h2 class="hndle ui-sortable-handle"
|
28 |
<?php } ?>
|
29 |
<div class="wrap">
|
30 |
<div id="icon-plugins" class="icon32"></div>
|
31 |
+
<h1><?php matomo_header_icon(); ?> <?php esc_html_e( 'Summary', 'matomo' ); ?></h1>
|
32 |
<?php
|
33 |
if ( Dates::TODAY === $report_date ) {
|
34 |
echo '<div class="notice notice-info" style="padding:8px;">' . esc_html__( 'Reports for today are only refreshed approximately every hour through the WordPress cronjob.', 'matomo' ) . '</div>';
|
79 |
)
|
80 |
);
|
81 |
?>
|
82 |
+
" style="color: inherit;text-decoration: none;" target="_blank" rel="noreferrer noopener"
|
83 |
class="dashicons-before dashicons-external" aria-hidden="true"></a></button>
|
84 |
<?php } ?>
|
85 |
<h2 class="hndle ui-sortable-handle"
|
classes/WpMatomo/Admin/views/systemreport.php
CHANGED
@@ -57,6 +57,8 @@ if ( ! function_exists( 'matomo_format_value_text' ) ) {
|
|
57 |
</div>
|
58 |
<?php } ?>
|
59 |
<div id="icon-plugins" class="icon32"></div>
|
|
|
|
|
60 |
<h2 class="nav-tab-wrapper">
|
61 |
<a href="?page=<?php echo Menu::SLUG_SYSTEM_REPORT; ?>"
|
62 |
class="nav-tab <?php echo empty( $matomo_active_tab ) ? 'nav-tab-active' : ''; ?>"> System report</a>
|
57 |
</div>
|
58 |
<?php } ?>
|
59 |
<div id="icon-plugins" class="icon32"></div>
|
60 |
+
<h1><?php matomo_header_icon(); ?> <?php esc_html_e( 'Diagnostics', 'matomo' ); ?></h1>
|
61 |
+
|
62 |
<h2 class="nav-tab-wrapper">
|
63 |
<a href="?page=<?php echo Menu::SLUG_SYSTEM_REPORT; ?>"
|
64 |
class="nav-tab <?php echo empty( $matomo_active_tab ) ? 'nav-tab-active' : ''; ?>"> System report</a>
|
classes/WpMatomo/Installer.php
CHANGED
@@ -25,6 +25,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
25 |
|
26 |
class Installer {
|
27 |
|
|
|
|
|
28 |
/**
|
29 |
* @var Settings
|
30 |
*/
|
@@ -111,6 +113,8 @@ class Installer {
|
|
111 |
// also to set up all the other users
|
112 |
wp_schedule_single_event( time() + 35, ScheduledTasks::EVENT_SYNC );
|
113 |
|
|
|
|
|
114 |
$this->create_website();
|
115 |
$this->create_user(); // we sync users as early as possible to make sure things are set up correctly
|
116 |
$this->install_tracker();
|
25 |
|
26 |
class Installer {
|
27 |
|
28 |
+
const OPTION_NAME_INSTALL_DATE = 'matomo-install-date';
|
29 |
+
|
30 |
/**
|
31 |
* @var Settings
|
32 |
*/
|
113 |
// also to set up all the other users
|
114 |
wp_schedule_single_event( time() + 35, ScheduledTasks::EVENT_SYNC );
|
115 |
|
116 |
+
update_option(self::OPTION_NAME_INSTALL_DATE, time());
|
117 |
+
|
118 |
$this->create_website();
|
119 |
$this->create_user(); // we sync users as early as possible to make sure things are set up correctly
|
120 |
$this->install_tracker();
|
classes/WpMatomo/Referral.php
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
*/
|
9 |
+
|
10 |
+
namespace WpMatomo;
|
11 |
+
|
12 |
+
use WP_Roles;
|
13 |
+
use WpMatomo\Admin\Menu;
|
14 |
+
|
15 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
16 |
+
exit; // if accessed directly
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Every 90 days we show a please review notice until the user dismisses this notice or clicks on rate us.
|
21 |
+
* We only show this notice on Matomo screens.
|
22 |
+
*
|
23 |
+
* @package WpMatomo
|
24 |
+
*/
|
25 |
+
class Referral {
|
26 |
+
|
27 |
+
const OPTION_NAME_REFERRAL_DISMISSED = 'matomo-referral-dismissed';
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var int
|
31 |
+
*/
|
32 |
+
private $time;
|
33 |
+
|
34 |
+
public function __construct() {
|
35 |
+
$this->time = time();
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @internal for tests only
|
40 |
+
* @param int $time
|
41 |
+
*/
|
42 |
+
public function set_time( $time ) {
|
43 |
+
$this->time = $time;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function register_hooks() {
|
47 |
+
$self = $this;
|
48 |
+
|
49 |
+
add_action(
|
50 |
+
'wp_ajax_matomo_referral_dismiss_admin_notice',
|
51 |
+
function () use ( $self ) {
|
52 |
+
if ( is_admin() && $self->should_show() && $self->can_refer() ) {
|
53 |
+
// no need for an nonce check here as it's nothing critical
|
54 |
+
if ( ! empty( $_POST['forever'] ) ) {
|
55 |
+
$self->dismiss_forever();
|
56 |
+
} else {
|
57 |
+
$self->dismiss();
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
);
|
62 |
+
add_action(
|
63 |
+
'admin_notices',
|
64 |
+
function () use ( $self ) {
|
65 |
+
if ( $self->can_refer() && $self->should_show_on_screen() ) {
|
66 |
+
$self->render();
|
67 |
+
}
|
68 |
+
}
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
public function render() {
|
73 |
+
include 'views/referral.php';
|
74 |
+
}
|
75 |
+
|
76 |
+
public function should_show_on_screen() {
|
77 |
+
if ( ! is_admin() ) {
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
$screen = get_current_screen();
|
81 |
+
return $screen && $screen->id && strpos( $screen->id, 'matomo-' ) === 0;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function can_refer() {
|
85 |
+
return current_user_can( Capabilities::KEY_VIEW );
|
86 |
+
}
|
87 |
+
|
88 |
+
public function dismiss_forever() {
|
89 |
+
$tenYears = 60 * 60 * 24 * 365 * 10;
|
90 |
+
update_option( self::OPTION_NAME_REFERRAL_DISMISSED, $this->time + $tenYears );
|
91 |
+
}
|
92 |
+
|
93 |
+
public function dismiss() {
|
94 |
+
update_option( self::OPTION_NAME_REFERRAL_DISMISSED, $this->time, true );
|
95 |
+
}
|
96 |
+
|
97 |
+
public function get_last_dismissed() {
|
98 |
+
return get_option( self::OPTION_NAME_REFERRAL_DISMISSED );
|
99 |
+
}
|
100 |
+
|
101 |
+
private function get_days_in_seconds( $num_days ) {
|
102 |
+
return 60 * 60 * 24 * $num_days;
|
103 |
+
}
|
104 |
+
|
105 |
+
public function should_show() {
|
106 |
+
$dismissed = $this->get_last_dismissed();
|
107 |
+
|
108 |
+
if ( ! $dismissed ) {
|
109 |
+
// the first time we check... we set it back 30 days cause we want to see first rating after 60 days
|
110 |
+
$this->time = $this->time - $this->get_days_in_seconds( 30 );
|
111 |
+
$this->dismiss();
|
112 |
+
return false;
|
113 |
+
}
|
114 |
+
|
115 |
+
$ninetyDaysInSeconds = $this->get_days_in_seconds( 90 );
|
116 |
+
|
117 |
+
if ( $this->time > ( $dismissed + $ninetyDaysInSeconds ) ) {
|
118 |
+
return true;
|
119 |
+
}
|
120 |
+
|
121 |
+
return false;
|
122 |
+
}
|
123 |
+
|
124 |
+
|
125 |
+
}
|
classes/WpMatomo/ScheduledTasks.php
CHANGED
@@ -47,9 +47,9 @@ class ScheduledTasks {
|
|
47 |
}
|
48 |
|
49 |
public function add_weekly_schedule( $schedules ) {
|
50 |
-
$schedules['
|
51 |
-
'interval' => 60 * 60 * 24 *
|
52 |
-
'display' => __( '
|
53 |
);
|
54 |
|
55 |
return $schedules;
|
@@ -126,7 +126,7 @@ class ScheduledTasks {
|
|
126 |
),
|
127 |
self::EVENT_GEOIP => array(
|
128 |
'name' => 'Update GeoIP DB',
|
129 |
-
'interval' => '
|
130 |
'method' => 'update_geo_ip2_db',
|
131 |
),
|
132 |
);
|
@@ -148,7 +148,15 @@ class ScheduledTasks {
|
|
148 |
$this->logger->log( 'Scheduled tasks update geoip database' );
|
149 |
try {
|
150 |
Bootstrap::do_bootstrap();
|
151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
$updater = new GeoIP2AutoUpdater();
|
153 |
$updater->update();
|
154 |
if ( LocationProvider::getCurrentProviderId() !== Php::ID && LocationProvider::getProviderById( Php::ID ) ) {
|
47 |
}
|
48 |
|
49 |
public function add_weekly_schedule( $schedules ) {
|
50 |
+
$schedules['matomo_monthly'] = array(
|
51 |
+
'interval' => 60 * 60 * 24 * 30,
|
52 |
+
'display' => __( 'Monthly', 'matomo' ),
|
53 |
);
|
54 |
|
55 |
return $schedules;
|
126 |
),
|
127 |
self::EVENT_GEOIP => array(
|
128 |
'name' => 'Update GeoIP DB',
|
129 |
+
'interval' => 'matomo_monthly',
|
130 |
'method' => 'update_geo_ip2_db',
|
131 |
),
|
132 |
);
|
148 |
$this->logger->log( 'Scheduled tasks update geoip database' );
|
149 |
try {
|
150 |
Bootstrap::do_bootstrap();
|
151 |
+
|
152 |
+
$maxmind_license = $this->settings->get_global_option('maxmind_license_key');
|
153 |
+
if (empty($maxmind_license)) {
|
154 |
+
$db_url = GeoIp2::getDbIpLiteUrl();
|
155 |
+
} else {
|
156 |
+
$db_url = 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz&license_key=' . $maxmind_license;
|
157 |
+
}
|
158 |
+
|
159 |
+
Option::set( GeoIP2AutoUpdater::LOC_URL_OPTION_NAME, $db_url);
|
160 |
$updater = new GeoIP2AutoUpdater();
|
161 |
$updater->update();
|
162 |
if ( LocationProvider::getCurrentProviderId() !== Php::ID && LocationProvider::getProviderById( Php::ID ) ) {
|
classes/WpMatomo/Settings.php
CHANGED
@@ -91,6 +91,7 @@ class Settings {
|
|
91 |
'track_user_id' => 'disabled',
|
92 |
'track_datacfasync' => false,
|
93 |
'force_protocol' => 'disabled',
|
|
|
94 |
self::SHOW_GET_STARTED_PAGE => 1,
|
95 |
);
|
96 |
|
91 |
'track_user_id' => 'disabled',
|
92 |
'track_datacfasync' => false,
|
93 |
'force_protocol' => 'disabled',
|
94 |
+
'maxmind_license_key' => '',
|
95 |
self::SHOW_GET_STARTED_PAGE => 1,
|
96 |
);
|
97 |
|
classes/WpMatomo/Site/Sync.php
CHANGED
@@ -233,8 +233,11 @@ class Sync {
|
|
233 |
|
234 |
private function check_and_try_to_set_default_timezone( $timezone ) {
|
235 |
try {
|
236 |
-
|
237 |
-
|
|
|
|
|
|
|
238 |
} catch ( \Exception $e ) {
|
239 |
return false;
|
240 |
}
|
233 |
|
234 |
private function check_and_try_to_set_default_timezone( $timezone ) {
|
235 |
try {
|
236 |
+
Access::doAsSuperUser(function () use ($timezone) {
|
237 |
+
// make sure we're loading the latest instance with all up to date dependencies... mainly needed for tests
|
238 |
+
SitesManager\API::unsetInstance();
|
239 |
+
SitesManager\API::getInstance()->setDefaultTimezone( $timezone );
|
240 |
+
});
|
241 |
} catch ( \Exception $e ) {
|
242 |
return false;
|
243 |
}
|
classes/WpMatomo/TrackingCode.php
CHANGED
@@ -54,9 +54,11 @@ class TrackingCode {
|
|
54 |
add_filter( 'wp_redirect', array( $this, 'forward_cross_domain_visitor_id' ) );
|
55 |
}
|
56 |
|
57 |
-
|
|
|
|
|
58 |
$prefix = 'wp';
|
59 |
-
if ( is_admin
|
60 |
$prefix = 'admin';
|
61 |
}
|
62 |
|
54 |
add_filter( 'wp_redirect', array( $this, 'forward_cross_domain_visitor_id' ) );
|
55 |
}
|
56 |
|
57 |
+
$is_admin = is_admin() || !empty($GLOBALS['MATOMO_LOADED_DIRECTLY']);
|
58 |
+
|
59 |
+
if ( ! $is_admin || $this->settings->is_admin_tracking_enabled() ) {
|
60 |
$prefix = 'wp';
|
61 |
+
if ( $is_admin ) {
|
62 |
$prefix = 'admin';
|
63 |
}
|
64 |
|
classes/WpMatomo/Updater.php
CHANGED
@@ -130,7 +130,7 @@ class Updater {
|
|
130 |
@file_put_contents( $upload_dir . '/index.htm', '//hello' );
|
131 |
@file_put_contents(
|
132 |
$upload_dir . '/.htaccess',
|
133 |
-
'<Files
|
134 |
' . ServerFilesGenerator::getDenyHtaccessContent() . '
|
135 |
</Files>
|
136 |
<Files ~ "(\.js)$">
|
130 |
@file_put_contents( $upload_dir . '/index.htm', '//hello' );
|
131 |
@file_put_contents(
|
132 |
$upload_dir . '/.htaccess',
|
133 |
+
'<Files ~ "(\.mmdb)$">
|
134 |
' . ServerFilesGenerator::getDenyHtaccessContent() . '
|
135 |
</Files>
|
136 |
<Files ~ "(\.js)$">
|
classes/WpMatomo/views/referral.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Matomo - free/libre analytics platform
|
4 |
+
*
|
5 |
+
* @link https://matomo.org
|
6 |
+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
7 |
+
* @package matomo
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
11 |
+
exit;
|
12 |
+
}
|
13 |
+
?>
|
14 |
+
<div class="notice notice-info is-dismissible" id="matomo-referral">
|
15 |
+
<p>
|
16 |
+
<?php esc_html_e( 'Like Matomo? We would really appreciate if you took 1 minute to rate us.', 'matomo' ); ?>
|
17 |
+
|
18 |
+
<a href="https://wordpress.org/support/plugin/matomo/reviews/?rate=5#new-post" target="_blank" rel="noreferrer noopener"
|
19 |
+
class="button matomo-dismiss-forever"><?php esc_html_e( 'Rate Matomo', 'matomo' ) ?></a>
|
20 |
+
</p>
|
21 |
+
<div style="clear:both;"></div>
|
22 |
+
</div>
|
matomo.php
CHANGED
@@ -4,10 +4,10 @@
|
|
4 |
* Description: The #1 Google Analytics alternative that gives you full control over your data and protects the privacy for your users. Free, secure and open.
|
5 |
* Author: Matomo
|
6 |
* Author URI: https://matomo.org
|
7 |
-
* Version: 1.0.
|
8 |
* Domain Path: /languages
|
9 |
* WC requires at least: 2.4.0
|
10 |
-
* WC tested up to:
|
11 |
*
|
12 |
* Matomo - free/libre analytics platform
|
13 |
*
|
@@ -36,7 +36,8 @@ $GLOBALS['MATOMO_PLUGINS_ENABLED'] = array();
|
|
36 |
$GLOBALS['MATOMO_PLUGIN_FILES'] = array( MATOMO_ANALYTICS_FILE );
|
37 |
|
38 |
function matomo_has_compatible_content_dir() {
|
39 |
-
if ( !empty( $
|
|
|
40 |
return true;
|
41 |
}
|
42 |
|
@@ -54,7 +55,52 @@ function matomo_has_compatible_content_dir() {
|
|
54 |
$absPath . DIRECTORY_SEPARATOR . 'wp-content'
|
55 |
);
|
56 |
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
}
|
59 |
|
60 |
function matomo_is_app_request() {
|
4 |
* Description: The #1 Google Analytics alternative that gives you full control over your data and protects the privacy for your users. Free, secure and open.
|
5 |
* Author: Matomo
|
6 |
* Author URI: https://matomo.org
|
7 |
+
* Version: 1.0.4
|
8 |
* Domain Path: /languages
|
9 |
* WC requires at least: 2.4.0
|
10 |
+
* WC tested up to: 4.0.0
|
11 |
*
|
12 |
* Matomo - free/libre analytics platform
|
13 |
*
|
36 |
$GLOBALS['MATOMO_PLUGIN_FILES'] = array( MATOMO_ANALYTICS_FILE );
|
37 |
|
38 |
function matomo_has_compatible_content_dir() {
|
39 |
+
if ( !empty( $_SERVER['MATOMO_WP_ROOT_PATH'] )
|
40 |
+
&& file_exists( rtrim($_SERVER['MATOMO_WP_ROOT_PATH'], '/') . '/wp-load.php' ) ) {
|
41 |
return true;
|
42 |
}
|
43 |
|
55 |
$absPath . DIRECTORY_SEPARATOR . 'wp-content'
|
56 |
);
|
57 |
|
58 |
+
if (in_array($contentDir, $absPaths, true)) {
|
59 |
+
return true;
|
60 |
+
}
|
61 |
+
|
62 |
+
$wpload_base = '../../../wp-load.php';
|
63 |
+
$wpload_full = dirname( __FILE__ ) . '/' . $wpload_base;
|
64 |
+
if ( file_exists($wpload_full ) && is_readable( $wpload_full ) ) {
|
65 |
+
return true;
|
66 |
+
} elseif (realpath( $wpload_full ) && file_exists(realpath( $wpload_full )) && is_readable(realpath( $wpload_full ))) {
|
67 |
+
return true;
|
68 |
+
} elseif (!empty($_SERVER['SCRIPT_FILENAME']) && file_exists($_SERVER['SCRIPT_FILENAME'])) {
|
69 |
+
// seems symlinked... eg the wp-content dir or wp-content/plugins dir is symlinked from some very much other place...
|
70 |
+
$wpload_full = dirname($_SERVER['SCRIPT_FILENAME']) . '/' . $wpload_base;
|
71 |
+
if ( file_exists($wpload_full ) ) {
|
72 |
+
return true;
|
73 |
+
} elseif (realpath( $wpload_full ) && file_exists(realpath( $wpload_full ))) {
|
74 |
+
return true;
|
75 |
+
} elseif (file_exists(dirname( $_SERVER['SCRIPT_FILENAME'] )) . '/wp-load.php') {
|
76 |
+
return true;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
// look in plugins directory if there is a config file for us
|
81 |
+
$wpload_config = dirname(__FILE__) . '/../matomo.wpload_dir.php';
|
82 |
+
if (file_exists( $wpload_config) && is_readable($wpload_config)) {
|
83 |
+
$content = @file_get_contents($wpload_config); // we do not include that file for security reasons
|
84 |
+
if (!empty($content)) {
|
85 |
+
$content = str_replace(array('<?php', 'exit;'), '', $content);
|
86 |
+
$content = preg_replace('/\s/', '', $content);
|
87 |
+
$content = trim(ltrim(trim($content), '#')); // the path may be commented out # /abs/path
|
88 |
+
if (strpos($content, DIRECTORY_SEPARATOR) === 0) {
|
89 |
+
$wpload_file = rtrim($content, DIRECTORY_SEPARATOR) . '/wp-load.php';
|
90 |
+
return file_exists($wpload_file) && is_readable($wpload_file);
|
91 |
+
}
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
function matomo_header_icon( $full = false ) {
|
99 |
+
$file = 'logo';
|
100 |
+
if ($full) {
|
101 |
+
$file = 'logo-full';
|
102 |
+
}
|
103 |
+
echo '<img height="32" src="' . plugins_url( 'assets/img/'.$file.'.png', MATOMO_ANALYTICS_FILE ) . '" class="matomo-header-icon">';
|
104 |
}
|
105 |
|
106 |
function matomo_is_app_request() {
|
plugins/WordPress/Controller.php
CHANGED
@@ -18,7 +18,8 @@ class Controller extends \Piwik\Plugin\Controller
|
|
18 |
public function index()
|
19 |
{
|
20 |
if (!is_user_logged_in()) {
|
21 |
-
|
|
|
22 |
}
|
23 |
}
|
24 |
|
18 |
public function index()
|
19 |
{
|
20 |
if (!is_user_logged_in()) {
|
21 |
+
$redirect_url = WordPress::getWpLoginUrl();
|
22 |
+
wp_safe_redirect($redirect_url);
|
23 |
}
|
24 |
}
|
25 |
|
plugins/WordPress/WordPress.php
CHANGED
@@ -334,6 +334,22 @@ class WordPress extends Plugin
|
|
334 |
}
|
335 |
}
|
336 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
337 |
public function noAccess(Exception $exception)
|
338 |
{
|
339 |
if (Common::isXmlHttpRequest()) {
|
@@ -342,7 +358,8 @@ class WordPress extends Plugin
|
|
342 |
return;
|
343 |
}
|
344 |
|
345 |
-
|
|
|
346 |
exit;
|
347 |
}
|
348 |
|
334 |
}
|
335 |
}
|
336 |
|
337 |
+
public static function getWpLoginUrl()
|
338 |
+
{
|
339 |
+
$forceFrontPage = defined('MATOMO_LOGIN_REDIRECT') && MATOMO_LOGIN_REDIRECT === 'frontpage';
|
340 |
+
$forceLoginUrl = defined('MATOMO_LOGIN_REDIRECT') && MATOMO_LOGIN_REDIRECT === 'login';
|
341 |
+
|
342 |
+
if (!$forceLoginUrl &&
|
343 |
+
($forceFrontPage
|
344 |
+
|| is_plugin_active('wps-hide-login/wps-hide-login.php'))) {
|
345 |
+
$redirect_url = home_url();
|
346 |
+
} else {
|
347 |
+
$redirect_url = wp_login_url(\WpMatomo\Admin\Menu::get_reporting_url());
|
348 |
+
}
|
349 |
+
|
350 |
+
return $redirect_url;
|
351 |
+
}
|
352 |
+
|
353 |
public function noAccess(Exception $exception)
|
354 |
{
|
355 |
if (Common::isXmlHttpRequest()) {
|
358 |
return;
|
359 |
}
|
360 |
|
361 |
+
$redirect_url = WordPress::getWpLoginUrl();
|
362 |
+
wp_safe_redirect($redirect_url);
|
363 |
exit;
|
364 |
}
|
365 |
|
plugins/WordPress/WpAssetManager.php
CHANGED
@@ -57,7 +57,8 @@ class WpAssetManager extends AssetManager
|
|
57 |
$jsFiles[] = 'jquery/ui/effect.min.js';
|
58 |
|
59 |
foreach ($jsFiles as $jsFile) {
|
60 |
-
$
|
|
|
61 |
}
|
62 |
|
63 |
$result .= "<script type=\"text/javascript\">window.$ = jQuery;</script>";
|
57 |
$jsFiles[] = 'jquery/ui/effect.min.js';
|
58 |
|
59 |
foreach ($jsFiles as $jsFile) {
|
60 |
+
$jQueryPath = includes_url('js/' . $jsFile);
|
61 |
+
$result .= sprintf(self::JS_IMPORT_DIRECTIVE, $jQueryPath);
|
62 |
}
|
63 |
|
64 |
$result .= "<script type=\"text/javascript\">window.$ = jQuery;</script>";
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
|
|
4 |
Tags: matomo,piwik,analytics,statistics,stats,tracking,ecommerce
|
5 |
Requires at least: 4.8
|
6 |
Tested up to: 5.4
|
7 |
-
Stable tag: 1.0.
|
8 |
Requires PHP: 7.2
|
9 |
License: GPLv3 or later
|
10 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
@@ -13,6 +13,8 @@ Matomo is the #1 Google Analytics alternative that gives you full control over y
|
|
13 |
|
14 |
== Description ==
|
15 |
|
|
|
|
|
16 |
For all you WordPress website owners wanting an easier way to get customer insights to grow your business, you can now get the solution the professionals use, for free!
|
17 |
|
18 |
Matomo Analytics is the #1 used Google Analytics alternative that offers a powerful range of features, security and protects the privacy of your users. This enables you to learn how to improve your website, make the right decisions for your business and stand out in the crowd in a safe and trustworthy way.
|
@@ -158,3 +160,20 @@ Needing to know more? [Click here to view all of our FAQs on our website](https:
|
|
158 |
9. Options to anonymize data so you don't track personal data.
|
159 |
10. Automatically delete old data you no longer need to be privacy compliant and to free your server from not needed data.
|
160 |
11. Summary page for getting a quick overview.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
Tags: matomo,piwik,analytics,statistics,stats,tracking,ecommerce
|
5 |
Requires at least: 4.8
|
6 |
Tested up to: 5.4
|
7 |
+
Stable tag: 1.0.4
|
8 |
Requires PHP: 7.2
|
9 |
License: GPLv3 or later
|
10 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
13 |
|
14 |
== Description ==
|
15 |
|
16 |
+
If you are already using Matomo On-Premise (formerly Piwik) or Matomo Cloud, please use the [WP-Matomo plugin](https://wordpress.org/plugins/wp-piwik/).
|
17 |
+
|
18 |
For all you WordPress website owners wanting an easier way to get customer insights to grow your business, you can now get the solution the professionals use, for free!
|
19 |
|
20 |
Matomo Analytics is the #1 used Google Analytics alternative that offers a powerful range of features, security and protects the privacy of your users. This enables you to learn how to improve your website, make the right decisions for your business and stand out in the crowd in a safe and trustworthy way.
|
160 |
9. Options to anonymize data so you don't track personal data.
|
161 |
10. Automatically delete old data you no longer need to be privacy compliant and to free your server from not needed data.
|
162 |
11. Summary page for getting a quick overview.
|
163 |
+
|
164 |
+
== Changelog ==
|
165 |
+
|
166 |
+
= 1.0.4 =
|
167 |
+
* Update Matomo core to 3.13.4
|
168 |
+
* Fix the website's timezone may be set to UTC instead of the WP timezone
|
169 |
+
* Improve compatibility with PHP 7.4 by fixing more notices
|
170 |
+
* Add a review link to the About page
|
171 |
+
* Add a newsletter signup possibility to the About page.
|
172 |
+
* Support MaxMind geolocation database
|
173 |
+
* Better support for hiding login URLs eg with WPS plugin
|
174 |
+
* Show header icon images
|
175 |
+
* Update GeoIP DB monthly instead of weekly
|
176 |
+
* Ask for a review every 90 days unless dismissed
|
177 |
+
* Possibility to configure proxy client header
|
178 |
+
|
179 |
+
[See changelog for all versions](https://github.com/matomo-org/wp-matomo/blob/develop/CHANGELOG.md).
|