Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code to check plugin license status and download link #22567

Open
wants to merge 40 commits into
base: 5.x-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1566ba0
Adds code to check plugin wise license status and download link, #PG-…
AltamashShaikh Sep 9, 2024
aa9e9dc
Build vue files
AltamashShaikh Sep 9, 2024
1733200
fixes PHPCS error
AltamashShaikh Sep 9, 2024
cea99cc
Merge branch 'PG-3814-check-license-status' of github.com:matomo-org/…
AltamashShaikh Sep 9, 2024
41e6bdd
Merge branch '5.x-dev' into PG-3814-check-license-status
AltamashShaikh Sep 10, 2024
6453547
PR feedback applied
AltamashShaikh Sep 10, 2024
a4cedc7
phpcs fixes
AltamashShaikh Sep 10, 2024
eb74c7e
More refactoring
AltamashShaikh Sep 10, 2024
e6db1fb
Added UI tests
AltamashShaikh Sep 11, 2024
82470d6
Adds more UI tests
AltamashShaikh Sep 11, 2024
1fcd690
PR fixes and UI test updated
AltamashShaikh Sep 11, 2024
4266ca5
Fixes Ui tests and adds new screenshot
AltamashShaikh Sep 11, 2024
02d6244
Improved checks to check if it has a download link
AltamashShaikh Sep 11, 2024
b8d1ff5
textual changes
AltamashShaikh Sep 11, 2024
c086caf
Fixes some tests
AltamashShaikh Sep 11, 2024
787744d
Fixes UI tests
AltamashShaikh Sep 12, 2024
8b67883
Fixes for UI test
AltamashShaikh Sep 12, 2024
8a1045c
UI fixes
AltamashShaikh Sep 12, 2024
8af5450
Ui fixes to capture correct screenshot
AltamashShaikh Sep 12, 2024
499b6e9
Updated text
AltamashShaikh Sep 12, 2024
030215c
Merge branch '5.x-dev' into PG-3814-check-license-status
AltamashShaikh Sep 12, 2024
8be57be
UI screenshots updated and error messaged updated for PCLZIP error
AltamashShaikh Sep 12, 2024
a137489
Merge branch 'PG-3814-check-license-status' of github.com:matomo-org/…
AltamashShaikh Sep 12, 2024
204fe6f
PR changes
AltamashShaikh Sep 12, 2024
51e599c
Changes to show the error message correctly for PCLIB error
AltamashShaikh Sep 12, 2024
768b7ac
textual changes
AltamashShaikh Sep 13, 2024
c100007
Merge branch '5.x-dev' into PG-3814-check-license-status
AltamashShaikh Sep 13, 2024
a4f714b
Updated UI screenshot
AltamashShaikh Sep 13, 2024
30284d5
Merge branch '5.x-dev' into PG-3814-check-license-status
AltamashShaikh Sep 16, 2024
2e59000
Applied PR suggestion
AltamashShaikh Sep 16, 2024
49b62e1
Fixes PHPCS
AltamashShaikh Sep 16, 2024
a417dad
Fixes UI test
AltamashShaikh Sep 17, 2024
d50a4e9
Fixes for UI test
AltamashShaikh Sep 17, 2024
99fd028
UI test updated
AltamashShaikh Sep 18, 2024
1ed7127
UI screenshot updated
AltamashShaikh Sep 18, 2024
b24d025
Adds more UI tests
AltamashShaikh Sep 18, 2024
0d09caf
Adds new UI screenshot
AltamashShaikh Sep 18, 2024
322d155
Apply PR feedback
AltamashShaikh Sep 20, 2024
bb125ef
Fixes to display messages correctly
AltamashShaikh Sep 20, 2024
fbd05c3
Updated text
AltamashShaikh Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions plugins/Marketplace/Consumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class Consumer
private $consumer = false;
private $isValid = null;

/**
* @var array
*/
private $pluginLicenseStatus = null;

public function __construct(Api\Client $marketplaceClient)
{
$this->marketplaceClient = $marketplaceClient;
Expand Down Expand Up @@ -65,4 +70,18 @@ public function isValidConsumer()

return $this->isValid;
}

public function getConsumerPluginLicenseStatus(): array
{
if (!$this->pluginLicenseStatus) {
$consumer = $this->getConsumer();
if (!empty($consumer['licenses'])) {
foreach ($consumer['licenses'] as $license) {
$this->pluginLicenseStatus[$license['plugin']['name']] = ['licenseStatus' => $license['status']];
}
}
}

return $this->pluginLicenseStatus ?? [];
}
}
23 changes: 19 additions & 4 deletions plugins/Marketplace/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,16 +472,31 @@ private function createUpdateOrInstallView($template, $nonceName)

$pluginInfos = [];
foreach ($plugins as $pluginName) {
$pluginInfos[] = $this->plugins->getPluginInfo($pluginName);
$currentPluginInfo = $this->plugins->getPluginInfo($pluginName);
$pluginInfos[] = $currentPluginInfo;

try {
$this->pluginInstaller->installOrUpdatePluginFromMarketplace($pluginName);
} catch (\Exception $e) {
$notification = new Notification($e->getMessage());
$message = $e->getMessage();
$isRaw = false;
if (stripos($message, 'PCLZIP_ERR_BAD_FORMAT') !== false) {
sgiehl marked this conversation as resolved.
Show resolved Hide resolved
$faqLink = Url::addCampaignParametersToMatomoLink('https://matomo.org/faq/plugins/faq_21/');
if (!empty($currentPluginInfo['isPaid'])) {
$downloadLink = Url::addCampaignParametersToMatomoLink('https://shop.matomo.org/my-account/downloads');
$translateKey = 'Marketplace_PluginDownloadLinkMissing';
} else {
$downloadLink = Url::addCampaignParametersToMatomoLink('https://plugins.matomo.org/' . $pluginName);
$translateKey = 'Marketplace_PluginDownloadLinkMissingFree';
}
$message = Piwik::translate($translateKey, [$pluginName, "<a href='$downloadLink' target='_blank' rel='noreferrer noopener'>", '</a>', "<a href='$faqLink' target='_blank' rel='noreferrer noopener'>", '</a>']);
$isRaw = true;
}
$notification = new Notification($message);
$notification->context = Notification::CONTEXT_ERROR;
$notification->type = Notification::TYPE_PERSISTENT;
$notification->flags = Notification::FLAG_CLEAR;
if (method_exists($e, 'isHtmlMessage') && $e->isHtmlMessage()) {
if ((method_exists($e, 'isHtmlMessage') && $e->isHtmlMessage()) || $isRaw) {
$notification->raw = true;
}
Notification\Manager::notify('CorePluginsAdmin_InstallPlugin', $notification);
Expand Down Expand Up @@ -538,7 +553,7 @@ private function canPluginBeInstalled($plugin)
|| $this->pluginManager->isPluginLoaded($pluginName)
|| $this->pluginManager->isPluginActivated($pluginName);

return !$isAlreadyInstalled;
return !$isAlreadyInstalled && $plugin['hasDownloadLink'];
}

protected function configureViewAndCheckPermission($template)
Expand Down
5 changes: 5 additions & 0 deletions plugins/Marketplace/Marketplace.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ public function getClientSideTranslationKeys(&$translationKeys)
$translationKeys[] = 'CorePluginsAdmin_Activity';
$translationKeys[] = 'CorePluginsAdmin_Version';
$translationKeys[] = 'CorePluginsAdmin_Websites';
$translationKeys[] = 'Marketplace_PluginLicenseStatusPending';
$translationKeys[] = 'Marketplace_PluginLicenseStatusCancelled';
$translationKeys[] = 'Marketplace_PluginDownloadLinkMissing';
$translationKeys[] = 'Marketplace_PluginDownloadLinkMissingFree';
$translationKeys[] = 'Marketplace_PluginDownloadLinkMissingDescription';
}

/**
Expand Down
16 changes: 16 additions & 0 deletions plugins/Marketplace/Plugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,15 @@ private function enrichPluginInformation($plugin)
}
}

$haDownloadLink = false;
if (!empty($plugin['versions'])) {
$latestVersion = array_pop($plugin['versions']);
$hasDownloadLink = !empty($latestVersion['download']);
}
$plugin['hasDownloadLink'] = $hasDownloadLink;

$plugin = $this->addMissingRequirements($plugin);
$plugin = $this->addConsumerLicenseStatus($plugin);

$plugin['isEligibleForFreeTrial'] =
$plugin['canBePurchased']
Expand Down Expand Up @@ -444,4 +452,12 @@ private function prettifyNumberOfDownloads(&$plugin): void

$plugin['numDownloadsPretty'] = $nice;
}

private function addConsumerLicenseStatus($plugin): array
{
$consumerPluginLicenseInfo = $this->consumer->getConsumerPluginLicenseStatus();
$plugin['licenseStatus'] = $consumerPluginLicenseInfo[$plugin['name']] ?? '';

return $plugin;
}
}
7 changes: 6 additions & 1 deletion plugins/Marketplace/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@
"AutoUpdateDisabledWarning": "You cannot install or update the plugin directly as automatic updates are disabled in the config. To enable automatic updates set %1$s in %2$s.",
"ViewRepositoryChangelog": "View the changes",
"Show": "Show",
"Sort": "Sort"
"Sort": "Sort",
"PluginLicenseStatusPending": "Your license key for %1$s is in pending state. To resolve this, please go to your %2$sMarketplace account%3$s and update your payment details.",
"PluginLicenseStatusCancelled": "Your license key for %1$s is cancelled. To continue using this plugin, go to your %2$sMarketplace%3$s account and start a new subscription for this plugin.",
"PluginDownloadLinkMissing": "The %1$s plugin could not be installed. Please check your license. If you have an active subscription for the %1$s plugin, try installing it manually using a zip file downloaded from your %2$sMarketplace account%3$s. %4$sLearn more%5$s.",
"PluginDownloadLinkMissingFree": "The %1$s plugin could not be installed, try installing it manually using a zip file from this %2$slink%3$s. %4$sLearn more%5$s.",
"PluginDownloadLinkMissingDescription": "Unable to fetch download link for %1$s plugin."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function testGetNamesOfExpiredPaidPluginsValidLicenses()
{
$expired = $this->buildWithValidLicense();

$expected = array('exceeded' => array(), 'expired' => array(), 'noLicense' => array());
$expected = array('exceeded' => array(), 'expired' => array(), 'noLicense' => array('PaidPlugin3'));

$this->assertSame($expected, $expired->getPluginNamesOfInvalidLicenses());
}
Expand Down Expand Up @@ -162,7 +162,7 @@ public function testGetNamesOfExpiredPaidPluginsShouldCacheAnyResult()

$this->assertTrue($this->cache->contains($this->cacheKey));

$expected = array('exceeded' => array(), 'expired' => array(), 'noLicense' => array());
$expected = array('exceeded' => array(), 'expired' => array(), 'noLicense' => array('PaidPlugin3'));

$this->assertSame($expected, $this->cache->fetch($this->cacheKey));
}
Expand Down Expand Up @@ -214,7 +214,10 @@ public function testGetMessageExceededLicensesGetMessageExpiredLicensesValidLice

$this->assertNull($expired->getMessageExceededLicenses());
$this->assertNull($expired->getMessageExpiredLicenses());
$this->assertNull($expired->getMessageNoLicense());
$this->assertEquals(
'The following plugins have been deactivated because you are using them without a license: <strong>PaidPlugin3</strong>. <br/>To resolve this issue either update your license key, <strong><a href="https://shop.piwik.org/my-account" target="_blank" rel="noreferrer noopener">get a subscription now</a></strong> or deactivate the plugin. <br/><a href="?module=Marketplace&action=subscriptionOverview">View your plugin subscriptions.</a>',
$expired->getMessageNoLicense()
);
}

public function testGetMessageExceededLicensesGetMessageExpiredLicensesNoLicensesPaidPluginActivated()
Expand Down
2 changes: 2 additions & 0 deletions plugins/Marketplace/tests/Integration/PluginsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ public function testGetPluginInfoNotInstalledPluginShouldEnrichPluginInformation
'isEligibleForFreeTrial' => false,
'priceFrom' => null,
'numDownloadsPretty' => 0,
'hasDownloadLink' => true,
'licenseStatus' => '',
'category' => 'customisation',
];
$this->assertEquals($expected, $plugin);
Expand Down
63 changes: 63 additions & 0 deletions plugins/Marketplace/tests/UI/Marketplace_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,69 @@ describe("Marketplace", function () {
await captureMarketplace('paid_plugins_with_license_' + mode);
});

if (mode === 'superuser') {
it(mode + ' for a user with license key should be able to open install purchased plugins modal', async function () {
setEnvironment(mode, validLicense);

await page.goto('about:blank');
await page.goto(paidPluginsUrl);

const elem = await page.jQuery(
'.installAllPaidPlugins button'
);

await elem.click();

// give it some time to fetch, animate, and render everything properly
await page.waitForNetworkIdle();
await page.waitForTimeout(500);

const selector = '.modal.open';
await page.screenshotSelector(selector);

expect(await page.screenshotSelector(selector)).to.matchImage('install_purchased_plugins_modal_' + mode);
});
}

it(mode + ' should open paid plugins modal for paid plugin 1', async function () {
setEnvironment(mode, validLicense);
await page.goto('about:blank');
await page.goto(paidPluginsUrl);
await loadPluginDetailPage('Paid Plugin 1', false);

await captureWithPluginDetails('paid_plugin1_plugin_details_' + mode);
});

it(mode + ' should open paid plugins modal for paid plugin 2', async function () {
setEnvironment(mode, validLicense);
await page.goto('about:blank');
await page.goto(paidPluginsUrl);
await loadPluginDetailPage('Paid Plugin 2', false);

await captureWithPluginDetails('paid_plugin2_plugin_details_' + mode);
});

it(mode + ' should open paid plugins modal for paid plugin 3', async function () {
setEnvironment(mode, validLicense);
await loadPluginDetailPage('Paid Plugin 3', false);

await captureWithPluginDetails('paid_plugin3_plugin_details_' + mode);
});

it(mode + ' should open paid plugins modal for paid plugin 4', async function () {
setEnvironment(mode, validLicense);
await loadPluginDetailPage('Paid Plugin 4', false);

await captureWithPluginDetails('paid_plugin4_plugin_details_' + mode);
});

it(mode + ' should open paid plugins modal for paid plugin 5', async function () {
setEnvironment(mode, validLicense);
await loadPluginDetailPage('Paid Plugin 5', false);

await captureWithPluginDetails('paid_plugin5_plugin_details_' + mode);
});

it(mode + ' for a user with exceeded license key should be able to open paid plugins', async function() {
setEnvironment(mode, exceededLicense);
assumePaidPluginsActivated();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading