PageRenderTime 44ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/PrivacyManager/PrivacyManager.php

https://github.com/CodeYellowBV/piwik
PHP | 438 lines | 238 code | 62 blank | 138 comment | 35 complexity | c52d8baede262bd1677b76f8c6d8b17b MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\PrivacyManager;
  10. use Piwik\Common;
  11. use Piwik\Config as PiwikConfig;
  12. use Piwik\DataTable\DataTableInterface;
  13. use Piwik\Date;
  14. use Piwik\Db;
  15. use Piwik\Metrics;
  16. use Piwik\Option;
  17. use Piwik\Period;
  18. use Piwik\Period\Range;
  19. use Piwik\Plugins\Goals\Archiver;
  20. use Piwik\Site;
  21. use Piwik\Tracker\GoalManager;
  22. require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/LogDataPurger.php';
  23. require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/ReportsPurger.php';
  24. /**
  25. * Specifically include this for Tracker API (which does not use autoloader)
  26. */
  27. require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/DoNotTrackHeaderChecker.php';
  28. require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/IPAnonymizer.php';
  29. /**
  30. */
  31. class PrivacyManager extends \Piwik\Plugin
  32. {
  33. const OPTION_LAST_DELETE_PIWIK_LOGS = "lastDelete_piwik_logs";
  34. const OPTION_LAST_DELETE_PIWIK_REPORTS = 'lastDelete_piwik_reports';
  35. const OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL = "lastDelete_piwik_logs_initial";
  36. // options for data purging feature array[configName => configSection]
  37. public static $purgeDataOptions = array(
  38. 'delete_logs_enable' => 'Deletelogs',
  39. 'delete_logs_schedule_lowest_interval' => 'Deletelogs',
  40. 'delete_logs_older_than' => 'Deletelogs',
  41. 'delete_logs_max_rows_per_query' => 'Deletelogs',
  42. 'enable_auto_database_size_estimate' => 'Deletelogs',
  43. 'delete_reports_enable' => 'Deletereports',
  44. 'delete_reports_older_than' => 'Deletereports',
  45. 'delete_reports_keep_basic_metrics' => 'Deletereports',
  46. 'delete_reports_keep_day_reports' => 'Deletereports',
  47. 'delete_reports_keep_week_reports' => 'Deletereports',
  48. 'delete_reports_keep_month_reports' => 'Deletereports',
  49. 'delete_reports_keep_year_reports' => 'Deletereports',
  50. 'delete_reports_keep_range_reports' => 'Deletereports',
  51. 'delete_reports_keep_segment_reports' => 'Deletereports',
  52. );
  53. private $dntChecker = null;
  54. private $ipAnonymizer = null;
  55. /**
  56. * Constructor.
  57. */
  58. public function __construct()
  59. {
  60. parent::__construct();
  61. $this->dntChecker = new DoNotTrackHeaderChecker();
  62. $this->ipAnonymizer = new IPAnonymizer();
  63. }
  64. /**
  65. * Returns true if it is likely that the data for this report has been purged and if the
  66. * user should be told about that.
  67. *
  68. * In order for this function to return true, the following must also be true:
  69. * - The data table for this report must either be empty or not have been fetched.
  70. * - The period of this report is not a multiple period.
  71. * - The date of this report must be older than the delete_reports_older_than config option.
  72. * @param DataTableInterface $dataTable
  73. * @return bool
  74. */
  75. public static function hasReportBeenPurged($dataTable)
  76. {
  77. $strPeriod = Common::getRequestVar('period', false);
  78. $strDate = Common::getRequestVar('date', false);
  79. if (false !== $strPeriod
  80. && false !== $strDate
  81. && (is_null($dataTable)
  82. || (!empty($dataTable) && $dataTable->getRowsCount() == 0))
  83. ) {
  84. // if range, only look at the first date
  85. if ($strPeriod == 'range') {
  86. $idSite = Common::getRequestVar('idSite', '');
  87. if (intval($idSite) != 0) {
  88. $site = new Site($idSite);
  89. $timezone = $site->getTimezone();
  90. } else {
  91. $timezone = 'UTC';
  92. }
  93. $period = new Range('range', $strDate, $timezone);
  94. $reportDate = $period->getDateStart();
  95. } elseif (Period::isMultiplePeriod($strDate, $strPeriod)) {
  96. // if a multiple period, this function is irrelevant
  97. return false;
  98. } else {
  99. // otherwise, use the date as given
  100. $reportDate = Date::factory($strDate);
  101. }
  102. $reportYear = $reportDate->toString('Y');
  103. $reportMonth = $reportDate->toString('m');
  104. if (static::shouldReportBePurged($reportYear, $reportMonth)) {
  105. return true;
  106. }
  107. }
  108. return false;
  109. }
  110. /**
  111. * @see Piwik\Plugin::getListHooksRegistered
  112. */
  113. public function getListHooksRegistered()
  114. {
  115. return array(
  116. 'AssetManager.getJavaScriptFiles' => 'getJsFiles',
  117. 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral',
  118. 'Tracker.isExcludedVisit' => array($this->dntChecker, 'checkHeaderInTracker'),
  119. 'Tracker.setVisitorIp' => array($this->ipAnonymizer, 'setVisitorIpAddress'),
  120. );
  121. }
  122. public function setTrackerCacheGeneral(&$cacheContent)
  123. {
  124. $config = new Config();
  125. $cacheContent = $config->setTrackerCacheGeneral($cacheContent);
  126. }
  127. public function getJsFiles(&$jsFiles)
  128. {
  129. $jsFiles[] = "plugins/PrivacyManager/javascripts/privacySettings.js";
  130. }
  131. /**
  132. * Returns the settings for the data purging feature.
  133. *
  134. * @return array
  135. */
  136. public static function getPurgeDataSettings()
  137. {
  138. $settings = array();
  139. // load settings from ini config
  140. $config = PiwikConfig::getInstance();
  141. foreach (self::$purgeDataOptions as $configKey => $configSection) {
  142. $values = $config->$configSection;
  143. $settings[$configKey] = $values[$configKey];
  144. }
  145. if (!Controller::isDataPurgeSettingsEnabled()) {
  146. return $settings;
  147. }
  148. // load the settings for the data purging settings
  149. foreach (self::$purgeDataOptions as $configName => $configSection) {
  150. $value = Option::get($configName);
  151. if ($value !== false) {
  152. $settings[$configName] = $value;
  153. }
  154. }
  155. return $settings;
  156. }
  157. /**
  158. * Saves the supplied data purging settings.
  159. *
  160. * @param array $settings The settings to save.
  161. */
  162. public static function savePurgeDataSettings($settings)
  163. {
  164. foreach (self::$purgeDataOptions as $configName => $configSection) {
  165. if (isset($settings[$configName])) {
  166. Option::set($configName, $settings[$configName]);
  167. }
  168. }
  169. }
  170. /**
  171. * Deletes old archived data (reports & metrics).
  172. *
  173. * Archive tables are not optimized after, as that is handled by a separate scheduled task
  174. * in CoreAdminHome. This is a scheduled task and will only execute every N days. The number
  175. * of days is determined by the delete_logs_schedule_lowest_interval config option.
  176. *
  177. * If delete_reports_enable is set to 1, old archive data is deleted. The following
  178. * config options can tweak this behavior:
  179. * - delete_reports_older_than: The number of months after which archive data is considered
  180. * old. The current month is not considered when applying this
  181. * value.
  182. * - delete_reports_keep_basic_metrics: If set to 1, keeps certain metric data. Right now,
  183. * all metric data is kept.
  184. * - delete_reports_keep_day_reports: If set to 1, keeps old daily reports.
  185. * - delete_reports_keep_week_reports: If set to 1, keeps old weekly reports.
  186. * - delete_reports_keep_month_reports: If set to 1, keeps old monthly reports.
  187. * - delete_reports_keep_year_reports: If set to 1, keeps old yearly reports.
  188. */
  189. public function deleteReportData()
  190. {
  191. $settings = self::getPurgeDataSettings();
  192. // Make sure, data deletion is enabled
  193. if ($settings['delete_reports_enable'] == 0) {
  194. return false;
  195. }
  196. // make sure purging should run at this time (unless this is a forced purge)
  197. if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_REPORTS)) {
  198. return false;
  199. }
  200. // set last run time
  201. Option::set(self::OPTION_LAST_DELETE_PIWIK_REPORTS, Date::factory('today')->getTimestamp());
  202. ReportsPurger::make($settings, self::getAllMetricsToKeep())->purgeData();
  203. return true;
  204. }
  205. /**
  206. * Deletes old log data based on the options set in the Deletelogs config
  207. * section. This is a scheduled task and will only execute every N days. The number
  208. * of days is determined by the delete_logs_schedule_lowest_interval config option.
  209. *
  210. * If delete_logs_enable is set to 1, old data in the log_visit, log_conversion,
  211. * log_conversion_item and log_link_visit_action tables is deleted. The following
  212. * options can tweak this behavior:
  213. * - delete_logs_older_than: The number of days after which log data is considered old.
  214. *
  215. * @ToDo: return number of Rows deleted in last run; Display age of "oldest" row to help the user setting
  216. * the day offset;
  217. */
  218. public function deleteLogData()
  219. {
  220. $settings = self::getPurgeDataSettings();
  221. // Make sure, data deletion is enabled
  222. if ($settings['delete_logs_enable'] == 0) {
  223. return false;
  224. }
  225. // make sure purging should run at this time
  226. if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_LOGS)) {
  227. return false;
  228. }
  229. /*
  230. * Tell the DB that log deletion has run BEFORE deletion is executed;
  231. * If deletion / table optimization exceeds execution time, other tasks maybe prevented of being executed
  232. * every time, when the schedule is triggered.
  233. */
  234. $lastDeleteDate = Date::factory("today")->getTimestamp();
  235. Option::set(self::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDeleteDate);
  236. // execute the purge
  237. LogDataPurger::make($settings)->purgeData();
  238. return true;
  239. }
  240. /**
  241. * Returns an array describing what data would be purged if both log data & report
  242. * purging is invoked.
  243. *
  244. * The returned array maps table names with the number of rows that will be deleted.
  245. * If the table name is mapped with -1, the table will be dropped.
  246. *
  247. * @param array $settings The config options to use in the estimate. If null, the real
  248. * options are used.
  249. * @return array
  250. */
  251. public static function getPurgeEstimate($settings = null)
  252. {
  253. if (is_null($settings)) {
  254. $settings = self::getPurgeDataSettings();
  255. }
  256. $result = array();
  257. if ($settings['delete_logs_enable']) {
  258. $logDataPurger = LogDataPurger::make($settings);
  259. $result = array_merge($result, $logDataPurger->getPurgeEstimate());
  260. }
  261. if ($settings['delete_reports_enable']) {
  262. $reportsPurger = ReportsPurger::make($settings, self::getAllMetricsToKeep());
  263. $result = array_merge($result, $reportsPurger->getPurgeEstimate());
  264. }
  265. return $result;
  266. }
  267. /**
  268. * Returns true if a report with the given year & month should be purged or not.
  269. *
  270. * If reportsOlderThan is set to null or not supplied, this function will check if
  271. * a report should be purged, based on existing configuration. In this case, if
  272. * delete_reports_enable is set to 0, this function will return false.
  273. *
  274. * @param int $reportDateYear The year of the report in question.
  275. * @param int $reportDateMonth The month of the report in question.
  276. * @param int|Date $reportsOlderThan If an int, the number of months a report must be older than
  277. * in order to be purged. If a date, the date a report must be
  278. * older than in order to be purged.
  279. * @return bool
  280. */
  281. public static function shouldReportBePurged($reportDateYear, $reportDateMonth, $reportsOlderThan = null)
  282. {
  283. // if no 'older than' value/date was supplied, use existing config
  284. if (is_null($reportsOlderThan)) {
  285. // if report deletion is not enabled, the report shouldn't be purged
  286. $settings = self::getPurgeDataSettings();
  287. if ($settings['delete_reports_enable'] == 0) {
  288. return false;
  289. }
  290. $reportsOlderThan = $settings['delete_reports_older_than'];
  291. }
  292. // if a integer was supplied, assume it is the number of months a report must be older than
  293. if (!($reportsOlderThan instanceof Date)) {
  294. $reportsOlderThan = Date::factory('today')->subMonth(1 + $reportsOlderThan);
  295. }
  296. return ReportsPurger::shouldReportBePurged(
  297. $reportDateYear, $reportDateMonth, $reportsOlderThan);
  298. }
  299. /**
  300. * Returns the general metrics to keep when the 'delete_reports_keep_basic_metrics'
  301. * config is set to 1.
  302. */
  303. private static function getMetricsToKeep()
  304. {
  305. return array('nb_uniq_visitors', 'nb_visits', 'nb_actions', 'max_actions',
  306. 'sum_visit_length', 'bounce_count', 'nb_visits_converted', 'nb_conversions',
  307. 'revenue', 'quantity', 'price', 'orders');
  308. }
  309. /**
  310. * Returns the goal metrics to keep when the 'delete_reports_keep_basic_metrics'
  311. * config is set to 1.
  312. */
  313. private static function getGoalMetricsToKeep()
  314. {
  315. // keep all goal metrics
  316. return array_values(Metrics::$mappingFromIdToNameGoal);
  317. }
  318. /**
  319. * Returns the names of metrics that should be kept when purging as they appear in
  320. * archive tables.
  321. */
  322. public static function getAllMetricsToKeep()
  323. {
  324. $metricsToKeep = self::getMetricsToKeep();
  325. // convert goal metric names to correct archive names
  326. if (Common::isGoalPluginEnabled()) {
  327. $goalMetricsToKeep = self::getGoalMetricsToKeep();
  328. $maxGoalId = self::getMaxGoalId();
  329. // for each goal metric, there's a different name for each goal, including the overview,
  330. // the order report & cart report
  331. foreach ($goalMetricsToKeep as $metric) {
  332. for ($i = 1; $i <= $maxGoalId; ++$i) // maxGoalId can be 0
  333. {
  334. $metricsToKeep[] = Archiver::getRecordName($metric, $i);
  335. }
  336. $metricsToKeep[] = Archiver::getRecordName($metric);
  337. $metricsToKeep[] = Archiver::getRecordName($metric, GoalManager::IDGOAL_ORDER);
  338. $metricsToKeep[] = Archiver::getRecordName($metric, GoalManager::IDGOAL_CART);
  339. }
  340. }
  341. return $metricsToKeep;
  342. }
  343. /**
  344. * Returns true if one of the purge data tasks should run now, false if it shouldn't.
  345. */
  346. private function shouldPurgeData($settings, $lastRanOption)
  347. {
  348. // Log deletion may not run until it is once rescheduled (initial run). This is the
  349. // only way to guarantee the calculated next scheduled deletion time.
  350. $initialDelete = Option::get(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL);
  351. if (empty($initialDelete)) {
  352. Option::set(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
  353. return false;
  354. }
  355. // Make sure, log purging is allowed to run now
  356. $lastDelete = Option::get($lastRanOption);
  357. $deleteIntervalDays = $settings['delete_logs_schedule_lowest_interval'];
  358. $deleteIntervalSeconds = $this->getDeleteIntervalInSeconds($deleteIntervalDays);
  359. if ($lastDelete === false ||
  360. ($lastDelete !== false && ((int)$lastDelete + $deleteIntervalSeconds) <= time())
  361. ) {
  362. return true;
  363. } else // not time to run data purge
  364. {
  365. return false;
  366. }
  367. }
  368. function getDeleteIntervalInSeconds($deleteInterval)
  369. {
  370. return (int)$deleteInterval * 24 * 60 * 60;
  371. }
  372. private static function getMaxGoalId()
  373. {
  374. return Db::fetchOne("SELECT MAX(idgoal) FROM " . Common::prefixTable('goal'));
  375. }
  376. }