add icons for Character groups
This commit is contained in:
commit
2d9a41a5fe
3461 changed files with 594457 additions and 0 deletions
108
www/analytics/plugins/PrivacyManager/Config.php
Normal file
108
www/analytics/plugins/PrivacyManager/Config.php
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\Option;
|
||||
use Piwik\Tracker\Cache;
|
||||
|
||||
/**
|
||||
* @property bool $doNotTrackEnabled Enable / Disable Do Not Track {@see DoNotTrackHeaderChecker}
|
||||
* @property bool $ipAnonymizerEnabled Enable / Disable IP Anonymizer {@see IPAnonymizer}
|
||||
* @property bool $useAnonymizedIpForVisitEnrichment Set this setting to 0 to let plugins use the full
|
||||
* non-anonymized IP address when enriching visitor information.
|
||||
* When set to 1, by default, Geo Location via geoip and Provider reverse name lookups
|
||||
* will use the anonymized IP address when anonymization is enabled.
|
||||
* @property int $ipAddressMaskLength Anonymize a visitor's IP address after testing for "Ip exclude"
|
||||
* This value is the level of anonymization Piwik will use; if the IP
|
||||
* anonymization is deactivated, this value is ignored. For IPv4/IPv6 addresses,
|
||||
* valid values are the number of octets in IP address to mask (from 0 to 4).
|
||||
* For IPv6 addresses 0..4 means that 0, 64, 80, 104 or all bits are masked.
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
private $properties = array(
|
||||
'useAnonymizedIpForVisitEnrichment' => array('type' => 'boolean', 'default' => true),
|
||||
'ipAddressMaskLength' => array('type' => 'integer', 'default' => 1),
|
||||
'doNotTrackEnabled' => array('type' => 'boolean', 'default' => true),
|
||||
'ipAnonymizerEnabled' => array('type' => 'boolean', 'default' => false),
|
||||
);
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if (!array_key_exists($name, $this->properties)) {
|
||||
throw new \Exception(sprintf('Property %s does not exist', $name));
|
||||
}
|
||||
|
||||
$this->set($name, $value, $this->properties[$name]);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->properties)) {
|
||||
throw new \Exception(sprintf('Property %s does not exist', $name));
|
||||
}
|
||||
|
||||
return $this->getFromTrackerCache($name, $this->properties[$name]);
|
||||
}
|
||||
|
||||
private function prefix($optionName)
|
||||
{
|
||||
return 'PrivacyManager.' . $optionName;
|
||||
}
|
||||
|
||||
private function getFromTrackerCache($name, $config)
|
||||
{
|
||||
$name = $this->prefix($name);
|
||||
$cache = Cache::getCacheGeneral();
|
||||
|
||||
if (array_key_exists($name, $cache)) {
|
||||
$value = $cache[$name];
|
||||
settype($value, $config['type']);
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $config['default'];
|
||||
}
|
||||
|
||||
private function getFromOption($name, $config)
|
||||
{
|
||||
$name = $this->prefix($name);
|
||||
$value = Option::get($name);
|
||||
|
||||
if (false !== $value) {
|
||||
settype($value, $config['type']);
|
||||
} else {
|
||||
$value = $config['default'];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function set($name, $value, $config)
|
||||
{
|
||||
if ('boolean' == $config['type']) {
|
||||
$value = $value ? '1' : '0';
|
||||
} else {
|
||||
settype($value, $config['type']);
|
||||
}
|
||||
|
||||
Option::set($this->prefix($name), $value);
|
||||
Cache::clearCacheGeneral();
|
||||
}
|
||||
|
||||
public function setTrackerCacheGeneral($cacheContent)
|
||||
{
|
||||
foreach ($this->properties as $name => $config) {
|
||||
$cacheContent[$this->prefix($name)] = $this->getFromOption($name, $config);
|
||||
}
|
||||
|
||||
return $cacheContent;
|
||||
}
|
||||
|
||||
}
|
||||
312
www/analytics/plugins/PrivacyManager/Controller.php
Normal file
312
www/analytics/plugins/PrivacyManager/Controller.php
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Config as PiwikConfig;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\MetricsFormatter;
|
||||
use Piwik\Nonce;
|
||||
use Piwik\Notification;
|
||||
use Piwik\Option;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\DBStats\MySQLMetadataProvider;
|
||||
use Piwik\Plugins\LanguagesManager\LanguagesManager;
|
||||
use Piwik\TaskScheduler;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Controller extends \Piwik\Plugin\ControllerAdmin
|
||||
{
|
||||
const OPTION_LAST_DELETE_PIWIK_LOGS = "lastDelete_piwik_logs";
|
||||
const ACTIVATE_DNT_NONCE = 'PrivacyManager.activateDnt';
|
||||
const DEACTIVATE_DNT_NONCE = 'PrivacyManager.deactivateDnt';
|
||||
|
||||
public function saveSettings()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$this->checkTokenInUrl();
|
||||
switch (Common::getRequestVar('form')) {
|
||||
case("formMaskLength"):
|
||||
$enable = Common::getRequestVar("anonymizeIPEnable", 0);
|
||||
if ($enable == 1) {
|
||||
IPAnonymizer::activate();
|
||||
} else if ($enable == 0) {
|
||||
IPAnonymizer::deactivate();
|
||||
} else {
|
||||
// pass
|
||||
}
|
||||
|
||||
$privacyConfig = new Config();
|
||||
$privacyConfig->ipAddressMaskLength = Common::getRequestVar("maskLength", 1);
|
||||
$privacyConfig->useAnonymizedIpForVisitEnrichment = Common::getRequestVar("useAnonymizedIpForVisitEnrichment", 1);
|
||||
break;
|
||||
|
||||
case("formDeleteSettings"):
|
||||
$this->checkDataPurgeAdminSettingsIsEnabled();
|
||||
$settings = $this->getPurgeSettingsFromRequest();
|
||||
PrivacyManager::savePurgeDataSettings($settings);
|
||||
break;
|
||||
|
||||
default: //do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$notification = new Notification(Piwik::translate('General_YourChangesHaveBeenSaved'));
|
||||
$notification->context = Notification::CONTEXT_SUCCESS;
|
||||
Notification\Manager::notify('PrivacyManager_ChangesHaveBeenSaved', $notification);
|
||||
|
||||
$this->redirectToIndex('PrivacyManager', 'privacySettings', null, null, null, array('updated' => 1));
|
||||
}
|
||||
|
||||
private function checkDataPurgeAdminSettingsIsEnabled()
|
||||
{
|
||||
if (!self::isDataPurgeSettingsEnabled()) {
|
||||
throw new \Exception("Configuring deleting log data and report data has been disabled by Piwik admins.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function. Gets the delete logs/reports settings from the request and uses
|
||||
* them to populate config arrays.
|
||||
*
|
||||
* @return array An array containing the data deletion settings.
|
||||
*/
|
||||
private function getPurgeSettingsFromRequest()
|
||||
{
|
||||
$settings = array();
|
||||
|
||||
// delete logs settings
|
||||
$settings['delete_logs_enable'] = Common::getRequestVar("deleteEnable", 0);
|
||||
$settings['delete_logs_schedule_lowest_interval'] = Common::getRequestVar("deleteLowestInterval", 7);
|
||||
$settings['delete_logs_older_than'] = ((int)Common::getRequestVar("deleteOlderThan", 180) < 1) ?
|
||||
1 : Common::getRequestVar("deleteOlderThan", 180);
|
||||
|
||||
// delete reports settings
|
||||
$settings['delete_reports_enable'] = Common::getRequestVar("deleteReportsEnable", 0);
|
||||
$deleteReportsOlderThan = Common::getRequestVar("deleteReportsOlderThan", 3);
|
||||
$settings['delete_reports_older_than'] = $deleteReportsOlderThan < 3 ? 3 : $deleteReportsOlderThan;
|
||||
$settings['delete_reports_keep_basic_metrics'] = Common::getRequestVar("deleteReportsKeepBasic", 0);
|
||||
$settings['delete_reports_keep_day_reports'] = Common::getRequestVar("deleteReportsKeepDay", 0);
|
||||
$settings['delete_reports_keep_week_reports'] = Common::getRequestVar("deleteReportsKeepWeek", 0);
|
||||
$settings['delete_reports_keep_month_reports'] = Common::getRequestVar("deleteReportsKeepMonth", 0);
|
||||
$settings['delete_reports_keep_year_reports'] = Common::getRequestVar("deleteReportsKeepYear", 0);
|
||||
$settings['delete_reports_keep_range_reports'] = Common::getRequestVar("deleteReportsKeepRange", 0);
|
||||
$settings['delete_reports_keep_segment_reports'] = Common::getRequestVar("deleteReportsKeepSegments", 0);
|
||||
$settings['delete_logs_max_rows_per_query'] = PiwikConfig::getInstance()->Deletelogs['delete_logs_max_rows_per_query'];
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo's an HTML chunk describing the current database size, and the estimated space
|
||||
* savings after the scheduled data purge is run.
|
||||
*/
|
||||
public function getDatabaseSize()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
$view = new View('@PrivacyManager/getDatabaseSize');
|
||||
|
||||
$forceEstimate = Common::getRequestVar('forceEstimate', 0);
|
||||
$view->dbStats = $this->getDeleteDBSizeEstimate($getSettingsFromQuery = true, $forceEstimate);
|
||||
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function privacySettings()
|
||||
{
|
||||
Piwik::checkUserHasSomeAdminAccess();
|
||||
$view = new View('@PrivacyManager/privacySettings');
|
||||
|
||||
if (Piwik::hasUserSuperUserAccess()) {
|
||||
$view->deleteData = $this->getDeleteDataInfo();
|
||||
$view->anonymizeIP = $this->getAnonymizeIPInfo();
|
||||
$view->dntSupport = DoNotTrackHeaderChecker::isActive();
|
||||
$view->canDeleteLogActions = Db::isLockPrivilegeGranted();
|
||||
$view->dbUser = PiwikConfig::getInstance()->database['username'];
|
||||
$view->deactivateNonce = Nonce::getNonce(self::DEACTIVATE_DNT_NONCE);
|
||||
$view->activateNonce = Nonce::getNonce(self::ACTIVATE_DNT_NONCE);
|
||||
}
|
||||
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
|
||||
$this->setBasicVariablesView($view);
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a data purge, deleting log data and report data using the current config
|
||||
* options. Echo's the result of getDatabaseSize after purging.
|
||||
*/
|
||||
public function executeDataPurge()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
$this->checkTokenInUrl();
|
||||
|
||||
// if the request isn't a POST, redirect to index
|
||||
if ($_SERVER["REQUEST_METHOD"] != "POST"
|
||||
&& !Common::isPhpCliMode()
|
||||
) {
|
||||
$this->redirectToIndex('PrivacyManager', 'privacySettings');
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = PrivacyManager::getPurgeDataSettings();
|
||||
if ($settings['delete_logs_enable']) {
|
||||
$logDataPurger = LogDataPurger::make($settings);
|
||||
$logDataPurger->purgeData();
|
||||
}
|
||||
if ($settings['delete_reports_enable']) {
|
||||
$reportsPurger = ReportsPurger::make($settings, PrivacyManager::getAllMetricsToKeep());
|
||||
$reportsPurger->purgeData(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDeleteDBSizeEstimate($getSettingsFromQuery = false, $forceEstimate = false)
|
||||
{
|
||||
$this->checkDataPurgeAdminSettingsIsEnabled();
|
||||
|
||||
// get the purging settings & create two purger instances
|
||||
if ($getSettingsFromQuery) {
|
||||
$settings = $this->getPurgeSettingsFromRequest();
|
||||
} else {
|
||||
$settings = PrivacyManager::getPurgeDataSettings();
|
||||
}
|
||||
|
||||
$doDatabaseSizeEstimate = PiwikConfig::getInstance()->Deletelogs['enable_auto_database_size_estimate'];
|
||||
|
||||
// determine the DB size & purged DB size
|
||||
$metadataProvider = new MySQLMetadataProvider();
|
||||
$tableStatuses = $metadataProvider->getAllTablesStatus();
|
||||
|
||||
$totalBytes = 0;
|
||||
foreach ($tableStatuses as $status) {
|
||||
$totalBytes += $status['Data_length'] + $status['Index_length'];
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'currentSize' => MetricsFormatter::getPrettySizeFromBytes($totalBytes)
|
||||
);
|
||||
|
||||
// if the db size estimate feature is enabled, get the estimate
|
||||
if ($doDatabaseSizeEstimate || $forceEstimate == 1) {
|
||||
// maps tables whose data will be deleted with number of rows that will be deleted
|
||||
// if a value is -1, it means the table will be dropped.
|
||||
$deletedDataSummary = PrivacyManager::getPurgeEstimate($settings);
|
||||
|
||||
$totalAfterPurge = $totalBytes;
|
||||
foreach ($tableStatuses as $status) {
|
||||
$tableName = $status['Name'];
|
||||
if (isset($deletedDataSummary[$tableName])) {
|
||||
$tableTotalBytes = $status['Data_length'] + $status['Index_length'];
|
||||
|
||||
// if dropping the table
|
||||
if ($deletedDataSummary[$tableName] === ReportsPurger::DROP_TABLE) {
|
||||
$totalAfterPurge -= $tableTotalBytes;
|
||||
} else // if just deleting rows
|
||||
{
|
||||
if ($status['Rows'] > 0) {
|
||||
$totalAfterPurge -= ($tableTotalBytes / $status['Rows']) * $deletedDataSummary[$tableName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['sizeAfterPurge'] = MetricsFormatter::getPrettySizeFromBytes($totalAfterPurge);
|
||||
$result['spaceSaved'] = MetricsFormatter::getPrettySizeFromBytes($totalBytes - $totalAfterPurge);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getAnonymizeIPInfo()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
$anonymizeIP = array();
|
||||
|
||||
$privacyConfig = new Config();
|
||||
$anonymizeIP["enabled"] = IpAnonymizer::isActive();
|
||||
$anonymizeIP["maskLength"] = $privacyConfig->ipAddressMaskLength;
|
||||
$anonymizeIP["useAnonymizedIpForVisitEnrichment"] = $privacyConfig->useAnonymizedIpForVisitEnrichment;
|
||||
|
||||
return $anonymizeIP;
|
||||
}
|
||||
|
||||
protected function getDeleteDataInfo()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
$deleteDataInfos = array();
|
||||
$taskScheduler = new TaskScheduler();
|
||||
$deleteDataInfos["config"] = PrivacyManager::getPurgeDataSettings();
|
||||
$deleteDataInfos["deleteTables"] =
|
||||
"<br/>" . implode(", ", LogDataPurger::getDeleteTableLogTables());
|
||||
|
||||
$scheduleTimetable = $taskScheduler->getScheduledTimeForMethod("PrivacyManager", "deleteLogTables");
|
||||
|
||||
$optionTable = Option::get(self::OPTION_LAST_DELETE_PIWIK_LOGS);
|
||||
|
||||
//If task was already rescheduled, read time from taskTimetable. Else, calculate next possible runtime.
|
||||
if (!empty($scheduleTimetable) && ($scheduleTimetable - time() > 0)) {
|
||||
$nextPossibleSchedule = (int)$scheduleTimetable;
|
||||
} else {
|
||||
$date = Date::factory("today");
|
||||
$nextPossibleSchedule = $date->addDay(1)->getTimestamp();
|
||||
}
|
||||
|
||||
//deletion schedule did not run before
|
||||
if (empty($optionTable)) {
|
||||
$deleteDataInfos["lastRun"] = false;
|
||||
|
||||
//next run ASAP (with next schedule run)
|
||||
$date = Date::factory("today");
|
||||
$deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
|
||||
} else {
|
||||
$deleteDataInfos["lastRun"] = $optionTable;
|
||||
$deleteDataInfos["lastRunPretty"] = Date::factory((int)$optionTable)->getLocalized('%day% %shortMonth% %longYear%');
|
||||
|
||||
//Calculate next run based on last run + interval
|
||||
$nextScheduleRun = (int)($deleteDataInfos["lastRun"] + $deleteDataInfos["config"]["delete_logs_schedule_lowest_interval"] * 24 * 60 * 60);
|
||||
|
||||
//is the calculated next run in the past? (e.g. plugin was disabled in the meantime or something) -> run ASAP
|
||||
if (($nextScheduleRun - time()) <= 0) {
|
||||
$deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
|
||||
} else {
|
||||
$deleteDataInfos["nextScheduleTime"] = $nextScheduleRun;
|
||||
}
|
||||
}
|
||||
|
||||
$deleteDataInfos["nextRunPretty"] = MetricsFormatter::getPrettyTimeFromSeconds($deleteDataInfos["nextScheduleTime"] - time());
|
||||
|
||||
return $deleteDataInfos;
|
||||
}
|
||||
|
||||
public function deactivateDoNotTrack()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
Nonce::checkNonce(self::DEACTIVATE_DNT_NONCE);
|
||||
|
||||
DoNotTrackHeaderChecker::deactivate();
|
||||
|
||||
$this->redirectToIndex('PrivacyManager', 'privacySettings');
|
||||
}
|
||||
|
||||
public function activateDoNotTrack()
|
||||
{
|
||||
Piwik::checkUserHasSuperUserAccess();
|
||||
Nonce::checkNonce(self::ACTIVATE_DNT_NONCE);
|
||||
|
||||
DoNotTrackHeaderChecker::activate();
|
||||
|
||||
$this->redirectToIndex('PrivacyManager', 'privacySettings');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Tracker\IgnoreCookie;
|
||||
use Piwik\Tracker\Request;
|
||||
|
||||
/**
|
||||
* Excludes visits where user agent's request contains either:
|
||||
*
|
||||
* - X-Do-Not-Track header (used by AdBlockPlus and NoScript)
|
||||
* - DNT header (used by Mozilla)
|
||||
*
|
||||
*/
|
||||
class DoNotTrackHeaderChecker
|
||||
{
|
||||
/**
|
||||
* Checks for DoNotTrack headers and if found, sets `$exclude` to `true`.
|
||||
*/
|
||||
public function checkHeaderInTracker(&$exclude)
|
||||
{
|
||||
if (!$this->isActive()
|
||||
|| $exclude
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((isset($_SERVER['HTTP_X_DO_NOT_TRACK']) && $_SERVER['HTTP_X_DO_NOT_TRACK'] === '1')
|
||||
|| (isset($_SERVER['HTTP_DNT']) && substr($_SERVER['HTTP_DNT'], 0, 1) === '1')
|
||||
) {
|
||||
$request = new Request($_REQUEST);
|
||||
$ua = $request->getUserAgent();
|
||||
if (strpos($ua, 'MSIE 10') !== false
|
||||
|| strpos($ua, 'Trident/7') !== false) {
|
||||
Common::printDebug("INTERNET EXPLORER 10 and 11 enable DoNotTrack by default; so Piwik ignores DNT for all IE10 + IE11 browsers...");
|
||||
return;
|
||||
}
|
||||
|
||||
$exclude = true;
|
||||
Common::printDebug("DoNotTrack found.");
|
||||
|
||||
$trackingCookie = IgnoreCookie::getTrackingCookie();
|
||||
$trackingCookie->delete();
|
||||
|
||||
// this is an optional supplement to the site's tracking status resource at:
|
||||
// /.well-known/dnt
|
||||
// per Tracking Preference Expression (draft)
|
||||
header('Tk: 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates DoNotTrack header checking. This function will not be called by the Tracker.
|
||||
*/
|
||||
public static function deactivate()
|
||||
{
|
||||
$config = new Config();
|
||||
$config->doNotTrackEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates DoNotTrack header checking. This function will not be called by the Tracker.
|
||||
*/
|
||||
public static function activate()
|
||||
{
|
||||
$config = new Config();
|
||||
$config->doNotTrackEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if server side DoNotTrack support is enabled, false if otherwise.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isActive()
|
||||
{
|
||||
$config = new Config();
|
||||
return $config->doNotTrackEnabled;
|
||||
}
|
||||
}
|
||||
96
www/analytics/plugins/PrivacyManager/IPAnonymizer.php
Normal file
96
www/analytics/plugins/PrivacyManager/IPAnonymizer.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\IP;
|
||||
|
||||
/**
|
||||
* Anonymize visitor IP addresses to comply with the privacy laws/guidelines in countries, such as Germany.
|
||||
*/
|
||||
class IPAnonymizer
|
||||
{
|
||||
/**
|
||||
* Internal function to mask portions of the visitor IP address
|
||||
*
|
||||
* @param string $ip IP address in network address format
|
||||
* @param int $maskLength Number of octets to reset
|
||||
* @return string
|
||||
*/
|
||||
public static function applyIPMask($ip, $maskLength)
|
||||
{
|
||||
// IPv4 or mapped IPv4 in IPv6
|
||||
if (IP::isIPv4($ip)) {
|
||||
$i = strlen($ip);
|
||||
if ($maskLength > $i) {
|
||||
$maskLength = $i;
|
||||
}
|
||||
|
||||
while ($maskLength-- > 0) {
|
||||
$ip[--$i] = chr(0);
|
||||
}
|
||||
} else {
|
||||
$masks = array(
|
||||
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
|
||||
'ffff:ffff:ffff:ffff::',
|
||||
'ffff:ffff:ffff:0000::',
|
||||
'ffff:ff00:0000:0000::'
|
||||
);
|
||||
$ip = $ip & pack('a16', inet_pton($masks[$maskLength]));
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook on Tracker.Visit.setVisitorIp to anomymize visitor IP addresses
|
||||
*/
|
||||
public function setVisitorIpAddress(&$ip)
|
||||
{
|
||||
if (!$this->isActive()) {
|
||||
Common::printDebug("Visitor IP was _not_ anonymized: ". IP::N2P($ip));
|
||||
return;
|
||||
}
|
||||
|
||||
$originalIp = $ip;
|
||||
|
||||
$privacyConfig = new Config();
|
||||
|
||||
$ip = self::applyIPMask($ip, $privacyConfig->ipAddressMaskLength);
|
||||
Common::printDebug("Visitor IP (was: ". IP::N2P($originalIp) .") has been anonymized: ". IP::N2P($ip));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates IP anonymization. This function will not be called by the Tracker.
|
||||
*/
|
||||
public static function deactivate()
|
||||
{
|
||||
$privacyConfig = new Config();
|
||||
$privacyConfig->ipAnonymizerEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates IP anonymization. This function will not be called by the Tracker.
|
||||
*/
|
||||
public static function activate()
|
||||
{
|
||||
$privacyConfig = new Config();
|
||||
$privacyConfig->ipAnonymizerEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if IP anonymization support is enabled, false if otherwise.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isActive()
|
||||
{
|
||||
$privacyConfig = new Config();
|
||||
return $privacyConfig->ipAnonymizerEnabled;
|
||||
}
|
||||
}
|
||||
334
www/analytics/plugins/PrivacyManager/LogDataPurger.php
Executable file
334
www/analytics/plugins/PrivacyManager/LogDataPurger.php
Executable file
|
|
@ -0,0 +1,334 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\Log;
|
||||
use Piwik\Piwik;
|
||||
|
||||
/**
|
||||
* Purges the log_visit, log_conversion and related tables of old visit data.
|
||||
*/
|
||||
class LogDataPurger
|
||||
{
|
||||
const TEMP_TABLE_NAME = 'tmp_log_actions_to_keep';
|
||||
|
||||
/**
|
||||
* The max set of rows each table scan select should query at one time.
|
||||
*/
|
||||
public static $selectSegmentSize = 100000;
|
||||
|
||||
/**
|
||||
* The number of days after which log entries are considered old.
|
||||
*/
|
||||
private $deleteLogsOlderThan;
|
||||
|
||||
/**
|
||||
* The number of rows to delete per DELETE query.
|
||||
*/
|
||||
private $maxRowsToDeletePerQuery;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $deleteLogsOlderThan The number of days after which log entires are considered old.
|
||||
* Visits and related data whose age is greater than this number
|
||||
* will be purged.
|
||||
* @param int $maxRowsToDeletePerQuery The maximum number of rows to delete in one query. Used to
|
||||
* make sure log tables aren't locked for too long.
|
||||
*/
|
||||
public function __construct($deleteLogsOlderThan, $maxRowsToDeletePerQuery)
|
||||
{
|
||||
$this->deleteLogsOlderThan = $deleteLogsOlderThan;
|
||||
$this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges old data from the following tables:
|
||||
* - log_visit
|
||||
* - log_link_visit_action
|
||||
* - log_conversion
|
||||
* - log_conversion_item
|
||||
* - log_action
|
||||
*/
|
||||
public function purgeData()
|
||||
{
|
||||
$maxIdVisit = $this->getDeleteIdVisitOffset();
|
||||
|
||||
// break if no ID was found (nothing to delete for given period)
|
||||
if (empty($maxIdVisit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$logTables = self::getDeleteTableLogTables();
|
||||
|
||||
// delete data from log tables
|
||||
$where = "WHERE idvisit <= ?";
|
||||
foreach ($logTables as $logTable) {
|
||||
// deleting from log_action must be handled differently, so we do it later
|
||||
if ($logTable != Common::prefixTable('log_action')) {
|
||||
Db::deleteAllRows($logTable, $where, "idvisit ASC", $this->maxRowsToDeletePerQuery, array($maxIdVisit));
|
||||
}
|
||||
}
|
||||
|
||||
// delete unused actions from the log_action table (but only if we can lock tables)
|
||||
if (Db::isLockPrivilegeGranted()) {
|
||||
$this->purgeUnusedLogActions();
|
||||
} else {
|
||||
$logMessage = get_class($this) . ": LOCK TABLES privilege not granted; skipping unused actions purge";
|
||||
Log::warning($logMessage);
|
||||
}
|
||||
|
||||
// optimize table overhead after deletion
|
||||
Db::optimizeTables($logTables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array describing what data would be purged if purging were invoked.
|
||||
*
|
||||
* This function returns an array that maps table names with the number of rows
|
||||
* that will be deleted.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPurgeEstimate()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
// deal w/ log tables that will be purged
|
||||
$maxIdVisit = $this->getDeleteIdVisitOffset();
|
||||
if (!empty($maxIdVisit)) {
|
||||
foreach ($this->getDeleteTableLogTables() as $table) {
|
||||
// getting an estimate for log_action is not supported since it can take too long
|
||||
if ($table != Common::prefixTable('log_action')) {
|
||||
$rowCount = $this->getLogTableDeleteCount($table, $maxIdVisit);
|
||||
if ($rowCount > 0) {
|
||||
$result[$table] = $rowCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely delete all unused log_action rows.
|
||||
*/
|
||||
private function purgeUnusedLogActions()
|
||||
{
|
||||
$this->createTempTable();
|
||||
|
||||
// get current max ID in log tables w/ idaction references.
|
||||
$maxIds = $this->getMaxIdsInLogTables();
|
||||
|
||||
// do large insert (inserting everything before maxIds) w/o locking tables...
|
||||
$this->insertActionsToKeep($maxIds, $deleteOlderThanMax = true);
|
||||
|
||||
// ... then do small insert w/ locked tables to minimize the amount of time tables are locked.
|
||||
$this->lockLogTables();
|
||||
$this->insertActionsToKeep($maxIds, $deleteOlderThanMax = false);
|
||||
|
||||
// delete before unlocking tables so there's no chance a new log row that references an
|
||||
// unused action will be inserted.
|
||||
$this->deleteUnusedActions();
|
||||
Db::unlockAllTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* get highest idVisit to delete rows from
|
||||
* @return string
|
||||
*/
|
||||
private function getDeleteIdVisitOffset()
|
||||
{
|
||||
$logVisit = Common::prefixTable("log_visit");
|
||||
|
||||
// get max idvisit
|
||||
$maxIdVisit = Db::fetchOne("SELECT MAX(idvisit) FROM $logVisit");
|
||||
if (empty($maxIdVisit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// select highest idvisit to delete from
|
||||
$dateStart = Date::factory("today")->subDay($this->deleteLogsOlderThan);
|
||||
$sql = "SELECT idvisit
|
||||
FROM $logVisit
|
||||
WHERE '" . $dateStart->toString('Y-m-d H:i:s') . "' > visit_last_action_time
|
||||
AND idvisit <= ?
|
||||
AND idvisit > ?
|
||||
ORDER BY idvisit DESC
|
||||
LIMIT 1";
|
||||
|
||||
return Db::segmentedFetchFirst($sql, $maxIdVisit, 0, -self::$selectSegmentSize);
|
||||
}
|
||||
|
||||
private function getLogTableDeleteCount($table, $maxIdVisit)
|
||||
{
|
||||
$sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?";
|
||||
return (int)Db::fetchOne($sql, array($maxIdVisit));
|
||||
}
|
||||
|
||||
private function createTempTable()
|
||||
{
|
||||
$sql = "CREATE TEMPORARY TABLE " . Common::prefixTable(self::TEMP_TABLE_NAME) . " (
|
||||
idaction INT(11),
|
||||
PRIMARY KEY (idaction)
|
||||
)";
|
||||
Db::query($sql);
|
||||
}
|
||||
|
||||
private function getMaxIdsInLogTables()
|
||||
{
|
||||
$tables = array('log_conversion', 'log_link_visit_action', 'log_visit', 'log_conversion_item');
|
||||
$idColumns = $this->getTableIdColumns();
|
||||
|
||||
$result = array();
|
||||
foreach ($tables as $table) {
|
||||
$idCol = $idColumns[$table];
|
||||
$result[$table] = Db::fetchOne("SELECT MAX($idCol) FROM " . Common::prefixTable($table));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function insertActionsToKeep($maxIds, $olderThan = true)
|
||||
{
|
||||
$tempTableName = Common::prefixTable(self::TEMP_TABLE_NAME);
|
||||
|
||||
$idColumns = $this->getTableIdColumns();
|
||||
foreach ($this->getIdActionColumns() as $table => $columns) {
|
||||
$idCol = $idColumns[$table];
|
||||
|
||||
foreach ($columns as $col) {
|
||||
$select = "SELECT $col FROM " . Common::prefixTable($table) . " WHERE $idCol >= ? AND $idCol < ?";
|
||||
$sql = "INSERT IGNORE INTO $tempTableName $select";
|
||||
|
||||
if ($olderThan) {
|
||||
$start = 0;
|
||||
$finish = $maxIds[$table];
|
||||
} else {
|
||||
$start = $maxIds[$table];
|
||||
$finish = Db::fetchOne("SELECT MAX($idCol) FROM " . Common::prefixTable($table));
|
||||
}
|
||||
|
||||
Db::segmentedQuery($sql, $start, $finish, self::$selectSegmentSize);
|
||||
}
|
||||
}
|
||||
|
||||
// allow code to be executed after data is inserted. for concurrency testing purposes.
|
||||
if ($olderThan) {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
Piwik::postEvent("LogDataPurger.ActionsToKeepInserted.olderThan");
|
||||
} else {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
Piwik::postEvent("LogDataPurger.ActionsToKeepInserted.newerThan");
|
||||
}
|
||||
}
|
||||
|
||||
private function lockLogTables()
|
||||
{
|
||||
Db::lockTables(
|
||||
$readLocks = Common::prefixTables('log_conversion',
|
||||
'log_link_visit_action',
|
||||
'log_visit',
|
||||
'log_conversion_item'),
|
||||
$writeLocks = Common::prefixTables('log_action')
|
||||
);
|
||||
}
|
||||
|
||||
private function deleteUnusedActions()
|
||||
{
|
||||
list($logActionTable, $tempTableName) = Common::prefixTables("log_action", self::TEMP_TABLE_NAME);
|
||||
|
||||
$deleteSql = "DELETE LOW_PRIORITY QUICK IGNORE $logActionTable
|
||||
FROM $logActionTable
|
||||
LEFT JOIN $tempTableName tmp ON tmp.idaction = $logActionTable.idaction
|
||||
WHERE tmp.idaction IS NULL";
|
||||
|
||||
Db::query($deleteSql);
|
||||
}
|
||||
|
||||
private function getIdActionColumns()
|
||||
{
|
||||
return array(
|
||||
'log_link_visit_action' => array('idaction_url',
|
||||
'idaction_url_ref',
|
||||
'idaction_name',
|
||||
'idaction_name_ref',
|
||||
'idaction_event_category',
|
||||
'idaction_event_action'
|
||||
),
|
||||
|
||||
'log_conversion' => array('idaction_url'),
|
||||
|
||||
'log_visit' => array('visit_exit_idaction_url',
|
||||
'visit_exit_idaction_name',
|
||||
'visit_entry_idaction_url',
|
||||
'visit_entry_idaction_name'),
|
||||
|
||||
'log_conversion_item' => array('idaction_sku',
|
||||
'idaction_name',
|
||||
'idaction_category',
|
||||
'idaction_category2',
|
||||
'idaction_category3',
|
||||
'idaction_category4',
|
||||
'idaction_category5')
|
||||
);
|
||||
}
|
||||
|
||||
private function getTableIdColumns()
|
||||
{
|
||||
return array(
|
||||
'log_link_visit_action' => 'idlink_va',
|
||||
'log_conversion' => 'idvisit',
|
||||
'log_visit' => 'idvisit',
|
||||
'log_conversion_item' => 'idvisit'
|
||||
);
|
||||
}
|
||||
|
||||
// let's hardcode, since these are not dynamically created tables
|
||||
public static function getDeleteTableLogTables()
|
||||
{
|
||||
$result = Common::prefixTables('log_conversion',
|
||||
'log_link_visit_action',
|
||||
'log_visit',
|
||||
'log_conversion_item');
|
||||
if (Db::isLockPrivilegeGranted()) {
|
||||
$result[] = Common::prefixTable('log_action');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function. Creates a new instance of LogDataPurger with the supplied array
|
||||
* of settings.
|
||||
*
|
||||
* $settings must contain values for the following keys:
|
||||
* - 'delete_logs_older_than': The number of days after which log entries are considered
|
||||
* old.
|
||||
* - 'delete_logs_max_rows_per_query': Max number of rows to DELETE in one query.
|
||||
*
|
||||
* @param array $settings Array of settings
|
||||
* @param bool $useRealTable
|
||||
* @return \Piwik\Plugins\PrivacyManager\LogDataPurger
|
||||
*/
|
||||
public static function make($settings, $useRealTable = false)
|
||||
{
|
||||
return new LogDataPurger(
|
||||
$settings['delete_logs_older_than'],
|
||||
$settings['delete_logs_max_rows_per_query']
|
||||
);
|
||||
}
|
||||
}
|
||||
469
www/analytics/plugins/PrivacyManager/PrivacyManager.php
Normal file
469
www/analytics/plugins/PrivacyManager/PrivacyManager.php
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Common;
|
||||
use Piwik\Config as PiwikConfig;
|
||||
use Piwik\DataTable\DataTableInterface;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\Menu\MenuAdmin;
|
||||
use Piwik\Metrics;
|
||||
use Piwik\Option;
|
||||
use Piwik\Period\Range;
|
||||
use Piwik\Period;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\Goals\Archiver;
|
||||
use Piwik\ScheduledTask;
|
||||
use Piwik\ScheduledTime;
|
||||
use Piwik\Site;
|
||||
use Piwik\Tracker\GoalManager;
|
||||
|
||||
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/LogDataPurger.php';
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/ReportsPurger.php';
|
||||
|
||||
/**
|
||||
* Specifically include this for Tracker API (which does not use autoloader)
|
||||
*/
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/DoNotTrackHeaderChecker.php';
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/IPAnonymizer.php';
|
||||
|
||||
/**
|
||||
*/
|
||||
class PrivacyManager extends \Piwik\Plugin
|
||||
{
|
||||
const OPTION_LAST_DELETE_PIWIK_LOGS = "lastDelete_piwik_logs";
|
||||
const OPTION_LAST_DELETE_PIWIK_REPORTS = 'lastDelete_piwik_reports';
|
||||
const OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL = "lastDelete_piwik_logs_initial";
|
||||
|
||||
// options for data purging feature array[configName => configSection]
|
||||
public static $purgeDataOptions = array(
|
||||
'delete_logs_enable' => 'Deletelogs',
|
||||
'delete_logs_schedule_lowest_interval' => 'Deletelogs',
|
||||
'delete_logs_older_than' => 'Deletelogs',
|
||||
'delete_logs_max_rows_per_query' => 'Deletelogs',
|
||||
'enable_auto_database_size_estimate' => 'Deletelogs',
|
||||
'delete_reports_enable' => 'Deletereports',
|
||||
'delete_reports_older_than' => 'Deletereports',
|
||||
'delete_reports_keep_basic_metrics' => 'Deletereports',
|
||||
'delete_reports_keep_day_reports' => 'Deletereports',
|
||||
'delete_reports_keep_week_reports' => 'Deletereports',
|
||||
'delete_reports_keep_month_reports' => 'Deletereports',
|
||||
'delete_reports_keep_year_reports' => 'Deletereports',
|
||||
'delete_reports_keep_range_reports' => 'Deletereports',
|
||||
'delete_reports_keep_segment_reports' => 'Deletereports',
|
||||
);
|
||||
|
||||
private $dntChecker = null;
|
||||
private $ipAnonymizer = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->dntChecker = new DoNotTrackHeaderChecker();
|
||||
$this->ipAnonymizer = new IPAnonymizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it is likely that the data for this report has been purged and if the
|
||||
* user should be told about that.
|
||||
*
|
||||
* In order for this function to return true, the following must also be true:
|
||||
* - The data table for this report must either be empty or not have been fetched.
|
||||
* - The period of this report is not a multiple period.
|
||||
* - The date of this report must be older than the delete_reports_older_than config option.
|
||||
* @param DataTableInterface $dataTable
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasReportBeenPurged($dataTable)
|
||||
{
|
||||
$strPeriod = Common::getRequestVar('period', false);
|
||||
$strDate = Common::getRequestVar('date', false);
|
||||
|
||||
if (false !== $strPeriod
|
||||
&& false !== $strDate
|
||||
&& (is_null($dataTable)
|
||||
|| (!empty($dataTable) && $dataTable->getRowsCount() == 0))
|
||||
) {
|
||||
// if range, only look at the first date
|
||||
if ($strPeriod == 'range') {
|
||||
|
||||
$idSite = Common::getRequestVar('idSite', '');
|
||||
|
||||
if (intval($idSite) != 0) {
|
||||
$site = new Site($idSite);
|
||||
$timezone = $site->getTimezone();
|
||||
} else {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
|
||||
$period = new Range('range', $strDate, $timezone);
|
||||
$reportDate = $period->getDateStart();
|
||||
|
||||
} elseif (Period::isMultiplePeriod($strDate, $strPeriod)) {
|
||||
|
||||
// if a multiple period, this function is irrelevant
|
||||
return false;
|
||||
|
||||
} else {
|
||||
// otherwise, use the date as given
|
||||
$reportDate = Date::factory($strDate);
|
||||
}
|
||||
|
||||
$reportYear = $reportDate->toString('Y');
|
||||
$reportMonth = $reportDate->toString('m');
|
||||
|
||||
if (static::shouldReportBePurged($reportYear, $reportMonth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Piwik\Plugin::getListHooksRegistered
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
{
|
||||
return array(
|
||||
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
|
||||
'Menu.Admin.addItems' => 'addMenu',
|
||||
'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
|
||||
'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral',
|
||||
'Tracker.isExcludedVisit' => array($this->dntChecker, 'checkHeaderInTracker'),
|
||||
'Tracker.setVisitorIp' => array($this->ipAnonymizer, 'setVisitorIpAddress'),
|
||||
);
|
||||
}
|
||||
|
||||
public function setTrackerCacheGeneral(&$cacheContent)
|
||||
{
|
||||
$config = new Config();
|
||||
$cacheContent = $config->setTrackerCacheGeneral($cacheContent);
|
||||
}
|
||||
|
||||
public function getScheduledTasks(&$tasks)
|
||||
{
|
||||
// both tasks are low priority so they will execute after most others, but not lowest, so
|
||||
// they will execute before the optimize tables task
|
||||
|
||||
$purgeReportDataTask = new ScheduledTask(
|
||||
$this, 'deleteReportData', null, ScheduledTime::factory('daily'), ScheduledTask::LOW_PRIORITY
|
||||
);
|
||||
$tasks[] = $purgeReportDataTask;
|
||||
|
||||
$purgeLogDataTask = new ScheduledTask(
|
||||
$this, 'deleteLogData', null, ScheduledTime::factory('daily'), ScheduledTask::LOW_PRIORITY
|
||||
);
|
||||
$tasks[] = $purgeLogDataTask;
|
||||
}
|
||||
|
||||
public function getJsFiles(&$jsFiles)
|
||||
{
|
||||
$jsFiles[] = "plugins/PrivacyManager/javascripts/privacySettings.js";
|
||||
}
|
||||
|
||||
function addMenu()
|
||||
{
|
||||
MenuAdmin::addEntry('PrivacyManager_MenuPrivacySettings',
|
||||
array('module' => 'PrivacyManager', 'action' => 'privacySettings'),
|
||||
Piwik::isUserHasSomeAdminAccess(),
|
||||
$order = 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the settings for the data purging feature.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPurgeDataSettings()
|
||||
{
|
||||
$settings = array();
|
||||
|
||||
// load settings from ini config
|
||||
$config = PiwikConfig::getInstance();
|
||||
foreach (self::$purgeDataOptions as $configKey => $configSection) {
|
||||
$values = $config->$configSection;
|
||||
$settings[$configKey] = $values[$configKey];
|
||||
}
|
||||
|
||||
if (!Controller::isDataPurgeSettingsEnabled()) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
// load the settings for the data purging settings
|
||||
foreach (self::$purgeDataOptions as $configName => $configSection) {
|
||||
$value = Option::get($configName);
|
||||
if ($value !== false) {
|
||||
$settings[$configName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the supplied data purging settings.
|
||||
*
|
||||
* @param array $settings The settings to save.
|
||||
*/
|
||||
public static function savePurgeDataSettings($settings)
|
||||
{
|
||||
foreach (self::$purgeDataOptions as $configName => $configSection) {
|
||||
if (isset($settings[$configName])) {
|
||||
Option::set($configName, $settings[$configName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes old archived data (reports & metrics).
|
||||
*
|
||||
* Archive tables are not optimized after, as that is handled by a separate scheduled task
|
||||
* in CoreAdminHome. This is a scheduled task and will only execute every N days. The number
|
||||
* of days is determined by the delete_logs_schedule_lowest_interval config option.
|
||||
*
|
||||
* If delete_reports_enable is set to 1, old archive data is deleted. The following
|
||||
* config options can tweak this behavior:
|
||||
* - delete_reports_older_than: The number of months after which archive data is considered
|
||||
* old. The current month is not considered when applying this
|
||||
* value.
|
||||
* - delete_reports_keep_basic_metrics: If set to 1, keeps certain metric data. Right now,
|
||||
* all metric data is kept.
|
||||
* - delete_reports_keep_day_reports: If set to 1, keeps old daily reports.
|
||||
* - delete_reports_keep_week_reports: If set to 1, keeps old weekly reports.
|
||||
* - delete_reports_keep_month_reports: If set to 1, keeps old monthly reports.
|
||||
* - delete_reports_keep_year_reports: If set to 1, keeps old yearly reports.
|
||||
*/
|
||||
public function deleteReportData()
|
||||
{
|
||||
$settings = self::getPurgeDataSettings();
|
||||
|
||||
// Make sure, data deletion is enabled
|
||||
if ($settings['delete_reports_enable'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure purging should run at this time (unless this is a forced purge)
|
||||
if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_REPORTS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set last run time
|
||||
Option::set(self::OPTION_LAST_DELETE_PIWIK_REPORTS, Date::factory('today')->getTimestamp());
|
||||
|
||||
ReportsPurger::make($settings, self::getAllMetricsToKeep())->purgeData();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes old log data based on the options set in the Deletelogs config
|
||||
* section. This is a scheduled task and will only execute every N days. The number
|
||||
* of days is determined by the delete_logs_schedule_lowest_interval config option.
|
||||
*
|
||||
* If delete_logs_enable is set to 1, old data in the log_visit, log_conversion,
|
||||
* log_conversion_item and log_link_visit_action tables is deleted. The following
|
||||
* options can tweak this behavior:
|
||||
* - delete_logs_older_than: The number of days after which log data is considered old.
|
||||
*
|
||||
* @ToDo: return number of Rows deleted in last run; Display age of "oldest" row to help the user setting
|
||||
* the day offset;
|
||||
*/
|
||||
public function deleteLogData()
|
||||
{
|
||||
$settings = self::getPurgeDataSettings();
|
||||
|
||||
// Make sure, data deletion is enabled
|
||||
if ($settings['delete_logs_enable'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure purging should run at this time
|
||||
if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_LOGS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the DB that log deletion has run BEFORE deletion is executed;
|
||||
* If deletion / table optimization exceeds execution time, other tasks maybe prevented of being executed
|
||||
* every time, when the schedule is triggered.
|
||||
*/
|
||||
$lastDeleteDate = Date::factory("today")->getTimestamp();
|
||||
Option::set(self::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDeleteDate);
|
||||
|
||||
// execute the purge
|
||||
LogDataPurger::make($settings)->purgeData();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array describing what data would be purged if both log data & report
|
||||
* purging is invoked.
|
||||
*
|
||||
* The returned array maps table names with the number of rows that will be deleted.
|
||||
* If the table name is mapped with -1, the table will be dropped.
|
||||
*
|
||||
* @param array $settings The config options to use in the estimate. If null, the real
|
||||
* options are used.
|
||||
* @return array
|
||||
*/
|
||||
public static function getPurgeEstimate($settings = null)
|
||||
{
|
||||
if (is_null($settings)) {
|
||||
$settings = self::getPurgeDataSettings();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
if ($settings['delete_logs_enable']) {
|
||||
$logDataPurger = LogDataPurger::make($settings);
|
||||
$result = array_merge($result, $logDataPurger->getPurgeEstimate());
|
||||
}
|
||||
|
||||
if ($settings['delete_reports_enable']) {
|
||||
$reportsPurger = ReportsPurger::make($settings, self::getAllMetricsToKeep());
|
||||
$result = array_merge($result, $reportsPurger->getPurgeEstimate());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a report with the given year & month should be purged or not.
|
||||
*
|
||||
* If reportsOlderThan is set to null or not supplied, this function will check if
|
||||
* a report should be purged, based on existing configuration. In this case, if
|
||||
* delete_reports_enable is set to 0, this function will return false.
|
||||
*
|
||||
* @param int $reportDateYear The year of the report in question.
|
||||
* @param int $reportDateMonth The month of the report in question.
|
||||
* @param int|Date $reportsOlderThan If an int, the number of months a report must be older than
|
||||
* in order to be purged. If a date, the date a report must be
|
||||
* older than in order to be purged.
|
||||
* @return bool
|
||||
*/
|
||||
public static function shouldReportBePurged($reportDateYear, $reportDateMonth, $reportsOlderThan = null)
|
||||
{
|
||||
// if no 'older than' value/date was supplied, use existing config
|
||||
if (is_null($reportsOlderThan)) {
|
||||
// if report deletion is not enabled, the report shouldn't be purged
|
||||
$settings = self::getPurgeDataSettings();
|
||||
if ($settings['delete_reports_enable'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$reportsOlderThan = $settings['delete_reports_older_than'];
|
||||
}
|
||||
|
||||
// if a integer was supplied, assume it is the number of months a report must be older than
|
||||
if (!($reportsOlderThan instanceof Date)) {
|
||||
$reportsOlderThan = Date::factory('today')->subMonth(1 + $reportsOlderThan);
|
||||
}
|
||||
|
||||
return ReportsPurger::shouldReportBePurged(
|
||||
$reportDateYear, $reportDateMonth, $reportsOlderThan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the general metrics to keep when the 'delete_reports_keep_basic_metrics'
|
||||
* config is set to 1.
|
||||
*/
|
||||
private static function getMetricsToKeep()
|
||||
{
|
||||
return array('nb_uniq_visitors', 'nb_visits', 'nb_actions', 'max_actions',
|
||||
'sum_visit_length', 'bounce_count', 'nb_visits_converted', 'nb_conversions',
|
||||
'revenue', 'quantity', 'price', 'orders');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the goal metrics to keep when the 'delete_reports_keep_basic_metrics'
|
||||
* config is set to 1.
|
||||
*/
|
||||
private static function getGoalMetricsToKeep()
|
||||
{
|
||||
// keep all goal metrics
|
||||
return array_values(Metrics::$mappingFromIdToNameGoal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of metrics that should be kept when purging as they appear in
|
||||
* archive tables.
|
||||
*/
|
||||
public static function getAllMetricsToKeep()
|
||||
{
|
||||
$metricsToKeep = self::getMetricsToKeep();
|
||||
|
||||
// convert goal metric names to correct archive names
|
||||
if (Common::isGoalPluginEnabled()) {
|
||||
$goalMetricsToKeep = self::getGoalMetricsToKeep();
|
||||
|
||||
$maxGoalId = self::getMaxGoalId();
|
||||
|
||||
// for each goal metric, there's a different name for each goal, including the overview,
|
||||
// the order report & cart report
|
||||
foreach ($goalMetricsToKeep as $metric) {
|
||||
for ($i = 1; $i <= $maxGoalId; ++$i) // maxGoalId can be 0
|
||||
{
|
||||
$metricsToKeep[] = Archiver::getRecordName($metric, $i);
|
||||
}
|
||||
|
||||
$metricsToKeep[] = Archiver::getRecordName($metric);
|
||||
$metricsToKeep[] = Archiver::getRecordName($metric, GoalManager::IDGOAL_ORDER);
|
||||
$metricsToKeep[] = Archiver::getRecordName($metric, GoalManager::IDGOAL_CART);
|
||||
}
|
||||
}
|
||||
|
||||
return $metricsToKeep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if one of the purge data tasks should run now, false if it shouldn't.
|
||||
*/
|
||||
private function shouldPurgeData($settings, $lastRanOption)
|
||||
{
|
||||
// Log deletion may not run until it is once rescheduled (initial run). This is the
|
||||
// only way to guarantee the calculated next scheduled deletion time.
|
||||
$initialDelete = Option::get(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL);
|
||||
if (empty($initialDelete)) {
|
||||
Option::set(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure, log purging is allowed to run now
|
||||
$lastDelete = Option::get($lastRanOption);
|
||||
$deleteIntervalDays = $settings['delete_logs_schedule_lowest_interval'];
|
||||
$deleteIntervalSeconds = $this->getDeleteIntervalInSeconds($deleteIntervalDays);
|
||||
|
||||
if ($lastDelete === false ||
|
||||
($lastDelete !== false && ((int)$lastDelete + $deleteIntervalSeconds) <= time())
|
||||
) {
|
||||
return true;
|
||||
} else // not time to run data purge
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getDeleteIntervalInSeconds($deleteInterval)
|
||||
{
|
||||
return (int)$deleteInterval * 24 * 60 * 60;
|
||||
}
|
||||
|
||||
private static function getMaxGoalId()
|
||||
{
|
||||
return Db::fetchOne("SELECT MAX(idgoal) FROM " . Common::prefixTable('goal'));
|
||||
}
|
||||
}
|
||||
380
www/analytics/plugins/PrivacyManager/ReportsPurger.php
Executable file
380
www/analytics/plugins/PrivacyManager/ReportsPurger.php
Executable file
|
|
@ -0,0 +1,380 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\PrivacyManager;
|
||||
|
||||
use Piwik\DataAccess\ArchiveTableCreator;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\DbHelper;
|
||||
use Piwik\Piwik;
|
||||
|
||||
/**
|
||||
* Purges archived reports and metrics that are considered old.
|
||||
*/
|
||||
class ReportsPurger
|
||||
{
|
||||
// constant used in database purging estimate to signify a table should be dropped
|
||||
const DROP_TABLE = -1;
|
||||
|
||||
/**
|
||||
* The max set of rows each table scan select should query at one time.
|
||||
*/
|
||||
public static $selectSegmentSize = 100000;
|
||||
|
||||
/**
|
||||
* The number of months after which report/metric data is considered old.
|
||||
*/
|
||||
private $deleteReportsOlderThan;
|
||||
|
||||
/**
|
||||
* Whether to keep basic metrics or not.
|
||||
*/
|
||||
private $keepBasicMetrics;
|
||||
|
||||
/**
|
||||
* Array of period types. Reports for these periods will not be purged.
|
||||
*/
|
||||
private $reportPeriodsToKeep;
|
||||
|
||||
/**
|
||||
* Whether to keep reports for segments or not.
|
||||
*/
|
||||
private $keepSegmentReports;
|
||||
|
||||
/**
|
||||
* The maximum number of rows to delete per DELETE query.
|
||||
*/
|
||||
private $maxRowsToDeletePerQuery;
|
||||
|
||||
/**
|
||||
* List of metrics that should be kept when purging. If $keepBasicMetrics is true,
|
||||
* these metrics will be saved.
|
||||
*/
|
||||
private $metricsToKeep;
|
||||
|
||||
/**
|
||||
* Array that maps a year and month ('2012_01') with lists of archive IDs for segmented
|
||||
* archives. Used to keep segmented reports when purging.
|
||||
*/
|
||||
private $segmentArchiveIds = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $deleteReportsOlderThan The number of months after which report/metric data
|
||||
* is considered old.
|
||||
* @param bool $keepBasicMetrics Whether to keep basic metrics or not.
|
||||
* @param array $reportPeriodsToKeep Array of period types. Reports for these periods will not
|
||||
* be purged.
|
||||
* @param bool $keepSegmentReports Whether to keep reports for segments or not.
|
||||
* @param array $metricsToKeep List of metrics that should be kept. if $keepBasicMetrics
|
||||
* is true, these metrics will be saved.
|
||||
* @param int $maxRowsToDeletePerQuery The maximum number of rows to delete per DELETE query.
|
||||
*/
|
||||
public function __construct($deleteReportsOlderThan, $keepBasicMetrics, $reportPeriodsToKeep,
|
||||
$keepSegmentReports, $metricsToKeep, $maxRowsToDeletePerQuery)
|
||||
{
|
||||
$this->deleteReportsOlderThan = $deleteReportsOlderThan;
|
||||
$this->keepBasicMetrics = $keepBasicMetrics;
|
||||
$this->reportPeriodsToKeep = $reportPeriodsToKeep;
|
||||
$this->keepSegmentReports = $keepSegmentReports;
|
||||
$this->metricsToKeep = $metricsToKeep;
|
||||
$this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges old report/metric data.
|
||||
*
|
||||
* If $keepBasicMetrics is false, old numeric tables will be dropped, otherwise only
|
||||
* the metrics not in $metricsToKeep will be deleted.
|
||||
*
|
||||
* If $reportPeriodsToKeep is an empty array, old blob tables will be dropped. Otherwise,
|
||||
* specific reports will be deleted, except reports for periods in $reportPeriodsToKeep.
|
||||
*
|
||||
* @param bool $optimize If tables should be optimized after rows are deleted. Normally,
|
||||
* this is handled by a scheduled task.
|
||||
*/
|
||||
public function purgeData($optimize = false)
|
||||
{
|
||||
// find archive tables to purge
|
||||
list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
|
||||
|
||||
// process blob tables first, since archive status is stored in the numeric archives
|
||||
if (!empty($oldBlobTables)) {
|
||||
// if no reports should be kept, drop tables, otherwise drop individual reports
|
||||
if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports) {
|
||||
Db::dropTables($oldBlobTables);
|
||||
} else {
|
||||
foreach ($oldBlobTables as $table) {
|
||||
$where = $this->getBlobTableWhereExpr($oldNumericTables, $table);
|
||||
if (!empty($where)) {
|
||||
$where = "WHERE $where";
|
||||
}
|
||||
Db::deleteAllRows($table, $where, "idarchive ASC", $this->maxRowsToDeletePerQuery);
|
||||
}
|
||||
|
||||
if ($optimize) {
|
||||
Db::optimizeTables($oldBlobTables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deal with numeric tables
|
||||
if (!empty($oldNumericTables)) {
|
||||
// if keep_basic_metrics is set, empty all numeric tables of metrics to purge
|
||||
if ($this->keepBasicMetrics == 1 && !empty($this->metricsToKeep)) {
|
||||
$where = "WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "') AND name NOT LIKE 'done%'";
|
||||
foreach ($oldNumericTables as $table) {
|
||||
Db::deleteAllRows($table, $where, "idarchive ASC", $this->maxRowsToDeletePerQuery);
|
||||
}
|
||||
|
||||
if ($optimize) {
|
||||
Db::optimizeTables($oldNumericTables);
|
||||
}
|
||||
} else // drop numeric tables
|
||||
{
|
||||
Db::dropTables($oldNumericTables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array describing what data would be purged if purging were invoked.
|
||||
*
|
||||
* This function returns an array that maps table names with the number of rows
|
||||
* that will be deleted. If a table name is mapped with self::DROP_TABLE, the table
|
||||
* will be dropped.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPurgeEstimate()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
// get archive tables that will be purged
|
||||
list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
|
||||
|
||||
// process blob tables first, since archive status is stored in the numeric archives
|
||||
if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports) {
|
||||
// not keeping any reports, so drop all tables
|
||||
foreach ($oldBlobTables as $table) {
|
||||
$result[$table] = self::DROP_TABLE;
|
||||
}
|
||||
} else {
|
||||
// figure out which rows will be deleted
|
||||
foreach ($oldBlobTables as $table) {
|
||||
$rowCount = $this->getBlobTableDeleteCount($oldNumericTables, $table);
|
||||
if ($rowCount > 0) {
|
||||
$result[$table] = $rowCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deal w/ numeric tables
|
||||
if ($this->keepBasicMetrics == 1) {
|
||||
// figure out which rows will be deleted
|
||||
foreach ($oldNumericTables as $table) {
|
||||
$rowCount = $this->getNumericTableDeleteCount($table);
|
||||
if ($rowCount > 0) {
|
||||
$result[$table] = $rowCount;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// not keeping any metrics, so drop the entire table
|
||||
foreach ($oldNumericTables as $table) {
|
||||
$result[$table] = self::DROP_TABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that finds every archive table whose reports are considered
|
||||
* old.
|
||||
*
|
||||
* @return array An array of two arrays. The first holds the numeric archive table
|
||||
* names, and the second holds the blob archive table names.
|
||||
*/
|
||||
private function getArchiveTablesToPurge()
|
||||
{
|
||||
// get month for which reports as old or older than, should be deleted
|
||||
// reports whose creation date <= this month will be deleted
|
||||
// (NOTE: we ignore how far we are in the current month)
|
||||
$toRemoveDate = Date::factory('today')->subMonth(1 + $this->deleteReportsOlderThan);
|
||||
|
||||
// find all archive tables that are older than N months
|
||||
$oldNumericTables = array();
|
||||
$oldBlobTables = array();
|
||||
foreach (DbHelper::getTablesInstalled() as $table) {
|
||||
$type = ArchiveTableCreator::getTypeFromTableName($table);
|
||||
if ($type === false) {
|
||||
continue;
|
||||
}
|
||||
$date = ArchiveTableCreator::getDateFromTableName($table);
|
||||
list($year, $month) = explode('_', $date);
|
||||
|
||||
if (self::shouldReportBePurged($year, $month, $toRemoveDate)) {
|
||||
if ($type == ArchiveTableCreator::NUMERIC_TABLE) {
|
||||
$oldNumericTables[] = $table;
|
||||
} else {
|
||||
$oldBlobTables[] = $table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($oldNumericTables, $oldBlobTables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a report with the given year & month should be purged or not.
|
||||
*
|
||||
* @param int $reportDateYear The year of the report in question.
|
||||
* @param int $reportDateMonth The month of the report in question.
|
||||
* @param Date $toRemoveDate The date a report must be older than in order to be purged.
|
||||
* @return bool
|
||||
*/
|
||||
public static function shouldReportBePurged($reportDateYear, $reportDateMonth, $toRemoveDate)
|
||||
{
|
||||
$toRemoveYear = (int)$toRemoveDate->toString('Y');
|
||||
$toRemoveMonth = (int)$toRemoveDate->toString('m');
|
||||
|
||||
return $reportDateYear < $toRemoveYear
|
||||
|| ($reportDateYear == $toRemoveYear && $reportDateMonth <= $toRemoveMonth);
|
||||
}
|
||||
|
||||
private function getNumericTableDeleteCount($table)
|
||||
{
|
||||
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
|
||||
|
||||
$sql = "SELECT COUNT(*)
|
||||
FROM $table
|
||||
WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "')
|
||||
AND name NOT LIKE 'done%'
|
||||
AND idarchive >= ?
|
||||
AND idarchive < ?";
|
||||
|
||||
$segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
|
||||
return array_sum($segments);
|
||||
}
|
||||
|
||||
private function getBlobTableDeleteCount($oldNumericTables, $table)
|
||||
{
|
||||
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
|
||||
|
||||
$sql = "SELECT COUNT(*)
|
||||
FROM $table
|
||||
WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . "
|
||||
AND idarchive >= ?
|
||||
AND idarchive < ?";
|
||||
|
||||
$segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
|
||||
return array_sum($segments);
|
||||
}
|
||||
|
||||
/** Returns SQL WHERE expression used to find reports that should be purged. */
|
||||
private function getBlobTableWhereExpr($oldNumericTables, $table)
|
||||
{
|
||||
$where = "";
|
||||
if (!empty($this->reportPeriodsToKeep)) // if keeping reports
|
||||
{
|
||||
$where = "period NOT IN (" . implode(',', $this->reportPeriodsToKeep) . ")";
|
||||
|
||||
// if not keeping segments make sure segments w/ kept periods are also deleted
|
||||
if (!$this->keepSegmentReports) {
|
||||
$this->findSegmentArchives($oldNumericTables);
|
||||
$archiveIds = $this->segmentArchiveIds[ArchiveTableCreator::getDateFromTableName($table)];
|
||||
|
||||
if (!empty($archiveIds)) {
|
||||
$where .= " OR idarchive IN (" . implode(',', $archiveIds) . ")";
|
||||
}
|
||||
}
|
||||
|
||||
$where = "($where)";
|
||||
}
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're going to keep segmented reports, we need to know which archives are
|
||||
* for segments. This info is only in the numeric tables, so we must query them.
|
||||
*/
|
||||
private function findSegmentArchives($numericTables)
|
||||
{
|
||||
if (!is_null($this->segmentArchiveIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($numericTables as $table) {
|
||||
$tableDate = ArchiveTableCreator::getDateFromTableName($table);
|
||||
|
||||
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
|
||||
|
||||
$sql = "SELECT idarchive
|
||||
FROM $table
|
||||
WHERE name != 'done'
|
||||
AND name LIKE 'done_%.%'
|
||||
AND idarchive >= ?
|
||||
AND idarchive < ?";
|
||||
|
||||
$this->segmentArchiveIds[$tableDate] = array();
|
||||
foreach (Db::segmentedFetchAll($sql, 0, $maxIdArchive, self::$selectSegmentSize) as $row) {
|
||||
$this->segmentArchiveIds[$tableDate][] = $row['idarchive'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function. Creates a new instance of ReportsPurger with the supplied array
|
||||
* of settings.
|
||||
*
|
||||
* $settings must contain the following keys:
|
||||
* -'delete_reports_older_than': The number of months after which reports/metrics are
|
||||
* considered old.
|
||||
* -'delete_reports_keep_basic_metrics': 1 if basic metrics should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_day_reports': 1 if daily reports should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_week_reports': 1 if weekly reports should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_month_reports': 1 if monthly reports should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_year_reports': 1 if yearly reports should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_range_reports': 1 if range reports should be kept, 0 if otherwise.
|
||||
* -'delete_reports_keep_segment_reports': 1 if reports for segments should be kept, 0 if otherwise.
|
||||
* -'delete_logs_max_rows_per_query': Maximum number of rows to delete in one DELETE query.
|
||||
*/
|
||||
public static function make($settings, $metricsToKeep)
|
||||
{
|
||||
return new ReportsPurger(
|
||||
$settings['delete_reports_older_than'],
|
||||
$settings['delete_reports_keep_basic_metrics'] == 1,
|
||||
self::getReportPeriodsToKeep($settings),
|
||||
$settings['delete_reports_keep_segment_reports'] == 1,
|
||||
$metricsToKeep,
|
||||
$settings['delete_logs_max_rows_per_query']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that returns an array period values based on the 'delete_reports_keep_*'
|
||||
* settings. The period values returned are the integer values stored in the DB.
|
||||
*
|
||||
* @param array $settings The settings to use.
|
||||
* @return array An array of period values that should be kept when purging old data.
|
||||
*/
|
||||
private static function getReportPeriodsToKeep($settings)
|
||||
{
|
||||
$keepReportPeriods = array();
|
||||
foreach (Piwik::$idPeriods as $strPeriod => $intPeriod) {
|
||||
$optionName = "delete_reports_keep_{$strPeriod}_reports";
|
||||
if ($settings[$optionName] == 1) {
|
||||
$keepReportPeriods[] = $intPeriod;
|
||||
}
|
||||
}
|
||||
return $keepReportPeriods;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
$(document).ready(function () {
|
||||
function toggleBlock(id, value) {
|
||||
$('#' + id).toggle(value == 1);
|
||||
}
|
||||
|
||||
function isEitherDeleteSectionEnabled() {
|
||||
return ($('input[name=deleteEnable]:checked').val() == 1)
|
||||
|| ($('input[name=deleteReportsEnable]:checked').val() == 1);
|
||||
}
|
||||
|
||||
function toggleOtherDeleteSections() {
|
||||
var showSection = isEitherDeleteSectionEnabled();
|
||||
toggleBlock('deleteDataEstimateSect', showSection);
|
||||
toggleBlock('deleteSchedulingSettings', showSection);
|
||||
}
|
||||
|
||||
// reloads purged database size estimate
|
||||
var currentRequest;
|
||||
|
||||
/**
|
||||
* @param {boolean} [forceEstimate] (defaults to false)
|
||||
*/
|
||||
function reloadDbStats(forceEstimate) {
|
||||
if (currentRequest) {
|
||||
currentRequest.abort();
|
||||
}
|
||||
|
||||
// if the section isn't visible or the manual estimate link is showing, abort
|
||||
// (unless on first load or forcing)
|
||||
var isFirstLoad = $('#deleteDataEstimate').html() == '';
|
||||
if (!isFirstLoad
|
||||
&& forceEstimate !== true
|
||||
&& (!isEitherDeleteSectionEnabled() || $('#getPurgeEstimateLink').length > 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('#deleteDataEstimate').hide();
|
||||
|
||||
var data = $('#formDeleteSettings').serializeArray();
|
||||
var formData = {};
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
formData[data[i].name] = data[i].value;
|
||||
}
|
||||
if (forceEstimate === true) {
|
||||
formData['forceEstimate'] = 1;
|
||||
}
|
||||
|
||||
currentRequest = new ajaxHelper();
|
||||
currentRequest.setLoadingElement('#deleteDataEstimateSect .loadingPiwik');
|
||||
currentRequest.addParams({
|
||||
module: 'PrivacyManager',
|
||||
action: 'getDatabaseSize'
|
||||
}, 'get');
|
||||
currentRequest.addParams(formData, 'post');
|
||||
currentRequest.setCallback(
|
||||
function (data) {
|
||||
currentRequest = undefined;
|
||||
$('#deleteDataEstimate').html(data).show();
|
||||
|
||||
// lock size of db size estimate
|
||||
$('#deleteDataEstimateSect').height($('#deleteDataEstimateSect').height());
|
||||
}
|
||||
);
|
||||
currentRequest.setFormat('html');
|
||||
currentRequest.send(false);
|
||||
}
|
||||
|
||||
// make sure certain sections only display if their corresponding features are enabled
|
||||
$('input[name=anonymizeIPEnable]').click(function () {
|
||||
toggleBlock("anonymizeIPenabled", $(this).val());
|
||||
});
|
||||
|
||||
$('input[name=deleteEnable]').click(function () {
|
||||
toggleBlock("deleteLogSettings", $(this).val());
|
||||
toggleOtherDeleteSections();
|
||||
}).change(reloadDbStats);
|
||||
|
||||
$('input[name=deleteReportsEnable]').click(function () {
|
||||
toggleBlock("deleteReportsSettings", $(this).val());
|
||||
toggleBlock("deleteOldReportsMoreInfo", $(this).val());
|
||||
toggleOtherDeleteSections();
|
||||
}).change(reloadDbStats);
|
||||
|
||||
// initial toggling calls
|
||||
$(function () {
|
||||
toggleBlock("deleteLogSettings", $("input[name=deleteEnable]:checked").val());
|
||||
toggleBlock("anonymizeIPenabled", $("input[name=anonymizeIPEnable]:checked").val());
|
||||
toggleBlock("deleteReportsSettings", $("input[name=deleteReportsEnable]:checked").val());
|
||||
toggleBlock("deleteOldReportsMoreInfo", $("input[name=deleteReportsEnable]:checked").val());
|
||||
toggleOtherDeleteSections();
|
||||
});
|
||||
|
||||
// make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
|
||||
$('#formDeleteSettings').find('input[type=text]').each(function () {
|
||||
$(this).change(reloadDbStats);
|
||||
});
|
||||
$('#formDeleteSettings').find('input[type=checkbox]').each(function () {
|
||||
$(this).click(reloadDbStats);
|
||||
});
|
||||
|
||||
// make sure when the delete log/report settings are submitted, a confirmation popup is
|
||||
// displayed first
|
||||
$('#deleteLogSettingsSubmit').click(function (e) {
|
||||
var deletingLogs = $("input[name=deleteEnable]:checked").val() == 1,
|
||||
deletingReports = $("input[name=deleteReportsEnable]:checked").val() == 1,
|
||||
confirm_id;
|
||||
|
||||
// hide all confirmation texts, then show the correct one based on what
|
||||
// type of deletion is enabled.
|
||||
$('#confirmDeleteSettings').find('>h2').each(function () {
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
if (deletingLogs) {
|
||||
confirm_id = deletingReports ? "deleteBothConfirm" : "deleteLogsConfirm";
|
||||
}
|
||||
else if (deletingReports) {
|
||||
confirm_id = "deleteReportsConfirm";
|
||||
}
|
||||
|
||||
if (confirm_id) {
|
||||
$("#" + confirm_id).show();
|
||||
e.preventDefault();
|
||||
|
||||
piwikHelper.modalConfirm('#confirmDeleteSettings', {
|
||||
yes: function () {
|
||||
$('#formDeleteSettings').submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
$('#formDeleteSettings').submit();
|
||||
}
|
||||
});
|
||||
|
||||
// execute purge now link click
|
||||
$('#purgeDataNowLink').click(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var link = this;
|
||||
|
||||
// if any option has been modified, abort purging and instruct user to save first
|
||||
var modified = false;
|
||||
$('#formDeleteSettings').find('input').each(function () {
|
||||
if (this.type === 'checkbox' || this.type === 'radio') {
|
||||
modified |= this.defaultChecked !== this.checked;
|
||||
} else {
|
||||
modified |= this.defaultValue !== this.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (modified) {
|
||||
piwikHelper.modalConfirm('#saveSettingsBeforePurge', {yes: function () {}});
|
||||
return;
|
||||
}
|
||||
|
||||
// ask user if they really want to delete their old data
|
||||
piwikHelper.modalConfirm('#confirmPurgeNow', {
|
||||
yes: function () {
|
||||
$(link).hide();
|
||||
|
||||
// execute a data purge
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.setLoadingElement('#deleteSchedulingSettings .loadingPiwik');
|
||||
ajaxRequest.addParams({
|
||||
module: 'PrivacyManager',
|
||||
action: 'executeDataPurge'
|
||||
}, 'get');
|
||||
ajaxRequest.setCallback(
|
||||
function () {
|
||||
// force reload
|
||||
$('#deleteDataEstimate').html('');
|
||||
reloadDbStats();
|
||||
|
||||
// show 'db purged' message
|
||||
$('#db-purged-message').fadeIn('slow');
|
||||
setTimeout(function () {
|
||||
// hide 'db purged' message & show link
|
||||
$('#db-purged-message').fadeOut('slow', function () {
|
||||
$(link).show();
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
);
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.send(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// get estimate link click
|
||||
$('#getPurgeEstimateLink').click(function (e) {
|
||||
e.preventDefault();
|
||||
reloadDbStats(true);
|
||||
});
|
||||
|
||||
// load initial db size estimate
|
||||
reloadDbStats();
|
||||
});
|
||||
7
www/analytics/plugins/PrivacyManager/templates/getDatabaseSize.twig
Executable file
7
www/analytics/plugins/PrivacyManager/templates/getDatabaseSize.twig
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
<p>{{ 'PrivacyManager_CurrentDBSize'|translate }}: {{ dbStats.currentSize }}</p>
|
||||
{% if dbStats.sizeAfterPurge is defined %}
|
||||
<p>{{ 'PrivacyManager_EstimatedDBSizeAfterPurge'|translate }}: <strong>{{ dbStats.sizeAfterPurge }}</strong></p>
|
||||
{% endif %}
|
||||
{% if dbStats.spaceSaved is defined %}
|
||||
<p>{{ 'PrivacyManager_EstimatedSpaceSaved'|translate }}: {{ dbStats.spaceSaved }}</p>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
{% extends 'admin.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% import 'macros.twig' as piwik %}
|
||||
{% if isSuperUser %}
|
||||
<h2 piwik-enriched-headline
|
||||
help-url="http://piwik.org/docs/privacy/">{{ 'PrivacyManager_TeaserHeadline'|translate }}</h2>
|
||||
<p>{{ 'PrivacyManager_Teaser'|translate('<a href="#anonymizeIPAnchor">',"</a>",'<a href="#deleteLogsAnchor">',"</a>",'<a href="#optOutAnchor">',"</a>")|raw }}
|
||||
{{'PrivacyManager_SeeAlsoOurOfficialGuidePrivacy'|translate('<strong><a href="http://piwik.org/privacy/" target="_blank">','</a></strong>')|raw }}</p>
|
||||
<h2 id="anonymizeIPAnchor">{{ 'PrivacyManager_UseAnonymizeIp'|translate }}</h2>
|
||||
<form method="post" action="{{ {'action':'saveSettings', 'form':'formMaskLength', 'token_auth':token_auth} | urlRewriteWithParameters }}" id="formMaskLength">
|
||||
<div id='anonymizeIpSettings'>
|
||||
<table class="adminTable" style='width:800px;'>
|
||||
<tr>
|
||||
<td width="250">{{ 'PrivacyManager_UseAnonymizeIp'|translate }}<br/>
|
||||
<span class="form-description">{{ 'PrivacyManager_AnonymizeIpDescription'|translate }}</span>
|
||||
</td>
|
||||
<td width='500'>
|
||||
<input id="anonymizeIPEnable-1" type="radio" name="anonymizeIPEnable" value="1" {% if anonymizeIP.enabled == '1' %}checked {% endif %}/>
|
||||
<label for="anonymizeIPEnable-1">{{ 'General_Yes'|translate }}</label>
|
||||
<input class="indented-radio-button" id="anonymizeIPEnable-0" type="radio" name="anonymizeIPEnable" value="0" {% if anonymizeIP.enabled == '0' %} checked {% endif %}/>
|
||||
<label for="anonymizeIPEnable-0">{{ 'General_No'|translate }}</label>
|
||||
<input type="hidden" name="token_auth" value="{{ token_auth }}"/>
|
||||
</td>
|
||||
<td width="200">
|
||||
<div style="width:180px">
|
||||
{{ piwik.inlineHelp('PrivacyManager_AnonymizeIpInlineHelp'|translate) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="anonymizeIPenabled">
|
||||
<table class="adminTable" style='width:800px;'>
|
||||
<tr>
|
||||
<td width="250">{{ 'PrivacyManager_AnonymizeIpMaskLengtDescription'|translate }}</td>
|
||||
<td width="500">
|
||||
<input id="maskLength-1" type="radio" name="maskLength" value="1" {% if anonymizeIP.maskLength == '1' %}
|
||||
checked {% endif %}/>
|
||||
<label for="maskLength-1">{{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("1","192.168.100.xxx") }}</label><br/>
|
||||
<input id="maskLength-2" type="radio" name="maskLength" value="2" {% if anonymizeIP.maskLength == '2' %}
|
||||
checked {% endif %}/>
|
||||
<label for="maskLength-2">{{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("2","192.168.xxx.xxx") }} <span
|
||||
class="form-description">{{ 'General_Recommended'|translate }}</span></label><br/>
|
||||
<input id="maskLength-3" type="radio" name="maskLength" value="3" {% if anonymizeIP.maskLength == '3' %}
|
||||
checked {% endif %}/>
|
||||
<label for="maskLength-3">{{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("3","192.xxx.xxx.xxx") }}</label>
|
||||
</td>
|
||||
<td width="200">
|
||||
<div style="width:180px">
|
||||
{{ piwik.inlineHelp('PrivacyManager_GeolocationAnonymizeIpNote'|translate) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="250">
|
||||
{{ 'PrivacyManager_UseAnonymizedIpForVisitEnrichment'|translate }}
|
||||
</td>
|
||||
<td width='500'>
|
||||
<input id="useAnonymizedIpForVisitEnrichment-1" type="radio" name="useAnonymizedIpForVisitEnrichment" value="1" {% if anonymizeIP.useAnonymizedIpForVisitEnrichment == '1' %}checked {% endif %}/>
|
||||
<label for="useAnonymizedIpForVisitEnrichment-1">{{ 'General_Yes'|translate }}</label>
|
||||
<span class="form-description">
|
||||
{{ 'PrivacyManager_RecommendedForPrivacy'|translate }}
|
||||
</span>
|
||||
<br/>
|
||||
<input id="useAnonymizedIpForVisitEnrichment-2" type="radio" name="useAnonymizedIpForVisitEnrichment" value="0" {% if anonymizeIP.useAnonymizedIpForVisitEnrichment == '0' %} checked {% endif %}/>
|
||||
<label for="useAnonymizedIpForVisitEnrichment-2">{{ 'General_No'|translate }}</label>
|
||||
</td>
|
||||
<td width="200">
|
||||
<div style="width:180px">
|
||||
{{ piwik.inlineHelp('PrivacyManager_UseAnonymizedIpForVisitEnrichmentNote'|translate) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="nonce" value="{% if anonymizeIP.enabled %}{{ deactivateNonce }}{% else %}{{ activateNonce }}{% endif %}">
|
||||
|
||||
<input type="submit" value="{{ 'General_Save'|translate }}" id="privacySettingsSubmit" class="submit"/>
|
||||
</form>
|
||||
|
||||
{% if isDataPurgeSettingsEnabled %}
|
||||
<div class="ui-confirm" id="confirmDeleteSettings">
|
||||
<h2 id="deleteLogsConfirm">{{ 'PrivacyManager_DeleteLogsConfirm'|translate }}</h2>
|
||||
|
||||
<h2 id="deleteReportsConfirm">{{ 'PrivacyManager_DeleteReportsConfirm'|translate }}</h2>
|
||||
|
||||
<h2 id="deleteBothConfirm">{{ 'PrivacyManager_DeleteBothConfirm'|translate }}</h2>
|
||||
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
|
||||
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
|
||||
</div>
|
||||
<div class="ui-confirm" id="saveSettingsBeforePurge">
|
||||
<h2>{{ 'PrivacyManager_SaveSettingsBeforePurge'|translate }}</h2>
|
||||
<input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
|
||||
</div>
|
||||
<div class="ui-confirm" id="confirmPurgeNow">
|
||||
<h2>{{ 'PrivacyManager_PurgeNowConfirm'|translate }}</h2>
|
||||
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
|
||||
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
|
||||
</div>
|
||||
<h2 id="deleteLogsAnchor">{{ 'PrivacyManager_DeleteDataSettings'|translate }}</h2>
|
||||
<p>{{ 'PrivacyManager_DeleteDataDescription'|translate }} {{ 'PrivacyManager_DeleteDataDescription2'|translate }}</p>
|
||||
<form method="post" action="{{ {'action':'saveSettings','form':'formDeleteSettings','token_auth':token_auth} | urlRewriteWithParameters }}" id="formDeleteSettings">
|
||||
<table class="adminTable" style='width:800px;'>
|
||||
<tr id='deleteLogSettingEnabled'>
|
||||
<td width="250">{{ 'PrivacyManager_UseDeleteLog'|translate }}<br/>
|
||||
|
||||
</td>
|
||||
<td width='500'>
|
||||
<input id="deleteEnable-1" type="radio" name="deleteEnable" value="1" {% if deleteData.config.delete_logs_enable == '1' %}
|
||||
checked {% endif %}/>
|
||||
<label for="deleteEnable-1">{{ 'General_Yes'|translate }}</label>
|
||||
<input class="indented-radio-button" id="deleteEnable-2" type="radio" name="deleteEnable" value="0"
|
||||
{% if deleteData.config.delete_logs_enable == '0' %}
|
||||
checked {% endif %}/>
|
||||
<label for="deleteEnable-2">{{ 'General_No'|translate }}</label>
|
||||
<span id="privacyManagerDeleteLogDescription" style="margin-top: 10px;display:inline-block;">
|
||||
{% set deleteLogDescription %}
|
||||
{{ 'PrivacyManager_DeleteLogDescription2'|translate|raw }}
|
||||
<a href="http://piwik.org/faq/general/#faq_125" target="_blank">
|
||||
{{ 'General_ClickHere'|translate }}
|
||||
</a>
|
||||
{% endset %}
|
||||
{{ deleteLogDescription|notification({'raw': true, 'placeat': '#privacyManagerDeleteLogDescription', 'noclear': true, 'context': 'warning'}) }}
|
||||
</span>
|
||||
</td>
|
||||
<td width="200">
|
||||
{% set deleteLogInfo %}
|
||||
{{ 'PrivacyManager_DeleteLogInfo'|translate(deleteData.deleteTables)|raw }}
|
||||
{% if not canDeleteLogActions %}
|
||||
<br/>
|
||||
<br/>
|
||||
{{ 'PrivacyManager_CannotLockSoDeleteLogActions'|translate(dbUser) }}
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
{{ piwik.inlineHelp(deleteLogInfo) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="deleteLogSettings">
|
||||
<td width="250"> </td>
|
||||
<td width="500">
|
||||
<label>{{ 'PrivacyManager_DeleteLogsOlderThan'|translate }}
|
||||
<input type="text" id="deleteOlderThan" value="{{ deleteData.config.delete_logs_older_than }}" style="width:55px;"
|
||||
name="deleteOlderThan"/>
|
||||
{{ 'CoreHome_PeriodDays'|translate }}</label><br/>
|
||||
<span class="form-description">{{ 'PrivacyManager_LeastDaysInput'|translate("1") }}</span>
|
||||
</td>
|
||||
<td width="200">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='deleteReportsSettingEnabled'>
|
||||
<td width="250">{{ 'PrivacyManager_UseDeleteReports'|translate }}
|
||||
</td>
|
||||
<td width="500">
|
||||
<input id="deleteReportsEnable-1" type="radio" name="deleteReportsEnable" value="1" {% if deleteData.config.delete_reports_enable == '1' %}checked="true"{% endif %} />
|
||||
<label for="deleteReportsEnable-1">{{ 'General_Yes'|translate }}</label>
|
||||
<input class="indented-radio-button" id="deleteReportsEnable-2" type="radio" name="deleteReportsEnable" value="0" {% if deleteData.config.delete_reports_enable == '0' %}checked="true"{% endif %}/>
|
||||
<label for="deleteReportsEnable-2">{{ 'General_No'|translate }}</label>
|
||||
|
||||
{% set useDeleteLog %}
|
||||
{% set deleteOldLogs %}{{ 'PrivacyManager_UseDeleteLog'|translate }}{% endset %}
|
||||
{{ 'PrivacyManager_DeleteReportsInfo'|translate('<em>','</em>')|raw }}
|
||||
<span id='deleteOldReportsMoreInfo'><br/><br/>
|
||||
{{ 'PrivacyManager_DeleteReportsInfo2'|translate(deleteOldLogs) }}<br/><br/>
|
||||
{{ 'PrivacyManager_DeleteReportsInfo3'|translate(deleteOldLogs) }}</span>
|
||||
{% endset %}
|
||||
<span id="privacyManagerUseDeleteLog" style="margin-top: 10px;display:inline-block;">
|
||||
{{ useDeleteLog|notification({'raw': true, 'placeat': '#privacyManagerUseDeleteLog', 'noclear': true, 'context': 'warning'}) }}
|
||||
</span>
|
||||
</td>
|
||||
<td width="200">
|
||||
{{ piwik.inlineHelp('PrivacyManager_DeleteReportsDetailedInfo'|translate('archive_numeric_*','archive_blob_*')) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='deleteReportsSettings'>
|
||||
<td width="250"> </td>
|
||||
<td width="500">
|
||||
<label>{{ 'PrivacyManager_DeleteReportsOlderThan'|translate }}
|
||||
<input type="text" id="deleteReportsOlderThan" value="{{ deleteData.config.delete_reports_older_than }}" style="width:42px;"
|
||||
name="deleteReportsOlderThan"/>
|
||||
{{ 'CoreHome_PeriodMonths'|translate }}
|
||||
</label><br/>
|
||||
<span class="form-description">{{ 'PrivacyManager_LeastMonthsInput'|translate("3") }}</span><br/><br/>
|
||||
<input id="deleteReportsKeepBasic" type="checkbox" name="deleteReportsKeepBasic" value="1"
|
||||
{% if deleteData.config.delete_reports_keep_basic_metrics %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepBasic">{{ 'PrivacyManager_KeepBasicMetrics'|translate }}
|
||||
<span class="form-description">{{ 'General_Recommended'|translate }}</span>
|
||||
</label><br/><br/>
|
||||
{{ 'PrivacyManager_KeepDataFor'|translate }}<br/><br/>
|
||||
<input id="deleteReportsKeepDay" type="checkbox" name="deleteReportsKeepDay" value="1"
|
||||
{% if deleteData.config.delete_reports_keep_day_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepDay">{{ 'General_DailyReports'|translate }}</label><br/>
|
||||
<input type="checkbox" name="deleteReportsKeepWeek" value="1" id="deleteReportsKeepWeek"
|
||||
{% if deleteData.config.delete_reports_keep_week_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepWeek">{{ 'General_WeeklyReports'|translate }}</label><br/>
|
||||
<input type="checkbox" name="deleteReportsKeepMonth" value="1" id="deleteReportsKeepMonth"
|
||||
{% if deleteData.config.delete_reports_keep_month_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepMonth">{{ 'General_MonthlyReports'|translate }}<span
|
||||
class="form-description">{{ 'General_Recommended'|translate }}</span></label><br/>
|
||||
<input type="checkbox" name="deleteReportsKeepYear" value="1" id="deleteReportsKeepYear"
|
||||
{% if deleteData.config.delete_reports_keep_year_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepYear">{{ 'General_YearlyReports'|translate }}<span
|
||||
class="form-description">{{ 'General_Recommended'|translate }}</span></label><br/>
|
||||
<input type="checkbox" name="deleteReportsKeepRange" value="1" id="deleteReportsKeepRange"
|
||||
{% if deleteData.config.delete_reports_keep_range_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepRange">{{ 'General_RangeReports'|translate }}
|
||||
</label><br/><br/>
|
||||
<input type="checkbox" name="deleteReportsKeepSegments" value="1" id="deleteReportsKeepSegments"
|
||||
{% if deleteData.config.delete_reports_keep_segment_reports %}checked="true"{% endif %}>
|
||||
<label for="deleteReportsKeepSegments">{{ 'PrivacyManager_KeepReportSegments'|translate }}</label><br/>
|
||||
</td>
|
||||
<td width="200">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="deleteDataEstimateSect"
|
||||
{% if deleteData.config.delete_reports_enable == '0' and deleteData.config.delete_logs_enable == '0' %}style="display:none;"{% endif %}>
|
||||
<td width="250">{{ 'PrivacyManager_ReportsDataSavedEstimate'|translate }}<br/></td>
|
||||
<td width="500">
|
||||
<div id="deleteDataEstimate"></div>
|
||||
<span class="loadingPiwik" style="display:none;"><img
|
||||
src="./plugins/Zeitgeist/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}</span>
|
||||
</td>
|
||||
<td width="200">
|
||||
{% if deleteData.config.enable_auto_database_size_estimate == '0' %}
|
||||
{% set manualEstimate %}
|
||||
<em><a id="getPurgeEstimateLink" style="width:280px" class="ui-inline-help" href="#">{{ 'PrivacyManager_GetPurgeEstimate'|translate }}</a></em>
|
||||
{% endset %}
|
||||
{{ piwik.inlineHelp(manualEstimate) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="deleteSchedulingSettings">
|
||||
<td width="250">{{ 'PrivacyManager_DeleteSchedulingSettings'|translate }}<br/></td>
|
||||
<td width="500">
|
||||
<label>{{ 'PrivacyManager_DeleteDataInterval'|translate }}
|
||||
<select id="deleteLowestInterval" name="deleteLowestInterval">
|
||||
<option {% if deleteData.config.delete_logs_schedule_lowest_interval == '1' %} selected="selected" {% endif %}
|
||||
value="1"> {{ 'CoreHome_PeriodDay'|translate }}</option>
|
||||
<option {% if deleteData.config.delete_logs_schedule_lowest_interval == '7' %} selected="selected" {% endif %}
|
||||
value="7">{{ 'CoreHome_PeriodWeek'|translate }}</option>
|
||||
<option {% if deleteData.config.delete_logs_schedule_lowest_interval == '30' %} selected="selected" {% endif %}
|
||||
value="30">{{ 'CoreHome_PeriodMonth'|translate }}</option>
|
||||
</select></label><br/><br/>
|
||||
</td>
|
||||
<td width="200">
|
||||
{% set purgeStats %}
|
||||
{% if deleteData.lastRun %}<strong>{{ 'PrivacyManager_LastDelete'|translate }}:</strong>
|
||||
{{ deleteData.lastRunPretty }}
|
||||
<br/>
|
||||
<br/>
|
||||
{% endif %}
|
||||
<strong>{{ 'PrivacyManager_NextDelete'|translate }}:</strong>
|
||||
{{ deleteData.nextRunPretty|raw }}
|
||||
<br/>
|
||||
<br/>
|
||||
<em><a id="purgeDataNowLink" href="#">{{ 'PrivacyManager_PurgeNow'|translate }}</a></em>
|
||||
<span class="loadingPiwik" style="display:none;"><img
|
||||
src="./plugins/Zeitgeist/images/loading-blue.gif"/> {{ 'PrivacyManager_PurgingData'|translate }}</span>
|
||||
<span id="db-purged-message" style="display: none;"><em>{{ 'PrivacyManager_DBPurged'|translate }}</em></span>
|
||||
{% endset %}
|
||||
{{ piwik.inlineHelp(purgeStats) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="button" value="{{ 'General_Save'|translate }}" id="deleteLogSettingsSubmit" class="submit"/>
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h2 id="DNT">{{ 'PrivacyManager_DoNotTrack_SupportDNTPreference'|translate }}</h2>
|
||||
<table class="adminTable" style='width:800px;'>
|
||||
<tr>
|
||||
<td width="650">
|
||||
<p>
|
||||
{% if dntSupport %}
|
||||
{% set action='deactivateDoNotTrack' %}
|
||||
{% set nonce=deactivateNonce %}
|
||||
<strong>{{ 'PrivacyManager_DoNotTrack_Enabled'|translate }}</strong>
|
||||
<br/>
|
||||
{{ 'PrivacyManager_DoNotTrack_EnabledMoreInfo'|translate }}
|
||||
{% else %}
|
||||
{% set action='activateDoNotTrack' %}
|
||||
{% set nonce=activateNonce %}
|
||||
{{ 'PrivacyManager_DoNotTrack_Disabled'|translate }} {{ 'PrivacyManager_DoNotTrack_DisabledMoreInfo'|translate }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<span style="margin-left:20px;">
|
||||
<a href='{{ {'module':'PrivacyManager','nonce':nonce,'action':action} | urlRewriteWithParameters }}#DNT'>›
|
||||
{% if dntSupport %}{{ 'PrivacyManager_DoNotTrack_Disable'|translate }} {{ 'General_NotRecommended'|translate }}
|
||||
{% else %}{{ 'PrivacyManager_DoNotTrack_Enable'|translate }} {{ 'General_Recommended'|translate }}{% endif %}
|
||||
<br/>
|
||||
</a></span>
|
||||
</td>
|
||||
<td width="200">
|
||||
{{ piwik.inlineHelp('PrivacyManager_DoNotTrack_Description'|translate) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<h2 id="optOutAnchor">{{ 'CoreAdminHome_OptOutForYourVisitors'|translate }}</h2>
|
||||
<p>{{ 'CoreAdminHome_OptOutExplanation'|translate }}
|
||||
{% set optOutUrl %}{{ piwikUrl }}index.php?module=CoreAdminHome&action=optOut&language={{ language }}{% endset %}
|
||||
{% set iframeOptOut %}<iframe style="border: 0; height: 200px; width: 600px;" src="{{ optOutUrl }}"></iframe>{% endset %}
|
||||
<code>{{ iframeOptOut|e('html') }}</code>
|
||||
<br/>
|
||||
{{ 'CoreAdminHome_OptOutExplanationBis'|translate("<a href='" ~ optOutUrl ~ "' target='_blank'>","</a>")|raw }}
|
||||
</p>
|
||||
|
||||
<div style="height:100px;"></div>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue