From ec9ba706e2d39adde233df1fc418588cc39b83c1 Mon Sep 17 00:00:00 2001
From: diosmosis <benaka@piwik.pro>
Date: Fri, 18 Sep 2015 01:49:25 -0700
Subject: [PATCH] Add event that allows plugins to archive data even if there
 are no visits for the period. Undocumented for now.

---
 core/ArchiveProcessor/Loader.php              | 29 +++++-
 core/ArchiveProcessor/PluginsArchiver.php     |  4 +-
 tests/PHPUnit/Framework/Fixture.php           |  2 +
 .../Integration/ArchiveWithNoVisitsTest.php   | 88 +++++++++++++++++++
 4 files changed, 121 insertions(+), 2 deletions(-)
 create mode 100644 tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php

diff --git a/core/ArchiveProcessor/Loader.php b/core/ArchiveProcessor/Loader.php
index f4c139b1ea..c08bf2c52c 100644
--- a/core/ArchiveProcessor/Loader.php
+++ b/core/ArchiveProcessor/Loader.php
@@ -9,10 +9,12 @@
 namespace Piwik\ArchiveProcessor;
 
 use Piwik\Archive;
+use Piwik\Cache;
 use Piwik\Config;
 use Piwik\DataAccess\ArchiveSelector;
 use Piwik\Date;
 use Piwik\Period;
+use Piwik\Piwik;
 
 /**
  * This class uses PluginsArchiver class to trigger data aggregation and create archives.
@@ -118,7 +120,9 @@ class Loader
             $visitsConverted = $metrics['nb_visits_converted'];
         }
 
-        if ($this->isThereSomeVisits($visits)) {
+        if ($this->isThereSomeVisits($visits)
+            || $this->shouldArchiveForSiteEvenWhenNoVisits()
+        ) {
             $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted);
         }
 
@@ -223,4 +227,27 @@ class Loader
 
         return $this->temporaryArchive;
     }
+
+    private function shouldArchiveForSiteEvenWhenNoVisits()
+    {
+        $idSitesToArchive = $this->getIdSitesToArchiveWhenNoVisits();
+        return in_array($this->params->getSite()->getId(), $idSitesToArchive);
+    }
+
+    private function getIdSitesToArchiveWhenNoVisits()
+    {
+        $cache = Cache::getTransientCache();
+        $cacheKey = 'Archiving.getIdSitesToArchiveWhenNoVisits';
+
+        if (!$cache->contains($cacheKey)) {
+            $idSites = array();
+
+            // leaving undocumented unless decided otherwise
+            Piwik::postEvent('Archiving.getIdSitesToArchiveWhenNoVisits', array(&$idSites));
+
+            $cache->save($cacheKey, $idSites);
+        }
+
+        return $cache->fetch($cacheKey);
+    }
 }
diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php
index ff9ddca499..28c90cb15c 100644
--- a/core/ArchiveProcessor/PluginsArchiver.php
+++ b/core/ArchiveProcessor/PluginsArchiver.php
@@ -41,9 +41,11 @@ class PluginsArchiver
     private $logAggregator;
 
     /**
+     * Public only for tests. Won't be necessary after DI changes are complete.
+     *
      * @var Archiver[] $archivers
      */
-    private static $archivers = array();
+    public static $archivers = array();
 
     public function __construct(Parameters $params, $isTemporaryArchive)
     {
diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php
index 3d53b796d8..391e5ef896 100644
--- a/tests/PHPUnit/Framework/Fixture.php
+++ b/tests/PHPUnit/Framework/Fixture.php
@@ -10,6 +10,7 @@ namespace Piwik\Tests\Framework;
 use Piwik\Access;
 use Piwik\Application\Environment;
 use Piwik\Archive;
+use Piwik\ArchiveProcessor\PluginsArchiver;
 use Piwik\Auth;
 use Piwik\Cache\Backend\File;
 use Piwik\Cache as PiwikCache;
@@ -352,6 +353,7 @@ class Fixture extends \PHPUnit_Framework_Assert
         ArchiveTableCreator::clear();
         \Piwik\Plugins\ScheduledReports\API::$cache = array();
         Singleton::clearAll();
+        PluginsArchiver::$archivers = array();
 
         $_GET = $_REQUEST = array();
         Translate::reset();
diff --git a/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php b/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php
new file mode 100644
index 0000000000..2063efdb25
--- /dev/null
+++ b/tests/PHPUnit/Integration/ArchiveWithNoVisitsTest.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration;
+
+use Piwik\ArchiveProcessor\PluginsArchiver;
+use Piwik\Cache;
+use Piwik\EventDispatcher;
+use Piwik\Plugin\Archiver;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Plugins\VisitsSummary\API as VisitsSummaryAPI;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+
+class ArchiveWithNoVisitsTest_MockArchiver extends Archiver
+{
+    public static $methodsCalled = array();
+
+    public function aggregateDayReport()
+    {
+        self::$methodsCalled[] = 'aggregateDayReport';
+    }
+
+    public function aggregateMultipleReports()
+    {
+        self::$methodsCalled[] = 'aggregateMultipleReports';
+    }
+}
+
+class ArchiveWithNoVisitsTest extends IntegrationTestCase
+{
+    public function setUp()
+    {
+        parent::setUp();
+
+        Fixture::createWebsite('2011-01-01');
+
+        ArchiveWithNoVisitsTest_MockArchiver::$methodsCalled = array();
+    }
+
+    public function test_getIdSitesToArchiveWhenNoVisits_CanBeUsedToTriggerArchiving_EvenIfSiteHasNoVisits()
+    {
+        // add our mock archiver instance
+        // TODO: should use a dummy plugin that is activated for this test explicitly, but that can be tricky, especially in the future
+
+        PluginsArchiver::$archivers['VisitsSummary'] = 'Piwik\Tests\Integration\ArchiveWithNoVisitsTest_MockArchiver';
+
+        // initiate archiving w/o adding the event and make sure no methods are called
+        VisitsSummaryAPI::getInstance()->get($idSite = 1, 'week', '2012-01-01');
+
+        $this->assertEmpty(ArchiveWithNoVisitsTest_MockArchiver::$methodsCalled);
+
+        // mark our only site as should archive when no visits
+        $eventDispatcher = $this->getEventDispatcher();
+        $eventDispatcher->addObserver('Archiving.getIdSitesToArchiveWhenNoVisits', function (&$idSites) {
+            $idSites[] = 1;
+        });
+
+        Cache::getTransientCache()->flushAll();
+
+        // initiate archiving and make sure both aggregate methods are called correctly
+        VisitsSummaryAPI::getInstance()->get($idSite = 1, 'week', '2012-01-10');
+
+        $expectedMethodCalls = array(
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateDayReport',
+            'aggregateMultipleReports',
+        );
+        $this->assertEquals($expectedMethodCalls, ArchiveWithNoVisitsTest_MockArchiver::$methodsCalled);
+    }
+
+    /**
+     * @return EventDispatcher
+     */
+    private function getEventDispatcher()
+    {
+        return self::$fixture->piwikEnvironment->getContainer()->get('Piwik\EventDispatcher');
+    }
+}
-- 
GitLab