Matomo Analytics – Ethical Stats. Powerful Insights. - Version 1.1.2

Version Description

Download this release

Release Info

Developer matomoteam
Plugin Icon 128x128 Matomo Analytics – Ethical Stats. Powerful Insights.
Version 1.1.2
Comparing to
See all releases

Code changes from version 1.1.1 to 1.1.2

app/core/Concurrency/Lock.php CHANGED
@@ -8,11 +8,14 @@
8
  */
9
  namespace Piwik\Concurrency;
10
 
 
11
  use Piwik\Common;
 
12
 
13
  class Lock
14
  {
15
  const MAX_KEY_LEN = 70;
 
16
 
17
  /**
18
  * @var LockBackend
@@ -24,18 +27,28 @@ class Lock
24
  private $lockKey = null;
25
  private $lockValue = null;
26
  private $defaultTtl = null;
 
27
 
28
  public function __construct(LockBackend $backend, $lockKeyStart, $defaultTtl = null)
29
  {
30
  $this->backend = $backend;
31
  $this->lockKeyStart = $lockKeyStart;
32
  $this->lockKey = $this->lockKeyStart;
33
- $this->defaultTtl = $defaultTtl;
34
  }
35
 
36
  public function reexpireLock()
37
  {
38
- $this->expireLock($this->defaultTtl);
 
 
 
 
 
 
 
 
 
39
  }
40
 
41
  public function getNumberOfAcquiredLocks()
@@ -81,6 +94,8 @@ class Lock
81
 
82
  if ($locked) {
83
  $this->lockValue = $lockValue;
 
 
84
  }
85
 
86
  return $locked;
@@ -125,6 +140,8 @@ class Lock
125
  return false;
126
  }
127
 
 
 
128
  return true;
129
  } else {
130
  Common::printDebug('Lock is not acquired, cannot update expiration.');
8
  */
9
  namespace Piwik\Concurrency;
10
 
11
+ use Piwik\ArchiveProcessor\ArchivingStatus;
12
  use Piwik\Common;
13
+ use Piwik\Date;
14
 
15
  class Lock
16
  {
17
  const MAX_KEY_LEN = 70;
18
+ const DEFAULT_TTL = 60;
19
 
20
  /**
21
  * @var LockBackend
27
  private $lockKey = null;
28
  private $lockValue = null;
29
  private $defaultTtl = null;
30
+ private $lastExpireTime = null;
31
 
32
  public function __construct(LockBackend $backend, $lockKeyStart, $defaultTtl = null)
33
  {
34
  $this->backend = $backend;
35
  $this->lockKeyStart = $lockKeyStart;
36
  $this->lockKey = $this->lockKeyStart;
37
+ $this->defaultTtl = $defaultTtl ?: self::DEFAULT_TTL;
38
  }
39
 
40
  public function reexpireLock()
41
  {
42
+ $timeBetweenReexpires = $this->defaultTtl - ($this->defaultTtl / 4);
43
+
44
+ $now = Date::getNowTimestamp();
45
+ if (!empty($this->lastExpireTime) &&
46
+ $now <= $this->lastExpireTime + $timeBetweenReexpires
47
+ ) {
48
+ return false;
49
+ }
50
+
51
+ return $this->expireLock($this->defaultTtl);
52
  }
53
 
54
  public function getNumberOfAcquiredLocks()
94
 
95
  if ($locked) {
96
  $this->lockValue = $lockValue;
97
+ $this->ttlUsed = $ttlInSeconds;
98
+ $this->lastExpireTime = Date::getNowTimestamp();
99
  }
100
 
101
  return $locked;
140
  return false;
141
  }
142
 
143
+ $this->lastExpireTime = Date::getNowTimestamp();
144
+
145
  return true;
146
  } else {
147
  Common::printDebug('Lock is not acquired, cannot update expiration.');
app/core/Config/IniFileChain.php CHANGED
@@ -97,9 +97,36 @@ class IniFileChain
97
  */
98
  public function set($name, $value)
99
  {
 
 
 
 
 
100
  $this->mergedSettings[$name] = $value;
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  /**
104
  * Returns all settings. Changes made to the array result will be reflected in the
105
  * IniFileChain instance.
97
  */
98
  public function set($name, $value)
99
  {
100
+ $name = $this->replaceSectionInvalidChars($name);
101
+ if ($value !== null) {
102
+ $value = $this->replaceInvalidChars($value);
103
+ }
104
+
105
  $this->mergedSettings[$name] = $value;
106
  }
107
 
108
+ private function replaceInvalidChars($value)
109
+ {
110
+ if (is_array($value)) {
111
+ $result = [];
112
+ foreach ($value as $key => $arrayValue) {
113
+ $key = $this->replaceInvalidChars($key);
114
+ if (is_array($arrayValue)) {
115
+ $arrayValue = $this->replaceInvalidChars($arrayValue);
116
+ }
117
+
118
+ $result[$key] = $arrayValue;
119
+ }
120
+ return $result;
121
+ } else {
122
+ return preg_replace('/[^a-zA-Z0-9_\[\]-]/', '', $value);
123
+ }
124
+ }
125
+
126
+ private function replaceSectionInvalidChars($value)
127
+ {
128
+ return preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
129
+ }
130
  /**
131
  * Returns all settings. Changes made to the array result will be reflected in the
132
  * IniFileChain instance.
app/core/DataAccess/ArchiveSelector.php CHANGED
@@ -89,11 +89,14 @@ class ArchiveSelector
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
  }
89
  ) { // the archive cannot be considered valid for this request (has wrong done flag value)
90
  return [false, $visits, $visitsConverted, true];
91
  }
92
+ if (!empty($minDatetimeArchiveProcessedUTC) && !is_object($minDatetimeArchiveProcessedUTC)) {
93
+ $minDatetimeArchiveProcessedUTC = Date::factory($minDatetimeArchiveProcessedUTC);
94
+ }
95
 
96
  // the archive is too old
97
  if ($minDatetimeArchiveProcessedUTC
98
  && isset($result['idarchive'])
99
+ && Date::factory($result['ts_archived'])->isEarlier($minDatetimeArchiveProcessedUTC)
100
  ) {
101
  return [false, $visits, $visitsConverted, true];
102
  }
app/core/DataAccess/ArchivingDbAdapter.php CHANGED
@@ -31,11 +31,6 @@ class ArchivingDbAdapter
31
  */
32
  private $logger;
33
 
34
- /**
35
- * @var int
36
- */
37
- private $lastReexpireTime = null;
38
-
39
  public function __construct($wrapped, Lock $archivingLock = null, LoggerInterface $logger = null)
40
  {
41
  $this->wrapped = $wrapped;
@@ -107,11 +102,7 @@ class ArchivingDbAdapter
107
  private function reexpireLock()
108
  {
109
  if ($this->archivingLock) {
110
- $timeBetweenReexpires = ArchivingStatus::DEFAULT_ARCHIVING_TTL / 4;
111
- if ($this->lastReexpireTime + $timeBetweenReexpires < time()) {
112
- $this->archivingLock->reexpireLock();
113
- $this->lastReexpireTime = time();
114
- }
115
  }
116
  }
117
  }
31
  */
32
  private $logger;
33
 
 
 
 
 
 
34
  public function __construct($wrapped, Lock $archivingLock = null, LoggerInterface $logger = null)
35
  {
36
  $this->wrapped = $wrapped;
102
  private function reexpireLock()
103
  {
104
  if ($this->archivingLock) {
105
+ $this->archivingLock->reexpireLock();
 
 
 
 
106
  }
107
  }
108
  }
app/core/DataAccess/Model.php CHANGED
@@ -59,7 +59,7 @@ class Model
59
  FROM `$archiveTable`
60
  WHERE name LIKE 'done%'
61
  AND `value` NOT IN (" . ArchiveWriter::DONE_ERROR . ")
62
- GROUP BY idsite, date1, date2, period, name";
63
 
64
  $archiveIds = array();
65
 
59
  FROM `$archiveTable`
60
  WHERE name LIKE 'done%'
61
  AND `value` NOT IN (" . ArchiveWriter::DONE_ERROR . ")
62
+ GROUP BY idsite, date1, date2, period, name HAVING count(*) > 1";
63
 
64
  $archiveIds = array();
65
 
app/core/Tracker/FingerprintSalt.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ *
8
+ */
9
+
10
+ namespace Piwik\Tracker;
11
+
12
+ use Piwik\Common;
13
+ use Piwik\Date;
14
+ use Piwik\Exception\InvalidRequestParameterException;
15
+ use Piwik\Exception\UnexpectedWebsiteFoundException;
16
+ use Piwik\Option;
17
+ use Piwik\Piwik;
18
+ use Piwik\SettingsServer;
19
+ use Piwik\Site;
20
+ use Piwik\Db as PiwikDb;
21
+
22
+ class FingerprintSalt
23
+ {
24
+ const OPTION_PREFIX = 'fingerprint_salt_';
25
+ const DELETE_FINGERPRINT_OLDER_THAN_SECONDS = 432000; // 5 days in seconds
26
+
27
+ public function generateSalt()
28
+ {
29
+ return Common::getRandomString(32);
30
+ }
31
+
32
+ public function deleteOldSalts()
33
+ {
34
+ // we want to make sure to delete salts that were created more than three days ago as they are likely not in
35
+ // use anymore. We should delete them to ensure the fingerprint is truly random for each day because if we used
36
+ // eg the regular salt then it would technically still be possible to try and regenerate the fingerprint based
37
+ // on certain information.
38
+ // Typically, only the salts for today and yesterday are used. However, if someone was to import historical data
39
+ // for the same day and this takes more than five days, then it could technically happen that we delete a
40
+ // fingerprint that is still in use now and as such after deletion a few visitors would have a new configId
41
+ // within one visit and such a new visit would be created. That should be very much edge case though.
42
+ $deleteSaltsCreatedBefore = Date::getNowTimestamp() - self::DELETE_FINGERPRINT_OLDER_THAN_SECONDS;
43
+ $options = Option::getLike(self::OPTION_PREFIX . '%');
44
+ $deleted = array();
45
+ foreach ($options as $name => $value) {
46
+ $value = $this->decode($value);
47
+ if (empty($value['time']) || $value['time'] < $deleteSaltsCreatedBefore) {
48
+ Option::delete($name);
49
+ $deleted[] = $name;
50
+ }
51
+ }
52
+
53
+ return $deleted;
54
+ }
55
+
56
+ public function getDateString(Date $date, $timezone)
57
+ {
58
+ $dateString = Date::factory($date->getTimestampUTC(), $timezone)->toString();
59
+ return $dateString;
60
+ }
61
+
62
+ private function encode($value)
63
+ {
64
+ return json_encode($value);
65
+ }
66
+
67
+ private function decode($value)
68
+ {
69
+ return @json_decode($value, true);
70
+ }
71
+
72
+ public function getSalt($dateString, $idSite)
73
+ {
74
+ $fingerprintSaltKey = self::OPTION_PREFIX . (int) $idSite . '_' . $dateString;
75
+ $salt = Option::get($fingerprintSaltKey);
76
+ if (!empty($salt)) {
77
+ $salt = $this->decode($salt);
78
+ }
79
+ if (empty($salt['value'])) {
80
+ $salt = array(
81
+ 'value' => $this->generateSalt(),
82
+ 'time' => Date::getNowTimestamp()
83
+ );
84
+ Option::set($fingerprintSaltKey, $this->encode($salt));
85
+ }
86
+ return $salt['value'];
87
+ }
88
+ }
app/core/Tracker/PageUrl.php CHANGED
@@ -36,14 +36,13 @@ class PageUrl
36
  * @param $idSite
37
  * @return bool|string Returned URL is HTML entities decoded
38
  */
39
- public static function excludeQueryParametersFromUrl($originalUrl, $idSite)
40
  {
41
  $originalUrl = self::cleanupUrl($originalUrl);
42
 
43
  $parsedUrl = @parse_url($originalUrl);
44
  $parsedUrl = self::cleanupHostAndHashTag($parsedUrl, $idSite);
45
- $parametersToExclude = self::getQueryParametersToExclude($idSite);
46
-
47
  if (empty($parsedUrl['query'])) {
48
  if (empty($parsedUrl['fragment'])) {
49
  return UrlHelper::getParseUrlReverse($parsedUrl);
36
  * @param $idSite
37
  * @return bool|string Returned URL is HTML entities decoded
38
  */
39
+ public static function excludeQueryParametersFromUrl($originalUrl, $idSite, $additionalParametersToExclude = [])
40
  {
41
  $originalUrl = self::cleanupUrl($originalUrl);
42
 
43
  $parsedUrl = @parse_url($originalUrl);
44
  $parsedUrl = self::cleanupHostAndHashTag($parsedUrl, $idSite);
45
+ $parametersToExclude = array_merge(self::getQueryParametersToExclude($idSite), $additionalParametersToExclude);
 
46
  if (empty($parsedUrl['query'])) {
47
  if (empty($parsedUrl['fragment'])) {
48
  return UrlHelper::getParseUrlReverse($parsedUrl);
app/core/Tracker/Request.php CHANGED
@@ -195,6 +195,16 @@ class Request
195
  return false;
196
  }
197
 
 
 
 
 
 
 
 
 
 
 
198
  Piwik::postEvent('Request.initAuthenticationObject');
199
 
200
  /** @var \Piwik\Auth $auth */
@@ -209,16 +219,6 @@ class Request
209
  return true;
210
  }
211
 
212
- // Now checking the list of admin token_auth cached in the Tracker config file
213
- if (!empty($idSite) && $idSite > 0) {
214
- $website = Cache::getCacheWebsiteAttributes($idSite);
215
- $hashedToken = UsersManager::hashTrackingToken((string) $tokenAuth, $idSite);
216
-
217
- if (array_key_exists('tracking_token_auth', $website)
218
- && in_array($hashedToken, $website['tracking_token_auth'], true)) {
219
- return true;
220
- }
221
- }
222
 
223
  Common::printDebug("WARNING! token_auth = $tokenAuth is not valid, Super User / Admin / Write was NOT authenticated");
224
 
195
  return false;
196
  }
197
 
198
+ // Now checking the list of admin token_auth cached in the Tracker config file
199
+ if (!empty($idSite) && $idSite > 0) {
200
+ $website = Cache::getCacheWebsiteAttributes($idSite);
201
+ $hashedToken = UsersManager::hashTrackingToken((string) $tokenAuth, $idSite);
202
+
203
+ if (array_key_exists('tracking_token_auth', $website)
204
+ && in_array($hashedToken, $website['tracking_token_auth'], true)) {
205
+ return true;
206
+ }
207
+ }
208
  Piwik::postEvent('Request.initAuthenticationObject');
209
 
210
  /** @var \Piwik\Auth $auth */
219
  return true;
220
  }
221
 
 
 
 
 
 
 
 
 
 
 
222
 
223
  Common::printDebug("WARNING! token_auth = $tokenAuth is not valid, Super User / Admin / Write was NOT authenticated");
224
 
app/core/Tracker/Settings.php CHANGED
@@ -10,6 +10,10 @@ namespace Piwik\Tracker;
10
 
11
  use Piwik\Config;
12
  use Piwik\Container\StaticContainer;
 
 
 
 
13
  use Piwik\Tracker;
14
  use Piwik\DeviceDetector\DeviceDetectorFactory;
15
  use Piwik\SettingsPiwik;
@@ -57,6 +61,31 @@ class Settings // TODO: merge w/ visitor recognizer or make it it's own service.
57
  }
58
 
59
  $browserLang = substr($request->getBrowserLanguage(), 0, 20); // limit the length of this string to match db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  return $this->getConfigHash(
62
  $request,
@@ -74,7 +103,8 @@ class Settings // TODO: merge w/ visitor recognizer or make it it's own service.
74
  $plugin_Silverlight,
75
  $plugin_Cookie,
76
  $ipAddress,
77
- $browserLang);
 
78
  }
79
 
80
  /**
@@ -96,12 +126,13 @@ class Settings // TODO: merge w/ visitor recognizer or make it it's own service.
96
  * @param $plugin_Cookie
97
  * @param $ip
98
  * @param $browserLang
 
99
  * @return string
100
  */
101
  protected function getConfigHash(Request $request, $os, $browserName, $browserVersion, $plugin_Flash, $plugin_Java,
102
  $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF,
103
  $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip,
104
- $browserLang)
105
  {
106
  // prevent the config hash from being the same, across different Piwik instances
107
  // (limits ability of different Piwik instances to cross-match users)
@@ -114,7 +145,8 @@ class Settings // TODO: merge w/ visitor recognizer or make it it's own service.
114
  . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie
115
  . $ip
116
  . $browserLang
117
- . $salt;
 
118
 
119
  if (!$this->isSameFingerprintsAcrossWebsites) {
120
  $configString .= $request->getIdSite();
10
 
11
  use Piwik\Config;
12
  use Piwik\Container\StaticContainer;
13
+ use Piwik\Date;
14
+ use Piwik\Option;
15
+ use Piwik\SettingsServer;
16
+ use Piwik\Site;
17
  use Piwik\Tracker;
18
  use Piwik\DeviceDetector\DeviceDetectorFactory;
19
  use Piwik\SettingsPiwik;
61
  }
62
 
63
  $browserLang = substr($request->getBrowserLanguage(), 0, 20); // limit the length of this string to match db
64
+ $trackerConfig = Config::getInstance()->Tracker;
65
+
66
+ $fingerprintSalt = '';
67
+
68
+ // fingerprint salt won't work when across multiple sites since all sites could have different timezones
69
+ // also cant add fingerprint salt for a specific day when we dont create new visit after midnight
70
+ if (!$this->isSameFingerprintsAcrossWebsites && !empty($trackerConfig['create_new_visit_after_midnight'])) {
71
+ $cache = Cache::getCacheWebsiteAttributes($request->getIdSite());
72
+ $date = Date::factory((int) $request->getCurrentTimestamp());
73
+ $fingerprintSaltKey = new FingerprintSalt();
74
+ $dateString = $fingerprintSaltKey->getDateString($date, $cache['timezone']);
75
+
76
+ if (!empty($cache[FingerprintSalt::OPTION_PREFIX . $dateString])) {
77
+ $fingerprintSalt = $cache[FingerprintSalt::OPTION_PREFIX . $dateString];
78
+ } else {
79
+ // we query the DB directly for requests older than 2-3 days...
80
+ $fingerprintSalt = $fingerprintSaltKey->getSalt($dateString, $request->getIdSite());
81
+ }
82
+
83
+ $fingerprintSalt .= $dateString;
84
+
85
+ if (defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE) {
86
+ $fingerprintSalt = ''; // use fixed value so they don't change randomly in tests
87
+ }
88
+ }
89
 
90
  return $this->getConfigHash(
91
  $request,
103
  $plugin_Silverlight,
104
  $plugin_Cookie,
105
  $ipAddress,
106
+ $browserLang,
107
+ $fingerprintSalt);
108
  }
109
 
110
  /**
126
  * @param $plugin_Cookie
127
  * @param $ip
128
  * @param $browserLang
129
+ * @param $fingerprintHash
130
  * @return string
131
  */
132
  protected function getConfigHash(Request $request, $os, $browserName, $browserVersion, $plugin_Flash, $plugin_Java,
133
  $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF,
134
  $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip,
135
+ $browserLang, $fingerprintHash)
136
  {
137
  // prevent the config hash from being the same, across different Piwik instances
138
  // (limits ability of different Piwik instances to cross-match users)
145
  . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie
146
  . $ip
147
  . $browserLang
148
+ . $salt
149
+ . $fingerprintHash;
150
 
151
  if (!$this->isSameFingerprintsAcrossWebsites) {
152
  $configString .= $request->getIdSite();
app/core/Tracker/Visit.php CHANGED
@@ -243,7 +243,8 @@ class Visit implements VisitInterface
243
  foreach ($this->requestProcessors as $processor) {
244
  $processor->onExistingVisit($valuesToUpdate, $this->visitProperties, $this->request);
245
  }
246
-
 
247
  $this->updateExistingVisit($valuesToUpdate);
248
 
249
  $this->visitProperties->setProperty('visit_last_action_time', $this->request->getCurrentTimestamp());
243
  foreach ($this->requestProcessors as $processor) {
244
  $processor->onExistingVisit($valuesToUpdate, $this->visitProperties, $this->request);
245
  }
246
+ $visitorRecognizer = StaticContainer::get(VisitorRecognizer::class);
247
+ $valuesToUpdate = $visitorRecognizer->removeUnchangedValues($this->visitProperties, $valuesToUpdate);
248
  $this->updateExistingVisit($valuesToUpdate);
249
 
250
  $this->visitProperties->setProperty('visit_last_action_time', $this->request->getCurrentTimestamp());
app/core/Tracker/VisitorRecognizer.php CHANGED
@@ -19,6 +19,8 @@ use Piwik\Tracker\Visit\VisitProperties;
19
  */
20
  class VisitorRecognizer
21
  {
 
 
22
  /**
23
  * Local variable cache for the getVisitFieldsPersist() method.
24
  *
@@ -105,6 +107,7 @@ class VisitorRecognizer
105
  if ($visitRow
106
  && count($visitRow) > 0
107
  ) {
 
108
  $visitProperties->setProperty('idvisitor', $visitRow['idvisitor']);
109
  $visitProperties->setProperty('user_id', $visitRow['user_id']);
110
 
@@ -121,6 +124,34 @@ class VisitorRecognizer
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  public function updateVisitPropertiesFromLastVisitRow(VisitProperties $visitProperties)
125
  {
126
  // These values will be used throughout the request
19
  */
20
  class VisitorRecognizer
21
  {
22
+ const KEY_ORIGINAL_VISIT_ROW = 'originalVisit';
23
+
24
  /**
25
  * Local variable cache for the getVisitFieldsPersist() method.
26
  *
107
  if ($visitRow
108
  && count($visitRow) > 0
109
  ) {
110
+ $visitProperties->setProperty(self::KEY_ORIGINAL_VISIT_ROW, $visitRow);
111
  $visitProperties->setProperty('idvisitor', $visitRow['idvisitor']);
112
  $visitProperties->setProperty('user_id', $visitRow['user_id']);
113
 
124
  }
125
  }
126
 
127
+ public function removeUnchangedValues(VisitProperties $visitProperties, $visit)
128
+ {
129
+ $originalRow = $visitProperties->getProperty(self::KEY_ORIGINAL_VISIT_ROW);
130
+
131
+ if (empty($originalRow)) {
132
+ return $visit;
133
+ }
134
+
135
+ if (!empty($originalRow['idvisitor'])
136
+ && !empty($visit['idvisitor'])
137
+ && bin2hex($originalRow['idvisitor']) === bin2hex($visit['idvisitor'])) {
138
+ unset($visit['idvisitor']);
139
+ }
140
+
141
+ $fieldsToCompareValue = array('user_id', 'visit_last_action_time', 'visit_total_time');
142
+ foreach ($fieldsToCompareValue as $field) {
143
+ if (!empty($originalRow[$field])
144
+ && !empty($visit[$field])
145
+ && $visit[$field] == $originalRow[$field]) {
146
+ // we can't use === eg for visit_total_time which may be partially an integer and sometimes a string
147
+ // because we check for !empty things should still work as expected though
148
+ // (eg we wouldn't compare false with 0)
149
+ unset($visit[$field]);
150
+ }
151
+ }
152
+
153
+ return $visit;
154
+ }
155
  public function updateVisitPropertiesFromLastVisitRow(VisitProperties $visitProperties)
156
  {
157
  // These values will be used throughout the request
app/core/Updates/3.13.6-b1.php CHANGED
@@ -20,6 +20,8 @@ class Updates_3_13_6_b1 extends PiwikUpdates
20
  global $wpdb;
21
 
22
  if ($wpdb->charset === 'utf8mb4') {
 
 
23
  $db_settings = new \WpMatomo\Db\Settings();
24
  $wpdb->query(sprintf('ALTER TABLE `%s` CHANGE `%s` `%s` %s',
25
  $db_settings->prefix_table_name('session'),
@@ -54,6 +56,7 @@ class Updates_3_13_6_b1 extends PiwikUpdates
54
  }
55
  }
56
  }
 
57
  }
58
  }
59
 
20
  global $wpdb;
21
 
22
  if ($wpdb->charset === 'utf8mb4') {
23
+ $save = $wpdb->show_errors(false);
24
+
25
  $db_settings = new \WpMatomo\Db\Settings();
26
  $wpdb->query(sprintf('ALTER TABLE `%s` CHANGE `%s` `%s` %s',
27
  $db_settings->prefix_table_name('session'),
56
  }
57
  }
58
  }
59
+ $wpdb->show_errors($save);
60
  }
61
  }
62
 
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.6-b1';
24
 
25
  public function isStableVersion($version)
26
  {
20
  * The current Matomo version.
21
  * @var string
22
  */
23
+ const VERSION = '3.13.6';
24
 
25
  public function isStableVersion($version)
26
  {
app/plugins/CoreAdminHome/Tasks.php CHANGED
@@ -32,6 +32,7 @@ use Piwik\Scheduler\Schedule\SpecificTime;
32
  use Piwik\Settings\Storage\Backend\MeasurableSettingsTable;
33
  use Piwik\Tracker\Failures;
34
  use Piwik\Site;
 
35
  use Piwik\Tracker\Visit\ReferrerSpamFilter;
36
  use Psr\Log\LoggerInterface;
37
  use Piwik\SettingsPiwik;
@@ -80,6 +81,7 @@ class Tasks extends \Piwik\Plugin\Tasks
80
 
81
  $this->daily('cleanupTrackingFailures', null, self::LOWEST_PRIORITY);
82
  $this->weekly('notifyTrackingFailures', null, self::LOWEST_PRIORITY);
 
83
 
84
  if(SettingsPiwik::isInternetEnabled() === true){
85
  $this->weekly('updateSpammerBlacklist');
@@ -88,6 +90,13 @@ class Tasks extends \Piwik\Plugin\Tasks
88
  $this->scheduleTrackingCodeReminderChecks();
89
  }
90
 
 
 
 
 
 
 
 
91
  public function invalidateOutdatedArchives()
92
  {
93
  if (!Rules::isBrowserTriggerEnabled()) {
32
  use Piwik\Settings\Storage\Backend\MeasurableSettingsTable;
33
  use Piwik\Tracker\Failures;
34
  use Piwik\Site;
35
+ use Piwik\Tracker\FingerprintSalt;
36
  use Piwik\Tracker\Visit\ReferrerSpamFilter;
37
  use Psr\Log\LoggerInterface;
38
  use Piwik\SettingsPiwik;
81
 
82
  $this->daily('cleanupTrackingFailures', null, self::LOWEST_PRIORITY);
83
  $this->weekly('notifyTrackingFailures', null, self::LOWEST_PRIORITY);
84
+ $this->daily('deleteOldFingerprintSalts', null, self::HIGH_PRIORITY);
85
 
86
  if(SettingsPiwik::isInternetEnabled() === true){
87
  $this->weekly('updateSpammerBlacklist');
90
  $this->scheduleTrackingCodeReminderChecks();
91
  }
92
 
93
+
94
+
95
+ public function deleteOldFingerprintSalts()
96
+ {
97
+ StaticContainer::get(FingerprintSalt::class)->deleteOldSalts();
98
+ }
99
+
100
  public function invalidateOutdatedArchives()
101
  {
102
  if (!Rules::isBrowserTriggerEnabled()) {
app/plugins/CoreHome/Columns/VisitLastActionTime.php CHANGED
@@ -14,6 +14,7 @@ use Piwik\Tracker\Action;
14
  use Piwik\Tracker\Request;
15
  use Piwik\Tracker\Visitor;
16
  use Piwik\Metrics\Formatter;
 
17
 
18
  require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
19
 
@@ -67,7 +68,14 @@ class VisitLastActionTime extends VisitDimension
67
  if ($request->getParam('ping') == 1) {
68
  return false;
69
  }
70
-
 
 
 
 
 
 
 
71
  return $this->onNewVisit($request, $visitor, $action);
72
  }
73
  }
14
  use Piwik\Tracker\Request;
15
  use Piwik\Tracker\Visitor;
16
  use Piwik\Metrics\Formatter;
17
+ use Piwik\Tracker\VisitorRecognizer;
18
 
19
  require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
20
 
68
  if ($request->getParam('ping') == 1) {
69
  return false;
70
  }
71
+ $originalVisit = $visitor->getVisitorColumn(VisitorRecognizer::KEY_ORIGINAL_VISIT_ROW);
72
+
73
+ if (!empty($originalVisit['visit_last_action_time'])
74
+ && Date::factory($originalVisit['visit_last_action_time'])->getTimestamp() > $request->getCurrentTimestamp()) {
75
+ // make sure to not set visit_last_action_time to an earlier time eg if tracking requests aren't sent in order
76
+ return $originalVisit['visit_last_action_time'];
77
+ }
78
+
79
  return $this->onNewVisit($request, $visitor, $action);
80
  }
81
  }
app/plugins/CustomPiwikJs/TrackingCode/PluginTrackerFiles.php CHANGED
@@ -41,9 +41,7 @@ class PluginTrackerFiles
41
  $dirs = array();
42
  $manager = Plugin\Manager::getInstance();
43
  foreach ($manager->getPluginsLoadedAndActivated() as $pluginName => $plugin) {
44
- if ($plugin->isTrackerPlugin()) {
45
- $dirs[$pluginName] = rtrim(Plugin\Manager::getPluginDirectory($pluginName), '/') . '/';
46
- }
47
  }
48
  return $dirs;
49
  }
41
  $dirs = array();
42
  $manager = Plugin\Manager::getInstance();
43
  foreach ($manager->getPluginsLoadedAndActivated() as $pluginName => $plugin) {
44
+ $dirs[$pluginName] = rtrim(Plugin\Manager::getPluginDirectory($pluginName), '/') . '/';
 
 
45
  }
46
  return $dirs;
47
  }
app/plugins/GeoIp2/Commands/ConvertRegionCodesToIso.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ namespace Piwik\Plugins\GeoIp2\Commands;
10
+
11
+ use Piwik\Common;
12
+ use Piwik\Db;
13
+ use Piwik\DbHelper;
14
+ use Piwik\Option;
15
+ use Piwik\Plugin\ConsoleCommand;
16
+ use Piwik\Plugins\UserCountry\LocationProvider;
17
+ use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
18
+ use Symfony\Component\Console\Input\InputInterface;
19
+ use Symfony\Component\Console\Output\OutputInterface;
20
+
21
+ class ConvertRegionCodesToIso extends ConsoleCommand
22
+ {
23
+ const OPTION_NAME = 'regioncodes_converted';
24
+ const MAPPING_TABLE_NAME = 'fips2iso';
25
+
26
+ protected function configure()
27
+ {
28
+ $this->setName('usercountry:convert-region-codes');
29
+ $this->setDescription("Convert FIPS region codes saved by GeoIP legacy provider to ISO.");
30
+ }
31
+
32
+ public function isEnabled()
33
+ {
34
+ return false;
35
+ }
36
+
37
+ /**
38
+ * @param InputInterface $input
39
+ * @param OutputInterface $output
40
+ * @return void|int
41
+ */
42
+ protected function execute(InputInterface $input, OutputInterface $output)
43
+ {
44
+ // chick if option is set to disable second run
45
+ if (Option::get(self::OPTION_NAME)) {
46
+ $output->writeln('Converting region codes already done.');
47
+ return;
48
+ }
49
+
50
+ $output->setDecorated(true);
51
+
52
+ $output->write('Creating mapping table in database');
53
+
54
+ Db::query('DROP table if exists ' . self::MAPPING_TABLE_NAME);
55
+
56
+ DbHelper::createTable(self::MAPPING_TABLE_NAME,
57
+ "`country_code` VARCHAR(2) NOT NULL,
58
+ `fips_code` VARCHAR(2) NOT NULL,
59
+ `iso_code` VARCHAR(4) NULL DEFAULT NULL,
60
+ PRIMARY KEY (`country_code`, `fips_code`)");
61
+
62
+ $output->writeln(' <fg=green>✓</>');
63
+
64
+ $mappings = include __DIR__ . '/../data/regionMapping.php';
65
+
66
+ $output->write('Inserting mapping data ');
67
+
68
+ $counter = 0;
69
+ foreach ($mappings as $country => $regionMapping) {
70
+ foreach ($regionMapping as $fips => $iso) {
71
+ if ($fips == $iso) {
72
+ continue; // nothing needs to be changed, so ignore the mapping
73
+ }
74
+
75
+ Db::query('INSERT INTO `'.Common::prefixTable(self::MAPPING_TABLE_NAME).'` VALUES (?, ?, ?)', [$country, $fips, $iso]);
76
+ $counter++;
77
+ if ($counter%50 == 0) {
78
+ $output->write('.');
79
+ }
80
+ }
81
+ }
82
+
83
+ $output->writeln(' <fg=green>✓</>');
84
+
85
+ $output->writeln('Updating Matomo log tables:');
86
+
87
+ $activationTime = Option::get(GeoIp2::SWITCH_TO_ISO_REGIONS_OPTION_NAME);
88
+ $activationDateTime = date('Y-m-d H:i:s', $activationTime);
89
+
90
+ // fix country and region of tibet so it wil be updated correctly afterwards
91
+ $tibetFixQuery = 'UPDATE %s SET location_country = "cn", location_region = "14" WHERE location_country = "ti"';
92
+
93
+ // replace invalid country codes used by GeoIP Legacy
94
+ $fixInvalidCountriesQuery = 'UPDATE %s SET location_country = "" WHERE location_country IN("AP", "EU", "A1", "A2")';
95
+
96
+ $query = "UPDATE %s INNER JOIN %s ON location_country = country_code AND location_region = fips_code SET location_region = iso_code
97
+ WHERE `%s` < ?";
98
+
99
+ $logTables = ['log_visit' => 'visit_first_action_time', 'log_conversion' => 'server_time'];
100
+
101
+ foreach ($logTables as $logTable => $dateField) {
102
+ $output->write('- Updating ' . $logTable);
103
+
104
+ Db::query(sprintf($tibetFixQuery, Common::prefixTable($logTable)));
105
+ Db::query(sprintf($fixInvalidCountriesQuery, Common::prefixTable($logTable)));
106
+
107
+ $sql = sprintf($query, Common::prefixTable($logTable), Common::prefixTable(self::MAPPING_TABLE_NAME), $dateField);
108
+ Db::query($sql, $activationDateTime);
109
+
110
+ $output->writeln(' <fg=green>✓</>');
111
+ }
112
+
113
+ $output->write('Removing mapping table from database ');
114
+ Db::dropTables(Common::prefixTable(self::MAPPING_TABLE_NAME));
115
+ $output->writeln(' <fg=green>✓</>');
116
+
117
+ // save option to prevent a second run
118
+ Option::set(self::OPTION_NAME, true);
119
+
120
+ $output->writeln('All region codes converted.');
121
+ }
122
+
123
+
124
+ }
app/plugins/Live/Controller.php CHANGED
@@ -12,6 +12,7 @@ use Piwik\API\Request;
12
  use Piwik\Common;
13
  use Piwik\Config;
14
  use Piwik\Container\StaticContainer;
 
15
  use Piwik\Piwik;
16
  use Piwik\Plugins\Goals\API as APIGoals;
17
  use Piwik\Plugins\Live\Visualizations\VisitorLog;
@@ -89,6 +90,7 @@ class Controller extends \Piwik\Plugin\Controller
89
  $view = new View('@Live/getLastVisitsStart');
90
  $view->idSite = (int) $this->idSite;
91
  $error = '';
 
92
  try {
93
  $api = new Request("method=Live.getLastVisitsDetails&idSite={$this->idSite}&filter_limit=10&format=original&serialize=0&disable_generic_filters=1");
94
  $visitors = $api->process();
12
  use Piwik\Common;
13
  use Piwik\Config;
14
  use Piwik\Container\StaticContainer;
15
+ use Piwik\DataTable;
16
  use Piwik\Piwik;
17
  use Piwik\Plugins\Goals\API as APIGoals;
18
  use Piwik\Plugins\Live\Visualizations\VisitorLog;
90
  $view = new View('@Live/getLastVisitsStart');
91
  $view->idSite = (int) $this->idSite;
92
  $error = '';
93
+ $visitors = new DataTable();
94
  try {
95
  $api = new Request("method=Live.getLastVisitsDetails&idSite={$this->idSite}&filter_limit=10&format=original&serialize=0&disable_generic_filters=1");
96
  $visitors = $api->process();
app/plugins/Morpheus/stylesheets/uibase/_header.less CHANGED
@@ -18,17 +18,6 @@
18
  }
19
  }
20
 
21
- // IE 10+
22
- @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
23
- #root #logo {
24
- width: 80px;
25
- }
26
-
27
- #root #logo img.default-piwik-logo {
28
- width: 100%;
29
- }
30
- }
31
-
32
  #javascriptDisabled,
33
  #javascriptDisabled a {
34
  font-weight: bold;
18
  }
19
  }
20
 
 
 
 
 
 
 
 
 
 
 
 
21
  #javascriptDisabled,
22
  #javascriptDisabled a {
23
  font-weight: bold;
app/plugins/Referrers/Columns/Base.php CHANGED
@@ -118,6 +118,7 @@ abstract class Base extends VisitDimension
118
  $this->nameReferrerAnalyzed .= rtrim($path, '/');
119
  }
120
  }
 
121
 
122
  $referrerInformation = array(
123
  'referer_type' => $this->typeReferrerAnalyzed,
@@ -137,6 +138,60 @@ abstract class Base extends VisitDimension
137
  return $referrerInformation;
138
  }
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  protected function getReferrerInformationFromRequest(Request $request, Visitor $visitor)
141
  {
142
  $referrerUrl = $request->getParam('urlref');
118
  $this->nameReferrerAnalyzed .= rtrim($path, '/');
119
  }
120
  }
121
+ $this->excludeQueryParamsFromReferrerUrl();
122
 
123
  $referrerInformation = array(
124
  'referer_type' => $this->typeReferrerAnalyzed,
138
  return $referrerInformation;
139
  }
140
 
141
+ protected function excludeQueryParamsFromReferrerUrl()
142
+ {
143
+ $parametersToExclude = [];
144
+
145
+ if (!empty($this->referrerHost) && strpos($this->referrerHost, 'instagram.com') !== false) {
146
+ $parametersToExclude[] = 'e';
147
+ $parametersToExclude[] = 's';
148
+ }
149
+ if (!empty($this->referrerHost) && strpos($this->referrerHost, 'facebook.com') !== false) {
150
+ $parametersToExclude[] = 'h';
151
+ $parametersToExclude[] = 'p';
152
+ }
153
+ if (!empty($this->referrerHost) && (strpos($this->referrerHost, 'google.') !== false || strpos($this->referrerHost, 'googleusercontent.') !== false)) {
154
+ $parametersToExclude[] = 'ust';
155
+ $parametersToExclude[] = 'usg';
156
+ $parametersToExclude[] = 'usd';
157
+ $parametersToExclude[] = 'sa';
158
+ $parametersToExclude[] = 'sntz';
159
+ $parametersToExclude[] = 'ei';
160
+ $parametersToExclude[] = 'sa';
161
+ $parametersToExclude[] = 'bvm';
162
+ $parametersToExclude[] = 'usg';
163
+ $parametersToExclude[] = 'ved';
164
+ $parametersToExclude[] = 'client';
165
+ $parametersToExclude[] = 'channel';
166
+ }
167
+
168
+ if (!empty($this->referrerHost) && strpos($this->referrerHost, 'main.exoclick.com') !== false) {
169
+ $parametersToExclude[] = 'data';
170
+ $parametersToExclude[] = 'wpn';
171
+ }
172
+ if (!empty($this->referrerHost) && strpos($this->referrerHost, 'youtube.com') !== false) {
173
+ $parametersToExclude[] = 'redir_token';
174
+ $parametersToExclude[] = 'html_redirect';
175
+ $parametersToExclude[] = 'continuation';
176
+ $parametersToExclude[] = 'feature';
177
+ }
178
+ if (!empty($this->referrerHost) && strpos($this->referrerHost, 'bing.com') !== false) {
179
+ $parametersToExclude[] = 'cvid';
180
+ $parametersToExclude[] = 'refig';
181
+ $parametersToExclude[] = 'elv';
182
+ $parametersToExclude[] = 'plvar';
183
+ $parametersToExclude[] = 'setlang';
184
+ $parametersToExclude[] = 'qs';
185
+ $parametersToExclude[] = 'cc';
186
+ $parametersToExclude[] = 'mkt';
187
+ $parametersToExclude[] = 'PC';
188
+ $parametersToExclude[] = 'form';
189
+ $parametersToExclude[] = 'src';
190
+ }
191
+
192
+ $this->referrerUrl = PageUrl::excludeQueryParametersFromUrl($this->referrerUrl, $this->idsite, $parametersToExclude);
193
+ }
194
+
195
  protected function getReferrerInformationFromRequest(Request $request, Visitor $visitor)
196
  {
197
  $referrerUrl = $request->getParam('urlref');
app/plugins/SitesManager/SitesManager.php CHANGED
@@ -13,12 +13,14 @@ use Piwik\API\Request;
13
  use Piwik\Common;
14
  use Piwik\Config;
15
  use Piwik\Container\StaticContainer;
 
16
  use Piwik\Exception\UnexpectedWebsiteFoundException;
17
  use Piwik\Option;
18
  use Piwik\Piwik;
19
  use Piwik\Plugins\CoreHome\SystemSummary;
20
  use Piwik\Settings\Storage\Backend\MeasurableSettingsTable;
21
  use Piwik\Tracker\Cache;
 
22
  use Piwik\Tracker\Model as TrackerModel;
23
  use Piwik\Session\SessionNamespace;
24
 
@@ -197,6 +199,14 @@ class SitesManager extends \Piwik\Plugin
197
  $array['timezone'] = $this->getTimezoneFromWebsite($website);
198
  $array['ts_created'] = $website['ts_created'];
199
  $array['type'] = $website['type'];
 
 
 
 
 
 
 
 
200
  }
201
 
202
  public function setTrackerCacheGeneral(&$cache)
13
  use Piwik\Common;
14
  use Piwik\Config;
15
  use Piwik\Container\StaticContainer;
16
+ use Piwik\Date;
17
  use Piwik\Exception\UnexpectedWebsiteFoundException;
18
  use Piwik\Option;
19
  use Piwik\Piwik;
20
  use Piwik\Plugins\CoreHome\SystemSummary;
21
  use Piwik\Settings\Storage\Backend\MeasurableSettingsTable;
22
  use Piwik\Tracker\Cache;
23
+ use Piwik\Tracker\FingerprintSalt;
24
  use Piwik\Tracker\Model as TrackerModel;
25
  use Piwik\Session\SessionNamespace;
26
 
199
  $array['timezone'] = $this->getTimezoneFromWebsite($website);
200
  $array['ts_created'] = $website['ts_created'];
201
  $array['type'] = $website['type'];
202
+
203
+ $datesToGenerateSalt = array(Date::now()->addDay(1), Date::now(), Date::now()->subDay(1), Date::now()->subDay(2));
204
+
205
+ $fingerprintSaltKey = new FingerprintSalt();
206
+ foreach ($datesToGenerateSalt as $date) {
207
+ $dateString = $fingerprintSaltKey->getDateString($date, $array['timezone']);
208
+ $array[FingerprintSalt::OPTION_PREFIX . $dateString] = $fingerprintSaltKey->getSalt($dateString, $idSite);
209
+ }
210
  }
211
 
212
  public function setTrackerCacheGeneral(&$cache)
app/plugins/UsersManager/API.php CHANGED
@@ -197,8 +197,15 @@ class API extends \Piwik\Plugin\API
197
  * @param string $preferenceName
198
  * @return bool|string
199
  */
200
- public function getUserPreference($userLogin, $preferenceName)
201
  {
 
 
 
 
 
 
 
202
  Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
203
 
204
  $optionValue = $this->getPreferenceValue($userLogin, $preferenceName);
197
  * @param string $preferenceName
198
  * @return bool|string
199
  */
200
+ public function getUserPreference($userLogin = false, $preferenceName)
201
  {
202
+ if ($userLogin === false) {
203
+ // the default value for first parameter is there to have it an optional parameter in the HTTP API
204
+ // in PHP it won't be optional. Could move parameter to the end of the method but did not want to break
205
+ // BC
206
+ $userLogin = Piwik::getCurrentUserLogin();
207
+ }
208
+
209
  Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
210
 
211
  $optionValue = $this->getPreferenceValue($userLogin, $preferenceName);
app/plugins/UsersManager/UsersManager.php CHANGED
@@ -217,6 +217,11 @@ class UsersManager extends \Piwik\Plugin
217
 
218
  public static function getPasswordHash($password)
219
  {
 
 
 
 
 
220
  self::checkBasicPasswordStrength($password);
221
 
222
  // if change here, should also edit the installation process
217
 
218
  public static function getPasswordHash($password)
219
  {
220
+ if (SettingsPiwik::isUserCredentialsSanityCheckEnabled()) {
221
+
222
+ self::checkBasicPasswordStrength($password);
223
+ }
224
+
225
  self::checkBasicPasswordStrength($password);
226
 
227
  // if change here, should also edit the installation process
assets/js/asset_manager_core_js.js CHANGED
@@ -1343,7 +1343,7 @@ return fetchAllPagesPromise;}}})();
1343
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
1344
  */
1345
  (function(){angular.module('piwikApp.service').factory('piwikPeriods',piwikPeriods);var periods={},periodOrder=[];piwik.addCustomPeriod=addCustomPeriod;function DayPeriod(date){this.dateInPeriod=date;}
1346
- DayPeriod.parse=singleDatePeriodFactory(DayPeriod);DayPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodDay');};DayPeriod.prototype={getPrettyString:function(){return format(this.dateInPeriod);},getDateRange:function(){return[this.dateInPeriod,this.dateInPeriod];}};addCustomPeriod('day',DayPeriod);function WeekPeriod(date){this.dateInPeriod=date;}
1347
  WeekPeriod.parse=singleDatePeriodFactory(WeekPeriod);WeekPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodWeek');};WeekPeriod.prototype={getPrettyString:function(){var weekDates=this.getDateRange(this.dateInPeriod);var startWeek=format(weekDates[0]);var endWeek=format(weekDates[1]);return _pk_translate('General_DateRangeFromTo',[startWeek,endWeek]);},getDateRange:function(){var daysToMonday=(this.dateInPeriod.getDay()+6)%7;var startWeek=new Date(this.dateInPeriod.getTime());startWeek.setDate(this.dateInPeriod.getDate()-daysToMonday);var endWeek=new Date(startWeek.getTime());endWeek.setDate(startWeek.getDate()+6);return[startWeek,endWeek];}};addCustomPeriod('week',WeekPeriod);function MonthPeriod(date){this.dateInPeriod=date;}
1348
  MonthPeriod.parse=singleDatePeriodFactory(MonthPeriod);MonthPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodMonth');};MonthPeriod.prototype={getPrettyString:function(){return _pk_translate('Intl_Month_Long_StandAlone_'+(this.dateInPeriod.getMonth()+1))+' '+
1349
  this.dateInPeriod.getFullYear();},getDateRange:function(){var startMonth=new Date(this.dateInPeriod.getTime());startMonth.setDate(1);var endMonth=new Date(this.dateInPeriod.getTime());endMonth.setMonth(endMonth.getMonth()+1);endMonth.setDate(0);return[startMonth,endMonth];}};addCustomPeriod('month',MonthPeriod);function YearPeriod(date){this.dateInPeriod=date;}
@@ -2172,7 +2172,11 @@ return dateFrom+','+dateTo;}else{return formatDate(vm.dateValue);}}
2172
  function setPiwikPeriodAndDate(period,date){vm.periodValue=period;vm.selectedPeriod=period;vm.dateValue=date;var currentDateString=formatDate(date);setRangeStartEndFromPeriod(period,currentDateString);propagateNewUrlParams(currentDateString,vm.selectedPeriod);initTopControls();}
2173
  function setRangeStartEndFromPeriod(period,dateStr){var dateRange=piwikPeriods.parse(period,dateStr).getDateRange();vm.startRangeDate=formatDate(dateRange[0]<piwikMinDate?piwikMinDate:dateRange[0]);vm.endRangeDate=formatDate(dateRange[1]>piwikMaxDate?piwikMaxDate:dateRange[1]);}
2174
  function getSelectedComparisonParams(){var previousDate;if(!vm.isComparing){return{};}
2175
- if(vm.comparePeriodType==='custom'){return{comparePeriods:['range'],compareDates:[vm.compareStartDate+','+vm.compareEndDate],};}else if(vm.comparePeriodType==='previousPeriod'){previousDate=getPreviousPeriodDateToSelectedPeriod();return{comparePeriods:[vm.selectedPeriod],compareDates:[previousDate],};}else if(vm.comparePeriodType==='previousYear'){var dateStr=vm.selectedPeriod==='range'?(vm.startRangeDate+','+vm.endRangeDate):vm.dateValue;var currentDateRange=piwikPeriods.parse(vm.selectedPeriod,dateStr).getDateRange();currentDateRange[0].setFullYear(currentDateRange[0].getFullYear()-1);currentDateRange[1].setFullYear(currentDateRange[1].getFullYear()-1);return{comparePeriods:['range'],compareDates:[piwikPeriods.format(currentDateRange[0])+','+piwikPeriods.format(currentDateRange[1])],};}else{console.warn("Unknown compare period type: "+vm.comparePeriodType);return{};}}
 
 
 
 
2176
  function getPreviousPeriodDateToSelectedPeriod(){if(vm.selectedPeriod==='range'){var currentStartRange=piwikPeriods.parseDate(vm.startRangeDate);var currentEndRange=piwikPeriods.parseDate(vm.endRangeDate);var newEndDate=piwikPeriods.RangePeriod.getLastNRange('day',2,currentStartRange).startDate;var rangeSize=Math.floor((currentEndRange-currentStartRange)/ 86400000);var newRange=piwikPeriods.RangePeriod.getLastNRange('day',1+rangeSize,newEndDate);return piwikPeriods.format(newRange.startDate)+','+piwikPeriods.format(newRange.endDate);}
2177
  var newStartDate=piwikPeriods.RangePeriod.getLastNRange(vm.selectedPeriod,2,vm.dateValue).startDate;return piwikPeriods.format(newStartDate);}
2178
  function propagateNewUrlParams(date,period){var compareParams=getSelectedComparisonParams();if(piwik.helper.isAngularRenderingThePage()){vm.closePeriodSelector();var $search=$location.search();var isCurrentlyComparing=piwikUrl.getSearchParam('compareSegments')||piwikUrl.getSearchParam('comparePeriods');if(date!==$search.date||period!==$search.period||vm.isComparing||isCurrentlyComparing){$search.date=date;$search.period=period;$search.compareSegments=piwikUrl.getSearchParam('compareSegments')||[];$.extend($search,compareParams);delete $search['compareSegments[]'];delete $search['comparePeriods[]'];delete $search['compareDates[]'];$location.search($.param($search));}
@@ -3589,14 +3593,14 @@ $(window).off('resize.'+this.uniqueId);this.map.clear();$(this.map.container).ht
3589
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
3590
  */
3591
  (function($){$.widget('piwik.liveWidget',{options:{maxRows:10,interval:3000,maxInterval:300000,dataUrlParams:null,onUpdate:null,fadeInSpeed:'slow'},currentInterval:null,updated:false,updateInterval:null,isStarted:true,_update:function(){this.updated=false;var that=this;var ajaxRequest=new ajaxHelper();ajaxRequest.addParams(this.options.dataUrlParams,'GET');ajaxRequest.setFormat('html');ajaxRequest.setCallback(function(r){if(that.options.replaceContent){$(that.element).html(r);if(that.options.fadeInSpeed){$(that.element).effect("highlight",{},that.options.fadeInSpeed);}}else{that._parseResponse(r);}
3592
- if(!that.updated){that.currentInterval+=that.options.interval;}else{that.currentInterval=that.options.interval;if(that.options.onUpdate)that.options.onUpdate();}
3593
  if(that.options.maxInterval<that.currentInterval){that.currentInterval=that.options.maxInterval;}
3594
  if(that.isStarted){window.clearTimeout(that.updateInterval);if(that.element.length&&$.contains(document,that.element[0])){that.updateInterval=window.setTimeout(function(){that._update()},that.currentInterval);}}});ajaxRequest.send();},_parseResponse:function(data){if(!data||!data.length){this.updated=false;return;}
3595
  var items=$('li.visit',$(data));for(var i=items.length;i--;){this._parseItem(items[i]);}
3596
  this._initTooltips();},_initTooltips:function(){$('li.visit').tooltip({items:'.visitorLogIconWithDetails',track:true,show:false,hide:false,content:function(){return $('<ul>').html($('ul',$(this)).html());},tooltipClass:'small'});},_parseItem:function(item){var visitId=$(item).attr('id');if($('#'+visitId,this.element).length){if($('#'+visitId,this.element).html()!=$(item).html()){this.updated=true;}
3597
  $('#'+visitId,this.element).remove();$(this.element).prepend(item);}else{this.updated=true;$(item).hide();$(this.element).prepend(item);$(item).fadeIn(this.options.fadeInSpeed);}
3598
  $('li.visit:gt('+(this.options.maxRows-1)+')',this.element).remove();},_create:function(){if(!this.options.dataUrlParams){console&&console.error('liveWidget error: dataUrlParams needs to be defined in settings.');return;}
3599
- this.currentInterval=this.options.interval;if(0===$(this.element).parents('.widget').length){var $rootScope=piwikHelper.getAngularDependency('$rootScope');$rootScope.$emit('hidePeriodSelector');}
3600
  var self=this;window.setTimeout(function(){self._initTooltips();},250);this.updateInterval=window.setTimeout(function(){self._update();},this.currentInterval);},_destroy:function(){this.stop();},update:function(){this._update();},start:function(){this.isStarted=true;this.currentInterval=0;this._update();},stop:function(){this.isStarted=false;window.clearTimeout(this.updateInterval);},started:function(){return this.isStarted;},setInterval:function(interval){this.currentInterval=interval;}});})(jQuery);$(function(){var refreshWidget=function(element,refreshAfterXSecs){if(!element.length||!$.contains(document,element[0])){return;}
3601
  function scheduleAnotherRequest(){setTimeout(function(){refreshWidget(element,refreshAfterXSecs);},refreshAfterXSecs*1000);}
3602
  if(Visibility.hidden()){scheduleAnotherRequest();return;}
1343
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
1344
  */
1345
  (function(){angular.module('piwikApp.service').factory('piwikPeriods',piwikPeriods);var periods={},periodOrder=[];piwik.addCustomPeriod=addCustomPeriod;function DayPeriod(date){this.dateInPeriod=date;}
1346
+ DayPeriod.parse=singleDatePeriodFactory(DayPeriod);DayPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodDay');};DayPeriod.prototype={getPrettyString:function(){return format(this.dateInPeriod);},getDateRange:function(){return[new Date(this.dateInPeriod.getTime()), new Date(this.dateInPeriod.getTime())];}};addCustomPeriod('day',DayPeriod);function WeekPeriod(date){this.dateInPeriod=date;}
1347
  WeekPeriod.parse=singleDatePeriodFactory(WeekPeriod);WeekPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodWeek');};WeekPeriod.prototype={getPrettyString:function(){var weekDates=this.getDateRange(this.dateInPeriod);var startWeek=format(weekDates[0]);var endWeek=format(weekDates[1]);return _pk_translate('General_DateRangeFromTo',[startWeek,endWeek]);},getDateRange:function(){var daysToMonday=(this.dateInPeriod.getDay()+6)%7;var startWeek=new Date(this.dateInPeriod.getTime());startWeek.setDate(this.dateInPeriod.getDate()-daysToMonday);var endWeek=new Date(startWeek.getTime());endWeek.setDate(startWeek.getDate()+6);return[startWeek,endWeek];}};addCustomPeriod('week',WeekPeriod);function MonthPeriod(date){this.dateInPeriod=date;}
1348
  MonthPeriod.parse=singleDatePeriodFactory(MonthPeriod);MonthPeriod.getDisplayText=function(){return _pk_translate('Intl_PeriodMonth');};MonthPeriod.prototype={getPrettyString:function(){return _pk_translate('Intl_Month_Long_StandAlone_'+(this.dateInPeriod.getMonth()+1))+' '+
1349
  this.dateInPeriod.getFullYear();},getDateRange:function(){var startMonth=new Date(this.dateInPeriod.getTime());startMonth.setDate(1);var endMonth=new Date(this.dateInPeriod.getTime());endMonth.setMonth(endMonth.getMonth()+1);endMonth.setDate(0);return[startMonth,endMonth];}};addCustomPeriod('month',MonthPeriod);function YearPeriod(date){this.dateInPeriod=date;}
2172
  function setPiwikPeriodAndDate(period,date){vm.periodValue=period;vm.selectedPeriod=period;vm.dateValue=date;var currentDateString=formatDate(date);setRangeStartEndFromPeriod(period,currentDateString);propagateNewUrlParams(currentDateString,vm.selectedPeriod);initTopControls();}
2173
  function setRangeStartEndFromPeriod(period,dateStr){var dateRange=piwikPeriods.parse(period,dateStr).getDateRange();vm.startRangeDate=formatDate(dateRange[0]<piwikMinDate?piwikMinDate:dateRange[0]);vm.endRangeDate=formatDate(dateRange[1]>piwikMaxDate?piwikMaxDate:dateRange[1]);}
2174
  function getSelectedComparisonParams(){var previousDate;if(!vm.isComparing){return{};}
2175
+ if(vm.comparePeriodType==='custom'){return{comparePeriods:['range'],compareDates:[vm.compareStartDate+','+vm.compareEndDate],};}else if(vm.comparePeriodType==='previousPeriod'){previousDate=getPreviousPeriodDateToSelectedPeriod();return{comparePeriods:[vm.selectedPeriod],compareDates:[previousDate],};}else if(vm.comparePeriodType==='previousYear'){var dateStr=vm.selectedPeriod==='range'?(vm.startRangeDate+','+vm.endRangeDate):vm.dateValue;var currentDateRange=piwikPeriods.parse(vm.selectedPeriod,dateStr).getDateRange();currentDateRange[0].setFullYear(currentDateRange[0].getFullYear()-1);currentDateRange[1].setFullYear(currentDateRange[1].getFullYear()-1);
2176
+ if (vm.selectedPeriod === 'range') {
2177
+ return { comparePeriods: ['range'], compareDates: [piwikPeriods.format(currentDateRange[0]) + ',' + piwikPeriods.format(currentDateRange[1])]};
2178
+ } return { comparePeriods: [vm.selectedPeriod],compareDates: [piwikPeriods.format(currentDateRange[0])] };
2179
+ }else{console.warn("Unknown compare period type: "+vm.comparePeriodType);return{};}}
2180
  function getPreviousPeriodDateToSelectedPeriod(){if(vm.selectedPeriod==='range'){var currentStartRange=piwikPeriods.parseDate(vm.startRangeDate);var currentEndRange=piwikPeriods.parseDate(vm.endRangeDate);var newEndDate=piwikPeriods.RangePeriod.getLastNRange('day',2,currentStartRange).startDate;var rangeSize=Math.floor((currentEndRange-currentStartRange)/ 86400000);var newRange=piwikPeriods.RangePeriod.getLastNRange('day',1+rangeSize,newEndDate);return piwikPeriods.format(newRange.startDate)+','+piwikPeriods.format(newRange.endDate);}
2181
  var newStartDate=piwikPeriods.RangePeriod.getLastNRange(vm.selectedPeriod,2,vm.dateValue).startDate;return piwikPeriods.format(newStartDate);}
2182
  function propagateNewUrlParams(date,period){var compareParams=getSelectedComparisonParams();if(piwik.helper.isAngularRenderingThePage()){vm.closePeriodSelector();var $search=$location.search();var isCurrentlyComparing=piwikUrl.getSearchParam('compareSegments')||piwikUrl.getSearchParam('comparePeriods');if(date!==$search.date||period!==$search.period||vm.isComparing||isCurrentlyComparing){$search.date=date;$search.period=period;$search.compareSegments=piwikUrl.getSearchParam('compareSegments')||[];$.extend($search,compareParams);delete $search['compareSegments[]'];delete $search['comparePeriods[]'];delete $search['compareDates[]'];$location.search($.param($search));}
3593
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
3594
  */
3595
  (function($){$.widget('piwik.liveWidget',{options:{maxRows:10,interval:3000,maxInterval:300000,dataUrlParams:null,onUpdate:null,fadeInSpeed:'slow'},currentInterval:null,updated:false,updateInterval:null,isStarted:true,_update:function(){this.updated=false;var that=this;var ajaxRequest=new ajaxHelper();ajaxRequest.addParams(this.options.dataUrlParams,'GET');ajaxRequest.setFormat('html');ajaxRequest.setCallback(function(r){if(that.options.replaceContent){$(that.element).html(r);if(that.options.fadeInSpeed){$(that.element).effect("highlight",{},that.options.fadeInSpeed);}}else{that._parseResponse(r);}
3596
+ that.options.interval = parseInt(that.options.interval, 10);if(!that.updated){that.currentInterval+=that.options.interval;}else{that.currentInterval=that.options.interval;if(that.options.onUpdate)that.options.onUpdate();}
3597
  if(that.options.maxInterval<that.currentInterval){that.currentInterval=that.options.maxInterval;}
3598
  if(that.isStarted){window.clearTimeout(that.updateInterval);if(that.element.length&&$.contains(document,that.element[0])){that.updateInterval=window.setTimeout(function(){that._update()},that.currentInterval);}}});ajaxRequest.send();},_parseResponse:function(data){if(!data||!data.length){this.updated=false;return;}
3599
  var items=$('li.visit',$(data));for(var i=items.length;i--;){this._parseItem(items[i]);}
3600
  this._initTooltips();},_initTooltips:function(){$('li.visit').tooltip({items:'.visitorLogIconWithDetails',track:true,show:false,hide:false,content:function(){return $('<ul>').html($('ul',$(this)).html());},tooltipClass:'small'});},_parseItem:function(item){var visitId=$(item).attr('id');if($('#'+visitId,this.element).length){if($('#'+visitId,this.element).html()!=$(item).html()){this.updated=true;}
3601
  $('#'+visitId,this.element).remove();$(this.element).prepend(item);}else{this.updated=true;$(item).hide();$(this.element).prepend(item);$(item).fadeIn(this.options.fadeInSpeed);}
3602
  $('li.visit:gt('+(this.options.maxRows-1)+')',this.element).remove();},_create:function(){if(!this.options.dataUrlParams){console&&console.error('liveWidget error: dataUrlParams needs to be defined in settings.');return;}
3603
+ this.currentInterval = parseInt(this.options.interval, 10);if(0===$(this.element).parents('.widget').length){var $rootScope=piwikHelper.getAngularDependency('$rootScope');$rootScope.$emit('hidePeriodSelector');}
3604
  var self=this;window.setTimeout(function(){self._initTooltips();},250);this.updateInterval=window.setTimeout(function(){self._update();},this.currentInterval);},_destroy:function(){this.stop();},update:function(){this._update();},start:function(){this.isStarted=true;this.currentInterval=0;this._update();},stop:function(){this.isStarted=false;window.clearTimeout(this.updateInterval);},started:function(){return this.isStarted;},setInterval:function(interval){this.currentInterval=interval;}});})(jQuery);$(function(){var refreshWidget=function(element,refreshAfterXSecs){if(!element.length||!$.contains(document,element[0])){return;}
3605
  function scheduleAnotherRequest(){setTimeout(function(){refreshWidget(element,refreshAfterXSecs);},refreshAfterXSecs*1000);}
3606
  if(Visibility.hidden()){scheduleAnotherRequest();return;}
classes/WpMatomo/Admin/SystemReport.php CHANGED
@@ -705,7 +705,7 @@ class SystemReport {
705
  );
706
  $consts = array('WP_DEBUG', 'WP_DEBUG_DISPLAY', 'WP_DEBUG_LOG', 'DISABLE_WP_CRON', 'FORCE_SSL_ADMIN', 'WP_CACHE',
707
  'CONCATENATE_SCRIPTS', 'COMPRESS_SCRIPTS', 'COMPRESS_CSS', 'ENFORCE_GZIP', 'WP_LOCAL_DEV',
708
- 'DIEONDBERROR', 'WPLANG');
709
  foreach ($consts as $const) {
710
  $rows[] = array(
711
  'name' => $const,
@@ -832,7 +832,13 @@ class SystemReport {
832
  );
833
 
834
  $rows[] = array(
835
- 'name' => 'Max Memory Limit',
 
 
 
 
 
 
836
  'value' => defined( 'WP_MAX_MEMORY_LIMIT' ) ? WP_MAX_MEMORY_LIMIT : '',
837
  'comment' => '',
838
  );
@@ -954,6 +960,16 @@ class SystemReport {
954
  'value' => defined('DB_COLLATE') ? DB_COLLATE : '',
955
  );
956
 
 
 
 
 
 
 
 
 
 
 
957
  if ( method_exists( $wpdb, 'parse_db_host' ) ) {
958
  $host_data = $wpdb->parse_db_host( DB_HOST );
959
  if ( $host_data ) {
705
  );
706
  $consts = array('WP_DEBUG', 'WP_DEBUG_DISPLAY', 'WP_DEBUG_LOG', 'DISABLE_WP_CRON', 'FORCE_SSL_ADMIN', 'WP_CACHE',
707
  'CONCATENATE_SCRIPTS', 'COMPRESS_SCRIPTS', 'COMPRESS_CSS', 'ENFORCE_GZIP', 'WP_LOCAL_DEV',
708
+ 'DIEONDBERROR', 'WPLANG', 'ALTERNATE_WP_CRON', 'WP_CRON_LOCK_TIMEOUT', 'WP_DISABLE_FATAL_ERROR_HANDLER');
709
  foreach ($consts as $const) {
710
  $rows[] = array(
711
  'name' => $const,
832
  );
833
 
834
  $rows[] = array(
835
+ 'name' => 'WP Memory Limit',
836
+ 'value' => defined( 'WP_MEMORY_LIMIT' ) ? WP_MEMORY_LIMIT : '',
837
+ 'comment' => '',
838
+ );
839
+
840
+ $rows[] = array(
841
+ 'name' => 'WP Max Memory Limit',
842
  'value' => defined( 'WP_MAX_MEMORY_LIMIT' ) ? WP_MAX_MEMORY_LIMIT : '',
843
  'comment' => '',
844
  );
960
  'value' => defined('DB_COLLATE') ? DB_COLLATE : '',
961
  );
962
 
963
+ $rows[] = array(
964
+ 'name' => 'SHOW ERRORS',
965
+ 'value' => !empty($wpdb->show_errors),
966
+ );
967
+
968
+ $rows[] = array(
969
+ 'name' => 'SUPPRESS ERRORS',
970
+ 'value' => !empty($wpdb->suppress_errors),
971
+ );
972
+
973
  if ( method_exists( $wpdb, 'parse_db_host' ) ) {
974
  $host_data = $wpdb->parse_db_host( DB_HOST );
975
  if ( $host_data ) {
classes/WpMatomo/Admin/views/marketplace.php CHANGED
@@ -279,12 +279,13 @@ $matomo_extra_url_params = '&' . http_build_query(
279
  ?>
280
 
281
  <div style="border: 1px solid #ddd;padding: 20px;margin-top: 30px;background: white;text-align: center;">
282
- <h1 style="color: red">Limited time offer! Matomo Premium Bundle only 199€/year (300€ off)</h1>
283
- <h3>Your marketing efforts are too valuable to focus on the wrong things.<br> Take your Matomo for WordPress to the next level to push out content and changes to your website that make you consistently more successful for less than 17€/month. 🚀</h3>
284
- <a href="https://matomo.org/wp-premium-bundle/" class="button button-primary"
285
  style="background: limegreen;border-color: limegreen;font-size: 18px;"
286
  target="_blank" rel="noreferrer noopener" role="button">Learn more</a>
287
-
 
288
  <h2>What's included in this bundle?</h2>
289
  <?php
290
 
279
  ?>
280
 
281
  <div style="border: 1px solid #ddd;padding: 20px;margin-top: 30px;background: white;text-align: center;">
282
+ <h3 style="color: red">Limited time offer! Matomo Premium Bundle only 199€/year (300€ off)</h3>
283
+ <h1>Your marketing efforts are too valuable to focus on the wrong things.<br> Take your Matomo for WordPress to the next level to push out content and changes to your website that make you consistently more successful. 🚀</h1>
284
+ <p><a href="https://matomo.org/wp-premium-bundle/" class="button button-primary"
285
  style="background: limegreen;border-color: limegreen;font-size: 18px;"
286
  target="_blank" rel="noreferrer noopener" role="button">Learn more</a>
287
+ </p>
288
+ <p><br></p>
289
  <h2>What's included in this bundle?</h2>
290
  <?php
291
 
classes/WpMatomo/Db/Settings.php CHANGED
@@ -42,8 +42,6 @@ class Settings {
42
  // list of existing temp tables
43
  $table_names_to_look_for = array(
44
  'access',
45
- 'archive_blob_2010_01',
46
- 'archive_numeric_2010_01',
47
  'brute_force_log',
48
  'goal',
49
  'locks',
42
  // list of existing temp tables
43
  $table_names_to_look_for = array(
44
  'access',
 
 
45
  'brute_force_log',
46
  'goal',
47
  'locks',
classes/WpMatomo/Db/WordPress.php CHANGED
@@ -18,6 +18,10 @@ require_once 'WordPressTracker.php';
18
 
19
  class WordPress extends Mysqli {
20
 
 
 
 
 
21
  private $old_suppress_errors_value = null;
22
 
23
  /**
@@ -52,6 +56,10 @@ class WordPress extends Mysqli {
52
  return true;
53
  }
54
 
 
 
 
 
55
  /**
56
  * Is the connection character set equal to utf8?
57
  *
@@ -94,9 +102,94 @@ class WordPress extends Mysqli {
94
  }
95
 
96
  public function listTables() {
 
97
  $sql = 'SHOW TABLES';
98
 
99
- return $this->fetchAll( $sql );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
 
102
  public function getServerVersion() {
18
 
19
  class WordPress extends Mysqli {
20
 
21
+ // needed to be compatbile with mysqli class when `getConnection()` is called and we cannot return the
22
+ // actual connection but return an instance of this.
23
+ public $error = '';
24
+
25
  private $old_suppress_errors_value = null;
26
 
27
  /**
56
  return true;
57
  }
58
 
59
+ public function getConnection() {
60
+ return $this;
61
+ }
62
+
63
  /**
64
  * Is the connection character set equal to utf8?
65
  *
102
  }
103
 
104
  public function listTables() {
105
+ global $wpdb;
106
  $sql = 'SHOW TABLES';
107
 
108
+ $tables = $wpdb->get_results( $sql, ARRAY_N );
109
+ $result = [];
110
+ foreach ($tables as $table) {
111
+ $result[] = $table[0];
112
+ }
113
+ return $result;
114
+ }
115
+
116
+ public function describeTable($tableName, $schemaName = null)
117
+ {
118
+ global $wpdb;
119
+
120
+ if ($schemaName) {
121
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true);
122
+ } else {
123
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true);
124
+ }
125
+
126
+ $result = $wpdb->get_results( $sql, ARRAY_A );
127
+
128
+ $desc = array();
129
+
130
+ $row_defaults = array(
131
+ 'Length' => null,
132
+ 'Scale' => null,
133
+ 'Precision' => null,
134
+ 'Unsigned' => null,
135
+ 'Primary' => false,
136
+ 'PrimaryPosition' => null,
137
+ 'Identity' => false
138
+ );
139
+ $i = 1;
140
+ $p = 1;
141
+ foreach ($result as $key => $row) {
142
+ $row = array_merge($row_defaults, $row);
143
+ if (preg_match('/unsigned/', $row['Type'])) {
144
+ $row['Unsigned'] = true;
145
+ }
146
+ if (preg_match('/^((?:var)?char)\((\d+)\)/', $row['Type'], $matches)) {
147
+ $row['Type'] = $matches[1];
148
+ $row['Length'] = $matches[2];
149
+ } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $row['Type'], $matches)) {
150
+ $row['Type'] = 'decimal';
151
+ $row['Precision'] = $matches[1];
152
+ $row['Scale'] = $matches[2];
153
+ } else if (preg_match('/^float\((\d+),(\d+)\)/', $row['Type'], $matches)) {
154
+ $row['Type'] = 'float';
155
+ $row['Precision'] = $matches[1];
156
+ $row['Scale'] = $matches[2];
157
+ } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row['Type'], $matches)) {
158
+ $row['Type'] = $matches[1];
159
+ /**
160
+ * The optional argument of a MySQL int type is not precision
161
+ * or length; it is only a hint for display width.
162
+ */
163
+ }
164
+ if (strtoupper($row['Key']) == 'PRI') {
165
+ $row['Primary'] = true;
166
+ $row['PrimaryPosition'] = $p;
167
+ if ($row['Extra'] == 'auto_increment') {
168
+ $row['Identity'] = true;
169
+ } else {
170
+ $row['Identity'] = false;
171
+ }
172
+ ++$p;
173
+ }
174
+ $desc[$this->foldCase($row['Field'])] = array(
175
+ 'SCHEMA_NAME' => null, // @todo
176
+ 'TABLE_NAME' => $this->foldCase($tableName),
177
+ 'COLUMN_NAME' => $this->foldCase($row['Field']),
178
+ 'COLUMN_POSITION' => $i,
179
+ 'DATA_TYPE' => $row['Type'],
180
+ 'DEFAULT' => $row['Default'],
181
+ 'NULLABLE' => (bool) ($row['Null'] == 'YES'),
182
+ 'LENGTH' => $row['Length'],
183
+ 'SCALE' => $row['Scale'],
184
+ 'PRECISION' => $row['Precision'],
185
+ 'UNSIGNED' => $row['Unsigned'],
186
+ 'PRIMARY' => $row['Primary'],
187
+ 'PRIMARY_POSITION' => $row['PrimaryPosition'],
188
+ 'IDENTITY' => $row['Identity']
189
+ );
190
+ ++$i;
191
+ }
192
+ return $desc;
193
  }
194
 
195
  public function getServerVersion() {
matomo.php CHANGED
@@ -4,7 +4,7 @@
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.1.1
8
  * Domain Path: /languages
9
  * WC requires at least: 2.4.0
10
  * WC tested up to: 4.0.0
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.1.2
8
  * Domain Path: /languages
9
  * WC requires at least: 2.4.0
10
  * WC tested up to: 4.0.0
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.1.1
8
  Requires PHP: 7.2
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
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.1.2
8
  Requires PHP: 7.2
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html