mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-23 01:02:16 +02:00
Compare commits
206 Commits
faf/data_t
...
feature/un
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7ce3fcf8a | ||
|
|
c156c09be0 | ||
|
|
3df12bba46 | ||
|
|
0912cc21cf | ||
|
|
3aa59ae0b7 | ||
|
|
a5b5d22e2f | ||
|
|
08a3044cd1 | ||
|
|
00f1c7498d | ||
|
|
a4345d0311 | ||
|
|
77a820105f | ||
|
|
03e1806f9a | ||
|
|
b6c48f4bba | ||
|
|
e9dd2dc3e6 | ||
|
|
ba6cc9dcba | ||
|
|
fb7a38c83f | ||
|
|
b29a3b5df8 | ||
|
|
29502a4297 | ||
|
|
c5b678b4cf | ||
|
|
cb46889e92 | ||
|
|
2fe487df3c | ||
|
|
ae1f9391f9 | ||
|
|
f4a1aca3f9 | ||
|
|
4d2722654a | ||
|
|
73ebf663d6 | ||
|
|
99a5b47899 | ||
|
|
109aa03901 | ||
|
|
80813dba22 | ||
|
|
da282e6c24 | ||
|
|
c4eaeeeb5f | ||
|
|
e605b147d3 | ||
|
|
bfcff9b9c8 | ||
|
|
cd9a755162 | ||
|
|
841ea9bb11 | ||
|
|
4582256f01 | ||
|
|
0fc7434794 | ||
|
|
42fdffe25b | ||
|
|
309f8f8727 | ||
|
|
16108a80fc | ||
|
|
7e0e357713 | ||
|
|
d358c41c24 | ||
|
|
d0ddb35f8d | ||
|
|
fc2cb86122 | ||
|
|
467d2755ca | ||
|
|
de0e94800a | ||
|
|
3b0832781a | ||
|
|
900bd6956a | ||
|
|
848d869954 | ||
|
|
020d720140 | ||
|
|
dc8f186443 | ||
|
|
423807d32a | ||
|
|
7819620013 | ||
|
|
0fb15e72d4 | ||
|
|
0b6e7d3258 | ||
|
|
8088fdc322 | ||
|
|
d3dbe7b231 | ||
|
|
f05d98aba8 | ||
|
|
999e4de7f1 | ||
|
|
327c1572a4 | ||
|
|
16cdc80778 | ||
|
|
962a8e68b3 | ||
|
|
d61742dffb | ||
|
|
9f15385545 | ||
|
|
751ad846a4 | ||
|
|
f3b55a648e | ||
|
|
5ff640dfe7 | ||
|
|
662d6e3fb4 | ||
|
|
cbafcccde5 | ||
|
|
7573b1bfba | ||
|
|
e7e7664cb8 | ||
|
|
1222507205 | ||
|
|
b133f52d83 | ||
|
|
e28addfd6d | ||
|
|
2ec0931896 | ||
|
|
ca30645d36 | ||
|
|
bd14a84677 | ||
|
|
ad0234a94f | ||
|
|
611f1e66f2 | ||
|
|
82a20c54a9 | ||
|
|
d36f68e3b0 | ||
|
|
9ca0dd643f | ||
|
|
e27aca728c | ||
|
|
c3824f421b | ||
|
|
ecc07f4575 | ||
|
|
bcf2123df0 | ||
|
|
8dca03f557 | ||
|
|
2129007512 | ||
|
|
14d01b89a7 | ||
|
|
e2bda81414 | ||
|
|
6b7d306006 | ||
|
|
40feabb866 | ||
|
|
e17eaa7430 | ||
|
|
deeee08e8c | ||
|
|
b7b01c94e7 | ||
|
|
ba29a41d98 | ||
|
|
073d4f15f5 | ||
|
|
91915f001b | ||
|
|
9b7c7c0b48 | ||
|
|
85b20f34a3 | ||
|
|
7a153c3698 | ||
|
|
1be0e61971 | ||
|
|
d53433e62d | ||
|
|
51f4dd7948 | ||
|
|
d2384bb274 | ||
|
|
cedc1ec711 | ||
|
|
9cb2d78b48 | ||
|
|
9c32e29e77 | ||
|
|
c6dd16549c | ||
|
|
f2b15554bb | ||
|
|
358c4383f8 | ||
|
|
890db04fa3 | ||
|
|
e1e2da2881 | ||
|
|
cb47ea4316 | ||
|
|
a3e8abe520 | ||
|
|
48e58f4323 | ||
|
|
337ccbb921 | ||
|
|
34930a93cb | ||
|
|
5f731d9f97 | ||
|
|
9b7fd7b398 | ||
|
|
e0eee6798d | ||
|
|
a8f1b6ea35 | ||
|
|
d49a50da5b | ||
|
|
6a34fb9a15 | ||
|
|
3befe469e8 | ||
|
|
add7e7a677 | ||
|
|
f80a074135 | ||
|
|
113e3bc110 | ||
|
|
c20c4644b1 | ||
|
|
b77a0e3297 | ||
|
|
a237b32115 | ||
|
|
681abc84b9 | ||
|
|
7775cfeccc | ||
|
|
222e08e29b | ||
|
|
773501baed | ||
|
|
150094341f | ||
|
|
e1215733e9 | ||
|
|
2b2e431d0a | ||
|
|
98312d1c15 | ||
|
|
fc08bed218 | ||
|
|
eb01828f06 | ||
|
|
4c0b54a0f7 | ||
|
|
8074643231 | ||
|
|
1fb264e630 | ||
|
|
2bfafeffe6 | ||
|
|
324a68df32 | ||
|
|
14c3dafa4d | ||
|
|
7e78bd8519 | ||
|
|
54aa56c5a8 | ||
|
|
62bd1b8f49 | ||
|
|
b5e95f297c | ||
|
|
f89cb71f61 | ||
|
|
b30ff8b832 | ||
|
|
5254a83327 | ||
|
|
b1f49a1568 | ||
|
|
336898da98 | ||
|
|
84803abd36 | ||
|
|
9370c83a69 | ||
|
|
f7dfeafa3f | ||
|
|
5b4030a95f | ||
|
|
fe6ba35ada | ||
|
|
789c4bb5ea | ||
|
|
f5f5334e5f | ||
|
|
51f0eea8e3 | ||
|
|
875b2f1f29 | ||
|
|
36fc0fb0ea | ||
|
|
4f9a4dcfa5 | ||
|
|
dfc6189cef | ||
|
|
ad33453860 | ||
|
|
f787cf0950 | ||
|
|
cf7a193f7b | ||
|
|
8bbd1ab621 | ||
|
|
20ae350286 | ||
|
|
df9d69c70f | ||
|
|
ba09c624f0 | ||
|
|
7611a0e5c8 | ||
|
|
19e60ea628 | ||
|
|
5aaae3ad9c | ||
|
|
3b6ead1307 | ||
|
|
4678684ce6 | ||
|
|
c008483625 | ||
|
|
bfef10d636 | ||
|
|
d84959ce6c | ||
|
|
b230623a32 | ||
|
|
375c1f3a73 | ||
|
|
ab7a737512 | ||
|
|
371819f13f | ||
|
|
56cb3cae4f | ||
|
|
11b985aaa7 | ||
|
|
59fe11b96e | ||
|
|
55b03941e8 | ||
|
|
2fcd224ffd | ||
|
|
b26e0c8a90 | ||
|
|
b967fb7f20 | ||
|
|
195038c941 | ||
|
|
9a8d87e2b5 | ||
|
|
385302c44c | ||
|
|
b563f113d0 | ||
|
|
715d9d3b1c | ||
|
|
7a6c2bc6a4 | ||
|
|
8919184ef9 | ||
|
|
7bfaebe4db | ||
|
|
8e10cf9b72 | ||
|
|
9f3d7d2c36 | ||
|
|
ae980e365d | ||
|
|
a2b01b3ed4 | ||
|
|
9cdc707bc5 | ||
|
|
76178c16b8 |
@@ -16,5 +16,5 @@ require_once(APPROOT.'/application/audit.category.class.inc.php');
|
|||||||
require_once(APPROOT.'/application/audit.domain.class.inc.php');
|
require_once(APPROOT.'/application/audit.domain.class.inc.php');
|
||||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||||
require_once(APPROOT.'/application/query.class.inc.php');
|
require_once(APPROOT.'/application/query.class.inc.php');
|
||||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
require_once(APPROOT.'/setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||||
require_once(APPROOT.'/application/utils.inc.php');
|
require_once(APPROOT.'/application/utils.inc.php');
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ EOF
|
|||||||
* @param array $aExtraParams
|
* @param array $aExtraParams
|
||||||
* @param bool $bCanEdit
|
* @param bool $bCanEdit
|
||||||
*
|
*
|
||||||
* @return \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
|
* @return null|\Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
|
||||||
*/
|
*/
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
|
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1387,7 +1387,7 @@ class DesignerIconSelectionField extends DesignerFormField
|
|||||||
public function AddAllowedValue($aValue)
|
public function AddAllowedValue($aValue)
|
||||||
{
|
{
|
||||||
// Add a null value to the list of allowed values
|
// Add a null value to the list of allowed values
|
||||||
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues);
|
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues ?? [null]);
|
||||||
}
|
}
|
||||||
public function EnableUpload($sIconUploadUrl)
|
public function EnableUpload($sIconUploadUrl)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -284,9 +284,7 @@ class utils
|
|||||||
}
|
}
|
||||||
// Read and record the value for switching the archive mode
|
// Read and record the value for switching the archive mode
|
||||||
$iCurrent = self::ReadParam('with-archive', $iDefault);
|
$iCurrent = self::ReadParam('with-archive', $iDefault);
|
||||||
if (Session::IsInitialized()) {
|
Session::Set('archive_mode', $iCurrent);
|
||||||
Session::Set('archive_mode', $iCurrent);
|
|
||||||
}
|
|
||||||
// Read and use the value for the current page (web services)
|
// Read and use the value for the current page (web services)
|
||||||
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
|
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
|
||||||
self::$bPageMode = ($iCurrent == 1);
|
self::$bPageMode = ($iCurrent == 1);
|
||||||
@@ -976,7 +974,7 @@ class utils
|
|||||||
return self::$oConfig;
|
return self::$oConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sProductionEnvConfigPath = self::GetConfigFilePath('production');
|
$sProductionEnvConfigPath = self::GetConfigFilePath(ITOP_DEFAULT_ENV);
|
||||||
if (file_exists($sProductionEnvConfigPath)) {
|
if (file_exists($sProductionEnvConfigPath)) {
|
||||||
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
|
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
|
||||||
self::SetConfig($oProductionEnvDiskConfig);
|
self::SetConfig($oProductionEnvDiskConfig);
|
||||||
@@ -3192,4 +3190,30 @@ TXT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read memory limit from the php.ini file
|
||||||
|
*
|
||||||
|
* @return int Memory limit in bytes
|
||||||
|
*/
|
||||||
|
public static function GetMemoryLimit(): int
|
||||||
|
{
|
||||||
|
$sLimit = ini_get('memory_limit');
|
||||||
|
if ($sLimit == '-1') {
|
||||||
|
return 128 * 1048576;
|
||||||
|
}
|
||||||
|
switch (substr($sLimit, -1)) {
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
return (int)$sLimit * 1048576;
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
return (int)$sLimit * 1024;
|
||||||
|
case 'G':
|
||||||
|
case 'g':
|
||||||
|
return (int)$sLimit * 1073741824;
|
||||||
|
default:
|
||||||
|
return (int)$sLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
|||||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||||
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
|
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
|
||||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
MetaModel::IncludeModule('setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||||
|
|
||||||
MetaModel::IncludeModule('core/event.class.inc.php');
|
MetaModel::IncludeModule('core/event.class.inc.php');
|
||||||
MetaModel::IncludeModule('core/action.class.inc.php');
|
MetaModel::IncludeModule('core/action.class.inc.php');
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Combodo\iTop\Config\Validator\iTopConfigAstValidator;
|
|
||||||
use Combodo\iTop\Config\Validator\iTopConfigSyntaxValidator;
|
|
||||||
|
|
||||||
define('ITOP_APPLICATION', 'iTop');
|
define('ITOP_APPLICATION', 'iTop');
|
||||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||||
|
|
||||||
@@ -1182,8 +1179,8 @@ class Config
|
|||||||
'tracking_level_linked_set_default' => [
|
'tracking_level_linked_set_default' => [
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
||||||
'default' => LINKSET_TRACKING_LIST,
|
'default' => LINKSET_TRACKING_NONE,
|
||||||
'value' => LINKSET_TRACKING_LIST,
|
'value' => LINKSET_TRACKING_NONE,
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
],
|
],
|
||||||
@@ -2755,14 +2752,13 @@ class Config
|
|||||||
*
|
*
|
||||||
* @param array $aParamValues
|
* @param array $aParamValues
|
||||||
* @param ?string $sModulesDir
|
* @param ?string $sModulesDir
|
||||||
* @param bool $bPreserveModuleSettings
|
|
||||||
*
|
*
|
||||||
* @return void The current object is modified directly
|
* @return void The current object is modified directly
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
*/
|
*/
|
||||||
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
|
public function UpdateFromParams($aParamValues, $sModulesDir = null)
|
||||||
{
|
{
|
||||||
if (isset($aParamValues['application_path'])) {
|
if (isset($aParamValues['application_path'])) {
|
||||||
$this->Set('app_root_url', $aParamValues['application_path']);
|
$this->Set('app_root_url', $aParamValues['application_path']);
|
||||||
@@ -2810,7 +2806,10 @@ class Config
|
|||||||
} else {
|
} else {
|
||||||
$aSelectedModules = null;
|
$aSelectedModules = null;
|
||||||
}
|
}
|
||||||
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
|
|
||||||
|
if (! is_null($sModulesDir)) {
|
||||||
|
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($aParamValues['source_dir'])) {
|
if (isset($aParamValues['source_dir'])) {
|
||||||
$this->Set('source_dir', $aParamValues['source_dir']);
|
$this->Set('source_dir', $aParamValues['source_dir']);
|
||||||
@@ -2828,17 +2827,13 @@ class Config
|
|||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
|
public function UpdateIncludes(string $sModulesDir, $aSelectedModules = null)
|
||||||
{
|
{
|
||||||
if ($sModulesDir === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the arrays below with default values for the application...
|
// Initialize the arrays below with default values for the application...
|
||||||
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
||||||
$aAddOns = $oEmptyConfig->GetAddOns();
|
$aAddOns = $oEmptyConfig->GetAddOns();
|
||||||
|
|
||||||
$aModules = ModuleDiscovery::GetAvailableModules([APPROOT.$sModulesDir]);
|
$aModules = ModuleDiscovery::GetModulesOrderedByDependencies([APPROOT.$sModulesDir]);
|
||||||
foreach ($aModules as $sModuleId => $aModuleInfo) {
|
foreach ($aModules as $sModuleId => $aModuleInfo) {
|
||||||
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules)) {
|
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules)) {
|
||||||
|
|||||||
@@ -5108,8 +5108,8 @@ abstract class DBObject implements iDisplay
|
|||||||
protected function GetReferencingObjectsForDeletion($bAllowAllData = false)
|
protected function GetReferencingObjectsForDeletion($bAllowAllData = false)
|
||||||
{
|
{
|
||||||
$aDependentObjects = [];
|
$aDependentObjects = [];
|
||||||
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
$aReferencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
||||||
foreach ($aRererencingMe as $sRemoteClass => $aExtKeys) {
|
foreach ($aReferencingMe as $sRemoteClass => $aExtKeys) {
|
||||||
/** @var \AttributeExternalKey $oExtKeyAttDef */
|
/** @var \AttributeExternalKey $oExtKeyAttDef */
|
||||||
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
|
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
|
||||||
// skip if external key doesn't require the deletion cascading
|
// skip if external key doesn't require the deletion cascading
|
||||||
|
|||||||
@@ -360,10 +360,10 @@ class DesignElement extends \DOMElement
|
|||||||
* @param string $sTagName
|
* @param string $sTagName
|
||||||
* @param string|null $sDefault
|
* @param string|null $sDefault
|
||||||
*
|
*
|
||||||
* @return string
|
* @return null|string
|
||||||
* @throws \DOMFormatException
|
* @throws \DOMFormatException
|
||||||
*/
|
*/
|
||||||
public function GetChildText($sTagName, $sDefault = null)
|
public function GetChildText($sTagName, $sDefault = null): ?string
|
||||||
{
|
{
|
||||||
$sRet = $sDefault;
|
$sRet = $sDefault;
|
||||||
if ($oChild = $this->GetOptionalElement($sTagName)) {
|
if ($oChild = $this->GetOptionalElement($sTagName)) {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
|||||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||||
use Combodo\iTop\Service\Events\EventData;
|
use Combodo\iTop\Service\Events\EventData;
|
||||||
use Combodo\iTop\Service\Events\EventService;
|
use Combodo\iTop\Service\Events\EventService;
|
||||||
|
use Combodo\iTop\Setup\ModuleDependency\Module;
|
||||||
|
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||||
|
|
||||||
require_once APPROOT.'core/modulehandler.class.inc.php';
|
require_once APPROOT.'core/modulehandler.class.inc.php';
|
||||||
require_once APPROOT.'core/querymodifier.class.inc.php';
|
require_once APPROOT.'core/querymodifier.class.inc.php';
|
||||||
@@ -128,7 +130,7 @@ abstract class MetaModel
|
|||||||
/** @var array */
|
/** @var array */
|
||||||
private static $m_aClassToFile = [];
|
private static $m_aClassToFile = [];
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected static $m_sEnvironment = 'production';
|
protected static $m_sEnvironment = ITOP_DEFAULT_ENV;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects currently created/updated.
|
* Objects currently created/updated.
|
||||||
@@ -462,6 +464,43 @@ abstract class MetaModel
|
|||||||
return call_user_func([$sClass, 'GetClassDescription'], $sClass);
|
return call_user_func([$sClass, 'GetClassDescription'], $sClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sClass
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
final public static function GetModuleName($sClass)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$oReflectionClass = new ReflectionClass($sClass);
|
||||||
|
$sDir = realpath(dirname($oReflectionClass->getFileName()));
|
||||||
|
$sApproot = realpath(APPROOT);
|
||||||
|
while (($sDir !== $sApproot) && (str_contains($sDir, $sApproot))) {
|
||||||
|
$aFiles = glob("$sDir/module.*.php");
|
||||||
|
if (count($aFiles) > 1) {
|
||||||
|
return 'core';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($aFiles) == 0) {
|
||||||
|
$sDir = realpath(dirname($sDir));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sModuleFilePath = $aFiles[0];
|
||||||
|
$aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
|
||||||
|
$sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID];
|
||||||
|
list($sModuleName, ) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
|
|
||||||
|
return $sModuleName;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new CoreException("Cannot find class module", ['class' => $sClass], '', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'core';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
*
|
*
|
||||||
@@ -5709,7 +5748,7 @@ abstract class MetaModel
|
|||||||
* @throws \DictExceptionUnknownLanguage
|
* @throws \DictExceptionUnknownLanguage
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
|
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = ITOP_DEFAULT_ENV)
|
||||||
{
|
{
|
||||||
// Startup on a new environment is not supported
|
// Startup on a new environment is not supported
|
||||||
static $bStarted = false;
|
static $bStarted = false;
|
||||||
|
|||||||
@@ -43,24 +43,24 @@ final class ormTagSet extends ormSet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param array $aTagCodes
|
* @param array|null $aItems
|
||||||
*
|
*
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \CoreUnexpectedValue when a code is invalid
|
* @throws \CoreUnexpectedValue when a code is invalid
|
||||||
*/
|
*/
|
||||||
public function SetValues($aTagCodes)
|
public function SetValues($aItems)
|
||||||
{
|
{
|
||||||
if (is_null($aTagCodes)) {
|
if (is_null($aItems)) {
|
||||||
$aTagCodes = [];
|
$aItems = [];
|
||||||
}
|
}
|
||||||
if (!is_array($aTagCodes)) {
|
if (!is_array($aItems)) {
|
||||||
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
|
throw new CoreUnexpectedValue("Wrong value {$aItems} for {$this->sClass}:{$this->sAttCode}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$oTags = [];
|
$oTags = [];
|
||||||
$iCount = 0;
|
$iCount = 0;
|
||||||
$bError = false;
|
$bError = false;
|
||||||
foreach ($aTagCodes as $sTagCode) {
|
foreach ($aItems as $sTagCode) {
|
||||||
$iCount++;
|
$iCount++;
|
||||||
if (($this->iLimit != 0) && ($iCount > $this->iLimit)) {
|
if (($this->iLimit != 0) && ($iCount > $this->iLimit)) {
|
||||||
$bError = true;
|
$bError = true;
|
||||||
|
|||||||
@@ -22,4 +22,6 @@
|
|||||||
@import "medallion-with-blocklist";
|
@import "medallion-with-blocklist";
|
||||||
@import "field-badge-within-datatable";
|
@import "field-badge-within-datatable";
|
||||||
@import "jquery-blockui-within-dialog";
|
@import "jquery-blockui-within-dialog";
|
||||||
@import "jquery-blockui-within-datatable";
|
@import "jquery-blockui-within-datatable";
|
||||||
|
@import "badge-with-badge";
|
||||||
|
@import "extension-details-with-extension-details";
|
||||||
10
css/backoffice/blocks-integrations/_badge-with-badge.scss
Normal file
10
css/backoffice/blocks-integrations/_badge-with-badge.scss
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
$ibo-badge--spacing-left--with-same-block: $ibo-spacing-200 !default;
|
||||||
|
|
||||||
|
.ibo-badge + .ibo-badge {
|
||||||
|
margin-left: $ibo-badge--spacing-left--with-same-block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
$ibo-extension-details--margin-top: $ibo-spacing-300 !default;
|
||||||
|
|
||||||
|
.ibo-extension-details + .ibo-extension-details,
|
||||||
|
.ibo-extension-details--information--description .ibo-extension-details {
|
||||||
|
margin-top: $ibo-extension-details--margin-top;
|
||||||
|
}
|
||||||
@@ -33,4 +33,5 @@
|
|||||||
@import "field-badge";
|
@import "field-badge";
|
||||||
@import "file-select";
|
@import "file-select";
|
||||||
@import "medallion-icon";
|
@import "medallion-icon";
|
||||||
@import "toast";
|
@import "toast";
|
||||||
|
@import "badge";
|
||||||
41
css/backoffice/components/_badge.scss
Normal file
41
css/backoffice/components/_badge.scss
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
$ibo-badge--padding-x : $ibo-spacing-200 !default;
|
||||||
|
$ibo-badge--padding-y : $ibo-spacing-100 !default;
|
||||||
|
$ibo-badge--border-radius : $ibo-border-radius-400 !default;
|
||||||
|
|
||||||
|
$ibo-badge-colors: (
|
||||||
|
'primary': ($ibo-color-primary-100, $ibo-color-primary-900),
|
||||||
|
'secondary': ($ibo-color-secondary-100, $ibo-color-secondary-900),
|
||||||
|
'neutral': ($ibo-color-secondary-100, $ibo-color-secondary-900),
|
||||||
|
'information': ($ibo-color-information-100, $ibo-color-information-900),
|
||||||
|
'success': ($ibo-color-success-100, $ibo-color-success-900),
|
||||||
|
'failure': ($ibo-color-danger-100, $ibo-color-danger-900),
|
||||||
|
'warning': ($ibo-color-warning-100,$ibo-color-warning-900),
|
||||||
|
'danger': ($ibo-color-danger-100,$ibo-color-danger-900),
|
||||||
|
'grey' : ($ibo-color-grey-100, $ibo-color-grey-900),
|
||||||
|
'blue-grey': ($ibo-color-blue-grey-100, $ibo-color-blue-grey-900),
|
||||||
|
'blue': ($ibo-color-blue-100, $ibo-color-blue-900),
|
||||||
|
'cyan': ($ibo-color-cyan-100, $ibo-color-cyan-900),
|
||||||
|
'green': ($ibo-color-green-100, $ibo-color-green-900),
|
||||||
|
'orange' : ($ibo-color-orange-100, $ibo-color-orange-900),
|
||||||
|
'red': ($ibo-color-red-100, $ibo-color-red-900),
|
||||||
|
'pink': ($ibo-color-pink-100, $ibo-color-pink-900),
|
||||||
|
) !default;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.ibo-badge {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding : $ibo-badge--padding-y $ibo-badge--padding-x;
|
||||||
|
border-radius : $ibo-badge--border-radius;
|
||||||
|
@extend %ibo-font-ral-med-50;
|
||||||
|
|
||||||
|
@each $sColor, $aColorValues in $ibo-badge-colors {
|
||||||
|
$bg-color: nth($aColorValues, 1);
|
||||||
|
$text-color: nth($aColorValues, 2);
|
||||||
|
&.ibo-is-#{$sColor} {
|
||||||
|
background-color: $bg-color;
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ $ibo-toggler--wrapper--height: 20px !default;
|
|||||||
|
|
||||||
$ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default;
|
$ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default;
|
||||||
$ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default;
|
$ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default;
|
||||||
|
$ibo-toggler--slider--disabled--background-color: $ibo-color-secondary-200 !default;
|
||||||
|
|
||||||
$ibo-toggler--slider--before--left: 3px !default;
|
$ibo-toggler--slider--before--left: 3px !default;
|
||||||
$ibo-toggler--slider--before--bottom: 3px !default;
|
$ibo-toggler--slider--before--bottom: 3px !default;
|
||||||
@@ -17,6 +18,7 @@ $ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default;
|
|||||||
$ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default;
|
$ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default;
|
||||||
|
|
||||||
$ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default;
|
$ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default;
|
||||||
|
$ibo-toggler--slider--checked-disabled--background-color: $ibo-color-primary-200 !default;
|
||||||
$ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
|
$ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
|
||||||
|
|
||||||
$ibo-toggler--label--margin-left: 4px !default;
|
$ibo-toggler--label--margin-left: 4px !default;
|
||||||
@@ -61,6 +63,13 @@ $ibo-toggler--label--margin-left: 4px !default;
|
|||||||
background-color: $ibo-toggler--slider--checked--background-color;
|
background-color: $ibo-toggler--slider--checked--background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ibo-toggler--wrapper input:disabled + .ibo-toggler--slider {
|
||||||
|
background-color: $ibo-toggler--slider--disabled--background-color;
|
||||||
|
}
|
||||||
|
.ibo-toggler--wrapper input:checked:disabled + .ibo-toggler--slider {
|
||||||
|
background-color: $ibo-toggler--slider--checked-disabled--background-color;
|
||||||
|
}
|
||||||
|
|
||||||
input:focus + .ibo-toggler--slider {
|
input:focus + .ibo-toggler--slider {
|
||||||
box-shadow: $ibo-toggler--slider--focus--box-shadow;
|
box-shadow: $ibo-toggler--slider--focus--box-shadow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,4 @@
|
|||||||
@import "wizard-container/wizard-container";
|
@import "wizard-container/wizard-container";
|
||||||
@import "object/all";
|
@import "object/all";
|
||||||
@import "activity-panel/all";
|
@import "activity-panel/all";
|
||||||
|
@import "extension/all";
|
||||||
1
css/backoffice/layout/extension/_all.scss
Normal file
1
css/backoffice/layout/extension/_all.scss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import "extension-details";
|
||||||
76
css/backoffice/layout/extension/_extension-details.scss
Normal file
76
css/backoffice/layout/extension/_extension-details.scss
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
$ibo-extension-details--information--metadata--padding: $ibo-spacing-200 !default;
|
||||||
|
$ibo-extension-details--information--metadata--delimiter: "-" !default;
|
||||||
|
$ibo-extension-details--information--metadata--color: $ibo-color-grey-700 !default;
|
||||||
|
$ibo-extension-details--actions--button--padding-y: 3px !default;
|
||||||
|
$ibo-extension-details--actions--button--padding-x: $ibo-button--padding-x !default;
|
||||||
|
|
||||||
|
.ibo-extension-details {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--actions {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information--label {
|
||||||
|
@extend %ibo-font-ral-med-150;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information--metadata {
|
||||||
|
@extend %ibo-font-ral-med-100;
|
||||||
|
color: $ibo-extension-details--information--metadata--color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information--description {
|
||||||
|
@extend %ibo-font-ral-med-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information--metadata span + span:before {
|
||||||
|
content: $ibo-extension-details--information--metadata--delimiter;
|
||||||
|
padding-left: $ibo-extension-details--information--metadata--padding;
|
||||||
|
padding-right: $ibo-extension-details--information--metadata--padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ibo-extension-details can have other ibo-extension-details inside its ibo-extension-details--information--description in the setup. We need to only affect direct children
|
||||||
|
.ibo-extension-details:has(>.ibo-extension-details--actions input:is([type="checkbox"], [type="radio"]):checked){
|
||||||
|
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.unchecked {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Merging the two lines below with :is([type="checkbox"], [type="radio"]) will generate a warning in scss compiler
|
||||||
|
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="checkbox"]:not(:checked)),
|
||||||
|
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="radio"]:not(:checked)) {
|
||||||
|
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.checked {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ibo-extension-details--actions > button {
|
||||||
|
padding: $ibo-extension-details--actions--button--padding-y $ibo-extension-details--actions--button--padding-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--actions:has(.toggler-install:not(:disabled)) .ibo-popover-menu--section a[data-resource-id="force_uninstall"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details .ibo-popover-menu ~ .ibo-button{
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.ibo-extension-details .ibo-popover-menu:has(.ibo-popover-menu--item) ~ .ibo-button{
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details .ibo-toggler--wrapper:has(.ibo-toggler.ibo-is-hidden){
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -316,29 +316,34 @@ fieldset {
|
|||||||
background-color: #F7FAFC;
|
background-color: #F7FAFC;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
.wiz-choice{
|
.wiz-choice{
|
||||||
&:checked ~ .description {
|
&:not(:checked) ~ label .checked{
|
||||||
#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked),
|
|
||||||
#itop-ticket-mgmt-itil-enhanced-portal:not(:checked) {
|
|
||||||
~ .description::after {
|
|
||||||
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
|
|
||||||
display: block;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: $legacy-portal-removal-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not(:checked) ~ label .setup-extension-tag.checked{
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
&:checked ~ label .setup-extension-tag.unchecked{
|
&:checked ~ label .unchecked{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details:has(>.ibo-extension-details--actions>input:checked) {
|
||||||
|
.ibo-extension-details:has(#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked), #itop-ticket-mgmt-itil-enhanced-portal:not(:checked)) {
|
||||||
|
.ibo-extension-details--information--description::after {
|
||||||
|
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $legacy-portal-removal-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details--information--metadata{
|
||||||
|
color: $ibo-color-grey-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-disabled {
|
||||||
|
color: $ibo-color-grey-700;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 1.17rem;
|
font-size: 1.17rem;
|
||||||
@@ -522,10 +527,12 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-setup-summary-title {
|
.ibo-setup-summary-title, .ibo-setup-summary-title:visited, .ibo-setup-summary-title:hover {
|
||||||
font-size: $ibo-font-size-150;
|
font-size: $ibo-font-size-150;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ibo-setup-licenses--components-list {
|
#ibo-setup-licenses--components-list {
|
||||||
background-color: $ibo-color-white-200;
|
background-color: $ibo-color-white-200;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
@@ -605,6 +612,7 @@ body {
|
|||||||
color:#a00000;
|
color:#a00000;
|
||||||
}
|
}
|
||||||
.setup-extension-tag {
|
.setup-extension-tag {
|
||||||
|
display: inline-flex;
|
||||||
background-color: grey;
|
background-color: grey;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding-left: 3px;
|
padding-left: 3px;
|
||||||
@@ -630,6 +638,21 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ibo-extension-details {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.ibo-extension-details--actions input{
|
||||||
|
margin:0.2em 0.5em;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
:not(.ibo-badge) ~ .ibo-badge{
|
||||||
|
margin-left:0.5em;
|
||||||
|
}
|
||||||
|
.ibo-extension-details--information--label i{
|
||||||
|
font-size : 0.9em;
|
||||||
|
margin-left:0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
|
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -676,14 +699,10 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress_content {
|
#progress_content *:not(.message) + .message {
|
||||||
height: 200px;
|
margin-top: 1.5rem;
|
||||||
overflow: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#installation_progress {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#fresh_content{
|
#fresh_content{
|
||||||
border: 0;
|
border: 0;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSS of the template page
|
||||||
|
*/
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Javascript file loaded in template page
|
||||||
|
*/
|
||||||
|
|
||||||
16
datamodels/2.x/combodo-data-feature-removal/composer.json
Normal file
16
datamodels/2.x/combodo-data-feature-removal/composer.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"classmap-authoritative": true
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Combodo\\iTop\\DataFeatureRemoval\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "combodo/combodo-data-feature-removal",
|
||||||
|
"type": "itop-extension",
|
||||||
|
"description": "iTop Data Feature Removal",
|
||||||
|
"require": {
|
||||||
|
"composer-runtime-api": "^2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
datamodels/2.x/combodo-data-feature-removal/composer.lock
generated
Normal file
20
datamodels/2.x/combodo-data-feature-removal/composer.lock
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "b862a55cbf5448fb99f0905a4db6529b",
|
||||||
|
"packages": [],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"composer-runtime-api": "^2.0"
|
||||||
|
},
|
||||||
|
"platform-dev": {},
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3">
|
||||||
|
<menus>
|
||||||
|
<menu id="DataFeatureRemovalMenu" xsi:type="WebPageMenuNode" _delta="define">
|
||||||
|
<rank>30</rank>
|
||||||
|
<parent>SystemTools</parent>
|
||||||
|
<url>$pages/exec.php?exec_module=combodo-data-feature-removal&exec_page=index.php&c[menu]=DataFeatureRemovalMenu</url>
|
||||||
|
<enable_admin_only>1</enable_admin_only>
|
||||||
|
</menu>
|
||||||
|
</menus>
|
||||||
|
<module_parameters>
|
||||||
|
<parameters id="combodo-data-feature-removal">
|
||||||
|
<max_count_estimation_for_safe_cleanup>100</max_count_estimation_for_safe_cleanup>
|
||||||
|
</parameters>
|
||||||
|
</module_parameters>
|
||||||
|
</itop_design>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*/
|
||||||
|
|
||||||
|
Dict::Add('EN US', 'English', 'English', [
|
||||||
|
'Menu:DataFeatureRemovalMenu' => 'Features Removal',
|
||||||
|
'combodo-data-feature-removal/Operation:Main/Title' => 'Features Removal',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Main:Title' => 'Features Removal',
|
||||||
|
'DataFeatureRemoval:Main:SubTitle' => 'Prepare features you want to enable/disable in a future setup',
|
||||||
|
'DataFeatureRemoval:Failure:Title' => 'Feature dry removal errors',
|
||||||
|
'DataFeatureRemoval:Helper:Title' => 'Enable or disable features that are installed in your iTop.',
|
||||||
|
'DataFeatureRemoval:Helper:Desc1' => 'It will prepare the setup step that proceeds to feature enabling or disabling.',
|
||||||
|
'DataFeatureRemoval:Helper:Desc2' => 'Analyze if there are any data or dependency preventing you from enabling/disabling a feature.',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Features:Title' => 'Features',
|
||||||
|
'DataFeatureRemoval:Execution:Title' => 'Deletion Executions',
|
||||||
|
'DataFeatureRemoval:Analysis:Title' => 'Analysis result',
|
||||||
|
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s element(s) to clean before continuing',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:DeletionPlan:Title' => 'Deletion plan',
|
||||||
|
'DataFeatureRemoval:DeletionPlan:SubTitle' => '%1$s rows to clean before continuing',
|
||||||
|
'DataFeatureRemoval:DoDeletion:Title' => 'Do deletion',
|
||||||
|
'DataFeatureRemoval:DoDeletion:SubTitle' => 'Remove all the entries from the database',
|
||||||
|
'DataFeatureRemoval:DeletionPlan:Error:Issues' => 'Some objects must be deleted manually prior to cleanup',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Element to remove',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Feature name',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module name',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:CleanupComplete:Title' => 'All clear.',
|
||||||
|
'DataFeatureRemoval:CompilComplete' => 'Compilation successful. No Cleanup needed. You can proceed to setup.',
|
||||||
|
|
||||||
|
'UI:Button:Analyze' => 'Analyze',
|
||||||
|
'UI:Button:ModifyChoices' => 'Modify Choices',
|
||||||
|
'UI:Button:AnalyzeAndSetup' => 'Analyze and go to setup',
|
||||||
|
'UI:Button:PlanDeletion' => 'Prepare deletion plan',
|
||||||
|
'UI:Button:DoDeletion' => 'Delete data',
|
||||||
|
'UI:Button:BackToMain' => 'Back to Feature Removal',
|
||||||
|
'UI:Button:Setup' => 'Back to setup',
|
||||||
|
|
||||||
|
'UI:Action:ForceUninstall' => 'Force uninstall',
|
||||||
|
'UI:Action:MoreInfo' => 'More information',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Empty' => 'No data to remove',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Column:Class' => 'Class',
|
||||||
|
'DataFeatureRemoval:Column:DeleteCount' => 'Entries to delete',
|
||||||
|
'DataFeatureRemoval:Column:UpdateCount' => 'Entries to update',
|
||||||
|
'DataFeatureRemoval:Column:IssueCount' => 'Issues found preventing automatic cleanup',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Column:DeletedCount' => 'Deleted entries',
|
||||||
|
'DataFeatureRemoval:Column:UpdatedCount' => 'Updated entries',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*/
|
||||||
|
|
||||||
|
Dict::Add('FR FR', 'French', 'Français', [
|
||||||
|
'Menu:DataFeatureRemovalMenu' => 'Suppression de fonctionnalités',
|
||||||
|
'combodo-data-feature-removal/Operation:Main/Title' => 'Suppression de fonctionnalités',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Main:Title' => 'Suppression de fonctionnalités',
|
||||||
|
'DataFeatureRemoval:Main:SubTitle' => 'Préparez les fonctionnalités que vous souhaitez activer ou désactiver lors d’une prochaine configuration',
|
||||||
|
'DataFeatureRemoval:Failure:Title' => 'Erreurs lors de la simulation de suppression de fonctionnalités',
|
||||||
|
'DataFeatureRemoval:Helper:Title' => 'Activez ou désactivez les fonctionnalités installées dans votre iTop.',
|
||||||
|
'DataFeatureRemoval:Helper:Desc1' => 'Cette étape prépare l’assistant de configuration à activer ou désactiver des fonctionnalités.',
|
||||||
|
'DataFeatureRemoval:Helper:Desc2' => 'Analyse si des données ou des dépendances empêchent l’activation ou la désactivation d’une fonctionnalité.',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Features:Title' => 'Fonctionnalités',
|
||||||
|
'DataFeatureRemoval:Execution:Title' => 'Suppressions',
|
||||||
|
'DataFeatureRemoval:Analysis:Title' => 'Résultat de l’analyse',
|
||||||
|
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s élément(s) à nettoyer avant de poursuivre',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:DeletionPlan:Title' => 'Plan de suppression',
|
||||||
|
'DataFeatureRemoval:DeletionPlan:SubTitle' => '%1$s ligne(s) à nettoyer avant de poursuivre',
|
||||||
|
'DataFeatureRemoval:DoDeletion:Title' => 'Exécuter la suppression',
|
||||||
|
'DataFeatureRemoval:DoDeletion:SubTitle' => 'Supprime toutes les entrées de la base de données',
|
||||||
|
'DataFeatureRemoval:DeletionPlan:Error:Issues' => 'Certains objets doivent être supprimés manuellement avant le nettoyage',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Élément à supprimer',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Fonctionnalité',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:CleanupComplete:Title' => 'All clear.',
|
||||||
|
'DataFeatureRemoval:CompilComplete' => 'Compilation successful. No Cleanup needed. You can proceed to setup.',
|
||||||
|
|
||||||
|
'UI:Button:Analyze' => 'Analyser',
|
||||||
|
'UI:Button:ModifyChoices' => 'Modifier les choix',
|
||||||
|
'UI:Button:AnalyzeAndSetup' => 'Analyser et ouvrir l’assistant de configuration',
|
||||||
|
'UI:Button:PlanDeletion' => 'Préparer le plan de suppression',
|
||||||
|
'UI:Button:DoDeletion' => 'Supprimer les données',
|
||||||
|
'UI:Button:BackToMain' => 'Retour à la suppression de fonctionnalités',
|
||||||
|
'UI:Button:Setup' => 'Retour à l’assistant de configuration',
|
||||||
|
|
||||||
|
'UI:Action:ForceUninstall' => 'Forcer la désinstallation',
|
||||||
|
'UI:Action:MoreInfo' => 'Plus d’informations',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Empty' => 'Aucune donnée à supprimer',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Column:Class' => 'Classe',
|
||||||
|
'DataFeatureRemoval:Column:DeleteCount' => 'Entrées à supprimer',
|
||||||
|
'DataFeatureRemoval:Column:UpdateCount' => 'Entrées à mettre à jour',
|
||||||
|
'DataFeatureRemoval:Column:IssueCount' => 'Problèmes empêchant le nettoyage automatique',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Column:DeletedCount' => 'Entrées supprimées',
|
||||||
|
'DataFeatureRemoval:Column:UpdatedCount' => 'Entrées mises à jour',
|
||||||
|
]);
|
||||||
20
datamodels/2.x/combodo-data-feature-removal/index.php
Normal file
20
datamodels/2.x/combodo-data-feature-removal/index.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval;
|
||||||
|
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Controller\DataFeatureRemovalController;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
|
|
||||||
|
require_once(APPROOT.'application/startup.inc.php');
|
||||||
|
|
||||||
|
DataFeatureRemovalLog::Enable();
|
||||||
|
|
||||||
|
$oController = new DataFeatureRemovalController(MODULESROOT.DataFeatureRemovalHelper::MODULE_NAME.'/templates', DataFeatureRemovalHelper::MODULE_NAME);
|
||||||
|
$oController->SetDefaultOperation('Main');
|
||||||
|
$oController->HandleOperation();
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// PHP Data Model definition file
|
||||||
|
|
||||||
|
// WARNING - WARNING - WARNING
|
||||||
|
// DO NOT EDIT THIS FILE (unless you know what you are doing)
|
||||||
|
//
|
||||||
|
// If you provide a datamodel.xxxx.xml file with your module,
|
||||||
|
// this file WILL BE overwritten by the compilation of the
|
||||||
|
// module (during the setup) if the datamodel.xxxx.xml file
|
||||||
|
// contains the definition of new classes or menus.
|
||||||
|
//
|
||||||
|
// The recommended way to define new classes (for iTop 2.0 and later) is via the XML definition.
|
||||||
|
// This file remains in the module's template only for the cases where there is:
|
||||||
|
// - either no new class or menu defined in the XML file
|
||||||
|
// - or no XML file at all supplied by the module
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// iTop module definition file
|
||||||
|
//
|
||||||
|
|
||||||
|
SetupWebPage::AddModule(
|
||||||
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
|
'combodo-data-feature-removal/3.3.0',
|
||||||
|
[
|
||||||
|
// Identification
|
||||||
|
//
|
||||||
|
'label' => 'iTop Data Feature Removal',
|
||||||
|
'category' => 'business',
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
//
|
||||||
|
'dependencies' => [
|
||||||
|
|
||||||
|
],
|
||||||
|
'mandatory' => true,
|
||||||
|
'visible' => false,
|
||||||
|
|
||||||
|
// Components
|
||||||
|
//
|
||||||
|
'datamodel' => [
|
||||||
|
'vendor/autoload.php',
|
||||||
|
'model.combodo-data-feature-removal.php', // Contains the PHP code generated by the "compilation" of datamodel.combodo-data-feature-removal.xml
|
||||||
|
],
|
||||||
|
'webservice' => [],
|
||||||
|
'data.struct' => [
|
||||||
|
// add your 'structure' definition XML files here,
|
||||||
|
],
|
||||||
|
'data.sample' => [
|
||||||
|
// add your sample data XML files here,
|
||||||
|
],
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
//
|
||||||
|
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
|
||||||
|
'doc.more_information' => '', // hyperlink to more information, if any
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
//
|
||||||
|
'settings' => [
|
||||||
|
// Module specific settings go here, if any
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
@@ -0,0 +1,444 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Controller;
|
||||||
|
|
||||||
|
require_once APPROOT.'setup/feature_removal/SetupAudit.php';
|
||||||
|
require_once APPROOT.'setup/feature_removal/DryRemovalRuntimeEnvironment.php';
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\Helper\Session;
|
||||||
|
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
||||||
|
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||||
|
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||||
|
use ContextTag;
|
||||||
|
use CoreException;
|
||||||
|
use Dict;
|
||||||
|
use Exception;
|
||||||
|
use MetaModel;
|
||||||
|
use MissingDependencyException;
|
||||||
|
use RunTimeEnvironment;
|
||||||
|
use SetupUtils;
|
||||||
|
use utils;
|
||||||
|
|
||||||
|
class DataFeatureRemovalController extends Controller
|
||||||
|
{
|
||||||
|
private ?array $aExtensionsToCheck = null;
|
||||||
|
private bool $bForcedUninstallation = false;
|
||||||
|
private array $aCountClassesToCleanup = [];
|
||||||
|
private array $aAnalysisDataTable = [];
|
||||||
|
private array $aDeletionExecutionSummary = [];
|
||||||
|
private ?RuntimeEnvironment $oRuntimeEnvironment = null;
|
||||||
|
|
||||||
|
private int $iCount = 0;
|
||||||
|
private int $iColumnCount = 2;
|
||||||
|
|
||||||
|
public function OperationMain($sErrorMessage = null): void
|
||||||
|
{
|
||||||
|
$aParams = [];
|
||||||
|
|
||||||
|
$this->AddAnalyzeParams();
|
||||||
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
|
$aParams['iColumnCount'] = $this->iColumnCount;
|
||||||
|
$aParams['aAvailableExtensions'] = $this->SplitArrayIntoColumns($this->GetAvailableExtensions(), $this->iColumnCount);
|
||||||
|
$aParams['aAnalysisDataTable'] = $this->aAnalysisDataTable;
|
||||||
|
$aParams['aClasses'] = array_keys($this->aCountClassesToCleanup);
|
||||||
|
$aParams['DataFeatureRemovalErrorMessage'] = $sErrorMessage;
|
||||||
|
$aParams['bHasData'] = $this->iCount > 0;
|
||||||
|
$aParams['sSetupUrl'] = utils::GetAbsoluteUrlAppRoot().'setup';
|
||||||
|
$aParams['iCount'] = $this->iCount;
|
||||||
|
|
||||||
|
Session::Set('bForceCompilation', true);
|
||||||
|
$this->AddLinkedStylesheet(utils::GetAbsoluteUrlModulesRoot().DataFeatureRemovalHelper::MODULE_NAME.'/assets/css/DataFeatureRemoval.css');
|
||||||
|
$this->AddLinkedScript(utils::GetAbsoluteUrlModulesRoot().DataFeatureRemovalHelper::MODULE_NAME.'/assets/js/DataFeatureRemoval.js');
|
||||||
|
$this->DisplayPage($aParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddAnalyzeParams(): void
|
||||||
|
{
|
||||||
|
$aData = [];
|
||||||
|
$aColumns = [];
|
||||||
|
$this->iCount = 0;
|
||||||
|
foreach ($this->aCountClassesToCleanup as $sClass => $iCount) {
|
||||||
|
$sModuleName = MetaModel::GetModuleName($sClass);
|
||||||
|
$aExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetIncludingExtensions($sModuleName);
|
||||||
|
$sExtensions = implode(' ', $aExtensions);
|
||||||
|
$aColumns = ['ClassName','FeatureName','Module','Occurrence'];
|
||||||
|
$aData[] = [$sClass,$sExtensions,$sModuleName,$iCount];
|
||||||
|
$this->iCount += $iCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->aAnalysisDataTable = $this->GetTableData('Analysis', $aColumns, $aData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function OperationAnalysisResult(): void
|
||||||
|
{
|
||||||
|
$aParams = [];
|
||||||
|
|
||||||
|
if (SetupUtils::IsSessionSetupTokenValid()) {
|
||||||
|
//from setup wizard/mtp
|
||||||
|
SetupUtils::EraseSetupToken();
|
||||||
|
} else {
|
||||||
|
//from same module
|
||||||
|
$this->ValidateTransactionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display changed extensions
|
||||||
|
$aHiddenInputNames = [
|
||||||
|
'selected_extensions',
|
||||||
|
'selected_modules',
|
||||||
|
'display_choices',
|
||||||
|
'added_extensions',
|
||||||
|
'removed_extensions',
|
||||||
|
'extensions_not_uninstallable',
|
||||||
|
];
|
||||||
|
|
||||||
|
$aHiddenInputs = [];
|
||||||
|
foreach ($aHiddenInputNames as $sInputName) {
|
||||||
|
$aHiddenInputs[$sInputName] = utils::ReadPostedParam($sInputName, "[]", utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||||
|
}
|
||||||
|
$aParams['aHiddenInputs'] = $aHiddenInputs;
|
||||||
|
|
||||||
|
$aAddedExtensions = json_decode($aHiddenInputs['added_extensions'], true);
|
||||||
|
|
||||||
|
$aRemovedExtensions = json_decode($aHiddenInputs['removed_extensions'], true);
|
||||||
|
if ("[]" === $aHiddenInputs['selected_modules']) {
|
||||||
|
//it does not come from setup
|
||||||
|
// we get extensions from 1st screen uiblocks
|
||||||
|
$this->ReadExtensionsDiff();
|
||||||
|
$aAddedExtensions = $this->aExtensionsToCheck['to_be_installed'];
|
||||||
|
$aHiddenInputs['added_extensions'] = $this->ConvertIntoSetupFormat($aAddedExtensions);
|
||||||
|
|
||||||
|
$aRemovedExtensions = $this->aExtensionsToCheck['to_be_removed'];
|
||||||
|
$aHiddenInputs['removed_extensions'] = $this->ConvertIntoSetupFormat($aRemovedExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
$aRemoveExtensionCodes = array_keys($aRemovedExtensions);
|
||||||
|
|
||||||
|
$aParams['aAddedExtensions'] = $aAddedExtensions;
|
||||||
|
$aParams['aRemovedExtensions'] = $aRemovedExtensions;
|
||||||
|
|
||||||
|
DataFeatureRemovalLog::Debug(__METHOD__.' Extensions given in parameter', null, [
|
||||||
|
'added_extensions' => $aAddedExtensions,
|
||||||
|
'removed_extensions' => $aRemovedExtensions]);
|
||||||
|
|
||||||
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
|
$aParams['iColumnCount'] = $this->iColumnCount;
|
||||||
|
$aParams['aAvailableExtensions'] = $this->SplitArrayIntoColumns($this->GetExtensionsDiff($aAddedExtensions, $aRemovedExtensions), $this->iColumnCount);
|
||||||
|
|
||||||
|
$bForceCompilation = Session::Get('bForceCompilation', false);
|
||||||
|
try {
|
||||||
|
$this->Compile($aRemoveExtensionCodes, $bForceCompilation);
|
||||||
|
} catch (CoreException $e) {
|
||||||
|
$aParams['DataFeatureRemovalErrorMessage'] = $e->getHtmlDesc();
|
||||||
|
$this->DisplayPage($aParams, 'AnalysisResult');
|
||||||
|
return;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$aParams['DataFeatureRemovalErrorMessage'] = $e->getMessage();
|
||||||
|
$this->DisplayPage($aParams, 'AnalysisResult');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("[]" === $aHiddenInputs['selected_modules']) {
|
||||||
|
//to make setup redirection work, we need to pass complex data structures to setup wizards (ie extension/module lists)
|
||||||
|
$oConfig = MetaModel::GetConfig();
|
||||||
|
$aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, $aAddedExtensions, $aRemovedExtensions);
|
||||||
|
$aHiddenInputs['selected_extensions'] = $this->ConvertIntoSetupFormat($aSelectedExtensions);
|
||||||
|
|
||||||
|
$oRunTimeEnvironment = $this->GetRuntimeEnvironment($aRemovedExtensions);
|
||||||
|
$aSearchDirs = [$oRunTimeEnvironment->GetBuildDir()];
|
||||||
|
$aSelectedModules = $oRunTimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions, $aSearchDirs);
|
||||||
|
$aHiddenInputs['selected_modules'] = $this->ConvertIntoSetupFormat($aSelectedModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
|
$oSetupAudit = new SetupAudit($sSourceEnv);
|
||||||
|
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
|
||||||
|
DataFeatureRemovalLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
||||||
|
|
||||||
|
$aParams['aClasses'] = $aGetRemovedClasses;
|
||||||
|
|
||||||
|
new ContextTag(ContextTag::TAG_SETUP);
|
||||||
|
$aParams['sLaunchSetupUrl'] = utils::GetAbsoluteUrlAppRoot().'setup/wizard.php';
|
||||||
|
$aParams['aSetupParams'] = array_merge([
|
||||||
|
"_class" => "WizStepLandingBeforeAudit",
|
||||||
|
"_params[authent]" => SetupUtils::CreateSetupToken(),
|
||||||
|
"operation" => "next",
|
||||||
|
], $aHiddenInputs);
|
||||||
|
|
||||||
|
[$aParams['aDeletionPlanSummary'], $aParams['iQueryCount'], $aParams['bDeletionPossible']] = $this->GetDeletionPlanSummaryTable($aGetRemovedClasses);
|
||||||
|
[$aParams['aDeletionExecutionSummary'], $aParams['bHasDeletionExecution']] = $this->GetExecutionSummaryTable();
|
||||||
|
$aParams['bDeletionNeeded'] = ($aParams['iQueryCount'] > 0);
|
||||||
|
Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary));
|
||||||
|
|
||||||
|
$this->DisplayPage($aParams, 'AnalysisResult');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ConvertIntoSetupFormat(array $aData): string
|
||||||
|
{
|
||||||
|
return json_encode($aData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aRemovedExtensions
|
||||||
|
* @param bool $bForceCompilation
|
||||||
|
* @return void
|
||||||
|
* @throws \ConfigException
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
private function Compile(array $aRemovedExtensions, bool $bForceCompilation = true): void
|
||||||
|
{
|
||||||
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
|
$sBuildDir = APPROOT."/env-$sSourceEnv-build";
|
||||||
|
if (! is_dir($sBuildDir)) {
|
||||||
|
SetupUtils::builddir($sBuildDir);
|
||||||
|
}
|
||||||
|
$bIsDirEmpty = count(scandir($sBuildDir)) === 2;
|
||||||
|
|
||||||
|
if ($bIsDirEmpty || $bForceCompilation) {
|
||||||
|
DataFeatureRemovalLog::Debug(
|
||||||
|
__METHOD__,
|
||||||
|
null,
|
||||||
|
['sSourceEnv' => $sSourceEnv, 'sBuildDir' => $sBuildDir, 'bIsDirEmpty' => $bIsDirEmpty, glob("$sBuildDir/*")]
|
||||||
|
);
|
||||||
|
$this->GetRuntimeEnvironment($aRemovedExtensions)->CompileFrom($sSourceEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetRuntimeEnvironment(array $aRemovedExtensions): RunTimeEnvironment
|
||||||
|
{
|
||||||
|
if (is_null($this->oRuntimeEnvironment)) {
|
||||||
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
|
$this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aRemovedExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->oRuntimeEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetExecutionSummaryTable(): array
|
||||||
|
{
|
||||||
|
$sName = 'ExcutionSummary';
|
||||||
|
|
||||||
|
$aTableData = [];
|
||||||
|
if (count($this->aDeletionExecutionSummary) === 0) {
|
||||||
|
return [$aTableData, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aColumns = ['Class', 'Total Deleted Count' , 'Total Updated Count', 'Deleted Count' , 'Updated Count'];
|
||||||
|
$aRows = [];
|
||||||
|
/** @var DataCleanupSummaryEntity $oSummary */
|
||||||
|
foreach ($this->aDeletionExecutionSummary as $sClass => $oSummary) {
|
||||||
|
$aRows[] = [
|
||||||
|
$sClass,
|
||||||
|
$oSummary->iTotalDeleteCount,
|
||||||
|
$oSummary->iTotalUpdateCount,
|
||||||
|
$oSummary->iDeleteCount,
|
||||||
|
$oSummary->iUpdateCount,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aTableData = $this->GetTableData($sName, $aColumns, $aRows);
|
||||||
|
|
||||||
|
return [$aTableData, true];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetDeletionPlanSummaryTable(array $aRemovedClasses): array
|
||||||
|
{
|
||||||
|
$sName = 'DeletionPlanSummary';
|
||||||
|
$oDataCleanupService = new DataCleanupService();
|
||||||
|
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aRemovedClasses);
|
||||||
|
$aColumns = ['Class', 'Delete Count' , 'Update Count', 'Issue Count'];
|
||||||
|
$aRows = [];
|
||||||
|
$iQueryCount = 0;
|
||||||
|
$bHasIssues = false;
|
||||||
|
foreach ($aDeletionPlanSummaryEntities as $oDeletionPlanSummaryEntity) {
|
||||||
|
$aRows[] = [
|
||||||
|
$oDeletionPlanSummaryEntity->sClass,
|
||||||
|
$oDeletionPlanSummaryEntity->iDeleteCount,
|
||||||
|
$oDeletionPlanSummaryEntity->iUpdateCount,
|
||||||
|
$oDeletionPlanSummaryEntity->iIssueCount,
|
||||||
|
];
|
||||||
|
$bHasIssues |= ($oDeletionPlanSummaryEntity->iIssueCount !== 0);
|
||||||
|
$iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount;
|
||||||
|
$iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount;
|
||||||
|
}
|
||||||
|
return [$this->GetTableData($sName, $aColumns, $aRows), $iQueryCount, !$bHasIssues];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function OperationDoDeletion(): void
|
||||||
|
{
|
||||||
|
$this->ValidateTransactionId();
|
||||||
|
|
||||||
|
$this->aDeletionExecutionSummary = unserialize(Session::Get('aDeletionExecutionSummary'));
|
||||||
|
Session::Unset('aDeletionExecutionSummary');
|
||||||
|
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||||
|
|
||||||
|
$oDataCleanupService = new DataCleanupService();
|
||||||
|
$aDeletionExecutionSummary = $oDataCleanupService->ExecuteCleanup($aClasses);
|
||||||
|
foreach ($aDeletionExecutionSummary as $sClass => $oExecutionSummary) {
|
||||||
|
if (!array_key_exists($sClass, $this->aDeletionExecutionSummary)) {
|
||||||
|
$this->aDeletionExecutionSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
|
}
|
||||||
|
$oSummary = $this->aDeletionExecutionSummary[$sClass];
|
||||||
|
$oSummary->iDeleteCount = $oExecutionSummary->iDeleteCount;
|
||||||
|
$oSummary->iUpdateCount = $oExecutionSummary->iUpdateCount;
|
||||||
|
$oSummary->iTotalDeleteCount += $oExecutionSummary->iDeleteCount;
|
||||||
|
$oSummary->iTotalUpdateCount += $oExecutionSummary->iUpdateCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->OperationAnalysisResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetAvailableExtensions(bool $bIncludePackageExtensions = false): array
|
||||||
|
{
|
||||||
|
$aExtensionsData = [];
|
||||||
|
if ($bIncludePackageExtensions) {
|
||||||
|
$aExtensionsRef = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetAllExtensionsWithPreviouslyInstalled();
|
||||||
|
} else {
|
||||||
|
$aExtensionsRef = DataFeatureRemoverExtensionService::GetInstance()->ReadItopExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aExtensionsRef as $oExtension) {
|
||||||
|
/** @var \iTopExtension $oExtension */
|
||||||
|
$aExtensionsData[$oExtension->sCode] = [
|
||||||
|
'version' => $oExtension->sVersion,
|
||||||
|
'label' => $oExtension->sLabel,
|
||||||
|
'code' => $oExtension->sCode,
|
||||||
|
'description' => $oExtension->sDescription,
|
||||||
|
'source' => $oExtension->GetExtensionSourceLabel(),
|
||||||
|
'installed' => $oExtension->bInstalled,
|
||||||
|
'extra_flags' => [
|
||||||
|
'uninstallable' => $oExtension->CanBeUninstalled(),
|
||||||
|
'remote' => $oExtension->IsRemote(),
|
||||||
|
'missing' => $oExtension->bRemovedFromDisk,
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aExtensionsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetExtensionsDiff(array $aAddedExtensions, array $aRemovedExtensions): array
|
||||||
|
{
|
||||||
|
$aExtensions = [];
|
||||||
|
foreach ($this->GetAvailableExtensions(true) as $sCode => $aExtension) {
|
||||||
|
$aExtension['extra_flags']['disabled'] = true;
|
||||||
|
if (isset($aAddedExtensions[$sCode])) {
|
||||||
|
$aExtension['extra_flags']['selected'] = true;
|
||||||
|
$aExtensions[$sCode] = $aExtension;
|
||||||
|
} elseif (isset($aRemovedExtensions[$sCode])) {
|
||||||
|
$aExtension['extra_flags']['selected'] = false;
|
||||||
|
$aExtensions[$sCode] = $aExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetTableData(string $sTableName, array $aColumns, array $aData): array
|
||||||
|
{
|
||||||
|
if (empty($aData)) {
|
||||||
|
return [
|
||||||
|
'Type' => 'Table',
|
||||||
|
'Columns' => [['label' => '']],
|
||||||
|
'Data' => [[ Dict::S('DataFeatureRemoval:Table:Empty')]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aNewColumns = [];
|
||||||
|
foreach ($aColumns as $sColumn) {
|
||||||
|
$aNewColumns[] = ['label' => Dict::S("DataFeatureRemoval:Table:$sTableName:$sColumn", Dict::S("DataFeatureRemoval:Column:$sColumn", $sColumn))];
|
||||||
|
}
|
||||||
|
$aColumns = $aNewColumns;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Type' => 'Table',
|
||||||
|
'Columns' => $aColumns,
|
||||||
|
'Data' => $aData,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
*/
|
||||||
|
private function ValidateTransactionId(): void
|
||||||
|
{
|
||||||
|
if (empty($_POST)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sTransactionId = utils::ReadPostedParam('transaction_id', null, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID);
|
||||||
|
DataFeatureRemovalLog::Debug(__FUNCTION__.": Transaction [$sTransactionId]");
|
||||||
|
if (empty($sTransactionId) || !utils::IsTransactionValid($sTransactionId, false)) {
|
||||||
|
throw new DataFeatureRemovalException(Dict::S("iTopUpdate:Error:InvalidToken"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read extensions selected from posted parameters
|
||||||
|
* @return int Number of extensions to be added or removed
|
||||||
|
*/
|
||||||
|
public function ReadExtensionsDiff(): int
|
||||||
|
{
|
||||||
|
if (!is_null($this->aExtensionsToCheck)) {
|
||||||
|
return count($this->aExtensionsToCheck['to_be_installed']) + count($this->aExtensionsToCheck['to_be_removed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$aAvailableExtensions = $this->GetAvailableExtensions();
|
||||||
|
$aSelectedExtensionsFromUI = utils::ReadPostedParam('aSelectedExtensions', []);
|
||||||
|
$this->aExtensionsToCheck = [
|
||||||
|
'to_be_installed' => [],
|
||||||
|
'to_be_removed' => [],
|
||||||
|
];
|
||||||
|
foreach ($aAvailableExtensions as $sCode => &$aExtensionData) {
|
||||||
|
if (!isset($aSelectedExtensionsFromUI[$sCode])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aExtensionData['installed'] && $aSelectedExtensionsFromUI[$sCode] !== 'on') {
|
||||||
|
$aExtensionData['extra_flags']['selected'] = false;
|
||||||
|
$sLabel = $aAvailableExtensions[$sCode]['label'];
|
||||||
|
$this->aExtensionsToCheck['to_be_removed'][$sCode] = $sLabel;
|
||||||
|
if (!$aExtensionData['extra_flags']['uninstallable'] || $aExtensionData['extra_flags']['remote']) {
|
||||||
|
$this->bForcedUninstallation = true;
|
||||||
|
}
|
||||||
|
} elseif (!$aExtensionData['installed'] && $aSelectedExtensionsFromUI[$sCode] === 'on') {
|
||||||
|
$aExtensionData['extra_flags']['selected'] = true;
|
||||||
|
$sLabel = $aAvailableExtensions[$sCode]['label'];
|
||||||
|
$this->aExtensionsToCheck['to_be_installed'][$sCode] = $sLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count($this->aExtensionsToCheck['to_be_installed']) + count($this->aExtensionsToCheck['to_be_removed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divide an array into several sub arrays, distributing elements so that every sub array has an equal amount of elements
|
||||||
|
* @param mixed[] $aInput
|
||||||
|
* @param int $iColNumber
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
private function SplitArrayIntoColumns(array $aInput, int $iColNumber)
|
||||||
|
{
|
||||||
|
$aOutput = array_fill(0, $iColNumber, []);
|
||||||
|
$iIndex = 0;
|
||||||
|
foreach ($aInput as $mItem) {
|
||||||
|
//Split extensions in $iColNumber columns
|
||||||
|
$aOutput[$iIndex % $this->iColumnCount][] = $mItem;
|
||||||
|
$iIndex++;
|
||||||
|
}
|
||||||
|
return $aOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Entity;
|
||||||
|
|
||||||
|
class DataCleanupSummaryEntity
|
||||||
|
{
|
||||||
|
public string $sClass;
|
||||||
|
public int $iIssueCount = 0;
|
||||||
|
public int $iUpdateCount = 0;
|
||||||
|
public int $iDeleteCount = 0;
|
||||||
|
public int $iTotalUpdateCount = 0;
|
||||||
|
public int $iTotalDeleteCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sClass
|
||||||
|
*/
|
||||||
|
public function __construct(string $sClass)
|
||||||
|
{
|
||||||
|
$this->sClass = $sClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Helper;
|
||||||
|
|
||||||
|
use MetaModel;
|
||||||
|
use utils;
|
||||||
|
|
||||||
|
class DataFeatureRemovalConfig
|
||||||
|
{
|
||||||
|
private static DataFeatureRemovalConfig $oInstance;
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function GetInstance(): DataFeatureRemovalConfig
|
||||||
|
{
|
||||||
|
if (!isset(self::$oInstance)) {
|
||||||
|
self::$oInstance = new DataFeatureRemovalConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Get(string $sParamName, $default = null)
|
||||||
|
{
|
||||||
|
return MetaModel::GetModuleSetting(DataFeatureRemovalHelper::MODULE_NAME, $sParamName, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetBoolean(string $sParamName, $default = null): bool
|
||||||
|
{
|
||||||
|
$res = $this->Get($sParamName, $default);
|
||||||
|
|
||||||
|
return boolval($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function IsEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->GetBoolean('enable', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Set(string $sParamName, $value)
|
||||||
|
{
|
||||||
|
$oConfig = utils::GetConfig();
|
||||||
|
$oConfig->SetModuleSetting(DataFeatureRemovalHelper::MODULE_NAME, $sParamName, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Helper;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class DataFeatureRemovalException extends Exception
|
||||||
|
{
|
||||||
|
public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null, array $aContext = [])
|
||||||
|
{
|
||||||
|
if (!is_null($previous)) {
|
||||||
|
$sStack = $previous->getTraceAsString();
|
||||||
|
$sError = $previous->getMessage();
|
||||||
|
} else {
|
||||||
|
$sStack = $this->getTraceAsString();
|
||||||
|
$sError = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$aContext['error'] = $sError;
|
||||||
|
$aContext['stack'] = $sStack;
|
||||||
|
DataFeatureRemovalLog::Error($message, null, $aContext);
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Helper;
|
||||||
|
|
||||||
|
class DataFeatureRemovalHelper
|
||||||
|
{
|
||||||
|
public const MODULE_NAME = 'combodo-data-feature-removal';
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Helper;
|
||||||
|
|
||||||
|
use LogAPI;
|
||||||
|
|
||||||
|
class DataFeatureRemovalLog extends LogAPI
|
||||||
|
{
|
||||||
|
public const CHANNEL_DEFAULT = 'DataFeatureRemoval';
|
||||||
|
|
||||||
|
protected static $m_oFileLog = null;
|
||||||
|
|
||||||
|
public static function Enable($sTargetFile = null)
|
||||||
|
{
|
||||||
|
if (empty($sTargetFile)) {
|
||||||
|
$sTargetFile = APPROOT.'log/error.log';
|
||||||
|
}
|
||||||
|
parent::Enable($sTargetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use CMDBObjectSet;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
|
use Combodo\iTop\Service\Limits\ExecutionLimits;
|
||||||
|
use DBObject;
|
||||||
|
use DBObjectSearch;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class DataCleanupService
|
||||||
|
{
|
||||||
|
private array $aVisited = [];
|
||||||
|
private iObjectService $oObjectService;
|
||||||
|
private ExecutionLimits $oExecutionLimits;
|
||||||
|
|
||||||
|
public function __construct(int $iMaxExecutionTime = 30, int $iMaxMemoryPercent = 80)
|
||||||
|
{
|
||||||
|
DataFeatureRemovalLog::Enable();
|
||||||
|
$this->oExecutionLimits = new ExecutionLimits($iMaxExecutionTime, $iMaxMemoryPercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a summary of the deletion plan computed for the classes.
|
||||||
|
* The result is used for display
|
||||||
|
*
|
||||||
|
* @param array|null $aClasses
|
||||||
|
*
|
||||||
|
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity>
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
*/
|
||||||
|
public function GetCleanupSummary(?array $aClasses): array
|
||||||
|
{
|
||||||
|
return $this->ExecuteCleanup($aClasses ?? [], oObjectService: new ObjectServiceSummary());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetNextObjectToDelete(array $aClasses): ?DBObject
|
||||||
|
{
|
||||||
|
foreach ($aClasses as $sClass) {
|
||||||
|
$oFilter = new DBObjectSearch($sClass);
|
||||||
|
$oFilter->AllowAllData();
|
||||||
|
$oSet = new \DBObjectSet($oFilter);
|
||||||
|
while ($oObject = $oSet->Fetch()) {
|
||||||
|
if (!$this->IsVisited($oObject)) {
|
||||||
|
return $oObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aClasses
|
||||||
|
* @param \Combodo\iTop\DataFeatureRemoval\Service\iObjectService|null $oObjectService
|
||||||
|
*
|
||||||
|
* @return array execution summary
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function ExecuteCleanup(array $aClasses, ?iObjectService $oObjectService = null): array
|
||||||
|
{
|
||||||
|
$this->oObjectService = $oObjectService ?? new ObjectService();
|
||||||
|
|
||||||
|
$this->aVisited = [];
|
||||||
|
|
||||||
|
while ($oObject = $this->GetNextObjectToDelete($aClasses)) {
|
||||||
|
if ($this->RecursiveDeletion($oObject) === false) {
|
||||||
|
// Timeout, stop here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->oObjectService->GetSummary();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function MarkObjectAsVisited(DBObject $oObject): void
|
||||||
|
{
|
||||||
|
$sClass = get_class($oObject);
|
||||||
|
$sId = $oObject->GetKey();
|
||||||
|
$sKey = "$sClass-$sId";
|
||||||
|
$this->aVisited[$sKey] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function IsVisited(DBObject $oObject): bool
|
||||||
|
{
|
||||||
|
$sClass = get_class($oObject);
|
||||||
|
$sId = $oObject->GetKey();
|
||||||
|
$sKey = "$sClass-$sId";
|
||||||
|
|
||||||
|
$bRes = $this->aVisited[$sKey] ?? false;
|
||||||
|
DataFeatureRemovalLog::Debug('Checking if object is visited', null, [$sKey, $bRes]);
|
||||||
|
return $bRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param \DBObject $oObjectToClean
|
||||||
|
*
|
||||||
|
* @return bool true if deletion is complete, false in case of timeout or memory limit reached
|
||||||
|
*
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
private function RecursiveDeletion(DBObject $oObjectToClean): bool
|
||||||
|
{
|
||||||
|
$this->MarkObjectAsVisited($oObjectToClean);
|
||||||
|
$sClass = get_class($oObjectToClean);
|
||||||
|
|
||||||
|
$aReferencingMe = MetaModel::EnumReferencingClasses($sClass);
|
||||||
|
foreach ($aReferencingMe as $sRemoteClass => $aExtKeys) {
|
||||||
|
/** @var \AttributeExternalKey $oExtKeyAttDef */
|
||||||
|
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
|
||||||
|
// skip if this external key is behind an external field
|
||||||
|
if (!$oExtKeyAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oSearch = new DBObjectSearch($sRemoteClass);
|
||||||
|
$oSearch->AddCondition($sExtKeyAttCode, $oObjectToClean->GetKey(), '=');
|
||||||
|
$oSearch->AllowAllData();
|
||||||
|
$oSet = new CMDBObjectSet($oSearch);
|
||||||
|
$oSet->OptimizeColumnLoad([$sRemoteClass => [$oExtKeyAttDef->GetCode()]]);
|
||||||
|
/** @var DBObject $oDependentObj */
|
||||||
|
while ($oDependentObj = $oSet->Fetch()) {
|
||||||
|
$iDeletePropagationOption = $oExtKeyAttDef->GetDeletionPropagationOption();
|
||||||
|
if ($iDeletePropagationOption == DEL_MANUAL) {
|
||||||
|
$this->oObjectService->SetIssue(get_class($oDependentObj));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oExtKeyAttDef->IsNullAllowed()) {
|
||||||
|
// Optional external key, list to reset
|
||||||
|
if (($iDeletePropagationOption == DEL_MOVEUP) && ($oExtKeyAttDef->IsHierarchicalKey())) {
|
||||||
|
// Move the child up one level i.e. set the same parent as the current object
|
||||||
|
$iParentId = $oObjectToClean->Get($oExtKeyAttDef->GetCode());
|
||||||
|
$this->oObjectService->Update($oDependentObj, $oExtKeyAttDef->GetCode(), $iParentId);
|
||||||
|
} else {
|
||||||
|
$this->oObjectService->Update($oDependentObj, $oExtKeyAttDef->GetCode(), 0);
|
||||||
|
}
|
||||||
|
if ($this->oExecutionLimits->ShouldStopExecution()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Propagate deletion only if not visited
|
||||||
|
if ($this->IsVisited($oDependentObj)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$this->RecursiveDeletion($oDependentObj)) {
|
||||||
|
// Timeout
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->oObjectService->Delete($sClass, $oObjectToClean->GetKey());
|
||||||
|
|
||||||
|
if ($this->oExecutionLimits->ShouldStopExecution()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use iTopExtension;
|
||||||
|
use iTopExtensionsMap;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class DataFeatureRemoverExtensionService
|
||||||
|
{
|
||||||
|
private static DataFeatureRemoverExtensionService $oInstance;
|
||||||
|
private ?iTopExtensionsMap $oMap = null;
|
||||||
|
private array $aItopExtensions = [];
|
||||||
|
private array $aIncludingExtensionsByModuleName = [];
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function GetInstance(): DataFeatureRemoverExtensionService
|
||||||
|
{
|
||||||
|
if (!isset(self::$oInstance)) {
|
||||||
|
self::$oInstance = new DataFeatureRemoverExtensionService();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function SetInstance(?DataFeatureRemoverExtensionService $oInstance): void
|
||||||
|
{
|
||||||
|
self::$oInstance = $oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sModuleName
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
*/
|
||||||
|
public function GetIncludingExtensions(string $sModuleName): array
|
||||||
|
{
|
||||||
|
if (count($this->aIncludingExtensionsByModuleName) === 0) {
|
||||||
|
foreach ($this->ReadItopExtensions() as $oExtension) {
|
||||||
|
$aModuleNames = $oExtension->aModules;
|
||||||
|
if (is_array($aModuleNames) && count($aModuleNames) > 0) {
|
||||||
|
foreach ($aModuleNames as $sModule) {
|
||||||
|
$aExtensions = $this->aIncludingExtensionsByModuleName[$sModule] ?? [];
|
||||||
|
$aExtensions[] = $oExtension->sLabel.'/'.$oExtension->sVersion;
|
||||||
|
$this->aIncludingExtensionsByModuleName[$sModule] = $aExtensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->aIncludingExtensionsByModuleName[$sModuleName] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \iTopExtensionsMap
|
||||||
|
*/
|
||||||
|
public function GetExtensionMap(): iTopExtensionsMap
|
||||||
|
{
|
||||||
|
if (is_null($this->oMap)) {
|
||||||
|
$this->oMap = new iTopExtensionsMap();
|
||||||
|
$this->oMap->LoadInstalledExtensionsFromDatabase(MetaModel::GetConfig());
|
||||||
|
}
|
||||||
|
return $this->oMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return iTopExtension[]
|
||||||
|
*/
|
||||||
|
public function ReadItopExtensions(): array
|
||||||
|
{
|
||||||
|
if (count($this->aItopExtensions) === 0) {
|
||||||
|
$this->aItopExtensions = $this->GetExtensionMap()->GetAllExtensionsToDisplayInSetup(true);
|
||||||
|
|
||||||
|
uasort($this->aItopExtensions, function (iTopExtension $oiTopExtension1, iTopExtension $oiTopExtension2) {
|
||||||
|
return strcmp($oiTopExtension1->sLabel, $oiTopExtension2->sLabel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->aItopExtensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use CMDBSource;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
|
use DBObject;
|
||||||
|
use DBObjectSearch;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class ObjectService extends ObjectServiceSummary
|
||||||
|
{
|
||||||
|
public function Update(DBObject $oToUpdate, string $sAttCode, $value): void
|
||||||
|
{
|
||||||
|
$oToUpdate->Set($sAttCode, $value);
|
||||||
|
$oToUpdate->DBUpdate();
|
||||||
|
parent::Update($oToUpdate, $sAttCode, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Delete(string $sClass, string $sId): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
CMDBSource::Query('START TRANSACTION');
|
||||||
|
// Delete any existing change tracking about the current object
|
||||||
|
$oFilter = new DBObjectSearch('CMDBChangeOp');
|
||||||
|
$oFilter->AddCondition('objclass', $sClass, '=');
|
||||||
|
$oFilter->AddCondition('objkey', $sId, '=');
|
||||||
|
MetaModel::PurgeData($oFilter);
|
||||||
|
|
||||||
|
// Delete the entry
|
||||||
|
$aClassesToRemove = array_merge(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL), MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_EXCLUDELEAF, false));
|
||||||
|
foreach ($aClassesToRemove as $sParentClass) {
|
||||||
|
/** @var DBObjectSearch $oFilter */
|
||||||
|
$oFilter = DBObjectSearch::FromOQL_AllData("SELECT $sParentClass WHERE id=:id");
|
||||||
|
$sQuery = $oFilter->MakeDeleteQuery(['id' => $sId]);
|
||||||
|
CMDBSource::DeleteFrom($sQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMDBSource::Query('COMMIT');
|
||||||
|
parent::Delete($sClass, $sId);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DataFeatureRemovalLog::Exception(__METHOD__.': Cleanup failed', $e);
|
||||||
|
CMDBSource::Query('ROLLBACK');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetIssue(string $sClass): void
|
||||||
|
{
|
||||||
|
throw new DataFeatureRemovalException('Deletion Plan cannot be executed due to issues');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
|
use DBObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage operation summary instead of doing the actual update or delete
|
||||||
|
*
|
||||||
|
* The summary is an array [class => DeletionPlanSummaryEntity]
|
||||||
|
*/
|
||||||
|
class ObjectServiceSummary implements iObjectService
|
||||||
|
{
|
||||||
|
private array $aSummary = [];
|
||||||
|
|
||||||
|
public function Update(DBObject $oToUpdate, string $sAttCode, $value): void
|
||||||
|
{
|
||||||
|
$sClass = get_class($oToUpdate);
|
||||||
|
DataFeatureRemovalLog::Debug('Object to update', null, ['class' => $sClass, 'id' => $oToUpdate->GetKey(), 'code' => $sAttCode, 'value' => "$value"]);
|
||||||
|
if (! array_key_exists($sClass, $this->aSummary)) {
|
||||||
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
|
}
|
||||||
|
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
||||||
|
$oDeletionPlanSummaryEntity->iUpdateCount++;
|
||||||
|
$oDeletionPlanSummaryEntity->iTotalUpdateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Delete(string $sClass, string $sId): void
|
||||||
|
{
|
||||||
|
DataFeatureRemovalLog::Debug('Object to delete', null, ['class' => $sClass, 'id' => $sId]);
|
||||||
|
if (!array_key_exists($sClass, $this->aSummary)) {
|
||||||
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
|
}
|
||||||
|
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
||||||
|
$oDeletionPlanSummaryEntity->iDeleteCount++;
|
||||||
|
$oDeletionPlanSummaryEntity->iTotalDeleteCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetIssue(string $sClass): void
|
||||||
|
{
|
||||||
|
DataFeatureRemovalLog::Debug('Issue on object', null, ['class' => $sClass]);
|
||||||
|
if (!array_key_exists($sClass, $this->aSummary)) {
|
||||||
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
|
}
|
||||||
|
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
||||||
|
$oDeletionPlanSummaryEntity->iIssueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetSummary(): array
|
||||||
|
{
|
||||||
|
return $this->aSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetSummary(array $aSummary): void
|
||||||
|
{
|
||||||
|
foreach ($aSummary as $sClass => $oPreviousSummaryEntity) {
|
||||||
|
$oSummaryEntity = new DataCleanupSummaryEntity($sClass);
|
||||||
|
$oSummaryEntity->iTotalUpdateCount = $oPreviousSummaryEntity->iTotalUpdateCount;
|
||||||
|
$oSummaryEntity->iTotalDeleteCount = $oPreviousSummaryEntity->iTotalDeleteCount;
|
||||||
|
|
||||||
|
$this->aSummary[$sClass] = $oSummaryEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use DBObject;
|
||||||
|
|
||||||
|
interface iObjectService
|
||||||
|
{
|
||||||
|
public function Update(DBObject $oToUpdate, string $sAttCode, $value): void;
|
||||||
|
|
||||||
|
public function Delete(string $sClass, string $sId): void;
|
||||||
|
|
||||||
|
public function SetIssue(string $sClass): void;
|
||||||
|
|
||||||
|
public function GetSummary(): array;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2026 Combodo SARL #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% UIPanel ForInformation { sTitle:'DataFeatureRemoval:Analysis:Title'|dict_s} %}
|
||||||
|
{% if null != DataFeatureRemovalErrorMessage %}
|
||||||
|
<div id="feature_removal_error_msg_div" style="display:block">
|
||||||
|
{% UIAlert ForFailure { sTitle:'DataFeatureRemoval:Failure:Title'|dict_s, sId: 'feature_removal_error_msg', sContent:DataFeatureRemovalErrorMessage } %}
|
||||||
|
{% EndUIAlert %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Features:Title'|dict_s, sSubTitle: '' } %}
|
||||||
|
{% UIMultiColumn Standard {} %}
|
||||||
|
{% for iColumnIndex in 0..iColumnCount-1 %}
|
||||||
|
{% UIColumn Standard {} %}
|
||||||
|
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||||
|
{% if aExtension['installed'] %}
|
||||||
|
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% else %}
|
||||||
|
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIColumn %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIMultiColumn %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
{% else %}
|
||||||
|
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Features:Title'|dict_s, sSubTitle: '' } %}
|
||||||
|
{% UIMultiColumn Standard {} %}
|
||||||
|
{% for iColumnIndex in 0..iColumnCount-1 %}
|
||||||
|
{% UIColumn Standard {} %}
|
||||||
|
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||||
|
{% if aExtension['installed'] %}
|
||||||
|
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% else %}
|
||||||
|
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIColumn %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIMultiColumn %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
|
||||||
|
{% if bDeletionNeeded %}
|
||||||
|
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:DeletionPlan:Title'|dict_s} %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIFieldSet %}
|
||||||
|
{% if bDeletionPossible %}
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
{% UIInput ForHidden { sName:'operation', sValue:'DoDeletion'} %}
|
||||||
|
{% for sKey, sClass in aClasses %}
|
||||||
|
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for sCode, sLabel in aAddedExtensions %}
|
||||||
|
{% UIInput ForHidden { sName:"aAddedExtensions[" ~ sCode ~ "]", sValue:sLabel } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for sCode, sLabel in aRemovedExtensions %}
|
||||||
|
{% UIInput ForHidden { sName:"aRemovedExtensions[" ~ sCode ~ "]", sValue:sLabel } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for sInputName, sValue in aHiddenInputs %}
|
||||||
|
{% UIInput ForHidden { sName:sInputName, sValue:sValue } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% else %}
|
||||||
|
{% UIAlert ForFailure { sContent: 'DataFeatureRemoval:DeletionPlan:Error:Issues'|dict_s } %}{% EndUIAlert %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% UIAlert ForSuccess { sTitle:'DataFeatureRemoval:CleanupComplete:Title'|dict_s, sContent:'DataFeatureRemoval:CompilComplete'|dict_s, sId:value } %}{% EndUIAlert %}
|
||||||
|
|
||||||
|
{% UIForm Standard {'sId':'launch-setup-form', Action:sLaunchSetupUrl, 'EncType': 'application/x-www-form-urlencoded'} %}
|
||||||
|
{% for sKey, sValue in aSetupParams %}
|
||||||
|
{% UIInput ForHidden { sName:sKey, sValue:sValue } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Setup'|dict_s, sName:'btn_setup', sId:'btn_setup', bIsSubmit:true} %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if bHasDeletionExecution %}
|
||||||
|
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:Execution:Title'|dict_s} %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aDeletionExecutionSummary', aColumns:aDeletionExecutionSummary.Columns, aData:aDeletionExecutionSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIFieldSet %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
{% UIInput ForHidden { sName:'operation', sValue:'Main'} %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:BackToMain'|dict_s, sName:'btn_back', sId:'btn_back', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% EndUIPanel %}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2026 Combodo SARL #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
{% UIPanel ForInformation { sTitle:'DataFeatureRemoval:DoDeletion:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:DoDeletion:SubTitle'|dict_s } %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aDeletionExecutionSummary', aColumns:aDeletionExecutionSummary.Columns, aData:aDeletionExecutionSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
{% UIInput ForHidden { sName:'operation', sValue:'Main'} %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:BackToMain'|dict_s, sName:'btn_back_to_main', sId:'btn_back_to_main', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2024 Combodo SAS #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden {sName:'operation', sValue:'AnalysisResult'} %}
|
||||||
|
{% UIInput ForHidden {sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
|
||||||
|
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Features:Title'|dict_s, sSubTitle: '' } %}
|
||||||
|
{% UIMultiColumn Standard {} %}
|
||||||
|
{% for iColumnIndex in 0..iColumnCount-1 %}
|
||||||
|
{% UIColumn Standard {} %}
|
||||||
|
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||||
|
{% if aExtension['installed'] %}
|
||||||
|
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% else %}
|
||||||
|
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIColumn %}
|
||||||
|
{% endfor %}
|
||||||
|
{% EndUIMultiColumn %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Analyze'|dict_s, sName:'btn_apply', sId:'btn_apply', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
{# Usable variables: #}
|
||||||
|
{# * sTitle => page title #}
|
||||||
|
{# * sMessage => success message #}
|
||||||
|
{# * sError => error message #}
|
||||||
|
|
||||||
|
{# DataFeatureRemoval #}
|
||||||
|
|
||||||
|
{% UIPanel ForInformation { sTitle:'DataFeatureRemoval:Main:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:Main:SubTitle'|dict_s } %}
|
||||||
|
|
||||||
|
{% UIAlert ForInformation { sTitle:'DataFeatureRemoval:Helper:Title'|dict_s } %}
|
||||||
|
{{ 'DataFeatureRemoval:Helper:Desc1'|dict_s }}<BR>
|
||||||
|
{{ 'DataFeatureRemoval:Helper:Desc2'|dict_s }}
|
||||||
|
{% EndUIAlert %}
|
||||||
|
|
||||||
|
{% include 'Features.html.twig' %}
|
||||||
|
{% EndUIPanel %}
|
||||||
22
datamodels/2.x/combodo-data-feature-removal/vendor/autoload.php
vendored
Normal file
22
datamodels/2.x/combodo-data-feature-removal/vendor/autoload.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit4f96a7199e2c0d90e547333758b26464::getLoader();
|
||||||
579
datamodels/2.x/combodo-data-feature-removal/vendor/composer/ClassLoader.php
vendored
Normal file
579
datamodels/2.x/combodo-data-feature-removal/vendor/composer/ClassLoader.php
vendored
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
396
datamodels/2.x/combodo-data-feature-removal/vendor/composer/InstalledVersions.php
vendored
Normal file
396
datamodels/2.x/combodo-data-feature-removal/vendor/composer/InstalledVersions.php
vendored
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
datamodels/2.x/combodo-data-feature-removal/vendor/composer/LICENSE
vendored
Normal file
21
datamodels/2.x/combodo-data-feature-removal/vendor/composer/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
21
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_classmap.php
vendored
Normal file
21
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_classmap.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => $baseDir . '/src/Controller/DataFeatureRemovalController.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => $baseDir . '/src/Entity/DataCleanupSummaryEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => $baseDir . '/src/Helper/DataFeatureRemovalConfig.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => $baseDir . '/src/Helper/DataFeatureRemovalException.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => $baseDir . '/src/Helper/DataFeatureRemovalHelper.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalLog' => $baseDir . '/src/Helper/DataFeatureRemovalLog.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataCleanupService' => $baseDir . '/src/Service/DataCleanupService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => $baseDir . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => $baseDir . '/src/Service/ObjectService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => $baseDir . '/src/Service/ObjectServiceSummary.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => $baseDir . '/src/Service/iObjectService.php',
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
);
|
||||||
9
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_namespaces.php
vendored
Normal file
9
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
10
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_psr4.php
vendored
Normal file
10
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\' => array($baseDir . '/src'),
|
||||||
|
);
|
||||||
37
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_real.php
vendored
Normal file
37
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_real.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit4f96a7199e2c0d90e547333758b26464
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit4f96a7199e2c0d90e547333758b26464', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit4f96a7199e2c0d90e547333758b26464', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit4f96a7199e2c0d90e547333758b26464::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->setClassMapAuthoritative(true);
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_static.php
vendored
Normal file
47
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_static.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit4f96a7199e2c0d90e547333758b26464
|
||||||
|
{
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'C' =>
|
||||||
|
array (
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\' => 32,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/../..' . '/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => __DIR__ . '/../..' . '/src/Controller/DataFeatureRemovalController.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => __DIR__ . '/../..' . '/src/Entity/DataCleanupSummaryEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalConfig.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalException.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalHelper.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalLog' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalLog.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataCleanupService' => __DIR__ . '/../..' . '/src/Service/DataCleanupService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => __DIR__ . '/../..' . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => __DIR__ . '/../..' . '/src/Service/ObjectService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => __DIR__ . '/../..' . '/src/Service/ObjectServiceSummary.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => __DIR__ . '/../..' . '/src/Service/iObjectService.php',
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit4f96a7199e2c0d90e547333758b26464::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit4f96a7199e2c0d90e547333758b26464::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit4f96a7199e2c0d90e547333758b26464::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
datamodels/2.x/combodo-data-feature-removal/vendor/composer/installed.json
vendored
Normal file
5
datamodels/2.x/combodo-data-feature-removal/vendor/composer/installed.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"packages": [],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
||||||
23
datamodels/2.x/combodo-data-feature-removal/vendor/composer/installed.php
vendored
Normal file
23
datamodels/2.x/combodo-data-feature-removal/vendor/composer/installed.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => 'combodo/combodo-data-feature-removal',
|
||||||
|
'pretty_version' => 'dev-develop',
|
||||||
|
'version' => 'dev-develop',
|
||||||
|
'reference' => '19bbf6759bb4f6f5814d9ec1b0b5514208efc0b2',
|
||||||
|
'type' => 'itop-extension',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'combodo/combodo-data-feature-removal' => array(
|
||||||
|
'pretty_version' => 'dev-develop',
|
||||||
|
'version' => 'dev-develop',
|
||||||
|
'reference' => '19bbf6759bb4f6f5814d9ec1b0b5514208efc0b2',
|
||||||
|
'type' => 'itop-extension',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
@@ -10,6 +10,7 @@ namespace Combodo\iTop\DBTools\Service;
|
|||||||
use CMDBSource;
|
use CMDBSource;
|
||||||
use DBObjectSearch;
|
use DBObjectSearch;
|
||||||
use DBObjectSet;
|
use DBObjectSet;
|
||||||
|
use IssueLog;
|
||||||
|
|
||||||
class DBToolsUtils
|
class DBToolsUtils
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,15 +94,6 @@
|
|||||||
</options>
|
</options>
|
||||||
</sub_options>
|
</sub_options>
|
||||||
</choice>
|
</choice>
|
||||||
<choice>
|
|
||||||
<extension_code>itop-flow-map</extension_code>
|
|
||||||
<title>Data flow</title>
|
|
||||||
<description>Map data flows between applications</description>
|
|
||||||
<modules type="array">
|
|
||||||
<module>itop-flow-map</module>
|
|
||||||
</modules>
|
|
||||||
<default>false</default>
|
|
||||||
</choice>
|
|
||||||
</options>
|
</options>
|
||||||
</step>
|
</step>
|
||||||
<step>
|
<step>
|
||||||
@@ -250,7 +241,7 @@
|
|||||||
<choice>
|
<choice>
|
||||||
<extension_code>itop-problem-mgmt</extension_code>
|
<extension_code>itop-problem-mgmt</extension_code>
|
||||||
<title>Problem Management</title>
|
<title>Problem Management</title>
|
||||||
<description>Select this option track "Problems" in iTop.</description>
|
<description>Select this option to track "Problems" in iTop.</description>
|
||||||
<modules type="array">
|
<modules type="array">
|
||||||
<module>itop-problem-mgmt</module>
|
<module>itop-problem-mgmt</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ try {
|
|||||||
$oPage = new JsonPage();
|
$oPage = new JsonPage();
|
||||||
$oPage->SetOutputDataOnly(true);
|
$oPage->SetOutputDataOnly(true);
|
||||||
|
|
||||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
$sEnvironment = utils::ReadParam('environment', ITOP_DEFAULT_ENV, false, 'raw_data');
|
||||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||||
if ($oRestoreMutex->IsLocked()) {
|
if ($oRestoreMutex->IsLocked()) {
|
||||||
DisplayErrorAndDie($oPage, '<p>'.Dict::S('bkp-restore-running').'</p>');
|
DisplayErrorAndDie($oPage, '<p>'.Dict::S('bkp-restore-running').'</p>');
|
||||||
@@ -156,7 +156,7 @@ try {
|
|||||||
require_once(APPROOT.'/setup/backup.class.inc.php');
|
require_once(APPROOT.'/setup/backup.class.inc.php');
|
||||||
require_once(__DIR__.'/dbrestore.class.inc.php');
|
require_once(__DIR__.'/dbrestore.class.inc.php');
|
||||||
|
|
||||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
$sEnvironment = utils::ReadParam('environment', ITOP_DEFAULT_ENV, false, 'raw_data');
|
||||||
try {
|
try {
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class DBRestore extends DBBackup
|
|||||||
*
|
*
|
||||||
* @uses \RunTimeEnvironment::CompileFrom()
|
* @uses \RunTimeEnvironment::CompileFrom()
|
||||||
*/
|
*/
|
||||||
public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production')
|
public function RestoreFromCompressedBackup($sFile, $sEnvironment = ITOP_DEFAULT_ENV)
|
||||||
{
|
{
|
||||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||||
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
|
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ function ExecuteMainOperation($oP)
|
|||||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||||
$oP->p("Sorry, iTop is in demonstration mode: the feature is disabled");
|
$oP->p("Sorry, iTop is in demonstration mode: the feature is disabled");
|
||||||
} else {
|
} else {
|
||||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
$sEnvironment = utils::ReadParam('environment', ITOP_DEFAULT_ENV, false, 'raw_data');
|
||||||
$oRestore->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
|
$oRestore->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versions History~~',
|
'iTopUpdate:UI:History' => 'Versions History~~',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Záložní soubory a databáze',
|
'iTopUpdate:UI:Backup:Label' => 'Záložní soubory a databáze',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Volné místo',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Volné místo',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Prostor obsazený Databází',
|
'iTopUpdate:UI:DBDiskSpace' => 'Prostor obsazený Databází',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versions History~~',
|
'iTopUpdate:UI:History' => 'Versions History~~',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database~~',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database~~',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
||||||
'iTopUpdate:UI:History' => 'Versionshistorie',
|
'iTopUpdate:UI:History' => 'Versionshistorie',
|
||||||
'iTopUpdate:UI:Progress' => 'Upgradefortschritt',
|
'iTopUpdate:UI:Progress' => 'Upgradefortschritt',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup von Dateien und Datenbank',
|
'iTopUpdate:UI:Backup:Label' => 'Backup von Dateien und Datenbank',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Wegen geringem verbleibenden Speicherplatz sollte kein Backup mehr erzeugt werden.',
|
'iTopUpdate:UI:Backup:Warning' => 'Wegen geringem verbleibenden Speicherplatz sollte kein Backup mehr erzeugt werden.',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Freier Speicherplatz',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Freier Speicherplatz',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' Speicherplatz',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' Speicherplatz',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Datenbankgröße',
|
'iTopUpdate:UI:DBDiskSpace' => 'Datenbankgröße',
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ Dict::Add('EN US', 'English', 'English', [
|
|||||||
'iTopUpdate:UI:History' => 'Versions History',
|
'iTopUpdate:UI:History' => 'Versions History',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
||||||
|
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space',
|
||||||
|
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space',
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ Dict::Add('EN GB', 'British English', 'British English', [
|
|||||||
'iTopUpdate:UI:History' => 'Versions History',
|
'iTopUpdate:UI:History' => 'Versions History',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
||||||
|
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space',
|
||||||
|
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space',
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
|||||||
'iTopUpdate:UI:Setup' => 'Configuración '.ITOP_APPLICATION_SHORT,
|
'iTopUpdate:UI:Setup' => 'Configuración '.ITOP_APPLICATION_SHORT,
|
||||||
'iTopUpdate:UI:History' => 'Historial de versiones',
|
'iTopUpdate:UI:History' => 'Historial de versiones',
|
||||||
'iTopUpdate:UI:Progress' => 'Progreso de actualización',
|
'iTopUpdate:UI:Progress' => 'Progreso de actualización',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Respaldo de archivos y base de datos',
|
'iTopUpdate:UI:Backup:Label' => 'Respaldo de archivos y base de datos',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'El respaldo no está recomendado por el limitado espacio en el dispositivo',
|
'iTopUpdate:UI:Backup:Warning' => 'El respaldo no está recomendado por el limitado espacio en el dispositivo',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Espacio libre en el dispositivo',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Espacio libre en el dispositivo',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => 'Espacio en disco de '.ITOP_APPLICATION_SHORT,
|
'iTopUpdate:UI:ItopDiskSpace' => 'Espacio en disco de '.ITOP_APPLICATION_SHORT,
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Espacio en disco de base de datos',
|
'iTopUpdate:UI:DBDiskSpace' => 'Espacio en disco de base de datos',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('FR FR', 'French', 'Français', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
||||||
'iTopUpdate:UI:History' => 'Historique des versions',
|
'iTopUpdate:UI:History' => 'Historique des versions',
|
||||||
'iTopUpdate:UI:Progress' => 'Progression de la mise à jour',
|
'iTopUpdate:UI:Progress' => 'Progression de la mise à jour',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Sauvegarde de la base de données',
|
'iTopUpdate:UI:Backup:Label' => 'Sauvegarde de la base de données',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'La sauvegarde n\'est pas conseillée à cause du manque de place disque disponible',
|
'iTopUpdate:UI:Backup:Warning' => 'La sauvegarde n\'est pas conseillée à cause du manque de place disque disponible',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Taille disque disponible',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Taille disque disponible',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => 'Taille disque utilisée par l\'application',
|
'iTopUpdate:UI:ItopDiskSpace' => 'Taille disque utilisée par l\'application',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Taille disque utilisée par la base de données',
|
'iTopUpdate:UI:DBDiskSpace' => 'Taille disque utilisée par la base de données',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Verziótörténet',
|
'iTopUpdate:UI:History' => 'Verziótörténet',
|
||||||
'iTopUpdate:UI:Progress' => 'A frissítés folyamata',
|
'iTopUpdate:UI:Progress' => 'A frissítés folyamata',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Mentés fájlok és adatbázis',
|
'iTopUpdate:UI:Backup:Label' => 'Mentés fájlok és adatbázis',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'A biztonsági mentés nem ajánlott a korlátozottan rendelkezésre álló lemezterület miatt.',
|
'iTopUpdate:UI:Backup:Warning' => 'A biztonsági mentés nem ajánlott a korlátozottan rendelkezésre álló lemezterület miatt.',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Lemez szabad terület',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Lemez szabad terület',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' lemezterület',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' lemezterület',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Adatbázis lemezterület',
|
'iTopUpdate:UI:DBDiskSpace' => 'Adatbázis lemezterület',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
|
||||||
'iTopUpdate:UI:History' => 'Storia delle Versioni',
|
'iTopUpdate:UI:History' => 'Storia delle Versioni',
|
||||||
'iTopUpdate:UI:Progress' => 'Progresso dell\'aggiornamento',
|
'iTopUpdate:UI:Progress' => 'Progresso dell\'aggiornamento',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup dei file e del database',
|
'iTopUpdate:UI:Backup:Label' => 'Backup dei file e del database',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup non raccomandato a causa dello spazio su disco limitato disponibile',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup non raccomandato a causa dello spazio su disco limitato disponibile',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Spazio libero su disco',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Spazio libero su disco',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' spazio su disco',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' spazio su disco',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Spazio su disco del Database',
|
'iTopUpdate:UI:DBDiskSpace' => 'Spazio su disco del Database',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versions History~~',
|
'iTopUpdate:UI:History' => 'Versions History~~',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database~~',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database~~',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' setup',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' setup',
|
||||||
'iTopUpdate:UI:History' => 'Versiegeschiedenis',
|
'iTopUpdate:UI:History' => 'Versiegeschiedenis',
|
||||||
'iTopUpdate:UI:Progress' => 'Voortgang van de upgrade',
|
'iTopUpdate:UI:Progress' => 'Voortgang van de upgrade',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Maak een backup van de bestanden en database',
|
'iTopUpdate:UI:Backup:Label' => 'Maak een backup van de bestanden en database',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Een backup maken wordt afgeraden doordat er weinig schijfruimte is',
|
'iTopUpdate:UI:Backup:Warning' => 'Een backup maken wordt afgeraden doordat er weinig schijfruimte is',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Vrije schijfruimte',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Vrije schijfruimte',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' schijfgebruik',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' schijfgebruik',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Database schijfgebruik',
|
'iTopUpdate:UI:DBDiskSpace' => 'Database schijfgebruik',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
|||||||
'iTopUpdate:UI:Action' => 'Aktualizacja',
|
'iTopUpdate:UI:Action' => 'Aktualizacja',
|
||||||
'iTopUpdate:UI:Setup' => 'Konfiguracja '.ITOP_APPLICATION_SHORT, 'iTopUpdate:UI:History' => 'Historia wersji',
|
'iTopUpdate:UI:Setup' => 'Konfiguracja '.ITOP_APPLICATION_SHORT, 'iTopUpdate:UI:History' => 'Historia wersji',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Kopie zapasowe plików i bazy danych',
|
'iTopUpdate:UI:Backup:Label' => 'Kopie zapasowe plików i bazy danych',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Tworzenie kopii zapasowych nie jest zalecane ze względu na ograniczoną ilość wolnego miejsca na dysku',
|
'iTopUpdate:UI:Backup:Warning' => 'Tworzenie kopii zapasowych nie jest zalecane ze względu na ograniczoną ilość wolnego miejsca na dysku',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Wolne miejsce na dysku',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Wolne miejsce na dysku',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => 'Przestrzeń dyskowa '.ITOP_APPLICATION_SHORT, 'iTopUpdate:UI:DBDiskSpace' => 'Przestrzeń dyskowa bazy danych',
|
'iTopUpdate:UI:ItopDiskSpace' => 'Przestrzeń dyskowa '.ITOP_APPLICATION_SHORT, 'iTopUpdate:UI:DBDiskSpace' => 'Przestrzeń dyskowa bazy danych',
|
||||||
'iTopUpdate:UI:FileUploadMaxSize' => 'Maksymalny rozmiar przesyłanego pliku',
|
'iTopUpdate:UI:FileUploadMaxSize' => 'Maksymalny rozmiar przesyłanego pliku',
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versões anteriores',
|
'iTopUpdate:UI:History' => 'Versões anteriores',
|
||||||
'iTopUpdate:UI:Progress' => 'Progresso da atualização',
|
'iTopUpdate:UI:Progress' => 'Progresso da atualização',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup de arquivos e banco de dados',
|
'iTopUpdate:UI:Backup:Label' => 'Backup de arquivos e banco de dados',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup não recomendado devido ao espaço em disco limitado',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup não recomendado devido ao espaço em disco limitado',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Espaço em disco disponível',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Espaço em disco disponível',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => 'Espaço em disco do '.ITOP_APPLICATION_SHORT,
|
'iTopUpdate:UI:ItopDiskSpace' => 'Espaço em disco do '.ITOP_APPLICATION_SHORT,
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Espaço em disco do banco de dados',
|
'iTopUpdate:UI:DBDiskSpace' => 'Espaço em disco do banco de dados',
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'История версий',
|
'iTopUpdate:UI:History' => 'История версий',
|
||||||
'iTopUpdate:UI:Progress' => 'Ход обновления',
|
'iTopUpdate:UI:Progress' => 'Ход обновления',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Создать резервную копию базы данных',
|
'iTopUpdate:UI:Backup:Label' => 'Создать резервную копию базы данных',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Резервное копирование не рекомендуется из-за ограниченного свободного места на диске',
|
'iTopUpdate:UI:Backup:Warning' => 'Резервное копирование не рекомендуется из-за ограниченного свободного места на диске',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Доступное дисковое пространство',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Доступное дисковое пространство',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => 'Размер приложения',
|
'iTopUpdate:UI:ItopDiskSpace' => 'Размер приложения',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Размер базы данных',
|
'iTopUpdate:UI:DBDiskSpace' => 'Размер базы данных',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versions History~~',
|
'iTopUpdate:UI:History' => 'Versions History~~',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database~~',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database~~',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup~~',
|
||||||
'iTopUpdate:UI:History' => 'Versions History~~',
|
'iTopUpdate:UI:History' => 'Versions History~~',
|
||||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database~~',
|
'iTopUpdate:UI:Backup:Label' => 'Backup files and database~~',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
'iTopUpdate:UI:Backup:Warning' => 'Backup is not recommended due to limited available disk space~~',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.' disk space~~',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~',
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
|||||||
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.'安装',
|
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.'安装',
|
||||||
'iTopUpdate:UI:History' => '版本历史',
|
'iTopUpdate:UI:History' => '版本历史',
|
||||||
'iTopUpdate:UI:Progress' => '升级进度',
|
'iTopUpdate:UI:Progress' => '升级进度',
|
||||||
'iTopUpdate:UI:DoBackup:Label' => '备份文件和数据库',
|
'iTopUpdate:UI:Backup:Label' => '备份文件和数据库',
|
||||||
'iTopUpdate:UI:DoBackup:Warning' => '由于磁盘空间不足, 不建议备份',
|
'iTopUpdate:UI:Backup:Warning' => '由于磁盘空间不足, 不建议备份',
|
||||||
'iTopUpdate:UI:DiskFreeSpace' => '磁盘剩余空间',
|
'iTopUpdate:UI:DiskFreeSpace' => '磁盘剩余空间',
|
||||||
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.'的磁盘空间',
|
'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.'的磁盘空间',
|
||||||
'iTopUpdate:UI:DBDiskSpace' => '数据库的磁盘空间',
|
'iTopUpdate:UI:DBDiskSpace' => '数据库的磁盘空间',
|
||||||
|
|||||||
@@ -46,10 +46,6 @@ class AjaxController extends Controller
|
|||||||
define('MODULESROOT', APPROOT.'env-production/');
|
define('MODULESROOT', APPROOT.'env-production/');
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once(MODULESROOT.'itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php');
|
|
||||||
require_once(MODULESROOT.'itop-core-update/src/Service/CoreUpdater.php');
|
|
||||||
require_once(MODULESROOT.'itop-core-update/src/Controller/AjaxController.php');
|
|
||||||
|
|
||||||
MetaModel::LoadConfig(utils::GetConfig());
|
MetaModel::LoadConfig(utils::GetConfig());
|
||||||
|
|
||||||
$sViewPath = MODULESROOT.'itop-core-update/templates';
|
$sViewPath = MODULESROOT.'itop-core-update/templates';
|
||||||
@@ -246,10 +242,10 @@ class AjaxController extends Controller
|
|||||||
$iResponseCode = 200;
|
$iResponseCode = 200;
|
||||||
try {
|
try {
|
||||||
$aParams['sAjaxURL'] = utils::GetAbsoluteUrlAppRoot().'/pages/UI.php';
|
$aParams['sAjaxURL'] = utils::GetAbsoluteUrlAppRoot().'/pages/UI.php';
|
||||||
$oConfig = new Config(APPCONF.'production'.'/'.ITOP_CONFIG_FILE);
|
$oConfig = new Config(APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE);
|
||||||
$oEnvironment = new RunTimeEnvironment('production');
|
$oEnvironment = new RunTimeEnvironment(ITOP_DEFAULT_ENV);
|
||||||
$oEnvironment->WriteConfigFileSafe($oConfig);
|
$oEnvironment->WriteConfigFileSafe($oConfig);
|
||||||
$oEnvironment->CompileFrom('production');
|
$oEnvironment->CompileFrom(ITOP_DEFAULT_ENV);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
IssueLog::Error('RebuildToolkitEnvironment: '.$e->getMessage());
|
IssueLog::Error('RebuildToolkitEnvironment: '.$e->getMessage());
|
||||||
$aParams['sError'] = $e->getMessage();
|
$aParams['sError'] = $e->getMessage();
|
||||||
|
|||||||
@@ -89,10 +89,10 @@ final class CoreUpdater
|
|||||||
// Compile code
|
// Compile code
|
||||||
SetupLog::Info('itop-core-update: Start checking compilation');
|
SetupLog::Info('itop-core-update: Start checking compilation');
|
||||||
|
|
||||||
$sFinalEnv = 'production';
|
$sFinalEnv = ITOP_DEFAULT_ENV;
|
||||||
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, false);
|
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, false);
|
||||||
$oRuntimeEnv->CheckDirectories($sFinalEnv);
|
$oRuntimeEnv->CheckDirectories($sFinalEnv);
|
||||||
$oRuntimeEnv->CompileFrom('production');
|
$oRuntimeEnv->CompileFrom($sFinalEnv);
|
||||||
|
|
||||||
$oRuntimeEnv->Rollback();
|
$oRuntimeEnv->Rollback();
|
||||||
|
|
||||||
@@ -117,10 +117,10 @@ final class CoreUpdater
|
|||||||
// Compile code
|
// Compile code
|
||||||
SetupLog::Info('itop-core-update: Start compilation');
|
SetupLog::Info('itop-core-update: Start compilation');
|
||||||
|
|
||||||
$sFinalEnv = 'production';
|
$sFinalEnv = ITOP_DEFAULT_ENV;
|
||||||
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, true);
|
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, true);
|
||||||
$oRuntimeEnv->CheckDirectories($sFinalEnv);
|
$oRuntimeEnv->CheckDirectories($sFinalEnv);
|
||||||
$oRuntimeEnv->CompileFrom('production');
|
$oRuntimeEnv->CompileFrom(ITOP_DEFAULT_ENV);
|
||||||
|
|
||||||
SetupLog::Info('itop-core-update: Compilation done');
|
SetupLog::Info('itop-core-update: Compilation done');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -142,7 +142,7 @@ final class CoreUpdater
|
|||||||
try {
|
try {
|
||||||
SetupLog::Info('itop-core-update: Start Update database');
|
SetupLog::Info('itop-core-update: Start Update database');
|
||||||
|
|
||||||
$sFinalEnv = 'production';
|
$sFinalEnv = ITOP_DEFAULT_ENV;
|
||||||
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, true);
|
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sFinalEnv, true);
|
||||||
$oConfig = $oRuntimeEnv->MakeConfigFile($sFinalEnv.' (built on '.date('Y-m-d').')');
|
$oConfig = $oRuntimeEnv->MakeConfigFile($sFinalEnv.' (built on '.date('Y-m-d').')');
|
||||||
$oConfig->Set('access_mode', ACCESS_FULL);
|
$oConfig->Set('access_mode', ACCESS_FULL);
|
||||||
@@ -155,21 +155,13 @@ final class CoreUpdater
|
|||||||
APPROOT.'extensions',
|
APPROOT.'extensions',
|
||||||
];
|
];
|
||||||
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $aDirsToScanForModules);
|
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $aDirsToScanForModules);
|
||||||
$aSelectedModules = [];
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'BeforeDatabaseCreation');
|
||||||
foreach ($aAvailableModules as $sModuleId => $aModule) {
|
|
||||||
if (($sModuleId == ROOT_MODULE) || ($sModuleId == DATAMODEL_MODULE)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$aSelectedModules[] = $sModuleId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation');
|
|
||||||
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade');
|
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade');
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDatabaseCreation');
|
||||||
$oRuntimeEnv->UpdatePredefinedObjects();
|
$oRuntimeEnv->UpdatePredefinedObjects();
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDatabaseSetup');
|
||||||
$oRuntimeEnv->LoadData($aAvailableModules, $aSelectedModules, false /* no sample data*/);
|
$oRuntimeEnv->LoadData($aAvailableModules, false /* no sample data*/);
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDataLoad');
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDataLoad');
|
||||||
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
||||||
$oExtensionsMap = new iTopExtensionsMap();
|
$oExtensionsMap = new iTopExtensionsMap();
|
||||||
// Default choices = as before
|
// Default choices = as before
|
||||||
@@ -187,7 +179,7 @@ final class CoreUpdater
|
|||||||
$oRuntimeEnv->RecordInstallation(
|
$oRuntimeEnv->RecordInstallation(
|
||||||
$oConfig,
|
$oConfig,
|
||||||
$sDataModelVersion,
|
$sDataModelVersion,
|
||||||
$aSelectedModules,
|
array_keys($aAvailableModules),
|
||||||
$aSelectedExtensionCodes,
|
$aSelectedExtensionCodes,
|
||||||
'Done by the iTop Core Updater'
|
'Done by the iTop Core Updater'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ require_once(APPROOT."setup/runtimeenv.class.inc.php");
|
|||||||
|
|
||||||
use Config;
|
use Config;
|
||||||
use Exception;
|
use Exception;
|
||||||
use ModelFactory;
|
|
||||||
use RunTimeEnvironment;
|
use RunTimeEnvironment;
|
||||||
use SetupUtils;
|
use SetupUtils;
|
||||||
|
|
||||||
@@ -25,33 +24,31 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
|||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function __construct($sEnvironment = 'production', $bAutoCommit = true)
|
public function __construct($sEnvironment = ITOP_DEFAULT_ENV, $bAutoCommit = true)
|
||||||
{
|
{
|
||||||
parent::__construct($sEnvironment, $bAutoCommit);
|
parent::__construct($sEnvironment, $bAutoCommit);
|
||||||
|
|
||||||
if ($sEnvironment != $this->sTargetEnv) {
|
if ($sEnvironment != $this->sBuildEnv) {
|
||||||
if (is_dir(APPROOT.'/env-'.$this->sTargetEnv)) {
|
if (is_dir(APPROOT.'/env-'.$this->sBuildEnv)) {
|
||||||
SetupUtils::rrmdir(APPROOT.'/env-'.$this->sTargetEnv);
|
SetupUtils::rrmdir(APPROOT.'/env-'.$this->sBuildEnv);
|
||||||
}
|
}
|
||||||
if (is_dir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) {
|
if (is_dir(APPROOT.'/data/'.$this->sBuildEnv.'-modules')) {
|
||||||
SetupUtils::rrmdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules');
|
SetupUtils::rrmdir(APPROOT.'/data/'.$this->sBuildEnv.'-modules');
|
||||||
}
|
}
|
||||||
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sTargetEnv.'-modules');
|
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sBuildEnv.'-modules');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $sTargetEnv
|
* @param $sBuildEnv
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function CheckDirectories($sTargetEnv)
|
public function CheckDirectories($sBuildEnv)
|
||||||
{
|
{
|
||||||
$sTargetDir = APPROOT.'env-'.$sTargetEnv;
|
$sCurrentEnvDir = APPROOT.'env-'.$sBuildEnv;
|
||||||
$sBuildDir = $sTargetDir.'-build';
|
self::CheckDirectory($sCurrentEnvDir);
|
||||||
|
self::CheckDirectory($sCurrentEnvDir.'-build');
|
||||||
self::CheckDirectory($sTargetDir);
|
|
||||||
self::CheckDirectory($sBuildDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,12 +80,12 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
|||||||
{
|
{
|
||||||
// Clone the default 'production' config file
|
// Clone the default 'production' config file
|
||||||
//
|
//
|
||||||
$oConfig = clone($this->GetConfig('production'));
|
$oConfig = clone($this->GetConfig(ITOP_DEFAULT_ENV));
|
||||||
|
|
||||||
$oConfig->UpdateIncludes('env-'.$this->sTargetEnv);
|
$oConfig->UpdateIncludes('env-'.$this->sBuildEnv);
|
||||||
|
|
||||||
if (is_null($sEnvironmentLabel)) {
|
if (is_null($sEnvironmentLabel)) {
|
||||||
$sEnvironmentLabel = $this->sTargetEnv;
|
$sEnvironmentLabel = $this->sBuildEnv;
|
||||||
}
|
}
|
||||||
$oConfig->Set('app_env_label', $sEnvironmentLabel, 'application updater');
|
$oConfig->Set('app_env_label', $sEnvironmentLabel, 'application updater');
|
||||||
|
|
||||||
@@ -104,7 +101,7 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
|||||||
protected function GetConfig($sEnvironment = null)
|
protected function GetConfig($sEnvironment = null)
|
||||||
{
|
{
|
||||||
if (is_null($sEnvironment)) {
|
if (is_null($sEnvironment)) {
|
||||||
$sEnvironment = $this->sTargetEnv;
|
$sEnvironment = $this->sBuildEnv;
|
||||||
}
|
}
|
||||||
$sFile = APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
$sFile = APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||||
if (file_exists($sFile)) {
|
if (file_exists($sFile)) {
|
||||||
@@ -115,37 +112,4 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
|||||||
}
|
}
|
||||||
throw new Exception('No configuration file available');
|
throw new Exception('No configuration file available');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
|
|
||||||
{
|
|
||||||
$aRet = parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
|
||||||
|
|
||||||
// Add new mandatory modules from datamodel 2.x only
|
|
||||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
|
||||||
if (!is_dir($sSourceDirFull)) {
|
|
||||||
throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)");
|
|
||||||
}
|
|
||||||
$aDirsToCompile = [$sSourceDirFull];
|
|
||||||
|
|
||||||
$oFactory = new ModelFactory($aDirsToCompile);
|
|
||||||
$aModules = $oFactory->FindModules();
|
|
||||||
$aAvailableModules = [];
|
|
||||||
/** @var \MFModule $oModule */
|
|
||||||
foreach ($aModules as $oModule) {
|
|
||||||
$aAvailableModules[$oModule->GetName()] = $oModule;
|
|
||||||
}
|
|
||||||
// TODO check the auto-selected modules here
|
|
||||||
foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) {
|
|
||||||
if ($oExtension->bMarkedAsChosen) {
|
|
||||||
foreach ($oExtension->aModules as $sModuleName) {
|
|
||||||
if (!isset($aRet[$sModuleName]) && isset($aAvailableModules[$sModuleName])) {
|
|
||||||
$aRet[$sModuleName] = $aAvailableModules[$sModuleName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,11 +65,11 @@
|
|||||||
{% UIFileSelect Standard {sName: 'file', sId: 'file','AddCSSClass':'ibo-is-hidden'} %}
|
{% UIFileSelect Standard {sName: 'file', sId: 'file','AddCSSClass':'ibo-is-hidden'} %}
|
||||||
|
|
||||||
{% UIAlert ForWarning {'sId':'dobackup-warning', 'IsHidden':true} %}
|
{% UIAlert ForWarning {'sId':'dobackup-warning', 'IsHidden':true} %}
|
||||||
{{ 'iTopUpdate:UI:DoBackup:Warning'|dict_s }}
|
{{ 'iTopUpdate:UI:Backup:Warning'|dict_s }}
|
||||||
{% EndUIAlert %}
|
{% EndUIAlert %}
|
||||||
|
|
||||||
{% UIContentBlock Standard {'aContainerClasses':['ibo-font-ral-nor-150']} %}
|
{% UIContentBlock Standard {'aContainerClasses':['ibo-font-ral-nor-150']} %}
|
||||||
{% UIInput Standard {'sType':'checkbox', 'sId':'doBackup', 'sName':'doBackup', 'sValue':'1', 'IsChecked':true, 'CSSClasses':['ibo-input-checkbox', 'ibo-input--label-left'], 'Label':'iTopUpdate:UI:DoBackup:Label'|dict_s} %}
|
{% UIInput Standard {'sType':'checkbox', 'sId':'doBackup', 'sName':'doBackup', 'sValue':'1', 'IsChecked':true, 'CSSClasses':['ibo-input-checkbox', 'ibo-input--label-left'], 'Label':'iTopUpdate:UI:Backup:Label'|dict_s} %}
|
||||||
{% EndUIContentBlock %}
|
{% EndUIContentBlock %}
|
||||||
|
|
||||||
{% UIContentBlock Standard {'aContainerClasses':['ibo-font-ral-nor-150']} %}
|
{% UIContentBlock Standard {'aContainerClasses':['ibo-font-ral-nor-150']} %}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ SetupWebPage::AddModule(
|
|||||||
// Components
|
// Components
|
||||||
//
|
//
|
||||||
'datamodel' => [
|
'datamodel' => [
|
||||||
|
'model.itop-faq-light.php',
|
||||||
],
|
],
|
||||||
'data.struct' => [
|
'data.struct' => [
|
||||||
//'data.struct.itop-knownerror-mgmt.xml',
|
//'data.struct.itop-knownerror-mgmt.xml',
|
||||||
|
|||||||
@@ -24,129 +24,13 @@
|
|||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Combodo\iTop\Application\WebPage\JsonPage;
|
use Combodo\iTop\HubConnector\Controller\HubController;
|
||||||
|
|
||||||
require_once(APPROOT.'application/utils.inc.php');
|
require_once(APPROOT.'application/utils.inc.php');
|
||||||
require_once(APPROOT.'core/log.class.inc.php');
|
require_once(APPROOT.'core/log.class.inc.php');
|
||||||
IssueLog::Enable(APPROOT.'log/error.log');
|
IssueLog::Enable(APPROOT.'log/error.log');
|
||||||
|
|
||||||
require_once(APPROOT.'setup/runtimeenv.class.inc.php');
|
require_once(__DIR__.'/src/Controller/HubController.php');
|
||||||
require_once(APPROOT.'setup/backup.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/mutex.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/dict.class.inc.php');
|
|
||||||
require_once(APPROOT.'setup/xmldataloader.class.inc.php');
|
|
||||||
require_once(__DIR__.'/hubruntimeenvironment.class.inc.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overload of DBBackup to handle logging
|
|
||||||
*/
|
|
||||||
class DBBackupWithErrorReporting extends DBBackup
|
|
||||||
{
|
|
||||||
protected $aInfos = [];
|
|
||||||
|
|
||||||
protected $aErrors = [];
|
|
||||||
|
|
||||||
protected function LogInfo($sMsg)
|
|
||||||
{
|
|
||||||
$aInfos[] = $sMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function LogError($sMsg)
|
|
||||||
{
|
|
||||||
IssueLog::Error($sMsg);
|
|
||||||
$aErrors[] = $sMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetInfos()
|
|
||||||
{
|
|
||||||
return $this->aInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetErrors()
|
|
||||||
{
|
|
||||||
return $this->aErrors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param string $sTargetFile
|
|
||||||
* @throws Exception
|
|
||||||
* @return DBBackupWithErrorReporting
|
|
||||||
*/
|
|
||||||
function DoBackup($sTargetFile)
|
|
||||||
{
|
|
||||||
// Make sure the target directory exists
|
|
||||||
$sBackupDir = dirname($sTargetFile);
|
|
||||||
SetupUtils::builddir($sBackupDir);
|
|
||||||
|
|
||||||
$oBackup = new DBBackupWithErrorReporting();
|
|
||||||
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
|
|
||||||
$sSourceConfigFile = APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE;
|
|
||||||
|
|
||||||
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
|
|
||||||
$oMutex->Lock();
|
|
||||||
try {
|
|
||||||
$oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$oMutex->Unlock();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
$oMutex->Unlock();
|
|
||||||
return $oBackup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Outputs the status of the current ajax execution (as a JSON structure)
|
|
||||||
*
|
|
||||||
* @param string $sMessage
|
|
||||||
* @param bool $bSuccess
|
|
||||||
* @param number $iErrorCode
|
|
||||||
* @param array $aMoreFields
|
|
||||||
* Extra fields to pass to the caller, if needed
|
|
||||||
*/
|
|
||||||
function ReportStatus($sMessage, $bSuccess, $iErrorCode = 0, $aMoreFields = [])
|
|
||||||
{
|
|
||||||
// Do not use AjaxPage during setup phases, because it uses InterfaceDiscovery in Twig compilation
|
|
||||||
$oPage = new JsonPage();
|
|
||||||
$aResult = [
|
|
||||||
'code' => $iErrorCode,
|
|
||||||
'message' => $sMessage,
|
|
||||||
'fields' => $aMoreFields,
|
|
||||||
];
|
|
||||||
$oPage->SetData($aResult);
|
|
||||||
$oPage->SetOutputDataOnly(true);
|
|
||||||
$oPage->output();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to output the status of a successful execution
|
|
||||||
*
|
|
||||||
* @param string $sMessage
|
|
||||||
* @param array $aMoreFields
|
|
||||||
* Extra fields to pass to the caller, if needed
|
|
||||||
*/
|
|
||||||
function ReportSuccess($sMessage, $aMoreFields = [])
|
|
||||||
{
|
|
||||||
ReportStatus($sMessage, true, 0, $aMoreFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to output the status of a failed execution
|
|
||||||
*
|
|
||||||
* @param string $sMessage
|
|
||||||
* @param number $iErrorCode
|
|
||||||
* @param array $aMoreFields
|
|
||||||
* Extra fields to pass to the caller, if needed
|
|
||||||
*/
|
|
||||||
function ReportError($sMessage, $iErrorCode, $aMoreFields = [])
|
|
||||||
{
|
|
||||||
if ($iErrorCode == 0) {
|
|
||||||
// 0 means no error, so change it if no meaningful error code is supplied
|
|
||||||
$iErrorCode = -1;
|
|
||||||
}
|
|
||||||
ReportStatus($sMessage, false, $iErrorCode, $aMoreFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SetupUtils::ExitMaintenanceMode(false); // Reset maintenance mode in case of problem
|
SetupUtils::ExitMaintenanceMode(false); // Reset maintenance mode in case of problem
|
||||||
@@ -183,7 +67,7 @@ try {
|
|||||||
foreach ($aChecks as $oCheckResult) {
|
foreach ($aChecks as $oCheckResult) {
|
||||||
if ($oCheckResult->iSeverity == CheckResult::ERROR) {
|
if ($oCheckResult->iSeverity == CheckResult::ERROR) {
|
||||||
$bFailed = true;
|
$bFailed = true;
|
||||||
ReportError($oCheckResult->sLabel, -2);
|
HubController::GetInstance()->ReportError($oCheckResult->sLabel, -2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$bFailed) {
|
if (!$bFailed) {
|
||||||
@@ -191,182 +75,27 @@ try {
|
|||||||
$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
|
$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
|
||||||
if ($fFreeSpace !== false) {
|
if ($fFreeSpace !== false) {
|
||||||
$sMessage = Dict::Format('iTopHub:BackupFreeDiskSpaceIn', SetupUtils::HumanReadableSize($fFreeSpace), dirname($sDBBackupPath));
|
$sMessage = Dict::Format('iTopHub:BackupFreeDiskSpaceIn', SetupUtils::HumanReadableSize($fFreeSpace), dirname($sDBBackupPath));
|
||||||
ReportSuccess($sMessage);
|
HubController::GetInstance()->ReportSuccess($sMessage);
|
||||||
} else {
|
} else {
|
||||||
ReportError(Dict::S('iTopHub:FailedToCheckFreeDiskSpace'), -1);
|
HubController::GetInstance()->ReportError(Dict::S('iTopHub:FailedToCheckFreeDiskSpace'), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'do_backup':
|
case 'do_backup':
|
||||||
require_once(APPROOT.'/application/startup.inc.php');
|
HubController::GetInstance()->LaunchBackup();
|
||||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
|
||||||
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
|
||||||
throw new Exception('Sorry the installation of extensions is not allowed in demo mode');
|
|
||||||
}
|
|
||||||
SetupLog::Info('Backup starts...');
|
|
||||||
set_time_limit(0);
|
|
||||||
$sBackupPath = APPROOT.'/data/backups/manual/backup-';
|
|
||||||
$iSuffix = 1;
|
|
||||||
$sSuffix = '';
|
|
||||||
// Generate a unique name...
|
|
||||||
do {
|
|
||||||
$sBackupFile = $sBackupPath.date('Y-m-d-His').$sSuffix;
|
|
||||||
$sSuffix = '-'.$iSuffix;
|
|
||||||
$iSuffix++ ;
|
|
||||||
} while (file_exists($sBackupFile));
|
|
||||||
|
|
||||||
$oBackup = DoBackup($sBackupFile);
|
|
||||||
$aErrors = $oBackup->GetErrors();
|
|
||||||
if (count($aErrors) > 0) {
|
|
||||||
SetupLog::Error('Backup failed.');
|
|
||||||
SetupLog::Error(implode("\n", $aErrors));
|
|
||||||
ReportError(Dict::S('iTopHub:BackupFailed'), -1, $aErrors);
|
|
||||||
} else {
|
|
||||||
SetupLog::Info('Backup successfully completed.');
|
|
||||||
ReportSuccess(Dict::S('iTopHub:BackupOk'));
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
SetupLog::Error($e->getMessage());
|
|
||||||
ReportError($e->getMessage(), $e->getCode());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'compile':
|
case 'compile':
|
||||||
SetupLog::Info('Deployment starts...');
|
HubController::GetInstance()->LaunchCompile();
|
||||||
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
|
|
||||||
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent')) {
|
|
||||||
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
|
||||||
}
|
|
||||||
// First step: prepare the datamodel, if it fails, roll-back
|
|
||||||
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
|
||||||
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
|
||||||
|
|
||||||
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
|
|
||||||
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
|
|
||||||
|
|
||||||
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
|
|
||||||
if ($oConfig->Get('demo_mode')) {
|
|
||||||
throw new Exception('Sorry the installation of extensions is not allowed in demo mode');
|
|
||||||
}
|
|
||||||
|
|
||||||
$aSelectModules = $oRuntimeEnv->CompileFrom('production', false); // WARNING symlinks does not seem to be compatible with manual Commit
|
|
||||||
|
|
||||||
$oRuntimeEnv->UpdateIncludes($oConfig);
|
|
||||||
|
|
||||||
$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
|
|
||||||
|
|
||||||
// Safety check: check the inter dependencies, will throw an exception in case of inconsistency
|
|
||||||
$oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
|
|
||||||
|
|
||||||
$oRuntimeEnv->CheckMetaModel(); // Will throw an exception if a problem is detected
|
|
||||||
|
|
||||||
// Everything seems Ok so far, commit in env-production!
|
|
||||||
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
|
|
||||||
$oRuntimeEnv->Commit();
|
|
||||||
|
|
||||||
// Report the success in a way that will be detected by the ajax caller
|
|
||||||
SetupLog::Info('Compilation completed...');
|
|
||||||
ReportSuccess('Ok'); // No access to Dict::S here
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'move_to_production':
|
case 'move_to_production':
|
||||||
// Second step: update the schema and the data
|
HubController::GetInstance()->LaunchDeploy();
|
||||||
// Everything happening below is based on env-production
|
|
||||||
$oRuntimeEnv = new RunTimeEnvironment('production', true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
|
|
||||||
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent')) {
|
|
||||||
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
if (file_exists(APPROOT.'data/hub/compile_authent')) {
|
|
||||||
unlink(APPROOT.'data/hub/compile_authent');
|
|
||||||
}
|
|
||||||
// Note: at this point, the dictionnary is not necessarily loaded
|
|
||||||
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
|
||||||
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
|
|
||||||
ReportError($e->getMessage(), $e->getCode());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
SetupLog::Info('Move to production starts...');
|
|
||||||
|
|
||||||
unlink(utils::GetDataPath().'hub/compile_authent');
|
|
||||||
// Load the "production" config file to clone & update it
|
|
||||||
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
|
|
||||||
SetupUtils::EnterReadOnlyMode($oConfig);
|
|
||||||
|
|
||||||
$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
|
|
||||||
|
|
||||||
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
|
|
||||||
|
|
||||||
$aSelectedModules = [];
|
|
||||||
foreach ($aAvailableModules as $sModuleId => $aModule) {
|
|
||||||
if (($sModuleId == ROOT_MODULE) || ($sModuleId == DATAMODEL_MODULE)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$aSelectedModules[] = $sModuleId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation');
|
|
||||||
|
|
||||||
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade');
|
|
||||||
|
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
|
|
||||||
|
|
||||||
$oRuntimeEnv->UpdatePredefinedObjects();
|
|
||||||
|
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
|
|
||||||
|
|
||||||
$oRuntimeEnv->LoadData($aAvailableModules, $aSelectedModules, false /* no sample data*/);
|
|
||||||
|
|
||||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDataLoad');
|
|
||||||
|
|
||||||
// Record the installation so that the "about box" knows about the installed modules
|
|
||||||
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
|
||||||
|
|
||||||
$oExtensionsMap = new iTopExtensionsMap();
|
|
||||||
|
|
||||||
// Default choices = as before
|
|
||||||
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
|
||||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
|
||||||
// Plus all "remote" extensions
|
|
||||||
if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE) {
|
|
||||||
$oExtensionsMap->MarkAsChosen($oExtension->sCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$aSelectedExtensionCodes = [];
|
|
||||||
foreach ($oExtensionsMap->GetChoices() as $oExtension) {
|
|
||||||
$aSelectedExtensionCodes[] = $oExtension->sCode;
|
|
||||||
}
|
|
||||||
$aSelectedExtensions = $oExtensionsMap->GetChoices();
|
|
||||||
$oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules, $aSelectedExtensionCodes, 'Done by the iTop Hub Connector');
|
|
||||||
|
|
||||||
// Report the success in a way that will be detected by the ajax caller
|
|
||||||
SetupLog::Info('Deployment successfully completed.');
|
|
||||||
ReportSuccess(Dict::S('iTopHub:CompiledOK'));
|
|
||||||
} catch (Exception $e) {
|
|
||||||
if (file_exists(utils::GetDataPath().'hub/compile_authent')) {
|
|
||||||
unlink(utils::GetDataPath().'hub/compile_authent');
|
|
||||||
}
|
|
||||||
// Note: at this point, the dictionnary is not necessarily loaded
|
|
||||||
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
|
||||||
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
|
|
||||||
ReportError($e->getMessage(), $e->getCode());
|
|
||||||
} finally {
|
|
||||||
SetupUtils::ExitReadOnlyMode();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ReportError("Invalid operation: '$sOperation'", -1);
|
HubController::GetInstance()->ReportError("Invalid operation: '$sOperation'", -1);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
||||||
@@ -374,5 +103,5 @@ try {
|
|||||||
|
|
||||||
utils::PopArchiveMode();
|
utils::PopArchiveMode();
|
||||||
|
|
||||||
ReportError($e->getMessage(), $e->getCode());
|
HubController::GetInstance()->ReportError($e->getMessage(), $e->getCode());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function DisplayStatus(WebPage $oPage)
|
|||||||
if (is_dir($sPath)) {
|
if (is_dir($sPath)) {
|
||||||
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
||||||
}
|
}
|
||||||
$oExtensionsMap = new iTopExtensionsMap('production', $aExtraDirs);
|
$oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs);
|
||||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||||
|
|
||||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||||
@@ -154,7 +154,7 @@ function DoInstall(WebPage $oPage)
|
|||||||
if (is_dir($sPath)) {
|
if (is_dir($sPath)) {
|
||||||
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
||||||
}
|
}
|
||||||
$oExtensionsMap = new iTopExtensionsMap('production', $aExtraDirs);
|
$oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs);
|
||||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||||
|
|
||||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||||
|
|||||||
@@ -186,9 +186,7 @@ function collect_configuration()
|
|||||||
|
|
||||||
// iTop modules
|
// iTop modules
|
||||||
$oConfig = MetaModel::GetConfig();
|
$oConfig = MetaModel::GetConfig();
|
||||||
$sLatestInstallationDate = CMDBSource::QueryToScalar("SELECT max(installed) FROM ".$oConfig->Get('db_subname')."priv_module_install");
|
$aInstalledModules = ModuleInstallationRepository::GetInstance()->ReadFromDB($oConfig);
|
||||||
// Get the latest installed modules, without the "root" ones (iTop version and datamodel version)
|
|
||||||
$aInstalledModules = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install WHERE installed = '".$sLatestInstallationDate."' AND parent_id != 0");
|
|
||||||
|
|
||||||
foreach ($aInstalledModules as $aDBInfo) {
|
foreach ($aInstalledModules as $aDBInfo) {
|
||||||
$aConfiguration['itop_modules'][$aDBInfo['name']] = $aDBInfo['version'];
|
$aConfiguration['itop_modules'][$aDBInfo['name']] = $aDBInfo['version'];
|
||||||
|
|||||||
@@ -0,0 +1,314 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\HubConnector\Controller;
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\WebPage\JsonPage;
|
||||||
|
use Combodo\iTop\HubConnector\Model\DBBackupWithErrorReporting;
|
||||||
|
use Combodo\iTop\HubConnector\setup\HubRunTimeEnvironment;
|
||||||
|
use Config;
|
||||||
|
use Dict;
|
||||||
|
use Exception;
|
||||||
|
use iTopExtension;
|
||||||
|
use iTopExtensionsMap;
|
||||||
|
use iTopMutex;
|
||||||
|
use LoginWebPage;
|
||||||
|
use MetaModel;
|
||||||
|
use RunTimeEnvironment;
|
||||||
|
use SecurityException;
|
||||||
|
use SetupLog;
|
||||||
|
use SetupUtils;
|
||||||
|
use utils;
|
||||||
|
|
||||||
|
require_once(APPROOT.'setup/runtimeenv.class.inc.php');
|
||||||
|
require_once(APPROOT.'setup/backup.class.inc.php');
|
||||||
|
require_once(APPROOT.'core/mutex.class.inc.php');
|
||||||
|
require_once(APPROOT.'core/dict.class.inc.php');
|
||||||
|
require_once(APPROOT.'setup/xmldataloader.class.inc.php');
|
||||||
|
require_once(__DIR__.'/../setup/hubruntimeenvironment.class.inc.php');
|
||||||
|
|
||||||
|
class HubController
|
||||||
|
{
|
||||||
|
private static HubController $oInstance;
|
||||||
|
protected $bOutputHeaders = false;
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function GetInstance(): HubController
|
||||||
|
{
|
||||||
|
if (!isset(self::$oInstance)) {
|
||||||
|
self::$oInstance = new HubController();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function SetInstance(?HubController $oInstance): void
|
||||||
|
{
|
||||||
|
self::$oInstance = $oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function LaunchBackup()
|
||||||
|
{
|
||||||
|
require_once(APPROOT.'/application/startup.inc.php');
|
||||||
|
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||||
|
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||||
|
throw new Exception('Sorry the installation of extensions is not allowed in demo mode');
|
||||||
|
}
|
||||||
|
SetupLog::Info('Backup starts...');
|
||||||
|
set_time_limit(0);
|
||||||
|
$sBackupPath = APPROOT.'/data/backups/manual/backup-';
|
||||||
|
$iSuffix = 1;
|
||||||
|
$sSuffix = '';
|
||||||
|
// Generate a unique name...
|
||||||
|
do {
|
||||||
|
$sBackupFile = $sBackupPath.date('Y-m-d-His').$sSuffix;
|
||||||
|
$sSuffix = '-'.$iSuffix;
|
||||||
|
$iSuffix++ ;
|
||||||
|
} while (file_exists($sBackupFile));
|
||||||
|
|
||||||
|
$oBackup = $this->DoBackup($sBackupFile);
|
||||||
|
$aErrors = $oBackup->GetErrors();
|
||||||
|
if (count($aErrors) > 0) {
|
||||||
|
SetupLog::Error('Backup failed.');
|
||||||
|
SetupLog::Error(implode("\n", $aErrors));
|
||||||
|
$this->ReportError(Dict::S('iTopHub:BackupFailed'), -1, $aErrors);
|
||||||
|
} else {
|
||||||
|
SetupLog::Info('Backup successfully completed.');
|
||||||
|
$this->ReportSuccess(Dict::S('iTopHub:BackupOk'));
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
SetupLog::Error($e->getMessage());
|
||||||
|
$this->ReportError($e->getMessage(), $e->getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string $sTargetFile
|
||||||
|
* @throws Exception
|
||||||
|
* @return DBBackupWithErrorReporting
|
||||||
|
*/
|
||||||
|
public function DoBackup($sTargetFile): DBBackupWithErrorReporting
|
||||||
|
{
|
||||||
|
// Make sure the target directory exists
|
||||||
|
$sBackupDir = dirname($sTargetFile);
|
||||||
|
SetupUtils::builddir($sBackupDir);
|
||||||
|
|
||||||
|
$oBackup = new DBBackupWithErrorReporting();
|
||||||
|
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
|
||||||
|
$sSourceConfigFile = APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE;
|
||||||
|
|
||||||
|
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
|
||||||
|
$oMutex->Lock();
|
||||||
|
try {
|
||||||
|
$oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$oMutex->Unlock();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
$oMutex->Unlock();
|
||||||
|
return $oBackup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function LaunchCompile()
|
||||||
|
{
|
||||||
|
SetupLog::Info('Deployment starts...');
|
||||||
|
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
|
||||||
|
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent')) {
|
||||||
|
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
||||||
|
}
|
||||||
|
// First step: prepare the datamodel, if it fails, roll-back
|
||||||
|
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
||||||
|
|
||||||
|
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
|
||||||
|
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
|
||||||
|
|
||||||
|
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
|
||||||
|
if ($oConfig->Get('demo_mode')) {
|
||||||
|
throw new Exception('Sorry the installation of extensions is not allowed in demo mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
$aSelectModules = $oRuntimeEnv->CompileFrom('production'); // WARNING symlinks does not seem to be compatible with manual Commit
|
||||||
|
|
||||||
|
$oRuntimeEnv->UpdateIncludes($oConfig);
|
||||||
|
|
||||||
|
$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
|
||||||
|
|
||||||
|
// Safety check: check the inter dependencies, will throw an exception in case of inconsistency
|
||||||
|
$oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
|
||||||
|
|
||||||
|
$oRuntimeEnv->CheckMetaModel(); // Will throw an exception if a problem is detected
|
||||||
|
|
||||||
|
// Everything seems Ok so far, commit in env-production!
|
||||||
|
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
|
||||||
|
$oRuntimeEnv->Commit();
|
||||||
|
|
||||||
|
// Report the success in a way that will be detected by the ajax caller
|
||||||
|
SetupLog::Info('Compilation completed...');
|
||||||
|
|
||||||
|
$this->ReportSuccess('Ok'); // No access to Dict::S here
|
||||||
|
}
|
||||||
|
|
||||||
|
public function LaunchDeploy()
|
||||||
|
{
|
||||||
|
// Second step: update the schema and the data
|
||||||
|
// Everything happening below is based on env-production
|
||||||
|
$oRuntimeEnv = new RunTimeEnvironment('production', true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
|
||||||
|
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent')) {
|
||||||
|
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (file_exists(utils::GetDataPath().'hub/compile_authent')) {
|
||||||
|
unlink(utils::GetDataPath().'hub/compile_authent');
|
||||||
|
}
|
||||||
|
// Note: at this point, the dictionnary is not necessarily loaded
|
||||||
|
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
||||||
|
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
|
||||||
|
$this->ReportError($e->getMessage(), $e->getCode());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SetupLog::Info('Move to production starts...');
|
||||||
|
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
|
||||||
|
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent')) {
|
||||||
|
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
||||||
|
}
|
||||||
|
unlink(utils::GetDataPath().'hub/compile_authent');
|
||||||
|
// Load the "production" config file to clone & update it
|
||||||
|
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
|
||||||
|
SetupUtils::EnterReadOnlyMode($oConfig);
|
||||||
|
|
||||||
|
$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
|
||||||
|
|
||||||
|
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
|
||||||
|
|
||||||
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'BeforeDatabaseCreation');
|
||||||
|
|
||||||
|
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade');
|
||||||
|
|
||||||
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDatabaseCreation');
|
||||||
|
|
||||||
|
$oRuntimeEnv->UpdatePredefinedObjects();
|
||||||
|
|
||||||
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDatabaseSetup');
|
||||||
|
|
||||||
|
$oRuntimeEnv->LoadData($aAvailableModules, false /* no sample data*/);
|
||||||
|
|
||||||
|
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDataLoad');
|
||||||
|
|
||||||
|
// Record the installation so that the "about box" knows about the installed modules
|
||||||
|
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
||||||
|
|
||||||
|
$oExtensionsMap = new iTopExtensionsMap();
|
||||||
|
|
||||||
|
// Default choices = as before
|
||||||
|
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
||||||
|
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||||
|
// Plus all "remote" extensions
|
||||||
|
if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE) {
|
||||||
|
$oExtensionsMap->MarkAsChosen($oExtension->sCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$aSelectedExtensionCodes = [];
|
||||||
|
foreach ($oExtensionsMap->GetChoices() as $oExtension) {
|
||||||
|
$aSelectedExtensionCodes[] = $oExtension->sCode;
|
||||||
|
}
|
||||||
|
$aSelectedExtensions = $oExtensionsMap->GetChoices();
|
||||||
|
$oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, array_keys($aAvailableModules), $aSelectedExtensionCodes, 'Done by the iTop Hub Connector');
|
||||||
|
|
||||||
|
// Report the success in a way that will be detected by the ajax caller
|
||||||
|
SetupLog::Info('Deployment successfully completed.');
|
||||||
|
$this->ReportSuccess(Dict::S('iTopHub:CompiledOK'));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (file_exists(utils::GetDataPath().'hub/compile_authent')) {
|
||||||
|
unlink(utils::GetDataPath().'hub/compile_authent');
|
||||||
|
}
|
||||||
|
// Note: at this point, the dictionnary is not necessarily loaded
|
||||||
|
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
|
||||||
|
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
|
||||||
|
$this->ReportError($e->getMessage(), $e->getCode());
|
||||||
|
} finally {
|
||||||
|
SetupUtils::ExitReadOnlyMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs the status of the current ajax execution (as a JSON structure)
|
||||||
|
*
|
||||||
|
* @param string $sMessage
|
||||||
|
* @param bool $bSuccess
|
||||||
|
* @param number $iErrorCode
|
||||||
|
* @param array $aMoreFields
|
||||||
|
* Extra fields to pass to the caller, if needed
|
||||||
|
*/
|
||||||
|
public function ReportStatus($sMessage, $bSuccess, $iErrorCode = 0, $aMoreFields = [])
|
||||||
|
{
|
||||||
|
// Do not use AjaxPage during setup phases, because it uses InterfaceDiscovery in Twig compilation
|
||||||
|
$this->oLastJsonPage = new JsonPage();
|
||||||
|
$this->oLastJsonPage->SetOutputHeaders($this->bOutputHeaders);
|
||||||
|
$aResult = [
|
||||||
|
'code' => $iErrorCode,
|
||||||
|
'message' => $sMessage,
|
||||||
|
'fields' => $aMoreFields,
|
||||||
|
];
|
||||||
|
$this->oLastJsonPage->SetData($aResult);
|
||||||
|
$this->oLastJsonPage->SetOutputDataOnly(true);
|
||||||
|
$this->oLastJsonPage->output();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ?JsonPage $oLastJsonPage = null;
|
||||||
|
|
||||||
|
public function GetLastJsonPage(): ?JsonPage
|
||||||
|
{
|
||||||
|
return $this->oLastJsonPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to output the status of a successful execution
|
||||||
|
*
|
||||||
|
* @param string $sMessage
|
||||||
|
* @param array $aMoreFields
|
||||||
|
* Extra fields to pass to the caller, if needed
|
||||||
|
*/
|
||||||
|
public function ReportSuccess($sMessage, $aMoreFields = [])
|
||||||
|
{
|
||||||
|
$this->ReportStatus($sMessage, true, 0, $aMoreFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to output the status of a failed execution
|
||||||
|
*
|
||||||
|
* @param string $sMessage
|
||||||
|
* @param number $iErrorCode
|
||||||
|
* @param array $aMoreFields
|
||||||
|
* Extra fields to pass to the caller, if needed
|
||||||
|
*/
|
||||||
|
public function ReportError($sMessage, $iErrorCode, $aMoreFields = [])
|
||||||
|
{
|
||||||
|
if ($iErrorCode == 0) {
|
||||||
|
// 0 means no error, so change it if no meaningful error code is supplied
|
||||||
|
$iErrorCode = -1;
|
||||||
|
}
|
||||||
|
$this->ReportStatus($sMessage, false, $iErrorCode, $aMoreFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dont print headers for testing purpose mainly
|
||||||
|
* @param bool bOutputHeaders
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function SetOutputHeaders(bool $bOutputHeaders): void
|
||||||
|
{
|
||||||
|
$this->bOutputHeaders = $bOutputHeaders;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\HubConnector\Model;
|
||||||
|
|
||||||
|
use DBBackup;
|
||||||
|
use IssueLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overload of DBBackup to handle logging
|
||||||
|
*/
|
||||||
|
class DBBackupWithErrorReporting extends DBBackup
|
||||||
|
{
|
||||||
|
protected $aInfos = [];
|
||||||
|
|
||||||
|
protected $aErrors = [];
|
||||||
|
|
||||||
|
protected function LogInfo($sMsg)
|
||||||
|
{
|
||||||
|
$this->aInfos[] = $sMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function LogError($sMsg)
|
||||||
|
{
|
||||||
|
IssueLog::Error($sMsg);
|
||||||
|
$this->aErrors[] = $sMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetInfos(): array
|
||||||
|
{
|
||||||
|
return $this->aInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetErrors(): array
|
||||||
|
{
|
||||||
|
return $this->aErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\HubConnector\setup;
|
||||||
|
|
||||||
|
use Config;
|
||||||
|
use Exception;
|
||||||
|
use RunTimeEnvironment;
|
||||||
|
use SetupUtils;
|
||||||
|
|
||||||
class HubRunTimeEnvironment extends RunTimeEnvironment
|
class HubRunTimeEnvironment extends RunTimeEnvironment
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
*
|
||||||
* @param string $sEnvironment
|
* @param string $sEnvironment
|
||||||
* @param string $bAutoCommit
|
* @param string $bAutoCommit
|
||||||
*/
|
*/
|
||||||
@@ -11,39 +19,42 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
|
|||||||
{
|
{
|
||||||
parent::__construct($sEnvironment, $bAutoCommit);
|
parent::__construct($sEnvironment, $bAutoCommit);
|
||||||
|
|
||||||
if ($sEnvironment != $this->sTargetEnv) {
|
if ($sEnvironment != $this->sBuildEnv) {
|
||||||
if (is_dir(APPROOT.'/env-'.$this->sTargetEnv)) {
|
if (is_dir(APPROOT.'/env-'.$this->sBuildEnv)) {
|
||||||
SetupUtils::rrmdir(APPROOT.'/env-'.$this->sTargetEnv);
|
SetupUtils::rrmdir(APPROOT.'/env-'.$this->sBuildEnv);
|
||||||
}
|
}
|
||||||
if (is_dir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) {
|
if (is_dir(APPROOT.'/data/'.$this->sBuildEnv.'-modules')) {
|
||||||
SetupUtils::rrmdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules');
|
SetupUtils::rrmdir(APPROOT.'/data/'.$this->sBuildEnv.'-modules');
|
||||||
}
|
}
|
||||||
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sTargetEnv.'-modules');
|
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sBuildEnv.'-modules');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the includes for the target environment
|
* Update the includes for the build environment
|
||||||
|
*
|
||||||
* @param Config $oConfig
|
* @param Config $oConfig
|
||||||
*/
|
*/
|
||||||
public function UpdateIncludes(Config $oConfig)
|
public function UpdateIncludes(Config $oConfig)
|
||||||
{
|
{
|
||||||
$oConfig->UpdateIncludes('env-'.$this->sTargetEnv); // TargetEnv != FinalEnv
|
$oConfig->UpdateIncludes('env-'.$this->sBuildEnv); // BuildEnv != FinalEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move an extension (path to folder of this extension) to the target environment
|
* Move an extension (path to folder of this extension) to the build environment
|
||||||
|
*
|
||||||
* @param string $sExtensionDirectory The folder of the extension
|
* @param string $sExtensionDirectory The folder of the extension
|
||||||
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function MoveExtension($sExtensionDirectory)
|
public function MoveExtension($sExtensionDirectory)
|
||||||
{
|
{
|
||||||
if (!is_dir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) {
|
if (!is_dir(APPROOT.'/data/'.$this->sBuildEnv.'-modules')) {
|
||||||
if (!mkdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) {
|
if (!mkdir(APPROOT.'/data/'.$this->sBuildEnv.'-modules')) {
|
||||||
throw new Exception("ERROR: failed to create directory:'".(APPROOT.'/data/'.$this->sTargetEnv.'-modules')."'");
|
throw new Exception("ERROR: failed to create directory:'".(APPROOT.'/data/'.$this->sBuildEnv.'-modules')."'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sDestinationPath = APPROOT.'/data/'.$this->sTargetEnv.'-modules/';
|
$sDestinationPath = APPROOT.'/data/'.$this->sBuildEnv.'-modules/';
|
||||||
|
|
||||||
// Make sure that the destination directory of the extension does not already exist
|
// Make sure that the destination directory of the extension does not already exist
|
||||||
if (is_dir($sDestinationPath.basename($sExtensionDirectory))) {
|
if (is_dir($sDestinationPath.basename($sExtensionDirectory))) {
|
||||||
@@ -56,9 +67,11 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the selected extensions located in the given directory in data/<target-env>-modules
|
* Move the selected extensions located in the given directory in data/<build-env>-modules
|
||||||
|
*
|
||||||
* @param string $sDownloadedExtensionsDir The directory to scan
|
* @param string $sDownloadedExtensionsDir The directory to scan
|
||||||
* @param string[] $aSelectedExtensionDirs The list of folders to move
|
* @param string[] $aSelectedExtensionDirs The list of folders to move
|
||||||
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function MoveSelectedExtensions($sDownloadedExtensionsDir, $aSelectedExtensionDirs)
|
public function MoveSelectedExtensions($sDownloadedExtensionsDir, $aSelectedExtensionDirs)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
Dict::Add('EN US', 'English', 'English', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('FR FR', 'French', 'Français', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installé',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'va être installé',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'pas installé',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'va être désinstallé',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'non désinstallable',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'supprimé du disque',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'À propos de %1$s',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'Plus d\'informations',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Forcer la désinstallation',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Plus d\'actions',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||||
|
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||||
|
]);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user