| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Plugins\CoreAdminHome; |
|
|
| use Piwik\API\Request; |
| use Piwik\ArchiveProcessor\Rules; |
| use Piwik\Archive\ArchivePurger; |
| use Piwik\Config; |
| use Piwik\Container\StaticContainer; |
| use Piwik\CronArchive; |
| use Piwik\DataAccess\ArchiveTableCreator; |
| use Piwik\DataAccess\Model as CoreModel; |
| use Piwik\Date; |
| use Piwik\Db; |
| use Piwik\Http; |
| use Piwik\Option; |
| use Piwik\Piwik; |
| use Piwik\Plugins\CoreAdminHome\Emails\JsTrackingCodeMissingEmail; |
| use Piwik\Plugins\CoreAdminHome\Emails\TrackingFailuresEmail; |
| use Piwik\Plugins\CoreAdminHome\Tasks\ArchivesToPurgeDistributedList; |
| use Piwik\Plugins\SegmentEditor\Model; |
| use Piwik\Plugins\SitesManager\SitesManager; |
| use Piwik\Scheduler\Schedule\SpecificTime; |
| use Piwik\Settings\Storage\Backend\MeasurableSettingsTable; |
| use Piwik\Tracker\Failures; |
| use Piwik\Site; |
| use Piwik\Tracker\FingerprintSalt; |
| use Piwik\Tracker\Visit\ReferrerSpamFilter; |
| use Piwik\Log\LoggerInterface; |
| use Piwik\Period\Month; |
| use Piwik\SettingsPiwik; |
|
|
| class Tasks extends \Piwik\Plugin\Tasks |
| { |
| public const TRACKING_CODE_CHECK_FLAG = 'trackingCodeExistsCheck'; |
| |
| |
| |
| private $archivePurger; |
|
|
| |
| |
| |
| private $logger; |
|
|
| |
| |
| |
| private $trackingFailures; |
|
|
| public function __construct(ArchivePurger $archivePurger, LoggerInterface $logger, Failures $failures) |
| { |
| $this->archivePurger = $archivePurger; |
| $this->logger = $logger; |
| $this->trackingFailures = $failures; |
| } |
|
|
| public function schedule() |
| { |
| |
| |
| $this->daily('invalidateOutdatedArchives', null, self::HIGH_PRIORITY); |
|
|
| $this->daily('deleteOldFingerprintSalts', null, self::HIGH_PRIORITY); |
|
|
| |
| $this->daily('purgeOutdatedArchives', null, self::HIGH_PRIORITY); |
|
|
| |
| $this->daily('purgeInvalidatedArchives', null, self::LOW_PRIORITY); |
| $this->daily('purgeBrokenArchivesCurrentMonth', null, self::LOW_PRIORITY); |
| $this->daily('purgeInvalidationsForDeletedSites', null, self::LOW_PRIORITY); |
|
|
| $this->weekly('purgeOrphanedArchives', null, self::NORMAL_PRIORITY); |
|
|
| |
| $this->monthly('optimizeArchiveTable', null, self::LOWEST_PRIORITY); |
|
|
| $this->daily('cleanupTrackingFailures', null, self::LOWEST_PRIORITY); |
| $this->weekly('notifyTrackingFailures', null, self::LOWEST_PRIORITY); |
|
|
| $generalConfig = Config::getInstance()->Tracker; |
| if ((SettingsPiwik::isInternetEnabled() === true) && $generalConfig['enable_spam_filter']) { |
| $this->weekly('updateSpammerList'); |
| } |
|
|
| $this->scheduleTrackingCodeReminderChecks(); |
| } |
|
|
| public function purgeInvalidationsForDeletedSites() |
| { |
| $coreModel = new CoreModel(); |
| $coreModel->deleteInvalidationsForDeletedSites(); |
| } |
|
|
| public function deleteOldFingerprintSalts() |
| { |
| StaticContainer::get(FingerprintSalt::class)->deleteOldSalts(); |
| } |
|
|
| public function invalidateOutdatedArchives() |
| { |
| if (!Rules::isBrowserTriggerEnabled()) { |
| $this->logger->info("Browser triggered archiving disabled, archives will be invalidated during core:archive."); |
| return; |
| } |
|
|
| $idSites = Request::processRequest('SitesManager.getAllSitesId'); |
| $cronArchive = new CronArchive(); |
| foreach ($idSites as $idSite) { |
| $cronArchive->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain($idSite); |
| } |
| } |
|
|
| private function scheduleTrackingCodeReminderChecks() |
| { |
| $daysToTrackedVisitsCheck = (int) Config::getInstance()->General['num_days_before_tracking_code_reminder']; |
| if ($daysToTrackedVisitsCheck <= 0) { |
| return; |
| } |
|
|
| |
| $sites = Request::processRequest('SitesManager.getAllSites'); |
|
|
| foreach ($sites as $site) { |
| $createdTime = Date::factory($site['ts_created']); |
| $scheduledTime = $createdTime->addDay($daysToTrackedVisitsCheck)->setTime('02:00:00'); |
|
|
| |
| |
| $isSiteOld = $createdTime->isEarlier(Date::today()->subDay($daysToTrackedVisitsCheck * 2)); |
|
|
| if ($isSiteOld || $this->hasTrackingCodeReminderRun($site['idsite'])) { |
| continue; |
| } |
|
|
| $schedule = new SpecificTime($scheduledTime->getTimestamp()); |
| $this->custom($this, 'checkSiteHasTrackedVisits', $site['idsite'], $schedule); |
| } |
| } |
|
|
| public function checkSiteHasTrackedVisits($idSite) |
| { |
| $this->rememberTrackingCodeReminderRan($idSite); |
|
|
| if (!SitesManager::shouldPerformEmptySiteCheck($idSite)) { |
| return; |
| } |
|
|
| if (SitesManager::hasTrackedAnyTraffic($idSite)) { |
| return; |
| } |
|
|
| |
| $creatingUser = Site::getCreatorLoginFor($idSite); |
| if (empty($creatingUser)) { |
| return; |
| } |
|
|
| try { |
| $user = Request::processRequest('UsersManager.getUser', [ |
| 'userLogin' => $creatingUser, |
| ]); |
| } catch (\Exception $e) { |
| return; |
| } |
|
|
| if (empty($user['email'])) { |
| return; |
| } |
|
|
| $container = StaticContainer::getContainer(); |
| $email = $container->make(JsTrackingCodeMissingEmail::class, array( |
| 'login' => $user['login'], |
| 'emailAddress' => $user['email'], |
| 'idSite' => $idSite, |
| )); |
| $email->send(); |
| } |
|
|
| private function hasTrackingCodeReminderRun($idSite) |
| { |
| $table = new MeasurableSettingsTable($idSite, 'CoreAdminHome'); |
| $settings = $table->load(); |
| return !empty($settings[self::TRACKING_CODE_CHECK_FLAG]); |
| } |
|
|
| private function rememberTrackingCodeReminderRan($idSite) |
| { |
| $table = new MeasurableSettingsTable($idSite, 'CoreAdminHome'); |
| $settings = $table->load(); |
| $settings[self::TRACKING_CODE_CHECK_FLAG] = 1; |
| $table->save($settings); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function cleanupTrackingFailures() |
| { |
| |
| $this->trackingFailures->removeFailuresOlderThanDays(Failures::CLEANUP_OLD_FAILURES_DAYS); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function notifyTrackingFailures() |
| { |
| $this->cleanupTrackingFailures(); |
| $failures = $this->trackingFailures->getAllFailures(); |
| $general = Config::getInstance()->General; |
| if (!empty($failures) && $general['enable_tracking_failures_notification']) { |
| $superUsers = Piwik::getAllSuperUserAccessEmailAddresses(); |
| foreach ($superUsers as $login => $email) { |
| $email = new TrackingFailuresEmail($login, $email, count($failures)); |
| $email->send(); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| public function purgeOutdatedArchives() |
| { |
| if ($this->willPurgingCausePotentialProblemInUI() && !Rules::$disablePureOutdatedArchive) { |
| $this->logger->info("Purging temporary archives: skipped (browser triggered archiving not enabled & not running after core:archive)"); |
| return false; |
| } |
|
|
| $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); |
|
|
| $this->logger->info("Purging archives in {tableCount} archive tables.", array('tableCount' => count($archiveTables))); |
|
|
| |
| |
| $datesPurged = array(); |
|
|
| foreach ($archiveTables as $table) { |
| $date = ArchiveTableCreator::getDateFromTableName($table); |
| [$year, $month] = explode('_', $date); |
|
|
| |
| if ($year > 1990) { |
| if (empty($datesPurged[$date])) { |
| try { |
| $dateObj = Date::factory("$year-$month-15"); |
| } catch (\Exception $e) { |
| continue; |
| } |
|
|
| $this->archivePurger->purgeOutdatedArchives($dateObj); |
| $this->archivePurger->purgeArchivesWithPeriodRange($dateObj); |
|
|
| $datesPurged[$date] = true; |
| } else { |
| $this->logger->debug("Date {date} already purged.", array('date' => $date)); |
| } |
| } else { |
| $this->logger->info("Skipping purging of archive tables *_{year}_{month}, year <= 1990.", array('year' => $year, 'month' => $month)); |
| } |
| } |
|
|
| return true; |
| } |
|
|
| public function purgeInvalidatedArchives() |
| { |
| $purgedDates = []; |
|
|
| $archivesToPurge = new ArchivesToPurgeDistributedList(); |
| foreach ($archivesToPurge->getAllAsDates() as $date) { |
| $this->archivePurger->purgeInvalidatedArchivesFrom($date); |
|
|
| $archivesToPurge->removeDate($date); |
|
|
| $purgedDates[$date->toString('Y-m')] = true; |
| } |
|
|
| |
| $today = Date::today(); |
| $todayStr = $today->toString('Y-m'); |
| if (empty($purgedDates[$todayStr])) { |
| $this->archivePurger->purgeInvalidatedArchivesFrom($today); |
| $purgedDates[$todayStr] = true; |
| } |
|
|
| |
| $yesterday = Date::yesterday(); |
| $yesterdayStr = $yesterday->toString('Y-m'); |
| if (empty($purgedDates[$yesterdayStr])) { |
| $this->archivePurger->purgeInvalidatedArchivesFrom($yesterday); |
| } |
|
|
| |
| $yearStart = $today->toString('Y-01'); |
| if (empty($purgedDates[$yearStart])) { |
| $this->archivePurger->purgeInvalidatedArchivesFrom(Date::factory($yearStart . '-01')); |
| } |
| } |
|
|
| |
| |
| |
| |
| public function purgeBrokenArchivesCurrentMonth(): bool |
| { |
| if ($this->willPurgingCausePotentialProblemInUI() && !Rules::$disablePureOutdatedArchive) { |
| $this->logger->info("Purging broken archives: skipped (browser triggered archiving not enabled & not running after core:archive)"); |
| return false; |
| } |
|
|
| $archiveTable = ArchiveTableCreator::getLatestArchiveTableInstalled(); |
|
|
| if (!empty($archiveTable)) { |
| $this->logger->info("Purging broken archives in {table} archive table.", ['table' => $archiveTable]); |
|
|
| $date = ArchiveTableCreator::getDateFromTableName($archiveTable); |
| [$year, $month] = explode('_', $date); |
|
|
| $monthPeriod = new Month(Date::factory("$year-$month-01")); |
|
|
| $this->archivePurger->purgeBrokenArchives($monthPeriod); |
| } else { |
| $this->logger->info("No archive tables found"); |
| } |
|
|
| return true; |
| } |
|
|
| public function optimizeArchiveTable() |
| { |
| $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); |
| Db\Schema::getInstance()->optimizeTables($archiveTables); |
| } |
|
|
| |
| |
| |
| |
| |
| public function updateSpammerList() |
| { |
| $url = 'https://raw.githubusercontent.com/matomo-org/referrer-spam-list/master/spammers.txt'; |
| $list = Http::sendHttpRequest($url, 30); |
|
|
| if (preg_match('/[<>&?"\']/', $list)) { |
| throw new \Exception(sprintf( |
| 'The spammers list downloaded from %s contains unexpected characters, considering it a fail', |
| $url |
| )); |
| } |
|
|
| $list = preg_split("/\r\n|\n|\r/", $list); |
| if (count($list) < 10) { |
| throw new \Exception(sprintf( |
| 'The spammers list downloaded from %s contains less than 10 entries, considering it a fail', |
| $url |
| )); |
| } |
|
|
| Option::set(ReferrerSpamFilter::OPTION_STORAGE_NAME, serialize($list)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function purgeOrphanedArchives() |
| { |
| $eightDaysAgo = Date::factory('now')->subDay(8); |
| $model = new Model(); |
| $deletedSegments = $model->getSegmentsDeletedSince($eightDaysAgo); |
|
|
| $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled('numeric'); |
|
|
| $datesPurged = array(); |
| foreach ($archiveTables as $table) { |
| $date = ArchiveTableCreator::getDateFromTableName($table); |
| [$year, $month] = explode('_', $date); |
|
|
| try { |
| $dateObj = Date::factory("$year-$month-15"); |
| } catch (\Exception $e) { |
| continue; |
| } |
|
|
| $this->archivePurger->purgeDeletedSiteArchives($dateObj); |
| if (count($deletedSegments)) { |
| $this->archivePurger->purgeDeletedSegmentArchives($dateObj, $deletedSegments); |
| } |
|
|
| $datesPurged[$date] = true; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function willPurgingCausePotentialProblemInUI() |
| { |
| return !Rules::isRequestAuthorizedToArchive(); |
| } |
| } |
|
|