N°9553 - Finalize localized sample and structural data

This commit is contained in:
v-dumas
2026-07-07 18:13:43 +02:00
parent 3d3c9f6e09
commit 65d15784f4
8 changed files with 67 additions and 128 deletions

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Set>
<NetworkDeviceType alias="NetworkDeviceType" id="10">
<name>Router</name>
</NetworkDeviceType>
<NetworkDeviceType alias="NetworkDeviceType" id="11">
<name>Switch</name>
</NetworkDeviceType>
</Set>

View File

@@ -25,14 +25,13 @@ SetupWebPage::AddModule(
'main.itop-config-mgmt.php',
],
'data.struct' => [
'data/en_us.data.itop-brand.xml',
'data/en_us.data.itop-networkdevicetype.xml',
'data/en_us.data.itop-osfamily.xml',
'data/en_us.data.itop-osversion.xml',
'data/data.itop-brand.xml',
'data/data.itop-networkdevicetype.xml',
'data/data.itop-osfamily.xml',
'data/data.itop-osversion.xml',
],
'data.sample' => [
'data/data.sample.model.xml',
'data/data.sample.networkdevicetype.xml',
'data/data.sample.servers.xml',
'data/data.sample.nw-devices.xml',
'data/data.sample.software.xml',

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Set>
<ContactType alias="ContactType" id="17">
<name>Customer manager</name>
</ContactType>
<ContactType alias="ContactType" id="12">
<name>Helpdesk</name>
</ContactType>
<ContactType alias="ContactType" id="15">
<name>Manager</name>
</ContactType>
<ContactType alias="ContactType" id="14">
<name>Support Agent</name>
</ContactType>
<ContactType alias="ContactType" id="13">
<name>Support level1</name>
</ContactType>
<ContactType alias="ContactType" id="16">
<name>Team leader</name>
</ContactType>
</Set>

View File

@@ -32,7 +32,6 @@ SetupWebPage::AddModule(
'data/data.sample.persons.xml',
'data/data.sample.teams.xml',
'data/data.sample.contactteam.xml',
'data/data.sample.contacttype.xml',
'data/data.sample.auditdomain.xml',
'data/data.sample.auditcategory.xml',
'data/data.sample.auditcategory-auditdomain.xml',

View File

@@ -61,6 +61,6 @@ class TicketsInstaller extends ModuleInstallerAPI
}
}
// Load localized structural data: predefined query phrases for notifications
static::LoadLocalizedDataOnCrossingVersion($oConfiguration, $sPreviousVersion, $sCurrentVersion, '3.0.0', __DIR__."/data/data.itop-tickets.en_us.xml");
static::LoadLocalizedDataOnCrossingVersion($oConfiguration, $sPreviousVersion, $sCurrentVersion, '3.0.0', utils::GetAbsoluteModulePath('itop-tickets')."/data/data.itop-tickets.en_us.xml");
}
}

View File

@@ -326,36 +326,11 @@ abstract class ModuleInstallerAPI
public static function LoadLocalizedDataOnCrossingVersion(Config $oConfiguration, ?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion, string $sDefaultFileName): void
{
self::AssertLoadLocalizedDataParametersAreValid($sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion);
// The loading is done only if
// - it's a first install of the module
// - or it's an upgrade of that module (PreviousVersion is less than the CurrentVersion), which means that we are really upgrading (and not reinstalling the same version or downgrading), and
// - either the FirstLoadingVersion is between the PreviousVersion and the CurrentVersion
// - or the FirstLoadingVersion is empty, forcing the loading on all upgrades,
if (($sPreviousVersion === '') ||
(version_compare($sPreviousVersion, $sCurrentVersion, '<')
&& version_compare($sPreviousVersion, $sFirstLoadingVersion, '<')
&& version_compare($sFirstLoadingVersion, $sCurrentVersion, '<='))) {
if (self::IsVersionCrossed($sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion)) {
$sWishedLanguage = $oConfiguration->GetDefaultLanguage();
self::LoadLocalizedData($sWishedLanguage, $sDefaultFileName);
}
}
/**
* Helper which will be removed as the standard knows how to load localized data on first install, but this is kept for backward compatibility.
* @param \Config $oConfiguration
* @param string $sPreviousVersion The previous version of the module (empty string in case of first install)
* @param string $sFilePattern The pattern of the file to load, with {{language_code}} as placeholder for the language code (e.g. 'data.sample.{{language_code}}.xml')
*
* @return void
*/
public static function LoadLocalizedDataOnNewInstall(Config $oConfiguration, ?string $sPreviousVersion, string $sFilePattern): void
{
if (utils::IsNullOrEmptyString($sPreviousVersion)) {
$sWishedLanguage = $oConfiguration->GetDefaultLanguage();
self::LoadLocalizedData($sWishedLanguage, $sFilePattern);
}
}
/**
* Helper to load a localized data file based on the default language of the application.
@@ -381,27 +356,6 @@ abstract class ModuleInstallerAPI
$oDataLoader->EndSession();
}
/**
* @throws \CoreUnexpectedValue
*/
private static function AssertLoadLocalizedDataParametersAreValid(?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion): void
{
if (($sPreviousVersion !== '') && !self::IsValidLocalizedDataVersion($sPreviousVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sPreviousVersion to be empty or match x.y[.z][-name], got '{$sPreviousVersion}'");
}
if (!self::IsValidLocalizedDataVersion($sCurrentVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sCurrentVersion to match x.y[.z][-name], got '{$sCurrentVersion}'");
}
if (($sFirstLoadingVersion !== '') && !self::IsValidLocalizedDataVersion($sFirstLoadingVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sFirstLoadingVersion to match x.y[.z][-name], got '{$sFirstLoadingVersion}'");
}
}
private static function IsValidLocalizedDataVersion(string $sVersion): bool
{
return (preg_match('/^\d+\.\d+(?:\.\d+)?(?:-[A-Za-z0-9]+)?$/', $sVersion) === 1);
}
/**
* Helper to get the localized file name for a given language code, based on the original file name which must end by .en_us.xml
* @param string $sLanguage The language code to use for localization (e.g. 'FR FR' or 'fr_fr')
@@ -431,4 +385,40 @@ abstract class ModuleInstallerAPI
}
return $sFileName;
}
/**
* @throws \CoreUnexpectedValue
*/
private static function AssertLoadLocalizedDataParametersAreValid(?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion): void
{
if (($sPreviousVersion !== '') && !self::IsValidLocalizedDataVersion($sPreviousVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sPreviousVersion to be empty or match x.y[.z][-name], got '{$sPreviousVersion}'");
}
if (!self::IsValidLocalizedDataVersion($sCurrentVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sCurrentVersion to match x.y[.z][-name], got '{$sCurrentVersion}'");
}
if (($sFirstLoadingVersion !== '') && !self::IsValidLocalizedDataVersion($sFirstLoadingVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sFirstLoadingVersion to match x.y[.z][-name], got '{$sFirstLoadingVersion}'");
}
}
private static function IsValidLocalizedDataVersion(string $sVersion): bool
{
return (preg_match('/^\d+\.\d+(?:\.\d+)?(?:-[A-Za-z0-9]+)?$/', $sVersion) === 1);
}
/**
* @param string|null $sPreviousVersion
* @param string|null $sCurrentVersion
* @param string $sFirstLoadingVersion
*
* @return bool
*/
private static function IsVersionCrossed(?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion): bool
{
return ($sPreviousVersion === '') ||
(version_compare($sPreviousVersion, $sCurrentVersion, '<')
&& version_compare($sPreviousVersion, $sFirstLoadingVersion, '<')
&& version_compare($sFirstLoadingVersion, $sCurrentVersion, '<='));
}
}

View File

@@ -1264,7 +1264,7 @@ class RunTimeEnvironment
if (!file_exists($sFileName)) {
throw(new Exception("File $sFileName does not exist"));
}
$sFileName = ModuleInstallerAPI::GetLocalizedFileName($sFileName, $sDefaultLanguage);
$sFileName = ModuleInstallerAPI::GetLocalizedFileName($sDefaultLanguage, $sFileName);
$oDataLoader->LoadFile($sFileName);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupLog::Info($sResult);

View File

@@ -334,53 +334,41 @@ SQL
}
/**
* @covers \ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion
* @dataProvider LoadLocalizedData_VersionConditionNotMetProvider
* @covers \ModuleInstallerAPI::IsVersionCrossed
* @dataProvider IsVersionCrossedProvider
*/
public function testLoadLocalizedData_DoesNotLoadWhenVersionConditionIsNotMet(string $sPreviousVersion, string $sCurrentVersion, string $sFirstLoadingVersion): void
public function testIsVersionCrossed_ReturnsExpectedValue(string $sPreviousVersion, string $sCurrentVersion, string $sFirstLoadingVersion, bool $bExpected): void
{
// Given
[$oConfig, $sOrgName, $sPattern] = $this->GivenLocalizedDataTestContext('XML_Load_NoLoad_', 'en_us', ['en_us']);
// When version gate conditions are not met
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, $sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion, $sPattern);
// Then no data loaded
$this->AssertOrganizationCountByName($sOrgName, 'en_us', 0);
$bIsVersionCrossed = $this->InvokeNonPublicStaticMethod(ModuleInstallerAPI::class, 'IsVersionCrossed', [$sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion]);
$this->assertSame($bExpected, $bIsVersionCrossed);
}
public function LoadLocalizedData_VersionConditionNotMetProvider(): array
public function IsVersionCrossedProvider(): array
{
return [
'Equal versions (reinstall)' => ['3.1.0', '3.1.0', '3.0.0'],
'Downgrade attempt' => ['3.2.0', '3.1.0', '3.0.0'],
'Upgrade but first loading version already passed' => ['3.1.0', '3.2.0', '3.0.0'],
'Upgrade with boundary equality on first loading version' => ['3.0.0', '3.1.0', '3.0.0'],
'Upgrade but first loading version empty' => ['3.1.0', '3.2.0', ''],
'First install always crosses' => ['', '3.2.0', '3.0.0', true],
'Upgrade crosses first loading version' => ['3.0.0', '3.2.0', '3.1.0', true],
'Upgrade but first loading version already passed' => ['3.1.0', '3.2.0', '3.0.0', false],
'Reinstall same version does not cross' => ['3.1.0', '3.1.0', '3.0.0', false],
'Downgrade does not cross' => ['3.2.0', '3.1.0', '3.0.0', false],
'First install with suffixed current version' => ['', '3.2-dev', '3.0.0', true],
'Upgrade with suffixed current version crosses' => ['1.0.3-2', '1.2.4-1', '1.1.0', true],
'Upgrade with suffixed current version below threshold does not cross' => ['1.0.0', '1.2.0-beta', '1.2.0', false],
];
}
/**
* @covers \ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion
* @dataProvider LoadLocalizedData_ValidVersionFormatsProvider
*/
public function testLoadLocalizedData_AcceptsSupportedVersionFormats(string $sCurrentVersion, string $sFirstLoadingVersion): void
public function testLoadLocalizedData_LoadsWhenVersionCrossingIsTrue(): void
{
[$oConfig, $sOrgName, $sPattern] = $this->GivenLocalizedDataTestContext('XML_Load_ValidVersion_', 'en_us', ['en_us']);
[$oConfig, $sOrgName, $sPattern] = $this->GivenLocalizedDataTestContext('XML_Load_VersionCrossingTrue_', 'en_us', ['en_us']);
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '', $sCurrentVersion, $sFirstLoadingVersion, $sPattern);
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '3.0.0', '3.2.0', '3.1.0', $sPattern);
$this->AssertOrganizationCountByName($sOrgName, 'en_us', 1);
}
public function LoadLocalizedData_ValidVersionFormatsProvider(): array
{
return [
'Current version with suffix' => ['3.2-dev', '3.0.0'],
'Current version x.y.z' => ['10.12.140-Tagada34', '1.0'],
'Current version x.y.z-suffix' => ['2.3.3-beta', '2.3.3-alpha'],
'Current version x.y.z-1' => ['1.2.4-1', '1.0.3-2'],
];
}
// Test when a file is loaded twice because of the version conditions, it doesn't create duplicates (idempotent loading)
public function testLoadLocalizedData_IdempotentLoading(): void
{
@@ -388,8 +376,8 @@ SQL
[$oConfig, $sOrgName, $sPattern] = $this->GivenLocalizedDataTestContext('XML_Load_Idempotent_', 'en_us', ['en_us']);
// When LoadLocalizedData is called twice with conditions that would load the file both times
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '', '3.1.0', '3.0.0', $sPattern);
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '3.1.0', '3.2.0', '', $sPattern);
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '', '3.0.1', '3.0.0', $sPattern);
ModuleInstallerAPI::LoadLocalizedDataOnCrossingVersion($oConfig, '3.0.1', '3.1.0', '3.0.2', $sPattern);
// Then no duplicate data loaded
$this->AssertOrganizationCountByName($sOrgName, 'en_us', 1);
@@ -420,30 +408,23 @@ SQL
'previous' => 'v3.2',
'current' => '3.2.0',
'first' => '3.0.0',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.{{language_code}}.xml',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.en_us.xml',
'message' => 'sPreviousVersion',
],
'Invalid current version format' => [
'previous' => '',
'current' => '3',
'first' => '3.0.0',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.{{language_code}}.xml',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.en_us.xml',
'message' => 'sCurrentVersion',
],
'Invalid first loading version format' => [
'previous' => '',
'current' => '3.2.0',
'first' => '3.0.0-beta.1',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.{{language_code}}.xml',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.en_us.xml',
'message' => 'sFirstLoadingVersion',
],
'Missing strict placeholder' => [
'previous' => '',
'current' => '3.2.0',
'first' => '3.0.0',
'pattern' => $sTmpDir.DIRECTORY_SEPARATOR.'data.{{LANGUAGE_CODE}}.xml',
'message' => "{{language_code}}",
],
];
}
@@ -462,7 +443,7 @@ SQL
$sTmpDir = static::CreateTmpdir();
$this->aFileToClean[] = $sTmpDir;
$sPattern = $sTmpDir.DIRECTORY_SEPARATOR.'data.{{language_code}}.xml';
$sPattern = $sTmpDir.DIRECTORY_SEPARATOR.'data.en_us.xml';
foreach ($aAvailableLanguages as $sAvailableLanguage) {
$this->GivenLocalizedDataFile($sTmpDir, $sAvailableLanguage, $sOrgName);