Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
],
'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',
],
Expand Down Expand Up @@ -102,6 +101,8 @@ public static function BeforeDatabaseCreation(Config $oConfiguration, $sPrevious
*/
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
// load localized data for NetworkDeviceType
static::LoadLocalizedDataOnCrossingVersion($oConfiguration, $sPreviousVersion, $sCurrentVersion,'3.3.0',__DIR__."/data/{{language_code}}.data.itop-networkdevicetype.xml" );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,7 @@ public static function BeforeDatabaseCreation(Config $oConfiguration, $sPrevious
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
// Load localized structural data: contract types
static::LoadLocalizedData(
$oConfiguration,
$sPreviousVersion,
$sCurrentVersion,
'3.3.0',
__DIR__."/data/{{language_code}}.data.itop-contracttype.xml"
);
static::LoadLocalizedDataOnNewInstall($oConfiguration, $sPreviousVersion, __DIR__."/data/{{language_code}}.data.itop-contracttype.xml");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,7 @@ public static function BeforeDatabaseCreation(Config $oConfiguration, $sPrevious
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
// Load localized structural data: contact types and document types
static::LoadLocalizedData(
$oConfiguration,
$sPreviousVersion,
$sCurrentVersion,
'3.3.0',
__DIR__."/data/{{language_code}}.data.itop-contracttype.xml"
);
static::LoadLocalizedDataOnNewInstall($oConfiguration, $sPreviousVersion, __DIR__."/data/{{language_code}}.data.itop-contracttype.xml");
}
}
}
16 changes: 2 additions & 14 deletions datamodels/2.x/itop-structure/module.itop-structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,8 @@ public static function BeforeDatabaseCreation(Config $oConfiguration, $sPrevious
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
// Load localized structural data: contact types and document types
static::LoadLocalizedData(
$oConfiguration,
$sPreviousVersion,
$sCurrentVersion,
'3.3.0',
__DIR__."/data/{{language_code}}.data.itop-contacttype.xml"
);
static::LoadLocalizedData(
$oConfiguration,
$sPreviousVersion,
$sCurrentVersion,
'3.3.0',
__DIR__."/data/{{language_code}}.data.itop-documenttype.xml"
);
static::LoadLocalizedDataOnNewInstall($oConfiguration, $sPreviousVersion, __DIR__."/data/{{language_code}}.data.itop-contacttype.xml");
static::LoadLocalizedDataOnNewInstall($oConfiguration, $sPreviousVersion, __DIR__."/data/{{language_code}}.data.itop-documenttype.xml");

// Default language will be used for actions
// Note: There is a issue when upgrading, default language cannot be retrieved from the passed configuration, we have to read it from the disk
Expand Down
2 changes: 1 addition & 1 deletion datamodels/2.x/itop-tickets/module.itop-tickets.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousV
}
}
// Load localized structural data: predefined query phrases for notifications
static::LoadLocalizedData($oConfiguration, $sPreviousVersion, $sCurrentVersion, '3.0.0', __DIR__."/data/{{language_code}}.data.itop-tickets.xml");
static::LoadLocalizedDataOnCrossingVersion($oConfiguration, $sPreviousVersion, $sCurrentVersion, '3.0.0', __DIR__."/data/data.itop-tickets.en_us.xml");
}
}
143 changes: 75 additions & 68 deletions setup/moduleinstaller.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,85 +310,65 @@ public static function RenameTableInDB(string $sOrigTable, string $sDstTable)
}

/**
* Helper to load localized data from a file, only if the module is being installed for the first time or if the module is being upgraded and the FirstLoadingVersion is between the PreviousVersion and the CurrentVersion.
*
* @param \Config $oConfiguration
* @param string $sPreviousVersion The previous version of the module (empty string will force the loading)
* @param string $sPreviousVersion The previous version of the module (empty string in case of first install)
* @param string $sCurrentVersion The current version of the module
* @param string $sFirstLoadingVersion The first module version for which the data loading should be performed (e.g. '3.0.0')
* @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')
* @param string $sDefaultFileName 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
* @throws \ConfigException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
public static function LoadLocalizedData(Config $oConfiguration, string $sPreviousVersion, string $sCurrentVersion, string $sFirstLoadingVersion, string $sFilePattern): void
public static function LoadLocalizedDataOnCrossingVersion(Config $oConfiguration, ?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion, string $sDefaultFileName): void
{
self::AssertLoadLocalizedDataParametersAreValid($sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion, $sFilePattern);
self::AssertLoadLocalizedDataParametersAreValid($sPreviousVersion, $sCurrentVersion, $sFirstLoadingVersion);

// It's not very clear if it makes sense to test a particular version,
// as the loading mechanism checks object existence using reconc_keys
// and do not recreate them, nor update existing.
// Without test, new entries added to the data files, would be automatically loaded
// 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, '<'))) {

// Note: There is an issue when upgrading, default language cannot be retrieved from the passed configuration, we have to read it from the disk
if (utils::IsNullOrEmptyString($sPreviousVersion)) {
// Fresh install
$sDefaultLanguage = $oConfiguration->GetDefaultLanguage();
} else {
// Upgrade
$sDefaultLanguage = utils::GetConfig(true)->GetDefaultLanguage();
}
&& version_compare($sPreviousVersion, $sFirstLoadingVersion, '<')
&& version_compare($sFirstLoadingVersion, $sCurrentVersion, '<='))) {

$sFileName = self::GetLocalizedFileName($sDefaultLanguage, $sFilePattern);
if ($sFileName !== '') {
self::XMLFileLoad($sFileName);
}
$sWishedLanguage = $oConfiguration->GetDefaultLanguage();
self::LoadLocalizedData($sWishedLanguage, $sDefaultFileName);
}
}

/**
* @throws \CoreUnexpectedValue
*/
private static function AssertLoadLocalizedDataParametersAreValid(string $sPreviousVersion, string $sCurrentVersion, string $sFirstLoadingVersion, string $sFilePattern): void
* 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 (($sPreviousVersion !== '') && !self::IsValidLocalizedDataVersion($sPreviousVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sPreviousVersion to be empty or match x.y[.z][-name], got '{$sPreviousVersion}'");
if (utils::IsNullOrEmptyString($sPreviousVersion)) {
$sWishedLanguage = $oConfiguration->GetDefaultLanguage();
self::LoadLocalizedData($sWishedLanguage, $sFilePattern);
}

if (!self::IsValidLocalizedDataVersion($sCurrentVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sCurrentVersion to match x.y[.z][-name], got '{$sCurrentVersion}'");
}

if (!self::IsValidLocalizedDataVersion($sFirstLoadingVersion)) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sFirstLoadingVersion to match x.y[.z][-name], got '{$sFirstLoadingVersion}'");
}

if (utils::IsNullOrEmptyString($sFilePattern)) {
throw new CoreUnexpectedValue('LoadLocalizedData expects sFilePattern to be a non-empty string');
}

if (substr_count($sFilePattern, '{{language_code}}') !== 1) {
throw new CoreUnexpectedValue("LoadLocalizedData expects sFilePattern to contain the exact placeholder '{{language_code}}' exactly once");
}
}

private static function IsValidLocalizedDataVersion(string $sVersion): bool
{
return (preg_match('/^\d+\.\d+(?:\.\d+)?(?:-[A-Za-z0-9]+)?$/', $sVersion) === 1);
}

/**
* @param array|string $sFileName
* @param \XMLDataLoader $oDataLoader
* Helper to load a localized data file based on the default language of the application.
* @param \Config $oConfiguration to retrieve the default language
* @param string $sDefaultFile The default file to load, must be ending with .en_us.xml to be localized
*
* @return void
* @throws \Exception
*/
public static function XMLFileLoad(string $sFileName): void
protected static function LoadLocalizedData(string $sDefaultLanguage, string $sDefaultFile): void
{

$sFileName = self::GetLocalizedFileName($sDefaultLanguage, $sDefaultFile);
if (!file_exists($sFileName)) {
throw new Exception("File $sFileName not found");
}
Expand All @@ -402,26 +382,53 @@ public static function XMLFileLoad(string $sFileName): void
}

/**
* @param string $sLanguage The language code to use for localization (e.g. 'EN US')
* @param string $sFilePattern The full path+name of the file to localize, with {{language_code}} as placeholder for the language code (e.g. 'data.sample.{{language_code}}.xml')
*
* @return string The localized file name if found, or an empty string if not found
* @throws \ConfigException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
public static function GetLocalizedFileName($sLanguage, string $sFilePattern): string
private static function AssertLoadLocalizedDataParametersAreValid(?string $sPreviousVersion, ?string $sCurrentVersion, string $sFirstLoadingVersion): void
{
$sLang = str_replace(' ', '_', strtolower($sLanguage));
$sFileName = str_replace('{{language_code}}', $sLang, $sFilePattern);
if (!file_exists($sFileName)) {
$sLang = 'en_us';
$sFileName = str_replace('{{language_code}}', $sLang, $sFilePattern);
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}'");
}
if (file_exists($sFileName)) {
return $sFileName;
}

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')
* @param string $sOriginalFileName The full path+name of the file to localize. If ending with .en_us.xml, it searches for a localized file.
*
* @return string The localized file name if found otherwise the original file name
* @throws \Exception If the original file name ends with .en_us.xml and does not exist
*/
public static function GetLocalizedFileName(string $sLanguage, string $sOriginalFileName): string
{
if (str_ends_with($sOriginalFileName, '.en_us.xml')) {
// If the original file which can be localized, does not exist, then even if the localized file itself exists, we want to raise this design issue.
if (!file_exists($sOriginalFileName)) {
throw new Exception("This file $sOriginalFileName not found, it must exist in the module for the iTop fallback language (en_us)");
}
// Search for a file for the requested language,
$sLang = '.'.str_replace(' ', '_', strtolower($sLanguage)).'.xml';
$sFileName = str_replace('.en_us.xml', $sLang, $sOriginalFileName);
// localized file not found, fall back to the original file (en_us)
if (!file_exists($sFileName)) {
SetupLog::Debug("File for iTop default language $sFileName not found, fall back to file $sOriginalFileName");
$sFileName = $sOriginalFileName;
}
} else {
SetupLog::Warning("No data file matching the pattern $sFilePattern and language_code $sLang was found.");
return '';
// If the original file is not localizable, then we just return it as is
$sFileName = $sOriginalFileName;
}
return $sFileName;
}
}
6 changes: 3 additions & 3 deletions setup/runtimeenv.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ public function LoadData($aAvailableModules, $bSampleData, $aSelectedModules = n
{
$oConfig = MetaModel::GetConfig();
$oConfig->Set('access_mode', ACCESS_FULL);

$sDefaultLanguage = $oConfig->GetDefaultLanguage();
$oDataLoader = new XMLDataLoader();

CMDBObject::SetCurrentChangeFromParams("Initialization from XML files for the selected modules ");
Expand Down Expand Up @@ -1245,7 +1245,7 @@ public function LoadData($aAvailableModules, $bSampleData, $aSelectedModules = n
// in the current database
foreach ($aPreviouslyLoadedFiles as $sFileRelativePath) {
$sFileName = APPROOT.$sFileRelativePath;
SetupLog::Info("Loading file: $sFileName (just to get the keys mapping)");
SetupLog::Info("Loading file: $sFileName (just to build & cache the keys mapping)");
if (!file_exists($sFileName)) {
throw(new Exception("File $sFileName does not exist"));
}
Expand All @@ -1261,7 +1261,7 @@ public function LoadData($aAvailableModules, $bSampleData, $aSelectedModules = n
if (!file_exists($sFileName)) {
throw(new Exception("File $sFileName does not exist"));
}

$sFileName = ModuleInstallerAPI::GetLocalizedFileName($sFileName, $sDefaultLanguage);
$oDataLoader->LoadFile($sFileName);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupLog::Info($sResult);
Expand Down
Loading