N°6964 - Add API to allow modules to register files to include in the backup (#547)

* Allow to include backup extra file via interface

* Update application/applicationextension.inc.php

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@me.com>

* Add iBackupExtraFilesExtension to plugin list

* decouple extra_files via config and interface

* Add unit tests for iBackupExtraFilesExtension

* Enable recursive creation of destination directories

* Update application/applicationextension.inc.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update setup/backup.class.inc.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update tests/php-unit-tests/unitary-tests/application/applicationextension/ApplicationExtensionTest.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update application/applicationextension.inc.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update core/metamodel.class.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update setup/backup.class.inc.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@me.com>
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
This commit is contained in:
Lars Kaltefleiter
2024-04-12 09:17:17 +02:00
committed by GitHub
parent 2a9add241e
commit 93e1f6ae03
4 changed files with 73 additions and 26 deletions

View File

@@ -2281,4 +2281,22 @@ interface iKPILoggerExtension
* @return mixed * @return mixed
*/ */
public function LogOperation($oKpiLogData); public function LogOperation($oKpiLogData);
} }
/**
* Implement this interface to add files to the backup
*
* @api
* @since 3.2.0
*/
interface iBackupExtraFilesExtension
{
/**
* Returns an array of files and directories to be included in the backup
*
* @api
*
* @return string[]
*/
public function GetExtraFiles(): array;
}

View File

@@ -265,35 +265,45 @@ class DBBackup
SetupUtils::copydir($sExtraDir, $sFile); SetupUtils::copydir($sExtraDir, $sFile);
$aRet[] = $sFile; $aRet[] = $sFile;
} }
$aExtraFiles = [];
if (MetaModel::GetConfig() !== null) // During unattended install config file may be absent if (MetaModel::GetConfig() !== null) // During unattended install config file may be absent
{ {
$aExtraFiles = MetaModel::GetModuleSetting('itop-backup', 'extra_files', []); $aExtraFiles = MetaModel::GetModuleSetting('itop-backup', 'extra_files', []);
foreach($aExtraFiles as $sExtraFileOrDir) }
foreach (utils::GetClassesForInterface('iBackupExtraFilesExtension', '', ['[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]']) as $sExtensionClass)
{
/** @var iBackupExtraFilesExtension $oExtensionInstance */
$oExtensionInstance = new $sExtensionClass();
$aExtraFiles = array_merge($aExtraFiles, $oExtensionInstance->GetExtraFiles());
}
foreach($aExtraFiles as $sExtraFileOrDir)
{
if(!file_exists(APPROOT.'/'.$sExtraFileOrDir)) {
continue; // Ignore non-existing files
}
$sExtraFullPath = utils::RealPath(APPROOT.'/'.$sExtraFileOrDir, APPROOT);
if ($sExtraFullPath === false)
{ {
if(!file_exists(APPROOT.'/'.$sExtraFileOrDir)) { throw new Exception("Backup: Aborting, resource '$sExtraFileOrDir'. Considered as UNSAFE because not inside the iTop directory.");
continue; // Ignore non-existing files }
} if (is_dir($sExtraFullPath))
{
$sExtraFullPath = utils::RealPath(APPROOT.'/'.$sExtraFileOrDir, APPROOT); $sFile = $sTmpFolder.'/'.$sExtraFileOrDir;
if ($sExtraFullPath === false) $this->LogInfo("backup: adding directory '$sExtraFileOrDir'");
{ SetupUtils::copydir($sExtraFullPath, $sFile);
throw new Exception("Backup: Aborting, resource '$sExtraFileOrDir'. Considered as UNSAFE because not inside the iTop directory."); $aRet[] = $sFile;
} }
if (is_dir($sExtraFullPath)) elseif (file_exists($sExtraFullPath))
{ {
$sFile = $sTmpFolder.'/'.$sExtraFileOrDir; $sFile = $sTmpFolder.'/'.$sExtraFileOrDir;
$this->LogInfo("backup: adding directory '$sExtraFileOrDir'"); $this->LogInfo("backup: adding file '$sExtraFileOrDir'");
SetupUtils::copydir($sExtraFullPath, $sFile); @mkdir(dirname($sFile), 0755, true);
$aRet[] = $sFile; copy($sExtraFullPath, $sFile);
} $aRet[] = $sFile;
elseif (file_exists($sExtraFullPath))
{
$sFile = $sTmpFolder.'/'.$sExtraFileOrDir;
$this->LogInfo("backup: adding file '$sExtraFileOrDir'");
@mkdir(dirname($sFile), 0755, true);
copy($sExtraFullPath, $sFile);
$aRet[] = $sFile;
}
} }
} }
if (!$bSkipSQLDumpForTesting) if (!$bSkipSQLDumpForTesting)

View File

@@ -161,6 +161,10 @@ class ApplicationExtensionTest extends ItopCustomDatamodelTestCase
\iNewsroomProvider::class, \iNewsroomProvider::class,
static::ENUM_API_CALL_METHOD_GETCLASSESFORINTERFACE, static::ENUM_API_CALL_METHOD_GETCLASSESFORINTERFACE,
], ],
\iBackupExtraFilesExtension::class => [
\iBackupExtraFilesExtension::class,
static::ENUM_API_CALL_METHOD_GETCLASSESFORINTERFACE,
],
]; ];
} }
} }

View File

@@ -350,6 +350,21 @@ class ExampleFor_iKPILoggerExtension implements \iKPILoggerExtension
{ {
// Do nothing, we just need the class to exists for the unit test // Do nothing, we just need the class to exists for the unit test
} }
}
]]></content>
</snippet>
<snippet id="ExampleFor_iBackupExtraFilesExtension" _delta="define">
<placement>core</placement>
<rank>0</rank>
<content><![CDATA[
class ExampleFor_iBackupExtraFilesExtension implements \iBackupExtraFilesExtension
{
public function GetExtraFiles()
{
return [
'foo'
];
}
} }
]]></content> ]]></content>
</snippet> </snippet>