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

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbenakamoorthi <benaka.moorthi@gmail.com>2012-04-07 06:30:49 +0400
committerbenakamoorthi <benaka.moorthi@gmail.com>2012-04-07 06:30:49 +0400
commitf4d248d5e1792e49a9eea42801a4ab120dc0663f (patch)
treee006abc7fa499ea4a13ec29637faf8ffeb36f849 /plugins
parent259028c93bdbe8bb19156bfe92d0c7dc5e47a336 (diff)
Fixes #53. Augmented the log data deletion feature. Added the ability to purge old reports and metrics.
Other changes: * Added the following plugin functions: Piwik_DropTables, Piwik_OptimizeTables, Piwik_DeleteAllRows. Also refactored existing code to use them. * Modified graph, tag cloud & datatable templates/views to show a different message if there's no data for a report and if its possible that report was purged. * Refactored DbStats API, added getAllTablesStatus method that doesn't modify the SHOW TABLE STATUS result. * Deletelogs config options are now stored in the DB. * Added task priority support to the TaskScheduler. git-svn-id: http://dev.piwik.org/svn/trunk@6174 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'plugins')
-rw-r--r--plugins/CoreAdminHome/CoreAdminHome.php9
-rw-r--r--plugins/CoreAdminHome/templates/generalSettings.tpl6
-rw-r--r--plugins/CoreHome/templates/cloud.tpl4
-rw-r--r--plugins/CoreHome/templates/datatable.tpl6
-rw-r--r--plugins/CoreHome/templates/graph.tpl6
-rw-r--r--plugins/DBStats/API.php35
-rw-r--r--plugins/DBStats/Controller.php2
-rw-r--r--plugins/DBStats/templates/DBStats.tpl4
-rw-r--r--plugins/Dashboard/Dashboard.php3
-rw-r--r--plugins/Goals/API.php2
-rw-r--r--plugins/LanguagesManager/LanguagesManager.php3
-rw-r--r--plugins/PrivacyManager/Controller.php174
-rwxr-xr-xplugins/PrivacyManager/LogDataPurger.php151
-rw-r--r--plugins/PrivacyManager/PrivacyManager.php443
-rwxr-xr-xplugins/PrivacyManager/ReportsPurger.php309
-rwxr-xr-xplugins/PrivacyManager/templates/databaseSize.tpl4
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.js141
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.tpl211
-rwxr-xr-xplugins/PrivacyManager/tests/PrivacyManager.test.php601
19 files changed, 1877 insertions, 237 deletions
diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php
index acdcf681ca..688d745fdd 100644
--- a/plugins/CoreAdminHome/CoreAdminHome.php
+++ b/plugins/CoreAdminHome/CoreAdminHome.php
@@ -39,9 +39,13 @@ class Piwik_CoreAdminHome extends Piwik_Plugin
function getScheduledTasks ( $notification )
{
$tasks = &$notification->getNotificationObject();
+
+ // low priority since tables should be optimized after they are modified
+ $priority = Piwik_ScheduledTask::LOW_PRIORITY;
$optimizeArchiveTableTask = new Piwik_ScheduledTask ( $this,
'optimizeArchiveTable',
- new Piwik_ScheduledTime_Daily() );
+ new Piwik_ScheduledTime_Daily(),
+ $priority );
$tasks[] = $optimizeArchiveTableTask;
}
@@ -84,8 +88,7 @@ class Piwik_CoreAdminHome extends Piwik_Plugin
{
return;
}
- $query = "OPTIMIZE TABLE " . implode(",", $archiveTables);
- Piwik_Query( $query );
+ Piwik_OptimizeTables($archiveTables);
}
private function isArchiveTable ( $tableName )
diff --git a/plugins/CoreAdminHome/templates/generalSettings.tpl b/plugins/CoreAdminHome/templates/generalSettings.tpl
index 20d54ae28f..e11c6f2669 100644
--- a/plugins/CoreAdminHome/templates/generalSettings.tpl
+++ b/plugins/CoreAdminHome/templates/generalSettings.tpl
@@ -162,10 +162,10 @@
<input type="submit" value="{'General_Save'|translate}" id="generalSettingsSubmit" class="submit" />
<br /><br />
-{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteLogSettings'|translate}{/capture}
-<h2>{'PrivacyManager_DeleteLogSettings'|translate}</h2>
+{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
+<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
<p>
- {'PrivacyManager_DeleteLogDescription'|translate}
+ {'PrivacyManager_DeleteDataDescription'|translate}
<br/>
<a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
{'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
diff --git a/plugins/CoreHome/templates/cloud.tpl b/plugins/CoreHome/templates/cloud.tpl
index dc9d630183..f6be9beaf5 100644
--- a/plugins/CoreHome/templates/cloud.tpl
+++ b/plugins/CoreHome/templates/cloud.tpl
@@ -4,7 +4,11 @@
{/if}
<div class="tagCloud">
{if count($cloudValues) == 0}
+ {if $showReportDataWasPurgedMessage}
+ <div class="pk-emptyDataTable">{'General_DataForThisTagCloudHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
+ {else}
<div class="pk-emptyDataTable">{'General_NoDataForTagCloud'|translate}</div>
+ {/if}
{else}
{foreach from=$cloudValues key=word item=value}
<span title="{$value.word} ({$value.value} {$columnTranslation})" class="word size{$value.size} {* we strike tags with 0 hits *} {if $value.value == 0}valueIsZero{/if}">
diff --git a/plugins/CoreHome/templates/datatable.tpl b/plugins/CoreHome/templates/datatable.tpl
index 0a7a20be61..235e8bcc30 100644
--- a/plugins/CoreHome/templates/datatable.tpl
+++ b/plugins/CoreHome/templates/datatable.tpl
@@ -7,7 +7,11 @@
{$arrayDataTable.message}
{else}
{if count($arrayDataTable) == 0}
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ {if $showReportDataWasPurgedMessage}
+ <div class="pk-emptyDataTable">{'CoreHome_DataForThisReportHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
+ {else}
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ {/if}
{else}
<a name="{$properties.uniqueId}"></a>
<table cellspacing="0" class="dataTable">
diff --git a/plugins/CoreHome/templates/graph.tpl b/plugins/CoreHome/templates/graph.tpl
index 2f3dc6156f..903a3e4916 100644
--- a/plugins/CoreHome/templates/graph.tpl
+++ b/plugins/CoreHome/templates/graph.tpl
@@ -33,7 +33,11 @@
{else}
<div><div id="{$chartDivId}" class="pk-emptyGraph">
+ {if $showReportDataWasPurgedMessage}
+ {'General_DataForThisGraphHasBeenPurged'|translate:$deleteReportsOlderThan}
+ {else}
{'General_NoDataForGraph'|translate}
+ {/if}
</div></div>
{/if}
@@ -44,4 +48,4 @@
{/if}
</div>
-</div> \ No newline at end of file
+</div>
diff --git a/plugins/DBStats/API.php b/plugins/DBStats/API.php
index 7ba5ab49ed..eb35cf86d5 100644
--- a/plugins/DBStats/API.php
+++ b/plugins/DBStats/API.php
@@ -82,18 +82,38 @@ class Piwik_DBStats_API
return $tables[0][$field];
}
}
-
- public function getAllTablesStatus()
+
+ /**
+ * Gets the result of a SHOW TABLE STATUS query for every table in the DB.
+ *
+ * @return array The table information.
+ */
+ public function getAllTablesStatus()
{
Piwik::checkUserIsSuperUser();
- $db = Zend_Registry::get('db');
+ $tablesPiwik = Piwik::getTablesInstalled();
+
+ $result = array();
+ foreach(Piwik_FetchAll("SHOW TABLE STATUS") as $t)
+ {
+ if (in_array($t['Name'], $tablesPiwik))
+ {
+ $result[] = $t;
+ }
+ }
+
+ return $result;
+ }
+
+ public function getAllTablesStatusPretty()
+ {
+ Piwik::checkUserIsSuperUser();
+
// http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html
- $tablesPiwik = Piwik::getTablesInstalled();
$total = array('Name' => 'Total', 'Data_length' => 0, 'Index_length' => 0, 'Rows' => 0);
- $table = array();
- foreach($tablesPiwik as $tableName)
+ $table = $this->getAllTablesStatus();
+ foreach($table as &$t)
{
- $t = $this->getTableStatus($tableName);
$total['Data_length'] += $t['Data_length'];
$total['Index_length'] += $t['Index_length'];
$total['Rows'] += $t['Rows'];
@@ -102,7 +122,6 @@ class Piwik_DBStats_API
$t['Data_length'] = Piwik::getPrettySizeFromBytes($t['Data_length']);
$t['Index_length'] = Piwik::getPrettySizeFromBytes($t['Index_length']);
$t['Rows'] = Piwik::getPrettySizeFromBytes($t['Rows']);
- $table[] = $t;
}
$total['Total_length'] = Piwik::getPrettySizeFromBytes($total['Data_length']+$total['Index_length']);
$total['Data_length'] = Piwik::getPrettySizeFromBytes($total['Data_length']);
diff --git a/plugins/DBStats/Controller.php b/plugins/DBStats/Controller.php
index 8feeb6e6a5..91444f91ff 100644
--- a/plugins/DBStats/Controller.php
+++ b/plugins/DBStats/Controller.php
@@ -20,7 +20,7 @@ class Piwik_DBStats_Controller extends Piwik_Controller_Admin
{
Piwik::checkUserIsSuperUser();
$view = Piwik_View::factory('DBStats');
- $view->tablesStatus = Piwik_DBStats_API::getInstance()->getAllTablesStatus();
+ $view->tablesStatus = Piwik_DBStats_API::getInstance()->getAllTablesStatusPretty();
$this->setBasicVariablesView($view);
$view->menu = Piwik_GetAdminMenu();
echo $view->render();
diff --git a/plugins/DBStats/templates/DBStats.tpl b/plugins/DBStats/templates/DBStats.tpl
index 4064e90729..686e3d1fca 100644
--- a/plugins/DBStats/templates/DBStats.tpl
+++ b/plugins/DBStats/templates/DBStats.tpl
@@ -9,8 +9,8 @@
<br />
{'DBStats_LearnMore'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>Piwik Auto Archiving</a>"}
<br />
-{'PrivacyManager_DeleteLogSettings'|translate}: <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
-{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteLogSettings'|translate}{/capture}
+{'PrivacyManager_DeleteDataSettings'|translate}: <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
+{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
{'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
</a>
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index 1da9ea7625..cbd781b017 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -140,8 +140,7 @@ class Piwik_Dashboard extends Piwik_Plugin
public function uninstall()
{
- $sql = "DROP TABLE ". Piwik_Common::prefixTable('user_dashboard') ;
- Piwik_Exec($sql);
+ Piwik_DropTables(Piwik_Common::prefixTable('user_dashboard'));
}
}
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index af912069c0..bc3a92c1f3 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -191,7 +191,7 @@ class Piwik_Goals_API
WHERE idsite = ?
AND idgoal = ?",
array($idSite, $idGoal));
- Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable("log_conversion")." WHERE idgoal = ?", $idGoal);
+ Piwik_DeleteAllRows(Piwik_Common::prefixTable("log_conversion"), "WHERE idgoal = ?", 100000, array($idGoal));
Piwik_Common::regenerateCacheWebsiteAttributes($idSite);
}
diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php
index febbd562f7..d1345cd59a 100644
--- a/plugins/LanguagesManager/LanguagesManager.php
+++ b/plugins/LanguagesManager/LanguagesManager.php
@@ -114,8 +114,7 @@ class Piwik_LanguagesManager extends Piwik_Plugin
*/
public function uninstall()
{
- $sql = "DROP TABLE ". Piwik_Common::prefixTable('user_language') ;
- Piwik_Exec($sql);
+ Piwik_DropTables(Piwik_Common::prefixTable('user_language'));
}
/**
diff --git a/plugins/PrivacyManager/Controller.php b/plugins/PrivacyManager/Controller.php
index f48ea48b9f..08eeaee806 100644
--- a/plugins/PrivacyManager/Controller.php
+++ b/plugins/PrivacyManager/Controller.php
@@ -36,15 +36,8 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
break;
case("formDeleteSettings"):
- $deleteLogs = Piwik_Config::getInstance()->Deletelogs;
- $deleteLogs['delete_logs_enable'] = Piwik_Common::getRequestVar("deleteEnable", 0);
- $deleteLogs['delete_logs_schedule_lowest_interval'] = Piwik_Common::getRequestVar("deleteLowestInterval", 7);
- $deleteLogs['delete_logs_older_than'] = ((int)Piwik_Common::getRequestVar("deleteOlderThan", 180) < 7) ?
- 7 : Piwik_Common::getRequestVar("deleteOlderThan", 180);
- $deleteLogs['delete_max_rows_per_run'] = Piwik_Common::getRequestVar("deleteMaxRows", 100);
-
- Piwik_Config::getInstance()->Deletelogs = $deleteLogs;
- Piwik_Config::getInstance()->forceSave();
+ $settings = $this->getPurgeSettingsFromRequest();
+ Piwik_PrivacyManager::savePurgeDataSettings($settings);
break;
default: //do nothing
@@ -54,6 +47,50 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
return $this->redirectToIndex('PrivacyManager', 'privacySettings', null, null, null, array('updated' => 1));
}
+
+ /**
+ * 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'] = Piwik_Common::getRequestVar("deleteEnable", 0);
+ $settings['delete_logs_schedule_lowest_interval'] = Piwik_Common::getRequestVar("deleteLowestInterval", 7);
+ $settings['delete_logs_older_than'] = ((int)Piwik_Common::getRequestVar("deleteOlderThan", 180) < 7) ?
+ 7 : Piwik_Common::getRequestVar("deleteOlderThan", 180);
+
+ // delete reports settings
+ $settings['delete_reports_enable'] = Piwik_Common::getRequestVar("deleteReportsEnable", 0);
+ $deleteReportsOlderThan = Piwik_Common::getRequestVar("deleteReportsOlderThan", 3);
+ $settings['delete_reports_older_than'] = $deleteReportsOlderThan < 3 ? 3 : $deleteReportsOlderThan;
+ $settings['delete_reports_keep_basic_metrics'] = Piwik_Common::getRequestVar("deleteReportsKeepBasic", 0);
+ $settings['delete_reports_keep_day_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepDay", 0);
+ $settings['delete_reports_keep_week_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepWeek", 0);
+ $settings['delete_reports_keep_month_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepMonth", 0);
+ $settings['delete_reports_keep_year_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepYear", 0);
+
+ 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::checkUserIsSuperUser();
+ $view = Piwik_View::factory('databaseSize');
+
+ $view->dbStats = $this->getDeleteDBSizeEstimate(true);
+ $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+
+ echo $view->render();
+ }
public function privacySettings()
{
@@ -64,7 +101,8 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
{
$deleteLogs = array();
- $view->deleteLogs = $this->getDeleteLogsInfo();
+ $view->deleteData = $this->getDeleteDataInfo();
+ $view->deleteDbStats = $this->getDeleteDBSizeEstimate();
$view->anonymizeIP = $this->getAnonymizeIPInfo();
}
$view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
@@ -79,6 +117,94 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
echo $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::checkUserIsSuperUser();
+
+ // if the request isn't a POST, redirect to index
+ if ($_SERVER["REQUEST_METHOD"] != "POST")
+ {
+ return $this->redirectToIndex('PrivacyManager', 'privacySettings');
+ }
+
+ $settings = Piwik_PrivacyManager::getPurgeDataSettings();
+
+ // execute the purge
+ if ($settings['delete_logs_enable'])
+ {
+ $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
+ $logDataPurger->purgeData();
+ }
+
+ if ($settings['delete_reports_enable'])
+ {
+ $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make(
+ $settings, Piwik_PrivacyManager::getMetricsToPurge());
+ $reportsPurger->purgeData();
+ }
+
+ // re-calculate db size estimate
+ $this->getDatabaseSize();
+ }
+
+ protected function getDeleteDBSizeEstimate( $getSettingsFromQuery = false )
+ {
+ // get the purging settings & create two purger instances
+ if ($getSettingsFromQuery)
+ {
+ $settings = $this->getPurgeSettingsFromRequest();
+ }
+ else
+ {
+ $settings = Piwik_PrivacyManager::getPurgeDataSettings();
+ }
+
+ // 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 = Piwik_PrivacyManager::getPurgeEstimate($settings);
+
+ // determine the DB size & purged DB size
+ $tableStatuses = Piwik_DBStats_API::getInstance()->getAllTablesStatus();
+
+ $totalBytes = 0;
+ foreach ($tableStatuses as $status)
+ {
+ $totalBytes += $status['Data_length'] + $status['Index_length'];
+ }
+
+ $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] === Piwik_PrivacyManager_ReportsPurger::DROP_TABLE)
+ {
+ $totalAfterPurge -= $tableTotalBytes;
+ }
+ else // if just deleting rows
+ {
+ $totalAfterPurge -= ($tableTotalBytes / $status['Rows']) * $deletedDataSummary[$tableName];
+ }
+ }
+ }
+
+ $result = array(
+ 'currentSize' => Piwik::getPrettySizeFromBytes($totalBytes),
+ 'sizeAfterPurge' => Piwik::getPrettySizeFromBytes($totalAfterPurge),
+ 'spaceSaved' => Piwik::getPrettySizeFromBytes($totalBytes - $totalAfterPurge)
+ );
+
+ return $result;
+ }
protected function getAnonymizeIPInfo()
{
@@ -95,14 +221,14 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
return $anonymizeIP;
}
- protected function getDeleteLogsInfo()
+ protected function getDeleteDataInfo()
{
Piwik::checkUserIsSuperUser();
- $deleteLogsInfos = array();
+ $deleteDataInfos = array();
$taskScheduler = new Piwik_TaskScheduler();
- $deleteLogsInfos["config"] = Piwik_Config::getInstance()->Deletelogs;
- $privacyManager = new Piwik_PrivacyManager();
- $deleteLogsInfos["deleteTables"] = implode(", ", $privacyManager->getDeleteTableLogTables());
+ $deleteDataInfos["config"] = Piwik_PrivacyManager::getPurgeDataSettings();
+ $deleteDataInfos["deleteTables"] =
+ implode(", ", Piwik_PrivacyManager_LogDataPurger::getDeleteTableLogTables());
$scheduleTimetable = $taskScheduler->getScheduledTimeForTask("Piwik_PrivacyManager", "deleteLogTables");
@@ -118,29 +244,29 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
//deletion schedule did not run before
if (empty($optionTable)) {
- $deleteLogsInfos["lastRun"] = false;
+ $deleteDataInfos["lastRun"] = false;
//next run ASAP (with next schedule run)
$date = Piwik_Date::factory("today");
- $deleteLogsInfos["nextScheduleTime"] = $nextPossibleSchedule;
+ $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
} else {
- $deleteLogsInfos["lastRun"] = $optionTable;
- $deleteLogsInfos["lastRunPretty"] = Piwik_Date::factory((int)$optionTable)->getLocalized('%day% %shortMonth% %longYear%');
+ $deleteDataInfos["lastRun"] = $optionTable;
+ $deleteDataInfos["lastRunPretty"] = Piwik_Date::factory((int)$optionTable)->getLocalized('%day% %shortMonth% %longYear%');
//Calculate next run based on last run + interval
- $nextScheduleRun = (int)($deleteLogsInfos["lastRun"] + $deleteLogsInfos["config"]["delete_logs_schedule_lowest_interval"] * 24 * 60 * 60);
+ $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) {
- $deleteLogsInfos["nextScheduleTime"] = $nextPossibleSchedule;
+ $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
} else {
- $deleteLogsInfos["nextScheduleTime"] = $nextScheduleRun;
+ $deleteDataInfos["nextScheduleTime"] = $nextScheduleRun;
}
}
- $deleteLogsInfos["nextRunPretty"] = Piwik::getPrettyTimeFromSeconds($deleteLogsInfos["nextScheduleTime"] - time());
+ $deleteDataInfos["nextRunPretty"] = Piwik::getPrettyTimeFromSeconds($deleteDataInfos["nextScheduleTime"] - time());
- return $deleteLogsInfos;
+ return $deleteDataInfos;
}
protected function handlePluginState($state = 0)
diff --git a/plugins/PrivacyManager/LogDataPurger.php b/plugins/PrivacyManager/LogDataPurger.php
new file mode 100755
index 0000000000..2b8ad3dc0d
--- /dev/null
+++ b/plugins/PrivacyManager/LogDataPurger.php
@@ -0,0 +1,151 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ * @version $Id: $
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_PrivacyManager
+ */
+
+/**
+ * Purges the log_visit, log_conversion and related tables of old visit data.
+ */
+class Piwik_PrivacyManager_LogDataPurger
+{
+ /**
+ * 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.
+ */
+ 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
+ */
+ 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)
+ {
+ Piwik_DeleteAllRows($logTable, $where, $this->maxRowsToDeletePerQuery, array($maxIdVisit));
+ }
+
+ // optimize table overhead after deletion
+ Piwik_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)
+ {
+ $rowCount = $this->getLogTableDeleteCount($table, $maxIdVisit);
+ if ($rowCount > 0)
+ {
+ $result[$table] = $rowCount;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * get highest idVisit to delete rows from
+ */
+ private function getDeleteIdVisitOffset()
+ {
+ $dateStart = Piwik_Date::factory("today")->subDay($this->deleteLogsOlderThan);
+
+ $sql = "SELECT idvisit
+ FROM ".Piwik_Common::prefixTable("log_visit")."
+ WHERE '".$dateStart->toString('Y-m-d H:i:s')."' > visit_last_action_time AND idvisit > 0
+ ORDER BY idvisit DESC
+ LIMIT 1";
+
+ return Piwik_FetchOne($sql);
+ }
+
+ private function getLogTableDeleteCount( $table, $maxIdVisit )
+ {
+ $sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?";
+ return (int)Piwik_FetchOne($sql, array($maxIdVisit));
+ }
+
+ // let's hardcode, since these are no dynamically created tables
+ // exclude piwik_log_action since it is a lookup table
+ public static function getDeleteTableLogTables()
+ {
+ return array(Piwik_Common::prefixTable("log_conversion"),
+ Piwik_Common::prefixTable("log_link_visit_action"),
+ Piwik_Common::prefixTable("log_visit"),
+ Piwik_Common::prefixTable("log_conversion_item"));
+ }
+
+ /**
+ * 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 $settings Array of settings
+ */
+ public static function make( $settings )
+ {
+ return new Piwik_PrivacyManager_LogDataPurger(
+ $settings['delete_logs_older_than'],
+ $settings['delete_logs_max_rows_per_query']
+ );
+ }
+}
+
diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php
index e008922f14..2706adb5f8 100644
--- a/plugins/PrivacyManager/PrivacyManager.php
+++ b/plugins/PrivacyManager/PrivacyManager.php
@@ -11,15 +11,40 @@
*/
/**
+ * @see plugins/PrivacyManager/LogDataPurger.php
+ */
+require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/LogDataPurger.php';
+
+/**
+ * @see plugins/PrivacyManager/ReportsPurger.php
+ */
+require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/ReportsPurger.php';
+
+/**
*
* @package Piwik_PrivacyManager
*/
class Piwik_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";
- const DELETE_MAX_ROWS_MULTIPLICATOR = 1000;
-
+
+ // default config options for data purging feature
+ public static $defaultPurgeDataOptions = array(
+ 'delete_logs_enable' => 0,
+ 'delete_logs_schedule_lowest_interval' => 7,
+ 'delete_logs_older_than' => 180,
+ 'delete_logs_max_rows_per_query' => 100000,
+ 'delete_reports_enable' => 0,
+ 'delete_reports_older_than' => 12,
+ 'delete_reports_keep_basic_metrics' => 1,
+ 'delete_reports_keep_day_reports' => 0,
+ 'delete_reports_keep_week_reports' => 0,
+ 'delete_reports_keep_month_reports' => 1,
+ 'delete_reports_keep_year_reports' => 1
+ );
+
public function getInformation()
{
return array(
@@ -39,14 +64,16 @@ class Piwik_PrivacyManager extends Piwik_Plugin
);
}
- function getScheduledTasks($notification)
- {
- $tasks = &$notification->getNotificationObject();
- $deleteLogTablesTask = new Piwik_ScheduledTask ($this,
- 'deleteLogTables',
- new Piwik_ScheduledTime_Daily());
- $tasks[] = $deleteLogTablesTask;
- }
+ function getScheduledTasks($notification)
+ {
+ $tasks = &$notification->getNotificationObject();
+
+ $purgeLogDataTask = new Piwik_ScheduledTask($this, 'deleteLogData', new Piwik_ScheduledTime_Daily());
+ $tasks[] = $purgeLogDataTask;
+
+ $purgeReportDataTask = new Piwik_ScheduledTask($this, 'deleteReportData', new Piwik_ScheduledTime_Daily());
+ $tasks[] = $purgeReportDataTask;
+ }
function getJsFiles($notification)
{
@@ -62,109 +89,329 @@ class Piwik_PrivacyManager extends Piwik_Plugin
Piwik::isUserHasSomeAdminAccess(),
$order = 8);
}
-
- /*
- * @ToDo: return number of Rows deleted in last run; Display age of "oldest" row to help the user setting the day offset;
+
+ /**
+ * Returns the settings for the data purging feature.
+ *
+ * @return array
+ */
+ public static function getPurgeDataSettings()
+ {
+ $settings = array();
+
+ // backwards compatibility: load old values in ini config if present
+ try
+ {
+ $oldSettings = array(
+ 'delete_logs_enable',
+ 'delete_logs_schedule_lowest_interval',
+ 'delete_logs_older_than'
+ );
+
+ $deleteLogsSettings = Piwik_Config::getInstance()->Deletelogs;
+ foreach ($oldSettings as $settingName)
+ {
+ $settings[$settingName] = $deleteLogsSettings[$settingName];
+ }
+ }
+ catch (Exception $e)
+ {
+ // ignore
+ }
+
+ // load the settings for the data purging settings
+ foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue)
+ {
+ $value = Piwik_GetOption($optionName);
+ if ($value !== false)
+ {
+ $settings[$optionName] = $value;
+ }
+ else
+ {
+ // if the option hasn't been set/created, use the default value
+ if (!isset($settings[$optionName]))
+ {
+ $settings[$optionName] = $defaultValue;
+ }
+
+ // option is not saved in the DB, so save it now
+ Piwik_SetOption($optionName, $settings[$optionName]);
+ }
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Saves the supplied data purging settings.
+ *
+ * @param array $settings The settings to save.
+ */
+ public static function savePurgeDataSettings( $settings )
+ {
+ $plugin = Piwik_PluginsManager::getInstance()->getLoadedPlugin('PrivacyManager');
+
+ foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue)
+ {
+ if (isset($settings[$optionName]))
+ {
+ Piwik_SetOption($optionName, $settings[$optionName]);
+ }
+ }
+ }
+
+ /**
+ * 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.
*/
- function deleteLogTables()
- {
- $deleteSettings = Piwik_Config::getInstance()->Deletelogs;
-
- //Make sure, log deletion is enabled
- if ($deleteSettings['delete_logs_enable'] == 0) {
+ public function deleteReportData()
+ {
+ $settings = self::getPurgeDataSettings();
+
+ // Make sure, data deletion is enabled
+ if ($settings['delete_reports_enable'] == 0)
+ {
return;
}
-
- //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 = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL);
- if (empty($initialDelete)) {
- Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
+
+ // 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;
+ }
+
+ // set last run time
+ Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_REPORTS, Piwik_Date::factory('today')->getTimestamp());
+
+ Piwik_PrivacyManager_ReportsPurger::make($settings, self::getMetricsToPurge())->purgeData();
+ }
+
+ /**
+ * 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;
}
-
- //Make sure, log purging is allowed to run now
- $lastDelete = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS);
- $deleteIntervalSeconds = $this->getDeleteIntervalInSeconds($deleteSettings['delete_logs_schedule_lowest_interval']);
-
- if ($lastDelete === false ||
- ($lastDelete !== false && ((int)$lastDelete + $deleteIntervalSeconds) <= time())
- ) {
-
- $maxIdVisit = $this->getDeleteIdVisitOffset($deleteSettings['delete_logs_older_than']);
-
- $logTables = $this->getDeleteTableLogTables();
-
- //set lastDelete time to today
- $date = Piwik_Date::factory("today");
- $lastDeleteDate = $date->getTimestamp();
-
- /*
- * 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.
- */
- Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDeleteDate);
-
- //Break if no ID was found (nothing to delete for given period)
- if (empty($maxIdVisit)) {
- return;
- }
-
- foreach ($logTables as $logTable) {
- $this->deleteRowsFromTable($logTable, $maxIdVisit, $deleteSettings['delete_max_rows_per_run'] * self::DELETE_MAX_ROWS_MULTIPLICATOR);
- }
-
- //optimize table overhead after deletion
- $query = "OPTIMIZE TABLE " . implode(",", $logTables);
- Piwik_Query($query);
+
+ // make sure purging should run at this time
+ if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_LOGS))
+ {
+ return;
}
+
+ /*
+ * 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 = Piwik_Date::factory("today")->getTimestamp();
+ Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDeleteDate);
+
+ // execute the purge
+ Piwik_PrivacyManager_LogDataPurger::make($settings)->purgeData();
}
-
- function getDeleteIntervalInSeconds($deleteInterval)
- {
- return (int)$deleteInterval * 24 * 60 * 60;
- }
-
- /*
- * get highest idVisit to delete rows from
+
+ /**
+ * 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
*/
- function getDeleteIdVisitOffset($deleteLogsOlderThan)
+ public static function getPurgeEstimate( $settings = null )
{
- $date = Piwik_Date::factory("today");
- $dateSubX = $date->subDay($deleteLogsOlderThan);
-
- $sql = "SELECT `idvisit` FROM " . Piwik_Common::prefixTable("log_visit")
- . " WHERE '" . $dateSubX->toString('Y-m-d H:i:s') . "' "
- . "> `visit_last_action_time` AND `idvisit` > 0 ORDER BY `idvisit` DESC LIMIT 1";
-
- $maxIdVisit = Piwik_FetchOne($sql);
-
- return $maxIdVisit;
+ if (is_null($settings))
+ {
+ $settings = self::getPurgeDataSettings();
+ }
+
+ $result = array();
+
+ if ($settings['delete_logs_enable'])
+ {
+ $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
+ $result = array_merge($result, $logDataPurger->getPurgeEstimate());
+ }
+
+ if ($settings['delete_reports_enable'])
+ {
+ $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make($settings, self::getMetricsToPurge());
+ $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|Piwik_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.
+ */
+ 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 Piwik_Date))
+ {
+ $reportsOlderThan = Piwik_Date::factory('today')->subMonth(1 + $reportsOlderThan);
+ }
+
+ return Piwik_PrivacyManager_ReportsPurger::shouldReportBePurged(
+ $reportDateYear, $reportDateMonth, $reportsOlderThan);
+ }
+
+ /**
+ * Returns the general metrics to purge when 'delete_reports_keep_basic_metrics' is set to 1.
+ * Right now, this is set to nothing, so the 'keep_basic_metrics' option will keep everything.
+ */
+ 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 purge when 'delete_reports_keep_basic_metrics' is set to 1.
+ * Right now, this is set to nothing, so the 'keep_basic_metrics' option will keep everything.
+ */
+ private static function getGoalMetricsToKeep()
+ {
+ // keep all goal metrics
+ return array_values(Piwik_Archive::$mappingFromIdToNameGoal);
+ }
+
+ /**
+ * Returns the metrics that should be purged based on the metrics that should be kept.
+ */
+ public static function getMetricsToPurge()
+ {
+ $metricsToKeep = self::getMetricsToKeep();
+
+ // the metrics to purge == all_metrics - metrics_to_keep
+ $metricsToPurge = array_diff(array_values(Piwik_Archive::$mappingFromIdToName), $metricsToKeep);
+
+ // convert goal metric names to correct archive names
+ if (Piwik_Common::isGoalPluginEnabled())
+ {
+ $goalMetricsToPurge
+ = array_diff(array_values(Piwik_Archive::$mappingFromIdToNameGoal), 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 ($goalMetricsToPurge as $metric)
+ {
+ for ($i = 1; $i != $maxGoalId; ++$i)
+ {
+ $metricsToPurge[] = Piwik_Goals::getRecordName($metric, $i);
+ }
+
+ $metricsToPurge[] = Piwik_Goals::getRecordName($metric);
+ $metricsToPurge[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
+ $metricsToPurge[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_CART);
+ }
+ }
+
+ return $metricsToPurge;
+ }
- function deleteRowsFromTable($table, $maxIdVisit, $maxRowsPerRun)
- {
- /*
- * @ToDo: check if DELETE ... tbl_name[.*] [, tbl_name[.*]] ... Statement performance is better (but LIMIT can't be used!). So for now, this is safer.
- * LOW_PRIORITY / QUICK / IGNORE read http://dev.mysql.com/doc/refman/5.0/en/delete.html
- */
-
- $sql = 'DELETE LOW_PRIORITY QUICK IGNORE FROM ' . $table . ' WHERE `idvisit` <= ? ';
-
- if(isset($maxRowsPerRun) && $maxRowsPerRun > 0) {
- $sql .= ' LIMIT ' . (int)$maxRowsPerRun;
+ /**
+ * 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 = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL);
+ if (empty($initialDelete))
+ {
+ Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
+ return false;
}
+
+ // Make sure, log purging is allowed to run now
+ $lastDelete = Piwik_GetOption($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;
+ }
+ }
- Piwik_Query($sql, array($maxIdVisit));
+ function getDeleteIntervalInSeconds($deleteInterval)
+ {
+ return (int)$deleteInterval * 24 * 60 * 60;
}
- //let's hardcode, since these are no dynamically created tables
- //exclude piwik_log_action since it is a lookup table
- function getDeleteTableLogTables()
+ private static function getMaxGoalId()
{
- return array(Piwik_Common::prefixTable("log_conversion"),
- Piwik_Common::prefixTable("log_link_visit_action"),
- Piwik_Common::prefixTable("log_visit"),
- Piwik_Common::prefixTable("log_conversion_item"));
+ return Piwik_FetchOne("SELECT MAX(idgoal) FROM ".Piwik_Common::prefixTable('goal'));
}
}
+
diff --git a/plugins/PrivacyManager/ReportsPurger.php b/plugins/PrivacyManager/ReportsPurger.php
new file mode 100755
index 0000000000..8e8d15e04a
--- /dev/null
+++ b/plugins/PrivacyManager/ReportsPurger.php
@@ -0,0 +1,309 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ * @version $Id: $
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_PrivacyManager
+ */
+
+/**
+ * Purges archived reports and metrics that are considered old.
+ */
+class Piwik_PrivacyManager_ReportsPurger
+{
+ // constant used in database purging estimate to signify a table should be dropped
+ const DROP_TABLE = -1;
+
+ /**
+ * 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;
+
+ /**
+ * The maximum number of rows to delete per DELETE query.
+ */
+ private $maxRowsToDeletePerQuery;
+
+ /**
+ * List of metrics that should be purged. If $keepBasicMetrics is true, only these
+ * metrics will be deleted.
+ */
+ private $metricsToPurge;
+
+ /**
+ * 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 array $metricsToPurge List of metrics that should be purged. if $keepBasicMetrics
+ * is true, only these metrics will be deleted.
+ * @param int $maxRowsToDeletePerQuery The maximum number of rows to delete per DELETE query.
+ */
+ public function __construct( $deleteReportsOlderThan, $keepBasicMetrics, $reportPeriodsToKeep,
+ $metricsToPurge, $maxRowsToDeletePerQuery )
+ {
+ $this->deleteReportsOlderThan = $deleteReportsOlderThan;
+ $this->keepBasicMetrics = $keepBasicMetrics;
+ $this->reportPeriodsToKeep = $reportPeriodsToKeep;
+ $this->metricsToPurge = $metricsToPurge;
+ $this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
+ }
+
+ /**
+ * Purges old report/metric data.
+ *
+ * If $keepBasicMetrics is false, old numeric tables will be dropped, otherwise only
+ * the metrics in $metricsToPurge 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.
+ */
+ public function purgeData()
+ {
+ // find archive tables to purge
+ list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
+
+ // 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)
+ {
+ if (!empty($this->metricsToPurge))
+ {
+ $where = "WHERE name IN ('".implode("','", $this->metricsToPurge)."')";
+ foreach ($oldNumericTables as $table)
+ {
+ Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
+ }
+ }
+ }
+ else // drop numeric tables
+ {
+ Piwik_DropTables($oldNumericTables);
+ }
+ }
+
+ // process blob tables
+ if (!empty($oldBlobTables))
+ {
+ // if no reports should be kept, drop tables, otherwise drop individual reports
+ if (empty($this->reportPeriodsToKeep))
+ {
+ Piwik_DropTables($oldBlobTables);
+ }
+ else
+ {
+ $where = "WHERE period NOT IN (".implode(',', $this->reportPeriodsToKeep).")";
+ foreach ($oldBlobTables as $table)
+ {
+ Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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();
+
+ // 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;
+ }
+ }
+
+ // deal w/ blob tables
+ if (!empty($this->reportPeriodsToKeep))
+ {
+ // figure out which rows will be deleted
+ foreach ($oldBlobTables as $table)
+ {
+ $rowCount = $this->getBlobTableDeleteCount($table);
+ if ($rowCount > 0)
+ {
+ $result[$table] = $rowCount;
+ }
+ }
+ }
+ else
+ {
+ // not keeping any reports, so drop all tables
+ foreach ($oldBlobTables 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 = Piwik_Date::factory('today')->subMonth(1 + $this->deleteReportsOlderThan);
+ $toRemoveYear = (int)$toRemoveDate->toString('Y');
+ $toRemoveMonth = (int)$toRemoveDate->toString('m');
+
+ // find all archive tables that are older than N months
+ $oldNumericTables = array();
+ $oldBlobTables = array();
+ foreach (Piwik::getTablesInstalled() as $table)
+ {
+ if (preg_match("/archive_(numeric|blob)_([0-9]+)_([0-9]+)/", $table, $matches))
+ {
+ $type = $matches[1];
+ $year = (int)$matches[2];
+ $month = (int)$matches[3];
+
+ if (self::shouldReportBePurged($year, $month, $toRemoveDate))
+ {
+ if ($type == "numeric")
+ {
+ $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 Piwik_Date $reportsOlderThan The date a report must be older than in order to be purged.
+ */
+ 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 )
+ {
+ $sql = "SELECT COUNT(*) FROM $table WHERE name IN ('".implode("','", $this->metricsToPurge)."')";
+ return (int)Piwik_FetchOne($sql);
+ }
+
+ private function getBlobTableDeleteCount( $table )
+ {
+ $sql = "SELECT COUNT(*) FROM $table WHERE period NOT IN (".implode(',', $this->reportPeriodsToKeep).")";
+ return (int)Piwik_FetchOne($sql);
+ }
+
+ /**
+ * 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_logs_max_rows_per_query': Maximum number of rows to delete in one DELETE query.
+ */
+ public static function make( $settings, $metricsToPurge )
+ {
+ return new Piwik_PrivacyManager_ReportsPurger(
+ $settings['delete_reports_older_than'],
+ $settings['delete_reports_keep_basic_metrics'] == 1,
+ self::getReportPeriodsToKeep($settings),
+ $metricsToPurge,
+ $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 $deleteReportSettings 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)
+ {
+ if ($strPeriod == 'range')
+ {
+ continue;
+ }
+
+ $optionName = "delete_reports_keep_{$strPeriod}_reports";
+ if ($settings[$optionName] == 1)
+ {
+ $keepReportPeriods[] = $intPeriod;
+ }
+ }
+ return $keepReportPeriods;
+ }
+}
+
diff --git a/plugins/PrivacyManager/templates/databaseSize.tpl b/plugins/PrivacyManager/templates/databaseSize.tpl
new file mode 100755
index 0000000000..fc3f6e956e
--- /dev/null
+++ b/plugins/PrivacyManager/templates/databaseSize.tpl
@@ -0,0 +1,4 @@
+<p>Current database size: {$dbStats.currentSize}</p>
+<p>Estimated database size after purge: {$dbStats.sizeAfterPurge}</p>
+<p>Estimated space saved: {$dbStats.spaceSaved}</p>
+
diff --git a/plugins/PrivacyManager/templates/privacySettings.js b/plugins/PrivacyManager/templates/privacySettings.js
index 4a778f957e..9c029cc7a0 100644
--- a/plugins/PrivacyManager/templates/privacySettings.js
+++ b/plugins/PrivacyManager/templates/privacySettings.js
@@ -5,21 +5,144 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function toggleBlock(id, value) {
- $('#' + id).toggle(value == 1);
-}
-
$(document).ready(function() {
- $(function() {
- toggleBlock("deleteLogSettings", $("input[name=deleteEnable]:checked").val());
- toggleBlock("anonymizeIPenabled", $("input[name=anonymizeIPEnable]:checked").val());
- });
-
+ function toggleBlock(id, value) {
+ $('#' + id).toggle(value == 1);
+ }
+
+ // reloads purged database size estimate
+ var currentRequest;
+ function reloadDbStats() {
+ if (currentRequest) {
+ currentRequest.abort();
+ }
+
+ $('#deleteDataEstimate').html('');
+ $('#deleteDataEstimateSect .loadingPiwik').show();
+
+ currentRequest = $.ajax({
+ type: 'GET',
+ url: 'index.php?module=PrivacyManager&action=getDatabaseSize',
+ dataType: 'html',
+ async: true,
+ error: piwikHelper.ajaxHandleError, // Callback when the request fails
+ data: $('#formDeleteSettings').serialize(),
+ success: function(data) {
+ currentRequest = undefined;
+ $('#deleteDataEstimateSect .loadingPiwik').hide();
+ $('#deleteDataEstimate').html(data);
+ }
+ });
+ }
+
+ // 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());
+ }).change(reloadDbStats);
+
+ $('input[name=deleteReportsEnable]').click(function() {
+ toggleBlock("deleteReportsSettings", $(this).val());
+ }).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());
+ });
+
+ // make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
+ $('#formDeleteSettings input[type=text]').each(function() {
+ $(this).change(reloadDbStats);
+ });
+ $('#formDeleteSettings 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>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 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();
+ $('#deleteSchedulingSettings .loadingPiwik').show();
+
+ // execute a data purge
+ $.ajax({
+ type: 'POST',
+ url: 'index.php?module=PrivacyManager&action=executeDataPurge',
+ dataType: 'html',
+ async: true,
+ error: piwikHelper.ajaxHandleError, // Callback when the request fails
+ success: function(data) { // ajax request will return new database estimate
+ $('#deleteSchedulingSettings .loadingPiwik').hide();
+ $(link).show();
+ $('#deleteDataEstimate').html(data);
+ }
+ });
+ }
+ });
});
});
diff --git a/plugins/PrivacyManager/templates/privacySettings.tpl b/plugins/PrivacyManager/templates/privacySettings.tpl
index d9680f8405..b3037bb628 100644
--- a/plugins/PrivacyManager/templates/privacySettings.tpl
+++ b/plugins/PrivacyManager/templates/privacySettings.tpl
@@ -51,92 +51,139 @@ See also our official guide <b><a href='http://piwik.org/privacy/' target='_blan
<input type="submit" value="{'General_Save'|translate}" id="privacySettingsSubmit" class="submit"/>
</form>
+<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>
+
<a name="deleteLogsAnchor"></a>
-<h2>{'PrivacyManager_DeleteLogSettings'|translate}</h2>
-<p>{'PrivacyManager_DeleteLogDescription'|translate}</p>
+<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
+<p>{'PrivacyManager_DeleteDataDescription'|translate}</p>
<form method="post" action="{url action=index form=formDeleteSettings}" id="formDeleteSettings" name="formMaskLength">
- <div id='deleteLogSettingEnabled'>
- <table class="adminTable" style='width:800px;'>
- <tr>
- <td width="250">{'PrivacyManager_UseDeleteLog'|translate}<br/>
-
- </td>
- <td width='500'>
- <label><input type="radio" name="deleteEnable" value="1" {if $deleteLogs.config.delete_logs_enable eq '1'}
- checked {/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="deleteEnable" value="0"
- style="margin-left:20px;" {if $deleteLogs.config.delete_logs_enable eq '0'}
- checked {/if}/> {'General_No'|translate}
- </label>
- <span class="ajaxSuccess">
- {'PrivacyManager_DeleteLogDescription2'|translate}
- <a href="http://piwik.org/faq/general/#faq_125" target="_blank">
- {'General_ClickHere'|translate}
- </a>
- </span>
- </td>
- <td width="200">
- {'PrivacyManager_DeleteLogInfo'|translate:$deleteLogs.deleteTables|inlineHelp}
- </td>
- </tr>
- </table>
- </div>
+ <table class="adminTable" style='width:800px;'>
+ <tr id='deleteLogSettingEnabled'>
+ <td width="250">{'PrivacyManager_UseDeleteLog'|translate}<br/>
- <div id="deleteLogSettings">
- <table class="adminTable" style='width:800px;'>
- <tr>
- <td width="250">&nbsp;</td>
- <td width="500">
- <label>{'PrivacyManager_DeleteLogsOlderThan'|translate}
- <input type="text" id="deleteOlderThan" value="{$deleteLogs.config.delete_logs_older_than}" style="width:30px;"
- name="deleteOlderThan">
- {'CoreHome_PeriodDays'|translate}</label><br/>
- <span class="form-description">{'PrivacyManager_LeastDaysInput'|translate:"7"}</span>
- </td>
- <td width="200">
+ </td>
+ <td width='500'>
+ <label><input type="radio" name="deleteEnable" value="1" {if $deleteData.config.delete_logs_enable eq '1'}
+ checked {/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="deleteEnable" value="0"
+ style="margin-left:20px;" {if $deleteData.config.delete_logs_enable eq '0'}
+ checked {/if}/> {'General_No'|translate}
+ </label>
+ <span class="ajaxSuccess">
+ {'PrivacyManager_DeleteLogDescription2'|translate}
+ <a href="http://piwik.org/faq/general/#faq_125" target="_blank">
+ {'General_ClickHere'|translate}
+ </a>
+ </span>
+ </td>
+ <td width="200">
+ {'PrivacyManager_DeleteLogInfo'|translate:$deleteData.deleteTables|inlineHelp}
+ </td>
+ </tr>
+ <tr id="deleteLogSettings">
+ <td width="250">&nbsp;</td>
+ <td width="500">
+ <label>{'PrivacyManager_DeleteLogsOlderThan'|translate}
+ <input type="text" id="deleteOlderThan" value="{$deleteData.config.delete_logs_older_than}" style="width:30px;"
+ name="deleteOlderThan"/>
+ {'CoreHome_PeriodDays'|translate}</label><br/>
+ <span class="form-description">{'PrivacyManager_LeastDaysInput'|translate:"7"}</span>
+ </td>
+ <td width="200">
- </td>
- </tr>
- <tr>
- <td width="250">&nbsp;</td>
- <td width="500">
- {'PrivacyManager_DeleteLogInterval'|translate}
- <select id="deleteLowestInterval" name="deleteLowestInterval">
- <option {if $deleteLogs.config.delete_logs_schedule_lowest_interval eq '1'} selected="selected" {/if}
- value="1"> {'CoreHome_PeriodDay'|translate}</option>
- <option {if $deleteLogs.config.delete_logs_schedule_lowest_interval eq '7'} selected="selected" {/if}
- value="7">{'CoreHome_PeriodWeek'|translate}</option>
- <option {if $deleteLogs.config.delete_logs_schedule_lowest_interval eq '30'} selected="selected" {/if}
- value="30">{'CoreHome_PeriodMonth'|translate}</option>
- </select>
- </td>
- <td width="200">
- {capture assign=purgeStats}
- {if $deleteLogs.lastRun}<strong>{'PrivacyManager_LastDelete'|translate}:</strong>
- {$deleteLogs.lastRunPretty}
- <br/><br/>{/if}
- <strong>{'PrivacyManager_NextDelete'|translate}:</strong>
- {$deleteLogs.nextRunPretty}
- {/capture}
- {$purgeStats|inlineHelp}
- </td>
- </tr>
- <tr>
- <td width="250">&nbsp;</td>
- <td width="500">
- {'PrivacyManager_DeleteMaxRows'|translate}
- <select id="deleteMaxRows" name="deleteMaxRows">
- <option {if $deleteLogs.config.delete_max_rows_per_run eq '100'} selected="selected" {/if} value="100">100.000</option>
- <option {if $deleteLogs.config.delete_max_rows_per_run eq '500'} selected="selected" {/if} value="500">500.000</option>
- <option {if $deleteLogs.config.delete_max_rows_per_run eq '1000'} selected="selected" {/if} value="1000">1.000.000</option>
- <option {if $deleteLogs.config.delete_max_rows_per_run eq '0'} selected="selected" {/if} value="0">{'PrivacyManager_DeleteMaxRowsNoLimit'|translate}</option>
- </select>
- </td>
- <td width="200"></td>
- </tr>
- </table>
- </div>
- <input type="submit" value="{'General_Save'|translate}" id="deleteLogSettingsSubmit" class="submit"/>
+ </td>
+ </tr>
+ <tr id='deleteReportsSettingEnabled'>
+ <td width="250">{'PrivacyManager_UseDeleteReports'|translate}<br/>
+
+ </td>
+ <td width="500">
+ <label><input type="radio" name="deleteReportsEnable" value="1" {if $deleteData.config.delete_reports_enable eq '1'}checked="true"{/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="deleteReportsEnable" value="0" {if $deleteData.config.delete_reports_enable eq '0'}checked="true"{/if} style="margin-left:20px;"/> {'General_No'|translate}</label>
+ <span class="ajaxSuccess">
+ {'PrivacyManager_DeleteReportsInfo'|translate}
+ </span>
+ </td>
+ <td width="200">
+ {'PrivacyManager_DeleteReportsDetailedInfo'|translate|inlineHelp}
+ </td>
+ </tr>
+ <tr id='deleteReportsSettings'>
+ <td width="250">&nbsp;</td>
+ <td width="500">
+ <label>{'PrivacyManager_DeleteReportsOlderThan'|translate}
+ <input type="text" id="deleteReportsOlderThan" value="{$deleteData.config.delete_reports_older_than}" style="width:30px;"
+ name="deleteReportsOlderThan"/>
+ {'CoreHome_PeriodMonths'|translate}
+ </label><br/>
+ <span class="form-description">{'PrivacyManager_LeastMonthsInput'|translate:"3"}</span><br/><br/>
+ <label><input type="checkbox" name="deleteReportsKeepBasic" value="1" {if $deleteData.config.delete_reports_keep_basic_metrics}checked="true"{/if}>{'PrivacyManager_KeepBasicMetrics'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input>
+ </label><br/><br/>
+ <label>{'PrivacyManager_KeepDataFor'|translate}</label><br/><br/>
+ <input type="checkbox" name="deleteReportsKeepDay" value="1" {if $deleteData.config.delete_reports_keep_day_reports}checked="true"{/if}>{'General_DailyReports'|translate}</input><br/>
+ <input type="checkbox" name="deleteReportsKeepWeek" value="1" {if $deleteData.config.delete_reports_keep_week_reports}checked="true"{/if}>{'General_WeeklyReports'|translate}</input><br/>
+ <input type="checkbox" name="deleteReportsKeepMonth" value="1" {if $deleteData.config.delete_reports_keep_month_reports}checked="true"{/if}>{'General_MonthlyReports'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input><br/>
+ <input type="checkbox" name="deleteReportsKeepYear" value="1" {if $deleteData.config.delete_reports_keep_year_reports}checked="true"{/if}>{'General_YearlyReports'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input><br/>
+ </td>
+ <td width="200">
+
+ </td>
+ </tr>
+ <tr id="deleteDataEstimateSect">
+ <td width="250">{'PrivacyManager_ReportsDataSavedEstimate'|translate}<br/></td>
+ <td width="500">
+ <div id="deleteDataEstimate">{include file="PrivacyManager/templates/databaseSize.tpl" dbStats=$deleteDbStats}</div>
+ <span class='loadingPiwik' style='display:none'><img src='/themes/default/images/loading-blue.gif' /> {'General_LoadingData'|translate}</span>
+ </td>
+ <td width="200">
+
+ </td>
+ </tr>
+ <tr id="deleteSchedulingSettings">
+ <td width="250">{'PrivacyManager_DeleteSchedulingSettings'|translate}<br/></td>
+ <td width="500">
+ {'PrivacyManager_DeleteDataInterval'|translate}
+ <select id="deleteLowestInterval" name="deleteLowestInterval">
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '1'} selected="selected" {/if}
+ value="1"> {'CoreHome_PeriodDay'|translate}</option>
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '7'} selected="selected" {/if}
+ value="7">{'CoreHome_PeriodWeek'|translate}</option>
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '30'} selected="selected" {/if}
+ value="30">{'CoreHome_PeriodMonth'|translate}</option>
+ </select><br/><br/>
+ <em><a id="purgeDataNowLink" href="#">{'PrivacyManager_PurgeNow'|translate}</a></em>
+ <span class='loadingPiwik' style='display:none'><img src='/themes/default/images/loading-blue.gif' /> {'PrivacyManager_PurgingData'|translate}</span>
+ </td>
+ <td width="200">
+ {capture assign=purgeStats}
+ {if $deleteData.lastRun}<strong>{'PrivacyManager_LastDelete'|translate}:</strong>
+ {$deleteData.lastRunPretty}
+ <br/><br/>{/if}
+ <strong>{'PrivacyManager_NextDelete'|translate}:</strong>
+ {$deleteData.nextRunPretty}
+ {/capture}
+ {$purgeStats|inlineHelp}
+ </td>
+ </tr>
+ </table>
+ <input type="button" value="{'General_Save'|translate}" id="deleteLogSettingsSubmit" class="submit"/>
</form>
{/if}
diff --git a/plugins/PrivacyManager/tests/PrivacyManager.test.php b/plugins/PrivacyManager/tests/PrivacyManager.test.php
new file mode 100755
index 0000000000..ca620b8844
--- /dev/null
+++ b/plugins/PrivacyManager/tests/PrivacyManager.test.php
@@ -0,0 +1,601 @@
+<?php
+if(!defined('PIWIK_CONFIG_TEST_INCLUDED'))
+{
+ require_once dirname(__FILE__)."/../../../tests/config_test.php";
+}
+
+require_once PIWIK_INCLUDE_PATH . '/tests/integration/Integration.php';
+
+class Test_Piwik_PrivacyManager extends Test_Integration
+{
+ // constants used in checking whether numeric tables are populated correctly.
+ // 'done' entries exist for every period, even if there's no metric data, so we need the
+ // total archive count for each month.
+ const TOTAL_JAN_ARCHIVE_COUNT = 37; // 31 + 4 + 1 + 1;
+ const TOTAL_FEB_ARCHIVE_COUNT = 34; // 29 + 4 + 1;
+
+ // the number of archive entries for a single metric if no purging is done. this #
+ // is dependent on the number of periods for which there were visits.
+ const JAN_METRIC_ARCHIVE_COUNT = 11; // 5 days + 4 weeks + 1 month + 1 year
+ const FEB_METRIC_ARCHIVE_COUNT = 11; // 6 days + 4 weeks + 1 month
+
+ private $idSite = null;
+ private $dateTime = null;
+ private $instance = null;
+ private $daysAgoStart = 50;
+
+ public function __construct( $title = '' )
+ {
+ parent::__construct($title);
+ $this->dateTime = Piwik_Date::factory('2012-02-28');
+ }
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Piwik_TablePartitioning::$tablesAlreadyInstalled = null;
+
+ // purging depends upon today's date, so 'older_than' parts must be dependent upon today
+ $today = Piwik_Date::factory('today');
+ $daysSinceToday = ($today->getTimestamp() - $this->dateTime->getTimestamp()) / (24 * 60 * 60);
+
+ $monthsSinceToday = 0;
+ for ($date = $today; $date->toString('Y-m') != $this->dateTime->toString('Y-m'); $date = $date->subMonth(1))
+ {
+ ++$monthsSinceToday;
+ }
+
+ // set default config
+ $settings = array();
+ $settings['delete_logs_enable'] = 1;
+ // purging log data from before 2012-01-24
+ $settings['delete_logs_older_than'] = 35 + $daysSinceToday;
+ $settings['delete_max_rows_per_run'] = 0;
+ $settings['delete_logs_schedule_lowest_interval'] = 7;
+ $settings['delete_reports_enable'] = 1;
+ $settings['delete_reports_older_than'] = $monthsSinceToday;
+ $settings['delete_reports_keep_basic_metrics'] = 0;
+ $settings['delete_reports_keep_day_reports'] = 0;
+ $settings['delete_reports_keep_week_reports'] = 0;
+ $settings['delete_reports_keep_month_reports'] = 0;
+ $settings['delete_reports_keep_year_reports'] = 0;
+ Piwik_PrivacyManager::savePurgeDataSettings($settings);
+
+ $this->instance = new Piwik_PrivacyManager();
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+
+ // remove archive tables (integration test teardown will only truncate)
+ $archiveTables = $this->getArchiveTableNames();
+ $archiveTables = array_merge($archiveTables['numeric'], $archiveTables['blob']);
+ foreach ($archiveTables as $table)
+ {
+ Piwik_Query("DROP TABLE IF EXISTS ".Piwik_Common::prefixTable($table));
+ }
+
+ // refresh table name caches so next test will pass
+ Piwik_TablePartitioning::$tablesAlreadyInstalled = null;
+ Piwik::getTablesInstalled(true);
+ }
+
+ /** Make sure the first time deleteLogData is run, nothing happens. */
+ public function test_deleteLogData_initialRun()
+ {
+ $this->addLogData();
+ $this->addReportData();
+
+ $this->instance->deleteLogData();
+
+ // check that initial option is set
+ $this->assertEqual(
+ 1, Piwik_GetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
+
+ // perform other checks
+ $this->checkNoDataChanges();
+ }
+
+ /** Make sure the first time deleteReportData is run, nothing happens. */
+ public function test_deleteReportData_initialRun()
+ {
+ $this->addLogData();
+ $this->addReportData();
+
+ $this->instance->deleteReportData();
+
+ // check that initial option is set
+ $this->assertEqual(
+ 1, Piwik_GetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
+
+ // perform other checks
+ $this->checkNoDataChanges();
+ }
+
+ /** Make sure the task is not run when its scheduled for later. */
+ public function test_purgeData_notTimeToRun()
+ {
+ $this->addLogData();
+ $this->addReportData();
+
+ $yesterdaySecs = Piwik_Date::factory('yesterday')->getTimestamp();
+
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $yesterdaySecs);
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $yesterdaySecs);
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkNoDataChanges();
+ }
+
+ /** Make sure purging data runs when scheduled. */
+ public function test_purgeData_notInitialAndTimeToRun()
+ {
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => -1
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+
+ $archiveTables = $this->getArchiveTableNames();
+
+ // January numeric table should be dropped
+ $this->assertFalse($this->tableExists($archiveTables['numeric'][0])); // January
+
+ // Check february metric count (5 metrics per period w/ visits + 1 'done' archive for every period)
+ $febRowCount = self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT;
+ $this->assertEqual($febRowCount, $this->getTableCount($archiveTables['numeric'][1])); // February
+
+ // January blob table should be dropped
+ $this->assertFalse($this->tableExists($archiveTables['blob'][0])); // January
+
+ // Check february blob count (1 blob per period w/ visits)
+ $this->assertEqual(self::FEB_METRIC_ARCHIVE_COUNT, $this->getTableCount($archiveTables['blob'][1])); // February
+ }
+
+ /** Make sure nothing happens when deleting logs & reports are both disabled. */
+ public function test_purgeData_bothDisabled()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_logs_enable' => 0,
+ 'delete_reports_enable' => 0
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array();
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkNoDataChanges();
+ }
+
+ /** Test that purgeData works when there's no data. */
+ public function test_purgeData_deleteLogsNoData()
+ {
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array();
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->assertEqual(0, $this->getTableCount('log_visit'));
+ $this->assertEqual(0, $this->getTableCount('log_conversion'));
+ $this->assertEqual(0, $this->getTableCount('log_link_visit_action'));
+ $this->assertEqual(0, $this->getTableCount('log_conversion_item'));
+
+ $archiveTables = $this->getArchiveTableNames();
+ $this->assertFalse($this->tableExists($archiveTables['numeric'][0])); // January
+ $this->assertFalse($this->tableExists($archiveTables['numeric'][1])); // February
+ $this->assertFalse($this->tableExists($archiveTables['blob'][0])); // January
+ $this->assertFalse($this->tableExists($archiveTables['blob'][1])); // February
+ }
+
+ /** Test that purgeData works correctly when the 'keep basic metrics' setting is set to true. */
+ public function test_purgeData_deleteReportsKeepBasicMetrics()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_reports_keep_basic_metrics' => 1
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => -1
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+
+ $archiveTables = $this->getArchiveTableNames();
+
+ // all numeric metrics should be saved (5 metrics per period w/ visits + 1 'done' archive for every period)
+ $janRowCount = self::JAN_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_JAN_ARCHIVE_COUNT;
+ $this->assertEqual($janRowCount, $this->getTableCount($archiveTables['numeric'][0])); // January
+
+ // check february numerics not deleted (5 metrics per period w/ visits + 1 'done' archive for every period)
+ $febRowCount = self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT;
+ $this->assertEqual($febRowCount, $this->getTableCount($archiveTables['numeric'][1])); // February
+
+ // check that the january blob table was dropped
+ $this->assertFalse($this->tableExists($archiveTables['blob'][0])); // January
+
+ // check for no changes in the february blob table
+ $this->assertEqual(self::FEB_METRIC_ARCHIVE_COUNT, $this->getTableCount($archiveTables['blob'][1])); // February
+ }
+
+ /** Test that purgeData works correctly when the 'keep daily reports' setting is set to true. */
+ public function test_purgeData_deleteReportsKeepDailyReports()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_reports_keep_day_reports' => 1
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 6 // removing 4 weeks, 1 month & 1 year
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+ $this->checkReportsAndMetricsPurged($janBlobsRemaining = 5); // 5 blobs for 5 days
+ }
+
+ /** Test that purgeData works correctly when the 'keep weekly reports' setting is set to true. */
+ public function test_purgeData_deleteReportsKeepWeeklyReports()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_reports_keep_week_reports' => 1
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 7 // 5 days, 1 month & 1 year to remove
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+ $this->checkReportsAndMetricsPurged($janBlobsRemaining = 4); // 4 blobs for 4 weeks
+ }
+
+ /** Test that purgeData works correctly when the 'keep monthly reports' setting is set to true. */
+ public function test_purgeData_deleteReportsKeepMonthlyReports()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_reports_keep_month_reports' => 1
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 10 // 5 days, 4 weeks, 1 year to remove
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+ $this->checkReportsAndMetricsPurged($janBlobsRemaining = 1); // 1 blob for 1 month
+ }
+
+ /** Test that purgeData works correctly when the 'keep yearly reports' setting is set to true. */
+ public function test_purgeData_deleteReportsKeepYearlyReports()
+ {
+ Piwik_PrivacyManager::savePurgeDataSettings(array(
+ 'delete_reports_keep_year_reports' => 1
+ ));
+
+ $this->addLogData();
+ $this->addReportData();
+
+ // get purge data prediction
+ $prediction = Piwik_PrivacyManager::getPurgeEstimate();
+
+ // perform checks on prediction
+ $expectedPrediction = array(
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 10 // 5 days, 4 weeks & 1 year to remove
+ );
+ $this->assertEqual($expectedPrediction, $prediction);
+
+ // purge data
+ $this->setTimeToRun();
+ $this->instance->deleteLogData();
+ $this->instance->deleteReportData();
+
+ // perform checks
+ $this->checkLogDataPurged();
+ $this->checkReportsAndMetricsPurged($janBlobsRemaining = 1); // 1 blob for 1 year
+ }
+
+ // --- utility functions follow ---
+
+ private function addLogData()
+ {
+ // tracks visits on the following days:
+ // - 2012-01-09
+ // - 2012-01-14
+ // - 2012-01-19
+ // - 2012-01-24
+ // - 2012-01-29
+ // - 2012-02-03
+ // - 2012-02-08
+ // - 2012-02-13
+ // - 2012-02-18
+ // - 2012-02-23
+ // - 2012-02-28
+ // 6 visits in feb, 5 in jan
+
+ $start = $this->dateTime;
+ $this->idSite = $this->createWebsite('2012-01-01', $ecommerce=1);
+ $idGoal = Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'match all', 'url', 'http', 'contains');
+
+ for ($daysAgo = $this->daysAgoStart; $daysAgo >= 0; $daysAgo -= 5) // one visit every 5 days
+ {
+ $dateTime = $start->subDay($daysAgo)->toString();
+ $t = $this->getTracker($this->idSite, $dateTime, $defaultInit = true);
+
+ $t->setUrl("http://whatever.com");
+ $t->doTrackPageView('First page view');
+
+ $t->setUrl("http://whatever.com/42");
+ $t->doTrackPageView('Second page view');
+
+ $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR' , $category = 'Electronics & Cameras',
+ $price = 1500, $quantity = 1);
+ $t->doTrackEcommerceOrder($orderId = '937nsjusu '.$dateTime, $grandTotal = 1111.11, $subTotal = 1000,
+ $tax = 111, $shipping = 0.11, $discount = 666);
+ }
+ }
+
+ private function addReportData()
+ {
+ $archive = Piwik_Archive::build($this->idSite, 'year', $this->dateTime);
+ $archive->getNumeric('nb_visits', 'nb_hits');
+
+ Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(
+ $this->idSite, 'year', $this->dateTime);
+
+ // months are added via the 'year' period, but weeks must be done manually
+ for ($daysAgo = $this->daysAgoStart; $daysAgo > 0; $daysAgo -= 7) // every week
+ {
+ $dateTime = $this->dateTime->subDay($daysAgo);
+
+ $archive = Piwik_Archive::build($this->idSite, 'week', $dateTime);
+ $archive->getNumeric('nb_visits');
+
+ Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(
+ $this->idSite, 'week', $dateTime);
+ }
+
+ // when archiving is initiated, the archive metrics & reports for EVERY loaded plugin
+ // are archived. don't want this test to depend on every possible metric, so get rid of
+ // the unwanted archive data now.
+ $metricsToSave = array(
+ 'nb_visits',
+ 'nb_actions',
+ Piwik_Goals::getRecordName('revenue'),
+ Piwik_Goals::getRecordName('nb_conversions', 1),
+ Piwik_Goals::getRecordName('revenue', Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+ );
+
+ $archiveTables = $this->getArchiveTableNames();
+ foreach ($archiveTables['numeric'] as $table)
+ {
+ $realTable = Piwik_Common::prefixTable($table);
+ Piwik_Query("DELETE FROM $realTable WHERE name NOT IN ('".implode("','", $metricsToSave)."', 'done')");
+ }
+ foreach ($archiveTables['blob'] as $table)
+ {
+ $realTable = Piwik_Common::prefixTable($table);
+ Piwik_Query("DELETE FROM $realTable WHERE name NOT IN ('VisitorInterest_timeGap')");
+ }
+ }
+
+ private function checkNoDataChanges()
+ {
+ // 11 visits total w/ 2 actions per visit & 2 conversions per visit. 1 e-commerce order per visit.
+ $this->assertEqual(11, $this->getTableCount('log_visit'));
+ $this->assertEqual(22, $this->getTableCount('log_conversion'));
+ $this->assertEqual(22, $this->getTableCount('log_link_visit_action'));
+ $this->assertEqual(11, $this->getTableCount('log_conversion_item'));
+
+ $archiveTables = $this->getArchiveTableNames();
+
+ // 2 entries per period w/ visits (5 for each metric) + 1 entry for every period in the month (the 'done' rows)
+ $janMetricCount = self::JAN_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_JAN_ARCHIVE_COUNT;
+ $this->assertEqual($janMetricCount, $this->getTableCount($archiveTables['numeric'][0])); // January
+
+ $febMetricCount = self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT;
+ $this->assertEqual($febMetricCount, $this->getTableCount($archiveTables['numeric'][1])); // February
+
+ // 1 entry per period w/ visits
+ $this->assertEqual(self::JAN_METRIC_ARCHIVE_COUNT, $this->getTableCount($archiveTables['blob'][0])); // January
+ $this->assertEqual(self::FEB_METRIC_ARCHIVE_COUNT, $this->getTableCount($archiveTables['blob'][1])); // February
+ }
+
+ /**
+ * Helper method. Performs checks after reports are purged. Checks that the january numeric table
+ * was dropped, that the february metric & blob tables are unaffected, and that the january blob
+ * table has a certain number of blobs.
+ */
+ private function checkReportsAndMetricsPurged( $janBlobsRemaining )
+ {
+ $archiveTables = $this->getArchiveTableNames();
+
+ // check that the january numeric table was dropped
+ $this->assertFalse($this->tableExists($archiveTables['numeric'][0])); // January
+
+ // check february numerics not deleted (5 metrics per period w/ visits + 1 'done' archive for every period)
+ $febRowCount = self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT;
+ $this->assertEqual($febRowCount, $this->getTableCount($archiveTables['numeric'][1])); // February
+
+ // check the january blob count
+ $this->assertEqual($janBlobsRemaining, $this->getTableCount($archiveTables['blob'][0])); // January
+
+ // check for no changes in the february blob table (1 blob for every period w/ visits in feb)
+ $this->assertEqual(self::FEB_METRIC_ARCHIVE_COUNT, $this->getTableCount($archiveTables['blob'][1])); // February
+ }
+
+ private function checkLogDataPurged()
+ {
+ // 3 days removed by purge, so 3 visits, 6 conversions, 6 actions & 3 e-commerce orders removed
+ $this->assertEqual(8, $this->getTableCount('log_visit'));
+ $this->assertEqual(16, $this->getTableCount('log_conversion'));
+ $this->assertEqual(16, $this->getTableCount('log_link_visit_action'));
+ $this->assertEqual(8, $this->getTableCount('log_conversion_item'));
+ }
+
+ private function setTimeToRun()
+ {
+ $lastDateSecs = Piwik_Date::factory('today')->subDay(8)->getTimestamp();
+
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDateSecs);
+ Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $lastDateSecs);
+ }
+
+ private function getTableCount( $tableName, $where = '' )
+ {
+ $sql = "SELECT COUNT(*) FROM ".Piwik_Common::prefixTable($tableName)." $where";
+ return Piwik_FetchOne($sql);
+ }
+
+ private function tableExists( $tableName )
+ {
+ $dbName = Piwik_Config::getInstance()->database['dbname'];
+
+ $sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?";
+ return Piwik_FetchOne($sql, array($dbName, Piwik_Common::prefixTable($tableName))) == 1;
+ }
+
+ private function getArchiveTableNames()
+ {
+ return array(
+ 'numeric' => array(
+ 'archive_numeric_2012_01',
+ 'archive_numeric_2012_02'
+ ),
+ 'blob' => array(
+ 'archive_blob_2012_01',
+ 'archive_blob_2012_02'
+ )
+ );
+ }
+
+ /**
+ * Irrelevant. Just needs to be defined.
+ */
+ public function getPathToTestDirectory()
+ {
+ return PIWIK_INCLUDE_PATH . '/tests/integration';
+ }
+}
+