/** * When tracking data in the past (using Tracking API), this function * can be used to invalidate reports for the idSites and dates where new data * was added. * DEV: If you call this API, the UI should display the data correctly, but will process * in real time, which could be very slow after large data imports. * After calling this function via REST, you can manually force all data * to be reprocessed by visiting the script as the Super User: * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0 * We recommend to use an hourly schedule of the script. * More information: http://piwik.org/setup-auto-archiving/ * * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates * @param string $dates Comma separated list of dates to invalidate for all these websites * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period. * Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see * other periods reports processed.. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); $invalidator = new ArchiveInvalidator(); $output = $invalidator->markArchivesAsInvalidated($idSites, $dates, $period); Site::clearCache(); return $output; }
/** * When tracking data in the past (using Tracking API), this function * can be used to invalidate reports for the idSites and dates where new data * was added. * DEV: If you call this API, the UI should display the data correctly, but will process * in real time, which could be very slow after large data imports. * After calling this function via REST, you can manually force all data * to be reprocessed by visiting the script as the Super User: * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0 * We recommend to use an hourly schedule of the script. * More information: http://piwik.org/setup-auto-archiving/ * * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates * @param string $dates Comma separated list of dates to invalidate for all these websites * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period. * Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see * other periods reports processed.. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates); $invalidator = new ArchiveInvalidator(); $output = $invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period); if ($invalidDates) { $output[] = 'Warning: some of the Dates to invalidate were invalid: ' . implode(", ", $invalidDates) . ". Piwik simply ignored those and proceeded with the others."; } Site::clearCache(); return $output; }
/** * Main Plugin Index * * @return mixed * @throws \Exception */ public function index() { Piwik::checkUserHasSomeAdminAccess(); if (isset($_SERVER['REQUEST_METHOD']) && 'POST' == $_SERVER['REQUEST_METHOD']) { // Cannot use Common::getRequestVar, because the function remove whitespaces and newline breaks $postedSiteData = isset($_POST['site']) ? $_POST['site'] : null; if (is_array($postedSiteData) && count($postedSiteData) > 0) { foreach ($postedSiteData as $id => $site) { if (!isset($site['css'], $site['file'])) { continue; } // Check URL if (!UrlHelper::isLookLikeUrl($site['file'])) { $site['file'] = null; } API::getInstance()->saveSite($id, $site['css'], $site['file']); } // Redirect to, clear POST vars $this->redirectToIndex('CustomOptOut', 'index'); return; } } $view = new View('@CustomOptOut/index.twig'); Site::clearCache(); if (Piwik::hasUserSuperUserAccess()) { $sitesRaw = APISiteManager::getInstance()->getAllSites(); } else { $sitesRaw = APISiteManager::getInstance()->getSitesWithAdminAccess(); } // Gets sites after Site.setSite hook was called $sites = array_values(Site::getSites()); if (count($sites) != count($sitesRaw)) { throw new \Exception("One or more website are missing or invalid."); } foreach ($sites as &$site) { $site['alias_urls'] = APISiteManager::getInstance()->getSiteUrlsFromId($site['idsite']); } $view->adminSites = $sites; $view->adminSitesCount = count($sites); $view->language = LanguagesManager::getLanguageCodeForCurrentUser(); $view->isEditorEnabled = API::getInstance()->isCssEditorEnabled(); $view->editorTheme = API::getInstance()->getEditorTheme(); $view->showOldLinks = false; $this->setBasicVariablesView($view); return $view->render(); }
/** * Main view showing listing of websites and settings */ public function index() { $view = new View('@SitesManager/index'); Site::clearCache(); if (Piwik::isUserIsSuperUser()) { $sitesRaw = API::getInstance()->getAllSites(); } else { $sitesRaw = API::getInstance()->getSitesWithAdminAccess(); } // Gets sites after Site.setSite hook was called $sites = array_values(Site::getSites()); if (count($sites) != count($sitesRaw)) { throw new Exception("One or more website are missing or invalid."); } foreach ($sites as &$site) { $site['alias_urls'] = API::getInstance()->getSiteUrlsFromId($site['idsite']); $site['excluded_ips'] = explode(',', $site['excluded_ips']); $site['excluded_parameters'] = explode(',', $site['excluded_parameters']); $site['excluded_user_agents'] = explode(',', $site['excluded_user_agents']); } $view->adminSites = $sites; $view->adminSitesCount = count($sites); $timezones = API::getInstance()->getTimezonesList(); $view->timezoneSupported = SettingsServer::isTimezoneSupportEnabled(); $view->timezones = Common::json_encode($timezones); $view->defaultTimezone = API::getInstance()->getDefaultTimezone(); $view->currencies = Common::json_encode(API::getInstance()->getCurrencyList()); $view->defaultCurrency = API::getInstance()->getDefaultCurrency(); $view->utcTime = Date::now()->getDatetime(); $excludedIpsGlobal = API::getInstance()->getExcludedIpsGlobal(); $view->globalExcludedIps = str_replace(',', "\n", $excludedIpsGlobal); $excludedQueryParametersGlobal = API::getInstance()->getExcludedQueryParametersGlobal(); $view->globalExcludedQueryParameters = str_replace(',', "\n", $excludedQueryParametersGlobal); $globalExcludedUserAgents = API::getInstance()->getExcludedUserAgentsGlobal(); $view->globalExcludedUserAgents = str_replace(',', "\n", $globalExcludedUserAgents); $view->globalSearchKeywordParameters = API::getInstance()->getSearchKeywordParametersGlobal(); $view->globalSearchCategoryParameters = API::getInstance()->getSearchCategoryParametersGlobal(); $view->isSearchCategoryTrackingEnabled = \Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables'); $view->allowSiteSpecificUserAgentExclude = API::getInstance()->isSiteSpecificUserAgentExcludeEnabled(); $view->globalKeepURLFragments = API::getInstance()->getKeepURLFragmentsGlobal(); $view->currentIpAddress = IP::getIpFromHeader(); $view->showAddSite = (bool) Common::getRequestVar('showaddsite', false); $this->setBasicVariablesView($view); return $view->render(); }
/** * Creates a new website * * @param string $timezone * @return Site */ private function _createWebsite($timezone = 'UTC') { $idSite = API::getInstance()->addSite("site1", array("http://piwik.net"), $ecommerce = 0, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = "", $excludedQueryParameters = "", $timezone); Site::clearCache(); return new Site($idSite); }
<?php /** * Proxy to normal piwik.php, but in testing mode * * - Use the tests database to record Tracking data * - Allows to overwrite the Visitor IP, and Server datetime * */ use Piwik\DataTable\Manager; use Piwik\Option; use Piwik\Plugins\UserCountry\LocationProvider\GeoIp; use Piwik\Site; use Piwik\Tracker\Cache; use Piwik\Tracker; require realpath(dirname(__FILE__)) . "/includes.php"; // Wrapping the request inside ob_start() calls to ensure that the Test // calling us waits for the full request to process before unblocking ob_start(); try { Piwik_TestingEnvironment::addHooks(); GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files'; Tracker::setTestEnvironment(); Manager::getInstance()->deleteAll(); Option::clearCache(); Site::clearCache(); include PIWIK_INCLUDE_PATH . '/piwik.php'; } catch (Exception $ex) { echo "Unexpected error during tracking: " . $ex->getMessage() . "\n" . $ex->getTraceAsString() . "\n"; } ob_end_flush();
/** * When tracking data in the past (using Tracking API), this function * can be used to invalidate reports for the idSites and dates where new data * was added. * DEV: If you call this API, the UI should display the data correctly, but will process * in real time, which could be very slow after large data imports. * After calling this function via REST, you can manually force all data * to be reprocessed by visiting the script as the Super User: * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0 * We recommend to use an hourly schedule of the script. * More information: http://piwik.org/setup-auto-archiving/ * * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates * @param string $dates Comma separated list of dates to invalidate for all these websites * @throws Exception * @return array */ public function invalidateArchivedReports($idSites, $dates) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); // Ensure the specified dates are valid $toInvalidate = $invalidDates = array(); $dates = explode(',', trim($dates)); $dates = array_unique($dates); foreach ($dates as $theDate) { $theDate = trim($theDate); try { $date = Date::factory($theDate); } catch (Exception $e) { $invalidDates[] = $theDate; continue; } if ($date->toString() == $theDate) { $toInvalidate[] = $date; } else { $invalidDates[] = $theDate; } } // If using the feature "Delete logs older than N days"... $purgeDataSettings = PrivacyManager::getPurgeDataSettings(); $logsAreDeletedBeforeThisDate = $purgeDataSettings['delete_logs_schedule_lowest_interval']; $logsDeleteEnabled = $purgeDataSettings['delete_logs_enable']; $minimumDateWithLogs = false; if ($logsDeleteEnabled && $logsAreDeletedBeforeThisDate) { $minimumDateWithLogs = Date::factory('today')->subDay($logsAreDeletedBeforeThisDate); } // Given the list of dates, process which tables they should be deleted from $minDate = false; $warningDates = $processedDates = array(); /* @var $date Date */ foreach ($toInvalidate as $date) { // we should only delete reports for dates that are more recent than N days if ($minimumDateWithLogs && $date->isEarlier($minimumDateWithLogs)) { $warningDates[] = $date->toString(); } else { $processedDates[] = $date->toString(); } $month = $date->toString('Y_m'); // For a given date, we must invalidate in the monthly archive table $datesByMonth[$month][] = $date->toString(); // But also the year stored in January $year = $date->toString('Y_01'); $datesByMonth[$year][] = $date->toString(); // but also weeks overlapping several months stored in the month where the week is starting /* @var $week Week */ $week = Period\Factory::build('week', $date); $weekAsString = $week->getDateStart()->toString('Y_m'); $datesByMonth[$weekAsString][] = $date->toString(); // Keep track of the minimum date for each website if ($minDate === false || $date->isEarlier($minDate)) { $minDate = $date; } } if (empty($minDate)) { throw new Exception("Check the 'dates' parameter is a valid date."); } // In each table, invalidate day/week/month/year containing this date $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); foreach ($archiveTables as $table) { // Extract Y_m from table name $suffix = ArchiveTableCreator::getDateFromTableName($table); if (!isset($datesByMonth[$suffix])) { continue; } // Dates which are to be deleted from this table $datesToDeleteInTable = $datesByMonth[$suffix]; // Build one statement to delete all dates from the given table $sql = $bind = array(); $datesToDeleteInTable = array_unique($datesToDeleteInTable); foreach ($datesToDeleteInTable as $dateToDelete) { $sql[] = '(date1 <= ? AND ? <= date2)'; $bind[] = $dateToDelete; $bind[] = $dateToDelete; } $sql = implode(" OR ", $sql); $query = "DELETE FROM {$table} " . " WHERE ( {$sql} ) " . " AND idsite IN (" . implode(",", $idSites) . ")"; Db::query($query, $bind); } \Piwik\Plugins\SitesManager\API::getInstance()->updateSiteCreatedTime($idSites, $minDate); // Force to re-process data for these websites in the next cron core:archive command run $invalidatedIdSites = self::getWebsiteIdsToInvalidate(); $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites); $invalidatedIdSites = array_unique($invalidatedIdSites); $invalidatedIdSites = array_values($invalidatedIdSites); Option::set(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites)); Site::clearCache(); $output = array(); // output logs if ($warningDates) { $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' . implode(", ", $warningDates) . "\n The last day with logs is " . $minimumDateWithLogs . ". " . "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'."; } $output[] = "Success. The following dates were invalidated successfully: " . implode(", ", $processedDates); return $output; }
private function postUpdateWebsite($idSite) { Site::clearCache(); Cache::regenerateCacheWebsiteAttributes($idSite); SiteUrls::clearSitesCache(); }
/** * Creates a website, then sets its creation date to a day earlier than specified dateTime * Useful to create a website now, but force data to be archived back in the past. * * @param string $dateTime eg '2010-01-01 12:34:56' * @param int $ecommerce * @param string $siteName * * @param bool|string $siteUrl * @param int $siteSearch * @param null|string $searchKeywordParameters * @param null|string $searchCategoryParameters * @return int idSite of website created */ public static function createWebsite($dateTime, $ecommerce = 0, $siteName = false, $siteUrl = false, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $timezone = null) { if ($siteName === false) { $siteName = self::DEFAULT_SITE_NAME; } $idSite = APISitesManager::getInstance()->addSite($siteName, $siteUrl === false ? "http://piwik.net/" : $siteUrl, $ecommerce, $siteSearch, $searchKeywordParameters, $searchCategoryParameters, $ips = null, $excludedQueryParameters = null, $timezone, $currency = null); // Manually set the website creation date to a day earlier than the earliest day we record stats for Db::get()->update(Common::prefixTable("site"), array('ts_created' => Date::factory($dateTime)->subDay(1)->getDatetime()), "idsite = {$idSite}"); // Clear the memory Website cache Site::clearCache(); return $idSite; }
/** * Invalidates report data, forcing it to be recomputed during the next archiving run. * * Note: This is done automatically when tracking or importing visits in the past. * * @param string $idSites Comma separated list of site IDs to invalidate reports for. * @param string $dates Comma separated list of dates of periods to invalidate reports for. * @param string|bool $period The type of period to invalidate: either 'day', 'week', 'month', 'year', 'range'. * The command will automatically cascade up, invalidating reports for parent periods as * well. So invalidating a day will invalidate the week it's in, the month it's in and the * year it's in, since those periods will need to be recomputed too. * @param string|bool $segment Optional. The segment to invalidate reports for. * @param bool $cascadeDown If true, child periods will be invalidated as well. So if it is requested to invalidate * a month, then all the weeks and days within that month will also be invalidated. But only * if this parameter is set. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false, $segment = false, $cascadeDown = false) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); if (!empty($segment)) { $segment = new Segment($segment, $idSites); } else { $segment = null; } list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates); $invalidationResult = $this->invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period, $segment, (bool) $cascadeDown); $output = $invalidationResult->makeOutputLogs(); if ($invalidDates) { $output[] = 'Warning: some of the Dates to invalidate were invalid: ' . implode(", ", $invalidDates) . ". Piwik simply ignored those and proceeded with the others."; } Site::clearCache(); // TODO: is this needed? it shouldn't be needed... return $invalidationResult->makeOutputLogs(); }
/** * Fetches the list of sites which names match the string pattern * * @param string $pattern * @param bool $_restrictSitesToLogin * @return array|string */ private function getSitesIdFromPattern($pattern, $_restrictSitesToLogin) { // First clear cache Site::clearCache(); if (empty($pattern)) { /** @var Scheduler $scheduler */ $scheduler = StaticContainer::getContainer()->get('Piwik\\Scheduler\\Scheduler'); // Then, warm the cache with only the data we should have access to if (Piwik::hasUserSuperUserAccess() && !$scheduler->isRunningTask()) { APISitesManager::getInstance()->getAllSites(); } else { APISitesManager::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin); } } else { $sites = Request::processRequest('SitesManager.getPatternMatchSites', array('pattern' => $pattern, 'showColumns' => '', 'hideColumns' => '', 'serialize' => 0, 'format' => 'original')); if (!empty($sites)) { $idSites = array(); foreach ($sites as $site) { $idSites[] = $site['idsite']; } $model = new ModelSitesManager(); $sites = $model->getSitesFromIds($idSites); // getPatternMatchSites does not return all sites information... Site::setSitesFromArray($sites); } } // Both calls above have called Site::setSitesFromArray. We now get these sites: $sitesToProblablyAdd = Site::getSites(); return $sitesToProblablyAdd; }
public function tearDown() { parent::tearDown(); Manager::getInstance()->deleteAll(); Option::clearCache(); Site::clearCache(); Cache::deleteTrackerCache(); ArchiveTableCreator::clear(); $tempTableName = Common::prefixTable(LogDataPurger::TEMP_TABLE_NAME); Db::query("DROP TABLE IF EXISTS " . $tempTableName); }
private function buildDataTable($idSitesOrIdSite, $period, $date, $segment, $_restrictSitesToLogin, $enhanced, $multipleWebsitesRequested) { $allWebsitesRequested = $idSitesOrIdSite == 'all'; if ($allWebsitesRequested) { // First clear cache Site::clearCache(); // Then, warm the cache with only the data we should have access to if (Piwik::isUserIsSuperUser() && !TaskScheduler::isTaskBeingExecuted()) { $sites = APISitesManager::getInstance()->getAllSites(); } else { $sites = APISitesManager::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin); } // Both calls above have called Site::setSitesFromArray. We now get these sites: $sitesToProblablyAdd = Site::getSites(); } else { $sitesToProblablyAdd = array(APISitesManager::getInstance()->getSiteFromId($idSitesOrIdSite)); } // build the archive type used to query archive data $archive = Archive::build($idSitesOrIdSite, $period, $date, $segment, $_restrictSitesToLogin); // determine what data will be displayed $fieldsToGet = array(); $columnNameRewrites = array(); $apiECommerceMetrics = array(); $apiMetrics = API::getApiMetrics($enhanced); foreach ($apiMetrics as $metricName => $metricSettings) { $fieldsToGet[] = $metricSettings[self::METRIC_RECORD_NAME_KEY]; $columnNameRewrites[$metricSettings[self::METRIC_RECORD_NAME_KEY]] = $metricName; if ($metricSettings[self::METRIC_IS_ECOMMERCE_KEY]) { $apiECommerceMetrics[$metricName] = $metricSettings; } } // get the data // $dataTable instanceOf Set $dataTable = $archive->getDataTableFromNumeric($fieldsToGet); $dataTable = $this->mergeDataTableMapAndPopulateLabel($idSitesOrIdSite, $multipleWebsitesRequested, $dataTable); if ($dataTable instanceof DataTable\Map) { foreach ($dataTable->getDataTables() as $table) { $this->addMissingWebsites($table, $fieldsToGet, $sitesToProblablyAdd); } } else { $this->addMissingWebsites($dataTable, $fieldsToGet, $sitesToProblablyAdd); } // calculate total visits/actions/revenue $this->setMetricsTotalsMetadata($dataTable, $apiMetrics); // if the period isn't a range & a lastN/previousN date isn't used, we get the same // data for the last period to show the evolution of visits/actions/revenue list($strLastDate, $lastPeriod) = Range::getLastDate($date, $period); if ($strLastDate !== false) { if ($lastPeriod !== false) { // NOTE: no easy way to set last period date metadata when range of dates is requested. // will be easier if DataTable\Map::metadata is removed, and metadata that is // put there is put directly in DataTable::metadata. $dataTable->setMetadata(self::getLastPeriodMetadataName('date'), $lastPeriod); } $pastArchive = Archive::build($idSitesOrIdSite, $period, $strLastDate, $segment, $_restrictSitesToLogin); $pastData = $pastArchive->getDataTableFromNumeric($fieldsToGet); $pastData = $this->mergeDataTableMapAndPopulateLabel($idSitesOrIdSite, $multipleWebsitesRequested, $pastData); // use past data to calculate evolution percentages $this->calculateEvolutionPercentages($dataTable, $pastData, $apiMetrics); $this->setPastDataMetadata($dataTable, $pastData, $apiMetrics); } // remove eCommerce related metrics on non eCommerce Piwik sites // note: this is not optimal in terms of performance: those metrics should not be retrieved in the first place if ($enhanced) { if ($dataTable instanceof DataTable\Map) { foreach ($dataTable->getDataTables() as $table) { $this->removeEcommerceRelatedMetricsOnNonEcommercePiwikSites($table, $apiECommerceMetrics); } } else { $this->removeEcommerceRelatedMetricsOnNonEcommercePiwikSites($dataTable, $apiECommerceMetrics); } } // move the site id to a metadata column $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'idsite')); // set the label of each row to the site name if ($multipleWebsitesRequested) { $dataTable->filter('ColumnCallbackReplace', array('label', '\\Piwik\\Site::getNameFor')); } else { $dataTable->filter('ColumnDelete', array('label')); } // replace record names with user friendly metric names $dataTable->filter('ReplaceColumnNames', array($columnNameRewrites)); // Ensures data set sorted, for Metadata output $dataTable->filter('Sort', array(self::NB_VISITS_METRIC, 'desc', $naturalSort = false)); // filter rows without visits // note: if only one website is queried and there are no visits, we can not remove the row otherwise // ResponseBuilder throws 'Call to a member function getColumns() on a non-object' if ($multipleWebsitesRequested && !$enhanced) { $dataTable->filter('ColumnCallbackDeleteRow', array(self::NB_VISITS_METRIC, function ($value) { return $value == 0; })); } return $dataTable; }
private function invalidatedReportsIfNeeded() { $siteIdsRequested = $this->getSiteIdsThatAreRequestedInThisArchiveButWereNotInvalidatedYet(); if (empty($siteIdsRequested)) { return; // all requested site ids were already handled } $sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); foreach ($sitesPerDays as $date => $siteIds) { if (empty($siteIds)) { continue; } $siteIdsToActuallyInvalidate = array_intersect($siteIds, $siteIdsRequested); if (empty($siteIdsToActuallyInvalidate)) { continue; // all site ids that should be handled are already handled } try { $this->invalidator->markArchivesAsInvalidated($siteIdsToActuallyInvalidate, array(Date::factory($date)), false); } catch (\Exception $e) { Site::clearCache(); throw $e; } } Site::clearCache(); }