diff --git a/CHANGELOG.md b/CHANGELOG.md index 25883d76efa59be8cc42b5efc9873024ae87de29..89c4882740150b3bdff53bde610305405b171610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ If the tracker is not initialised correctly, the browser console will display th * The creation of settings has slightly changed to improve performance. It is now possible to create new settings via the method `$this->makeSetting()` see `Piwik\Plugins\ExampleSettingsPlugin\SystemSettings` for an example. * It is no longer possible to define an introduction text for settings. * If requesting multipe periods for one report, the keys that define the range are no longer translated. For example before 3.0 an API response may contain: `<result date="From 2010-02-01 to 2010-02-07">` which is now `<result date="2010-02-01,2010-02-07">`. +* The method `Piwik\Plugin\Archiver::shouldRunEvenWhenNoVisits()` has been added. By overwriting this method and returning true, a plugin archiver can force the archiving to run even when there was no visit for the website/date/period/segment combination (by default, archivers are skipped when there is no visit). * The following deprecated events have been removed as mentioned. * `Tracker.existingVisitInformation` Use [dimensions](http://developer.piwik.org/guides/dimensions) instead of using `Tracker` events. * `Tracker.newVisitorInformation` diff --git a/core/ArchiveProcessor/Loader.php b/core/ArchiveProcessor/Loader.php index c08bf2c52cc26e45cd10e73b3a4c6a814736623d..e6c5015aabb3e51d299eb493fcfed6e2d0c9c14e 100644 --- a/core/ArchiveProcessor/Loader.php +++ b/core/ArchiveProcessor/Loader.php @@ -73,7 +73,7 @@ class Loader list($visits, $visitsConverted) = $this->prepareCoreMetricsArchive($visits, $visitsConverted); list($idArchive, $visits) = $this->prepareAllPluginsArchive($visits, $visitsConverted); - if ($this->isThereSomeVisits($visits)) { + if ($this->isThereSomeVisits($visits) || PluginsArchiver::doesAnyPluginArchiveWithoutVisits()) { return $idArchive; } return false; @@ -120,11 +120,8 @@ class Loader $visitsConverted = $metrics['nb_visits_converted']; } - if ($this->isThereSomeVisits($visits) - || $this->shouldArchiveForSiteEvenWhenNoVisits() - ) { - $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted); - } + $forceArchivingWithoutVisits = !$this->isThereSomeVisits($visits) && $this->shouldArchiveForSiteEvenWhenNoVisits(); + $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits); $idArchive = $pluginsArchiver->finalizeArchive(); diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php index 325b529aa37f68147b0858a94e11288ef90709f9..42b497c4b74d4f150bb118f0d083365d360176ac 100644 --- a/core/ArchiveProcessor/PluginsArchiver.php +++ b/core/ArchiveProcessor/PluginsArchiver.php @@ -94,14 +94,14 @@ class PluginsArchiver * Instantiates the Archiver class in each plugin that defines it, * and triggers Aggregation processing on these plugins. */ - public function callAggregateAllPlugins($visits, $visitsConverted) + public function callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits = false) { Log::debug("PluginsArchiver::%s: Initializing archiving process for all plugins [visits = %s, visits converted = %s]", __FUNCTION__, $visits, $visitsConverted); $this->archiveProcessor->setNumberOfVisits($visits, $visitsConverted); - $archivers = $this->getPluginArchivers(); + $archivers = static::getPluginArchivers(); foreach ($archivers as $pluginName => $archiverClass) { // We clean up below all tables created during this function call (and recursive calls) @@ -111,7 +111,12 @@ class PluginsArchiver $archiver = $this->makeNewArchiverObject($archiverClass, $pluginName); if (!$archiver->isEnabled()) { - Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s'.", __FUNCTION__, $pluginName); + Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (disabled).", __FUNCTION__, $pluginName); + continue; + } + + if (!$forceArchivingWithoutVisits && !$visits && !$archiver->shouldRunEvenWhenNoVisits()) { + Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (no visits).", __FUNCTION__, $pluginName); continue; } @@ -161,12 +166,28 @@ class PluginsArchiver return $this->archiveWriter->getIdArchive(); } + /** + * Returns if any plugin archiver archives without visits + */ + public static function doesAnyPluginArchiveWithoutVisits() + { + $archivers = static::getPluginArchivers(); + + foreach ($archivers as $pluginName => $archiverClass) { + if ($archiverClass::shouldRunEvenWhenNoVisits()) { + return true; + } + } + + return false; + } + /** * Loads Archiver class from any plugin that defines one. * * @return \Piwik\Plugin\Archiver[] */ - protected function getPluginArchivers() + protected static function getPluginArchivers() { if (empty(static::$archivers)) { $pluginNames = \Piwik\Plugin\Manager::getInstance()->getActivatedPlugins(); diff --git a/core/CronArchive.php b/core/CronArchive.php index bac5aabcda926660cc80169959186e7b03816acb..cc8a1de71c85a459f103d7ab90930ccc81977b22 100644 --- a/core/CronArchive.php +++ b/core/CronArchive.php @@ -9,6 +9,7 @@ namespace Piwik; use Exception; +use Piwik\ArchiveProcessor\PluginsArchiver; use Piwik\ArchiveProcessor\Rules; use Piwik\Archiver\Request; use Piwik\Container\StaticContainer; @@ -816,9 +817,11 @@ class CronArchive $this->requests++; $this->processed++; + $shouldArchiveWithoutVisits = PluginsArchiver::doesAnyPluginArchiveWithoutVisits(); + // If there is no visit today and we don't need to process this website, we can skip remaining archives if ( - 0 == $visitsToday + 0 == $visitsToday && !$shouldArchiveWithoutVisits && !$shouldArchivePeriods ) { $this->logger->info("Skipped website id $idSite, no visit today, " . $timerWebsite->__toString()); @@ -827,7 +830,7 @@ class CronArchive return false; } - if (0 == $visitsLastDays + if (0 == $visitsLastDays && !$shouldArchiveWithoutVisits && !$shouldArchivePeriods && $this->shouldArchiveAllSites ) { diff --git a/core/Plugin/Archiver.php b/core/Plugin/Archiver.php index 90e325bc8e14a7f0a99ba5f15738104e6c44a79a..fd247e0af65c6736759e216a33360d34a7f4ef6c 100644 --- a/core/Plugin/Archiver.php +++ b/core/Plugin/Archiver.php @@ -141,4 +141,16 @@ abstract class Archiver { return $this->enabled; } + + /** + * By overwriting this method and returning true, a plugin archiver can force the archiving to run even when there + * was no visit for the website/date/period/segment combination + * (by default, archivers are skipped when there is no visit). + * + * @return bool + */ + public static function shouldRunEvenWhenNoVisits() + { + return false; + } } diff --git a/tests/PHPUnit/Integration/ArchiveProcessor/PluginsArchiverTest.php b/tests/PHPUnit/Integration/ArchiveProcessor/PluginsArchiverTest.php index eaadfefe4508f296f5772895c1245f4dea72e280..3c22fd934c5efa6dc01e8119ab63d0a9c75e82fb 100644 --- a/tests/PHPUnit/Integration/ArchiveProcessor/PluginsArchiverTest.php +++ b/tests/PHPUnit/Integration/ArchiveProcessor/PluginsArchiverTest.php @@ -37,7 +37,7 @@ class CustomArchiver extends Archiver class CustomPluginsArchiver extends PluginsArchiver { - protected function getPluginArchivers() + protected static function getPluginArchivers() { return array( 'MyPluginName' => 'Piwik\Tests\Integration\Archive\CustomArchiver' diff --git a/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php b/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php index 2063efdb253fa26001b2918740c4205ccf9ef2a3..8f541e2d5d721cb5e1a599c947d2e46296c86f83 100644 --- a/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php +++ b/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php @@ -20,6 +20,8 @@ class ArchiveWithNoVisitsTest_MockArchiver extends Archiver { public static $methodsCalled = array(); + public static $runWithoutVisits = false; + public function aggregateDayReport() { self::$methodsCalled[] = 'aggregateDayReport'; @@ -29,6 +31,11 @@ class ArchiveWithNoVisitsTest_MockArchiver extends Archiver { self::$methodsCalled[] = 'aggregateMultipleReports'; } + + public static function shouldRunEvenWhenNoVisits() + { + return self::$runWithoutVisits; + } } class ArchiveWithNoVisitsTest extends IntegrationTestCase @@ -78,6 +85,28 @@ class ArchiveWithNoVisitsTest extends IntegrationTestCase $this->assertEquals($expectedMethodCalls, ArchiveWithNoVisitsTest_MockArchiver::$methodsCalled); } + public function test_PluginArchiver_CanBeUsedToTriggerArchiving_EvenIfSiteHasNoVisits() + { + PluginsArchiver::$archivers['VisitsSummary'] = 'Piwik\Tests\Integration\ArchiveWithNoVisitsTest_MockArchiver'; + + ArchiveWithNoVisitsTest_MockArchiver::$runWithoutVisits = true; + + // initiate archiving and make sure methods are called + VisitsSummaryAPI::getInstance()->get($idSite = 1, 'week', '2012-01-01'); + + $expectedMethodCalls = array( + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateDayReport', + 'aggregateMultipleReports', + ); + $this->assertEquals($expectedMethodCalls, ArchiveWithNoVisitsTest_MockArchiver::$methodsCalled); + } + /** * @return EventDispatcher */