Skip to content

Commit

Permalink
Redefine when revenue information is displayed on All Websites Dashbo…
Browse files Browse the repository at this point in the history
…ard (matomo-org#22886)

* Hide revenue information on All Websites Dashboard more often

* Update MultiSites fixture with different revenue sources

* Add tests for MultiSites revenue column configurations

* Update expected test results

* Fix Goals.getGoals API when requesting more than one site

* Add changelog entry for Goals.getGoals changes

* Tweak wording

---------

Co-authored-by: Michal Kleiner <[email protected]>
  • Loading branch information
mneudert and michalkleiner authored Jan 7, 2025
1 parent d565369 commit 359f042
Show file tree
Hide file tree
Showing 25 changed files with 491 additions and 737 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ This is the Developer Changelog for Matomo platform developers. All changes in o

The Product Changelog at **[matomo.org/changelog](https://matomo.org/changelog)** lets you see more details about any Matomo release, such as the list of new guides and FAQs, security fixes, and links to all closed issues.

## Matomo 5.3.0

### Breaking Changes

* When requesting goals for multiple sites at once using `Goals.getGoals`, the result will no longer be indexed by `idgoal`. Requesting the goals for a single site will still return them indexed by `idgoal`.

## Matomo 5.2.0

### Breaking Changes
Expand Down
18 changes: 15 additions & 3 deletions plugins/Goals/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,17 @@ public function getGoal($idSite, $idGoal)
* @param string|array $idSite Array or Comma separated list of website IDs to request the goals for
* @param bool $orderByName
*
* @return array Array of Goal attributes
* @return array Array of Goal attributes,
* indexed by "idgoal" when requesting a single site,
* no specific index when requesting multiple sites
*/
public function getGoals($idSite, bool $orderByName = false): array
{
if (is_array($idSite)) {
$idSite = array_map('intval', $idSite);
$idSite = implode(',', $idSite);
}

$cacheId = self::getCacheId($idSite);
$cache = $this->getGoalsInfoStaticCache();
if (!$cache->contains($cacheId)) {
Expand All @@ -101,10 +108,15 @@ public function getGoals($idSite, bool $orderByName = false): array
Piwik::checkUserHasViewAccess($idSite);

$goals = $this->getModel()->getActiveGoals($idSite);
$cleanedGoals = [];
$indexByIdGoal = 1 === count($idSite);

$cleanedGoals = array();
foreach ($goals as &$goal) {
$cleanedGoals[$goal['idgoal']] = $this->formatGoal($goal);
if ($indexByIdGoal) {
$cleanedGoals[$goal['idgoal']] = $this->formatGoal($goal);
} else {
$cleanedGoals[] = $this->formatGoal($goal);
}
}

$cache->save($cacheId, $cleanedGoals);
Expand Down
40 changes: 37 additions & 3 deletions plugins/Goals/tests/Integration/APITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,23 @@ class APITest extends IntegrationTestCase
*/
private $api;

private $idSite = 1;
/**
* @var int
*/
private $idSite;

/**
* @var int
*/
private $idSiteTwo;

public function setUp(): void
{
parent::setUp();
$this->api = API::getInstance();

Fixture::createWebsite('2014-01-01 00:00:00');
Fixture::createWebsite('2014-01-01 00:00:00');
$this->idSite = Fixture::createWebsite('2014-01-01 00:00:00');
$this->idSiteTwo = Fixture::createWebsite('2014-01-01 00:00:00');
}

/**
Expand Down Expand Up @@ -262,6 +270,32 @@ public function testGetGoalShouldReturnExistingGoal()
), $goal);
}

/**
* @param string|array $idSite
*
* @dataProvider getTestDataForMultipleSites
*/
public function testGetGoalsShouldReturnGoalsForMultipleSites($idSite): void
{
$idGoal = $this->api->addGoal($this->idSite, 'Goal Site One', 'url', 'http://site.one', 'exact');
$idGoalTwo = $this->api->addGoal($this->idSiteTwo, 'Goal Site Two', 'url', 'http://site.two', 'exact');
$goals = $this->api->getGoals($idSite);

$this->assertEqualsCanonicalizing(
[
$this->api->getGoal($this->idSite, $idGoal),
$this->api->getGoal($this->idSiteTwo, $idGoalTwo)
],
$goals
);
}

public function getTestDataForMultipleSites(): iterable
{
yield 'comma separated string' => ['1,2'];
yield 'array' => [[1, 2]];
}

private function assertHasGoals()
{
$goals = $this->getGoals();
Expand Down
34 changes: 33 additions & 1 deletion plugins/MultiSites/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use Piwik\Piwik;
use Piwik\Plugins\FeatureFlags\FeatureFlagManager;
use Piwik\Plugins\MultiSites\FeatureFlags\ImprovedAllWebsitesDashboard;
use Piwik\Plugins\Goals\API as GoalsAPI;
use Piwik\Plugins\SitesManager\API as SitesManagerAPI;
use Piwik\Translation\Translator;
use Piwik\View;

Expand Down Expand Up @@ -65,7 +67,7 @@ public function getSitesInfo($isWidgetized = false)
}

$view->isWidgetized = $isWidgetized;
$view->displayRevenueColumn = Common::isGoalPluginEnabled();
$view->displayRevenueColumn = $this->shouldDisplayRevenueColumn();
$view->limit = Config::getInstance()->General['all_websites_website_per_page'];
$view->show_sparklines = Config::getInstance()->General['show_multisites_sparklines'];

Expand Down Expand Up @@ -104,4 +106,34 @@ public function getEvolutionGraph($columns = false)
$view->requestConfig->totals = 0;
return $this->renderView($view);
}

private function shouldDisplayRevenueColumn(): bool
{
if (!Common::isGoalPluginEnabled()) {
return false;
}

if (!$this->featureFlagManager->isFeatureActive(ImprovedAllWebsitesDashboard::class)) {
return true;
}

$sites = SitesManagerAPI::getInstance()->getSitesWithAtLeastViewAccess();

foreach ($sites as $site) {
if ($site['ecommerce']) {
return true;
}
}

$idSites = array_column($sites, 'idsite');
$goals = GoalsAPI::getInstance()->getGoals($idSites);

foreach ($goals as $goal) {
if (0.0 < $goal['revenue'] || true === (bool) $goal['event_value_as_revenue']) {
return true;
}
}

return false;
}
}
124 changes: 86 additions & 38 deletions plugins/MultiSites/tests/Fixtures/ManySitesWithVisits.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Piwik\Plugins\MultiSites\tests\Fixtures;

use Piwik\Date;
use Piwik\Plugins\Goals\API as GoalsAPI;
use Piwik\Tests\Framework\Fixture;

/**
Expand All @@ -20,66 +21,113 @@
class ManySitesWithVisits extends Fixture
{
public $dateTime = '2013-01-23 01:23:45';
public $idSite = 1;

public $idSiteEcommerce;
public $idSiteGoalDefaultValue;
public $idSiteGoalEventValue;
public $idSiteGoalWithoutValue;

public $idGoalDefaultValue;
public $idGoalEventValue;
public $idGoalWithoutValue;

public function setUp(): void
{
$this->setUpWebsite();
$this->trackFirstVisit($this->idSite);
$this->trackSecondVisit($this->idSite);
$this->trackFirstVisit($siteId = 2);
$this->trackSecondVisit($siteId = 3);
$this->trackSecondVisit($siteId = 3);
$this->trackSecondVisit($siteId = 4);
$this->setUpWebsites();
$this->setUpGoals();

$this->trackVisitsForSite($this->idSiteEcommerce, 6);
$this->trackVisitsForSite($this->idSiteGoalDefaultValue, 3);
$this->trackVisitsForSite($this->idSiteGoalEventValue, 2);
$this->trackVisitsForSite($this->idSiteGoalWithoutValue, 1);
}

public function tearDown(): void
{
// empty
}

private function setUpWebsite()
private function setUpGoals(): void
{
for ($i = 1; $i <= 15; $i++) {
if (!self::siteCreated($i)) {
$idSite = self::createWebsite($this->dateTime, $ecommerce = 1, 'Site ' . $i);
$this->assertSame($i, $idSite);
}
}
$goalsApi = GoalsAPI::getInstance();

$this->idGoalDefaultValue = $goalsApi->addGoal(
$this->idSiteGoalDefaultValue,
'Goal With Value',
'manually',
'',
'',
false,
50.0
);

$this->idGoalEventValue = $goalsApi->addGoal(
$this->idSiteGoalEventValue,
'Goal Event Value',
'event_action',
'track value',
'exact',
false,
false,
false,
'',
true
);

$this->idGoalWithoutValue = $goalsApi->addGoal(
$this->idSiteGoalWithoutValue,
'Goal Without Value',
'manually',
'',
''
);
}

protected function trackFirstVisit($idSite)
private function setUpWebsites(): void
{
$t = self::getTracker($idSite, $this->dateTime, $defaultInit = true);

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
$t->setUrl('http://example.com/');
self::checkResponse($t->doTrackPageView('Viewing homepage'));
$this->idSiteEcommerce = self::createWebsite($this->dateTime, 1, 'Site Ecommerce');
$this->idSiteGoalDefaultValue = self::createWebsite($this->dateTime, 0, 'Site Goal Default Value');
$this->idSiteGoalEventValue = self::createWebsite($this->dateTime, 0, 'Site Goal Event Value');
$this->idSiteGoalWithoutValue = self::createWebsite($this->dateTime, 0, 'Site Goal Without Value');

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
$t->setUrl('http://example.com/sub/page');
self::checkResponse($t->doTrackPageView('Second page view'));
// create 11 empty websites
for ($i = 5; $i <= 15; $i++) {
$idSite = self::createWebsite($this->dateTime, 0, 'Site ' . $i);

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
$t->addEcommerceItem($sku = 'SKU_ID', $name = 'Test item!', $category = 'Test & Category', $price = 777, $quantity = 33);
self::checkResponse($t->doTrackEcommerceOrder('TestingOrder', $grandTotal = 33 * 77));
self::assertSame($i, $idSite);
}
}

protected function trackSecondVisit($idSite)
private function trackVisitsForSite(int $idSite, int $visitCount): void
{
$t = self::getTracker($idSite, $this->dateTime, $defaultInit = true);
$t->setIp('56.11.55.73');
for ($visit = 1; $visit <= $visitCount; $visit++) {
$visitDate = Date::factory($this->dateTime)->addHour($visit);
$tracker = self::getTracker($idSite, $visitDate->getDatetime());
$tracker->setUrl('http://example.com/');

self::checkResponse($tracker->doTrackPageView('Viewing homepage'));

$tracker->setForceVisitDateTime($visitDate->addHour(0.25)->getDatetime());

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
$t->setUrl('http://example.com/sub/page');
self::checkResponse($t->doTrackPageView('Viewing homepage'));
switch ($idSite) {
case $this->idSiteEcommerce:
$tracker->addEcommerceItem('SKU_ID', 'Test item!', 'Test & Category', 299.95, $visit);
self::checkResponse($tracker->doTrackEcommerceOrder('Order ' . $visit, 299.95 * $visit));
break;

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
$t->setUrl('http://example.com/?search=this is a site search query');
self::checkResponse($t->doTrackPageView('Site search query'));
case $this->idSiteGoalDefaultValue:
self::checkResponse($tracker->doTrackGoal($this->idGoalDefaultValue));
break;

$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
$t->addEcommerceItem($sku = 'SKU_ID2', $name = 'A durable item', $category = 'Best seller', $price = 321);
self::checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 33 * 77));
case $this->idSiteGoalEventValue:
self::checkResponse($tracker->doTrackEvent('value event', 'track value', false, 1337.0));
break;

case $this->idSiteGoalWithoutValue:
self::checkResponse($tracker->doTrackGoal($this->idGoalWithoutValue));
break;
}
}
}
}
Loading

0 comments on commit 359f042

Please sign in to comment.