Merge branch '5' into 6

This commit is contained in:
Steve Boyd 2024-03-04 11:22:28 +13:00
commit 165a65a017
8 changed files with 85 additions and 221 deletions

View File

@ -23,6 +23,7 @@
], ],
"require": { "require": {
"php": "^8.1", "php": "^8.1",
"composer-runtime-api": "^2.0",
"composer/installers": "^2.2", "composer/installers": "^2.2",
"guzzlehttp/guzzle": "^7.5.0", "guzzlehttp/guzzle": "^7.5.0",
"guzzlehttp/psr7": "^2.4.0", "guzzlehttp/psr7": "^2.4.0",
@ -60,6 +61,7 @@
"ext-xml": "*" "ext-xml": "*"
}, },
"require-dev": { "require-dev": {
"composer/semver": "^3.4",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.6",
"silverstripe/versioned": "^3", "silverstripe/versioned": "^3",
"squizlabs/php_codesniffer": "^3.7", "squizlabs/php_codesniffer": "^3.7",

View File

@ -3,11 +3,13 @@
namespace SilverStripe\Core\Manifest; namespace SilverStripe\Core\Manifest;
use InvalidArgumentException; use InvalidArgumentException;
use SilverStripe\Core\Config\Config; use Composer\InstalledVersions;
use SilverStripe\Dev\Deprecation;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injectable; use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
/** /**
* The version provider will look up configured modules and examine the composer.lock file * The version provider will look up configured modules and examine the composer.lock file
@ -30,7 +32,7 @@ class VersionProvider
use Injectable; use Injectable;
/** /**
* @var array * @var array<string,string>
*/ */
private static $modules = [ private static $modules = [
'silverstripe/framework' => 'Framework', 'silverstripe/framework' => 'Framework',
@ -50,10 +52,10 @@ class VersionProvider
return $version; return $version;
} }
$modules = $this->getModules(); $modules = $this->getModules();
$lockModules = $this->getModuleVersionFromComposer(array_keys($modules ?? [])); $lockModules = $this->getModuleVersionFromComposer(array_keys($modules));
$moduleVersions = []; $moduleVersions = [];
foreach ($modules as $module => $title) { foreach ($modules as $module => $title) {
if (!array_key_exists($module, $lockModules ?? [])) { if (!array_key_exists($module, $lockModules)) {
continue; continue;
} }
$version = $lockModules[$module]; $version = $lockModules[$module];
@ -145,14 +147,14 @@ class VersionProvider
* cwp/cwp-core => ['CWP', '4.4.4'] * cwp/cwp-core => ['CWP', '4.4.4']
* ] * ]
* *
* @param array $modules * @param array<string,array<int,string>> $modules
* @return array * @return array<string,array<int,string>>
*/ */
private function filterModules(array $modules) private function filterModules(array $modules)
{ {
$accountModule = []; $accountModule = [];
foreach ($modules as $module => $value) { foreach ($modules as $module => $value) {
if (!preg_match('#^([a-z0-9\-]+)/([a-z0-9\-]+)$#', $module ?? '', $m)) { if (!preg_match('#^([a-z0-9\-]+)/([a-z0-9\-]+)$#', $module, $m)) {
continue; continue;
} }
$account = $m[1]; $account = $m[1];
@ -169,7 +171,7 @@ class VersionProvider
/** /**
* Gets the configured core modules to use for the SilverStripe application version * Gets the configured core modules to use for the SilverStripe application version
* *
* @return array * @return array<string,string>
*/ */
public function getModules() public function getModules()
{ {
@ -180,19 +182,14 @@ class VersionProvider
/** /**
* Tries to obtain version number from composer.lock if it exists * Tries to obtain version number from composer.lock if it exists
* *
* @param array $modules * @param array<string> $modules
* @return array * @return array<string|string>
*/ */
public function getModuleVersionFromComposer($modules = []) public function getModuleVersionFromComposer($modules = [])
{ {
$versions = []; $versions = [];
$lockData = $this->getComposerLock(); foreach ($modules as $module) {
if ($lockData && !empty($lockData['packages'])) { $versions[$module] = InstalledVersions::getPrettyVersion($module);
foreach ($lockData['packages'] as $package) {
if (in_array($package['name'], $modules ?? []) && isset($package['version'])) {
$versions[$package['name']] = $package['version'];
}
}
} }
return $versions; return $versions;
} }
@ -200,35 +197,40 @@ class VersionProvider
/** /**
* Load composer.lock's contents and return it * Load composer.lock's contents and return it
* *
* @deprecated 5.1 Has been replaced by composer-runtime-api
* @param bool $cache * @param bool $cache
* @return array * @return array
*/ */
protected function getComposerLock($cache = true) protected function getComposerLock($cache = true)
{ {
Deprecation::notice("5.1", "Has been replaced by composer-runtime-api", Deprecation::SCOPE_METHOD);
$composerLockPath = $this->getComposerLockPath(); $composerLockPath = $this->getComposerLockPath();
if (!file_exists($composerLockPath ?? '')) { if (!file_exists($composerLockPath)) {
return []; return [];
} }
$lockData = []; $lockData = [];
$jsonData = file_get_contents($composerLockPath ?? ''); $jsonData = file_get_contents($composerLockPath);
$jsonData = $jsonData ? $jsonData : '';
$cacheKey = md5($jsonData);
if ($cache) { if ($cache) {
$cache = Injector::inst()->get(CacheInterface::class . '.VersionProvider_composerlock'); $cache = Injector::inst()->get(CacheInterface::class . '.VersionProvider_composerlock');
$cacheKey = md5($jsonData ?? '');
if ($versions = $cache->get($cacheKey)) { if ($versions = $cache->get($cacheKey)) {
$lockData = json_decode($versions ?? '', true); $lockData = json_decode($versions, true);
} }
} }
if (empty($lockData) && $jsonData) { if (empty($lockData) && $jsonData) {
$lockData = json_decode($jsonData ?? '', true); $lockData = json_decode($jsonData, true);
if ($cache) { if ($cache) {
$cache->set($cacheKey, $jsonData); $cache->set($cacheKey, $jsonData);
} }
} }
$lockData = $lockData ? $lockData : [];
return $lockData; return $lockData;
} }

View File

@ -59,11 +59,7 @@ class MoneyField extends FormField
public function __construct($name, $title = null, $value = "") public function __construct($name, $title = null, $value = "")
{ {
$this->setName($name); $this->setName($name);
$this->fieldAmount = NumericField::create( $this->buildAmountField();
"{$name}[Amount]",
_t('SilverStripe\\Forms\\MoneyField.FIELDLABELAMOUNT', 'Amount')
)
->setScale(2);
$this->buildCurrencyField(); $this->buildCurrencyField();
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
@ -75,6 +71,18 @@ class MoneyField extends FormField
$this->fieldCurrency = clone $this->fieldCurrency; $this->fieldCurrency = clone $this->fieldCurrency;
} }
/**
* Builds a field to input the amount of money
*/
protected function buildAmountField(): void
{
$this->fieldAmount = NumericField::create(
$this->name . '[Amount]',
_t('SilverStripe\\Forms\\MoneyField.FIELDLABELAMOUNT', 'Amount')
)
->setScale(2);
}
/** /**
* Builds a new currency field based on the allowed currencies configured * Builds a new currency field based on the allowed currencies configured
* *

View File

@ -2,15 +2,16 @@
namespace SilverStripe\Core\Tests\Manifest; namespace SilverStripe\Core\Tests\Manifest;
use SebastianBergmann\Version;
use Composer\Semver\VersionParser;
use SilverStripe\Dev\SapphireTest;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\VersionProvider; use SilverStripe\Core\Manifest\VersionProvider;
use SilverStripe\Dev\SapphireTest;
class VersionProviderTest extends SapphireTest class VersionProviderTest extends SapphireTest
{ {
/** /**
* @var VersionProvider * @var VersionProvider
*/ */
@ -22,17 +23,9 @@ class VersionProviderTest extends SapphireTest
$this->clearCache(); $this->clearCache();
} }
public function getMockProvider($composerLockPath = '') public function getProvider()
{ {
if ($composerLockPath == '') { $provider = Injector::inst()->get(VersionProvider::class);
// composer.lock file without silverstripe/recipe-core or silverstripe/recipe-cms
$composerLockPath = __DIR__ . '/fixtures/VersionProviderTest/composer.no-recipe.testlock';
}
/** @var VersionProvider $provider */
$provider = $this->getMockBuilder(VersionProvider::class)
->setMethods(['getComposerLockPath'])
->getMock();
$provider->method('getComposerLockPath')->willReturn($composerLockPath);
return $provider; return $provider;
} }
@ -44,7 +37,7 @@ class VersionProviderTest extends SapphireTest
'silverstripe/another' => 'Another', 'silverstripe/another' => 'Another',
'cwp/cwp-something' => 'CWP something', 'cwp/cwp-something' => 'CWP something',
]); ]);
$result = $this->getMockProvider()->getModules(); $result = $this->getProvider()->getModules();
$this->assertArrayHasKey('silverstripe/mypackage', $result); $this->assertArrayHasKey('silverstripe/mypackage', $result);
$this->assertArrayHasKey('silverstripe/somepackage', $result); $this->assertArrayHasKey('silverstripe/somepackage', $result);
$this->assertArrayHasKey('silverstripe/another', $result); $this->assertArrayHasKey('silverstripe/another', $result);
@ -56,7 +49,7 @@ class VersionProviderTest extends SapphireTest
Config::modify()->set(VersionProvider::class, 'modules', []); Config::modify()->set(VersionProvider::class, 'modules', []);
$this->assertEquals( $this->assertEquals(
['silverstripe/framework' => 'Framework'], ['silverstripe/framework' => 'Framework'],
$this->getMockProvider()->getModules() $this->getProvider()->getModules()
); );
} }
@ -65,7 +58,7 @@ class VersionProviderTest extends SapphireTest
Config::modify()->remove(VersionProvider::class, 'modules'); Config::modify()->remove(VersionProvider::class, 'modules');
$this->assertEquals( $this->assertEquals(
['silverstripe/framework' => 'Framework'], ['silverstripe/framework' => 'Framework'],
$this->getMockProvider()->getModules() $this->getProvider()->getModules()
); );
} }
@ -76,7 +69,7 @@ class VersionProviderTest extends SapphireTest
'silverstripe/framework' => 'Framework', 'silverstripe/framework' => 'Framework',
]); ]);
$result = $this->getMockProvider()->getModules(['silverstripe/framework']); $result = $this->getProvider()->getModules(['silverstripe/framework']);
$this->assertArrayHasKey('silverstripe/framework', $result); $this->assertArrayHasKey('silverstripe/framework', $result);
$this->assertNotEmpty($result['silverstripe/framework']); $this->assertNotEmpty($result['silverstripe/framework']);
} }
@ -87,131 +80,23 @@ class VersionProviderTest extends SapphireTest
'silverstripe/siteconfig' => 'SiteConfig', 'silverstripe/siteconfig' => 'SiteConfig',
'silverstripe/framework' => 'Framework' 'silverstripe/framework' => 'Framework'
]); ]);
$result = $this->getMockProvider()->getVersion(); $result = $this->getProvider()->getVersion();
$this->assertStringNotContainsString('SiteConfig: ', $result); $this->assertStringNotContainsString('SiteConfig: ', $result);
$this->assertStringContainsString('Framework: ', $result); $this->assertStringContainsString('Framework: ', $result);
$this->assertStringNotContainsString(', ', $result); $this->assertStringNotContainsString(', ', $result);
} }
public function testGetVersionNoRecipe()
{
// composer.lock file without silverstripe/recipe-core or silverstripe/recipe-cms
$provider = $this->getMockProvider(__DIR__ . '/fixtures/VersionProviderTest/composer.no-recipe.testlock');
Config::modify()->set(VersionProvider::class, 'modules', []);
$result = $provider->getVersion();
$this->assertStringContainsString('Framework: 1.2.3', $result);
$this->clearCache();
Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/framework' => 'Framework',
'silverstripe/recipe-core' => 'Core Recipe',
'silverstripe/cms' => 'CMS',
'silverstripe/recipe-cms' => 'CMS Recipe',
]);
$result = $provider->getVersion();
$this->assertStringNotContainsString('Framework: 1.2.3', $result);
$this->assertStringContainsString('CMS: 4.5.6', $result);
$this->assertStringNotContainsString('Core Recipe: 7.7.7', $result);
$this->assertStringNotContainsString('CMS Recipe: 8.8.8', $result);
}
public function testGetVersionRecipeCore()
{
// composer.lock file with silverstripe/recipe-core but not silverstripe/recipe-cms
$provider = $this->getMockProvider(__DIR__ . '/fixtures/VersionProviderTest/composer.recipe-core.testlock');
Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/framework' => 'Framework',
'silverstripe/recipe-core' => 'Core Recipe',
'silverstripe/cms' => 'CMS',
'silverstripe/recipe-cms' => 'CMS Recipe',
]);
$result = $provider->getVersion();
$this->assertStringNotContainsString('Framework: 1.2.3', $result);
$this->assertStringNotContainsString('Core Recipe: 7.7.7', $result);
$this->assertStringContainsString('CMS: 4.5.6', $result);
$this->assertStringNotContainsString('CMS Recipe: 8.8.8', $result);
}
public function testGetVersionRecipeCmsCore()
{
// composer.lock file with silverstripe/recipe-core and silverstripe/recipe-cms
$path = __DIR__ . '/fixtures/VersionProviderTest/composer.recipe-cms-core-and-cwpcore.testlock';
$provider = $this->getMockProvider($path);
Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/framework' => 'Framework',
'silverstripe/recipe-core' => 'Core Recipe',
'silverstripe/cms' => 'CMS',
'silverstripe/recipe-cms' => 'CMS Recipe',
]);
$result = $provider->getVersion();
$this->assertStringNotContainsString('Framework: 1.2.3', $result);
$this->assertStringNotContainsString('CMS: 4.5.6', $result);
$this->assertStringNotContainsString('Core Recipe: 7.7.7', $result);
$this->assertStringContainsString('CMS Recipe: 8.8.8', $result);
$this->assertStringNotContainsString('CWP: 9.9.9', $result);
$this->clearCache();
Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/framework' => 'Framework',
'silverstripe/recipe-core' => 'Core Recipe',
'silverstripe/cms' => 'CMS',
'silverstripe/recipe-cms' => 'CMS Recipe',
'cwp/cwp-core' => 'CWP',
]);
$result = $provider->getVersion();
$this->assertStringNotContainsString('Framework: 1.2.3', $result);
$this->assertStringNotContainsString('CMS: 4.5.6', $result);
$this->assertStringNotContainsString('Core Recipe: 7.7.7', $result);
$this->assertStringContainsString('CMS Recipe:', $result);
$this->assertStringContainsString('CWP: 9.9.9', $result);
}
public function testGetModulesFromComposerLock()
{
$mock = $this->getMockBuilder(VersionProvider::class)
->setMethods(['getComposerLock'])
->getMock();
$mock->expects($this->exactly(1))
->method('getComposerLock')
->will($this->returnValue([
'packages' => [
[
'name' => 'silverstripe/somepackage',
'version' => '1.2.3'
],
[
'name' => 'silverstripe/another',
'version' => '2.3.4'
]
]
]));
Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/somepackage' => 'Some Package'
]);
$result = $mock->getVersion();
$this->assertStringContainsString('Some Package: 1.2.3', $result);
}
public function testGetModuleVersion() public function testGetModuleVersion()
{ {
$provider = $this->getMockProvider(__DIR__ . '/fixtures/VersionProviderTest/composer.recipe-core.testlock'); $provider = $this->getProvider();
Config::modify()->set(VersionProvider::class, 'modules', [ Config::modify()->set(VersionProvider::class, 'modules', [
'silverstripe/framework' => 'Framework', 'silverstripe/framework' => 'Framework',
'silverstripe/recipe-core' => 'Core Recipe'
]); ]);
$this->assertSame('1.2.3', $provider->getModuleVersion('silverstripe/framework')); $moduleVersion = $provider->getModuleVersion('silverstripe/framework');
// assert that the temporary config changes in getModuleVersion() had no side-effects $parser = new VersionParser();
$this->assertIsString($parser->normalize($moduleVersion), "Expected a valid semver but got $moduleVersion");
$result = $provider->getVersion(); $result = $provider->getVersion();
$this->assertStringNotContainsString('Framework: 1.2.3', $result); $this->assertStringNotContainsString('Framework: 1.2.3', $result);
$this->assertStringContainsString('Core Recipe: 7.7.7', $result);
} }
private function clearCache() private function clearCache()

View File

@ -1,16 +0,0 @@
{
"_readme": [
"This is a fixture file for unit tests"
],
"content-hash": "abc123",
"packages": [
{
"name": "silverstripe/framework",
"version": "1.2.3"
},
{
"name": "silverstripe/cms",
"version": "4.5.6"
}
]
}

View File

@ -1,28 +0,0 @@
{
"_readme": [
"This is a fixture file for unit tests"
],
"content-hash": "abc888",
"packages": [
{
"name": "silverstripe/framework",
"version": "1.2.3"
},
{
"name": "silverstripe/cms",
"version": "4.5.6"
},
{
"name": "silverstripe/recipe-core",
"version": "7.7.7"
},
{
"name": "silverstripe/recipe-cms",
"version": "8.8.8"
},
{
"name": "cwp/cwp-core",
"version": "9.9.9"
}
]
}

View File

@ -1,20 +0,0 @@
{
"_readme": [
"This is a fixture file for unit tests"
],
"content-hash": "abc777",
"packages": [
{
"name": "silverstripe/framework",
"version": "1.2.3"
},
{
"name": "silverstripe/cms",
"version": "4.5.6"
},
{
"name": "silverstripe/recipe-core",
"version": "7.7.7"
}
]
}

View File

@ -2,9 +2,13 @@
namespace SilverStripe\Forms\Tests; namespace SilverStripe\Forms\Tests;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\NumericField;
use SilverStripe\Forms\RequiredFields; use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\Tests\MoneyFieldTest\CustomSetter_Object; use SilverStripe\Forms\Tests\MoneyFieldTest\CustomSetter_Object;
use SilverStripe\Forms\Tests\MoneyFieldTest\TestObject; use SilverStripe\Forms\Tests\MoneyFieldTest\TestObject;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\FieldType\DBMoney; use SilverStripe\ORM\FieldType\DBMoney;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\MoneyField; use SilverStripe\Forms\MoneyField;
@ -127,4 +131,31 @@ class MoneyFieldTest extends SapphireTest
]); ]);
$this->assertFalse($field->validate($validator)); $this->assertFalse($field->validate($validator));
} }
public function testGetCurrencyField(): void
{
$field = new MoneyField('Money');
$field->setAllowedCurrencies(['NZD', 'USD']);
$this->assertInstanceOf(DropdownField::class, $field->getCurrencyField());
$this->assertEquals('Money[Currency]', $field->getCurrencyField()->getName());
$field->setAllowedCurrencies(['USD']);
$this->assertInstanceOf(HiddenField::class, $field->getCurrencyField());
$this->assertEquals('Money[Currency]', $field->getCurrencyField()->getName());
$field->setAllowedCurrencies([]);
$this->assertInstanceOf(TextField::class, $field->getCurrencyField());
$this->assertEquals('Money[Currency]', $field->getCurrencyField()->getName());
}
public function testGetAmountField(): void
{
$field = new MoneyField('Money');
$this->assertInstanceOf(NumericField::class, $field->getAmountField());
$this->assertEquals(2, $field->getAmountField()->getScale());
$this->assertEquals('Money[Amount]', $field->getAmountField()->getName());
}
} }