mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-06 01:28:41 +02:00
Compare commits
137 Commits
develop
...
feature/un
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c89a75716e | ||
|
|
93b658646b | ||
|
|
3590ddedf0 | ||
|
|
0d0261b0cc | ||
|
|
7dd6903739 | ||
|
|
7f87182b15 | ||
|
|
2c380e8561 | ||
|
|
9cfaf59669 | ||
|
|
4ed3323956 | ||
|
|
271fed210a | ||
|
|
7bec8d9ad0 | ||
|
|
9362a0b0ae | ||
|
|
4f5d6c47fa | ||
|
|
45e0cee1ee | ||
|
|
fb8b4a07b3 | ||
|
|
dec5da0136 | ||
|
|
63532d20a5 | ||
|
|
5ae8edd2c0 | ||
|
|
a13543c0d3 | ||
|
|
8e581c903b | ||
|
|
c9fe2eda36 | ||
|
|
bd01cbb031 | ||
|
|
15eb0f86d7 | ||
|
|
12a06fcfc0 | ||
|
|
4743cdcf0f | ||
|
|
0d1af2964a | ||
|
|
47b93175b3 | ||
|
|
9b2259726b | ||
|
|
90ace885cd | ||
|
|
b667df2d6c | ||
|
|
99cfe95c32 | ||
|
|
10d1f5735e | ||
|
|
c94ba187bb | ||
|
|
9396fe6815 | ||
|
|
70f77aac68 | ||
|
|
358c4383f8 | ||
|
|
e1d0b0d200 | ||
|
|
890db04fa3 | ||
|
|
be29343b49 | ||
|
|
e1e2da2881 | ||
|
|
070b96c9b5 | ||
|
|
cb47ea4316 | ||
|
|
167cb84c93 | ||
|
|
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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -976,7 +976,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);
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
@@ -1173,8 +1170,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,
|
||||||
],
|
],
|
||||||
@@ -2691,14 +2688,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']);
|
||||||
@@ -2746,7 +2742,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']);
|
||||||
@@ -2764,17 +2763,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;
|
||||||
|
|||||||
@@ -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";
|
||||||
65
css/backoffice/layout/extension/_extension-details.scss
Normal file
65
css/backoffice/layout/extension/_extension-details.scss
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
$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;
|
||||||
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -681,9 +704,6 @@ body {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
text-align: center;
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
17
datamodels/2.x/combodo-data-feature-removal/composer.json
Normal file
17
datamodels/2.x/combodo-data-feature-removal/composer.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"classmap-authoritative": true
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Combodo\\iTop\\DataFeatureRemoval\\": "src",
|
||||||
|
"": "src/NoNamespace"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,58 @@
|
|||||||
|
<?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: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:ToManyOperations' => 'Too many entries to clean',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Element to remove',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Feature name',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module name',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'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:Issue' => 'Issue',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Column:DeletedCount' => 'Deleted entries',
|
||||||
|
'DataFeatureRemoval:Column:UpdatedCount' => 'Updated entries',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?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: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:ToManyOperations' => 'Trop d’entrées à nettoyer',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Élément à supprimer',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Fonctionnalité',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module',
|
||||||
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'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:Issue' => 'Problème',
|
||||||
|
|
||||||
|
'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,257 @@
|
|||||||
|
<?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\TwigBase\Controller\Controller;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalConfig;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\DeletionPlanService;
|
||||||
|
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||||
|
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||||
|
use Dict;
|
||||||
|
use Exception;
|
||||||
|
use IssueLog;
|
||||||
|
use MetaModel;
|
||||||
|
use utils;
|
||||||
|
|
||||||
|
class DataFeatureRemovalController extends Controller
|
||||||
|
{
|
||||||
|
private array $aSelectedExtensionsForCheck = [];
|
||||||
|
private array $aCountClassesToCleanup = [];
|
||||||
|
private array $aAnalysisDataTable = [];
|
||||||
|
private int $iCount = 0;
|
||||||
|
|
||||||
|
public function OperationMain($sErrorMessage = null): void
|
||||||
|
{
|
||||||
|
$aParams = [];
|
||||||
|
|
||||||
|
$this->ReadRemovedExtensions();
|
||||||
|
$this->AddAnalyzeParams();
|
||||||
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
|
$aParams['aExtensions'] = $this->GetExtensionsTable();
|
||||||
|
$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;
|
||||||
|
|
||||||
|
$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 OperationAnalyze(): void
|
||||||
|
{
|
||||||
|
$this->ReadRemovedExtensions();
|
||||||
|
|
||||||
|
$this->m_sOperation = 'Main';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (count($this->aSelectedExtensionsForCheck) > 0) {
|
||||||
|
$this->Analyze();
|
||||||
|
}
|
||||||
|
$this->OperationMain();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
IssueLog::Error(__METHOD__, null, ['stack' => $e->getTraceAsString(), 'exception' => $e->getMessage()]);
|
||||||
|
$this->OperationMain($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function Analyze(): void
|
||||||
|
{
|
||||||
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
|
$oDryRemovalRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $this->aSelectedExtensionsForCheck);
|
||||||
|
$oDryRemovalRuntimeEnvironment->CompileFrom($sSourceEnv);
|
||||||
|
|
||||||
|
$oSetupAudit = new SetupAudit($sSourceEnv);
|
||||||
|
$aGetRemovedClasses = $oSetupAudit->GetIssues();
|
||||||
|
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
||||||
|
$this->aCountClassesToCleanup = $aGetRemovedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function OperationDeletionPlan(): void
|
||||||
|
{
|
||||||
|
$aParams = [];
|
||||||
|
$this->ValidateTransactionId();
|
||||||
|
|
||||||
|
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||||
|
|
||||||
|
$aDeletionPlanSummaryEntities = DeletionPlanService::GetInstance()->GetDeletionPlanSummary($aClasses);
|
||||||
|
$aColumns = ['Class', 'DeleteCount' , 'UpdateCount', 'Issue'];
|
||||||
|
$aRows = [];
|
||||||
|
$iQueryCount = 0;
|
||||||
|
foreach ($aDeletionPlanSummaryEntities as $oDeletionPlanSummaryEntity) {
|
||||||
|
$aRows[] = [
|
||||||
|
$oDeletionPlanSummaryEntity->sClass,
|
||||||
|
$oDeletionPlanSummaryEntity->iDeleteCount,
|
||||||
|
$oDeletionPlanSummaryEntity->iUpdateCount,
|
||||||
|
$oDeletionPlanSummaryEntity->sIssue ?? '',
|
||||||
|
];
|
||||||
|
$iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount;
|
||||||
|
$iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
|
$aParams['aDeletionPlanSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows);
|
||||||
|
$aParams['aClasses'] = $aClasses;
|
||||||
|
$aParams['iQueryCount'] = $iQueryCount;
|
||||||
|
$aParams['bDeletionPossible'] = ($iQueryCount <= DataFeatureRemovalConfig::GetInstance()->Get('max_count_estimation_for_safe_cleanup', 100));
|
||||||
|
|
||||||
|
$this->DisplayPage($aParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function OperationDoDeletion(): void
|
||||||
|
{
|
||||||
|
$aParams = [];
|
||||||
|
$this->ValidateTransactionId();
|
||||||
|
|
||||||
|
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||||
|
|
||||||
|
$aDeletionExecutionSummary = DeletionPlanService::GetInstance()->ExecuteDeletionPlan($aClasses);
|
||||||
|
$aColumns = ['Class', 'DeletedCount' , 'UpdatedCount'];
|
||||||
|
$aRows = [];
|
||||||
|
foreach ($aDeletionExecutionSummary as $oDeletionExecutionSummaryEntity) {
|
||||||
|
$aRows[] = [
|
||||||
|
$oDeletionExecutionSummaryEntity->sClass,
|
||||||
|
$oDeletionExecutionSummaryEntity->iDeleteCount,
|
||||||
|
$oDeletionExecutionSummaryEntity->iUpdateCount,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
|
$aParams['aDeletionExecutionSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows);
|
||||||
|
$this->DisplayPage($aParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get installed extensions from disk
|
||||||
|
*
|
||||||
|
* @return array structure for twig datatable
|
||||||
|
*/
|
||||||
|
private function GetExtensionsTable(): array
|
||||||
|
{
|
||||||
|
$aExtensions = [];
|
||||||
|
$aColumns = ['', 'Version', 'Name', 'Code'];
|
||||||
|
|
||||||
|
foreach (DataFeatureRemoverExtensionService::GetInstance()->ReadItopExtensions() as $sCode => $oExtension) {
|
||||||
|
/** @var \iTopExtension $oExtension */
|
||||||
|
|
||||||
|
$sChecked = '';
|
||||||
|
$sDisabledHtml = '';
|
||||||
|
if ($oExtension->bRemovedFromDisk) {
|
||||||
|
$sDisabledHtml = 'disabled=""';
|
||||||
|
$sChecked = 'checked';
|
||||||
|
} elseif (in_array($sCode, $this->aSelectedExtensionsForCheck)) {
|
||||||
|
$sChecked = 'checked';
|
||||||
|
}
|
||||||
|
|
||||||
|
$sLabel = $oExtension->sLabel;
|
||||||
|
$sVersion = $oExtension->sVersion;
|
||||||
|
$sIdEnable = "aExtensions[$sCode][enable]";
|
||||||
|
|
||||||
|
$aExtensions[] = [
|
||||||
|
<<<HTML
|
||||||
|
<input type="checkbox" $sDisabledHtml class="extension_check" $sChecked id="$sIdEnable" name="$sIdEnable"/>
|
||||||
|
HTML,
|
||||||
|
$sVersion,
|
||||||
|
$sLabel,
|
||||||
|
$sCode,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->GetTableData('Extensions', $aColumns, $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);
|
||||||
|
IssueLog::Debug(__FUNCTION__.": Transaction [$sTransactionId]");
|
||||||
|
if (empty($sTransactionId) || !utils::IsTransactionValid($sTransactionId, false)) {
|
||||||
|
throw new DataFeatureRemovalException(Dict::S("iTopUpdate:Error:InvalidToken"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function ReadRemovedExtensions(): void
|
||||||
|
{
|
||||||
|
if (count($this->aSelectedExtensionsForCheck) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aSelectedExtensionsFromUI = utils::ReadPostedParam('aExtensions', []);
|
||||||
|
foreach ($aSelectedExtensionsFromUI as $sCode => $aData) {
|
||||||
|
$sValue = $aData['enable'] ?? 'off';
|
||||||
|
if (($sValue) === 'on') {
|
||||||
|
$this->aSelectedExtensionsForCheck[] = $sCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add source removed to check
|
||||||
|
foreach (DataFeatureRemoverExtensionService::GetInstance()->ReadItopExtensions() as $sCode => $oExtension) {
|
||||||
|
if ($oExtension->bRemovedFromDisk) {
|
||||||
|
$this->aSelectedExtensionsForCheck[] = $sCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Entity;
|
||||||
|
|
||||||
|
class DeletionPlanSummaryEntity
|
||||||
|
{
|
||||||
|
public string $sClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int : DEL_MANUAL|DEL_AUTO|DEL_SILENT|DEL_MOVEUP|DEL_NONE
|
||||||
|
* @see \AttributeDefinition DEL_xxx
|
||||||
|
*/
|
||||||
|
public int $iMode = 0;
|
||||||
|
public ?string $sIssue = null;
|
||||||
|
public int $iUpdateCount = 0;
|
||||||
|
public int $iDeleteCount = 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(static::$oInstance)) {
|
||||||
|
static::$oInstance = new DataFeatureRemovalConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::$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,80 @@
|
|||||||
|
<?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 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 iTopExtension[]
|
||||||
|
*/
|
||||||
|
public function ReadItopExtensions(): array
|
||||||
|
{
|
||||||
|
if (count($this->aItopExtensions) === 0) {
|
||||||
|
$oExtensionsMap = new iTopExtensionsMap();
|
||||||
|
$oExtensionsMap->LoadInstalledExtensionsFromDatabase(MetaModel::GetConfig());
|
||||||
|
$this->aItopExtensions = $oExtensionsMap->GetAllExtensionsToDisplayInSetup(true);
|
||||||
|
|
||||||
|
uasort($this->aItopExtensions, function (iTopExtension $oiTopExtension1, iTopExtension $oiTopExtension2) {
|
||||||
|
return strcmp($oiTopExtension1->sLabel, $oiTopExtension2->sLabel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->aItopExtensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use CMDBSource;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||||
|
use DBObjectSearch;
|
||||||
|
use DeletionPlan;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class DeletionPlanService
|
||||||
|
{
|
||||||
|
private static DeletionPlanService $oInstance;
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function GetInstance(): DeletionPlanService
|
||||||
|
{
|
||||||
|
if (!isset(self::$oInstance)) {
|
||||||
|
self::$oInstance = new DeletionPlanService();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function SetInstance(?DeletionPlanService $oInstance): void
|
||||||
|
{
|
||||||
|
self::$oInstance = $oInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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\DeletionPlanSummaryEntity>
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function GetDeletionPlanSummary(?array $aClasses): array
|
||||||
|
{
|
||||||
|
$aSummary = [];
|
||||||
|
if (is_null($aClasses)) {
|
||||||
|
return $aSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oDeletionPlan = $this->GetDeletionPlan($aClasses);
|
||||||
|
|
||||||
|
foreach ($oDeletionPlan->ListUpdates() as $sClass => $aUpdates) {
|
||||||
|
$oDeletionPlanSummaryEntity = new DeletionPlanSummaryEntity($sClass);
|
||||||
|
$oDeletionPlanSummaryEntity->iUpdateCount = count($aUpdates);
|
||||||
|
$aSummary[$sClass] = $oDeletionPlanSummaryEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($oDeletionPlan->ListDeletes() as $sClass => $aDeletes) {
|
||||||
|
$oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass);
|
||||||
|
$oDeletionPlanSummaryEntity->iDeleteCount = count($aDeletes);
|
||||||
|
|
||||||
|
$aDelete = array_shift($aDeletes);
|
||||||
|
$oDeletionPlanSummaryEntity->iMode = $aDelete['mode'];
|
||||||
|
$oDeletionPlanSummaryEntity->sIssue = $aDelete['issue'] ?? null;
|
||||||
|
|
||||||
|
$aSummary[$sClass] = $oDeletionPlanSummaryEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sClass
|
||||||
|
*
|
||||||
|
* @return \DBObject[]
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function GetAllObjects(string $sClass): array
|
||||||
|
{
|
||||||
|
$oFilter = new DBObjectSearch($sClass);
|
||||||
|
$oFilter->AllowAllData();
|
||||||
|
$oSet = new \DBObjectSet($oFilter);
|
||||||
|
return $oSet->ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aClasses
|
||||||
|
*
|
||||||
|
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity>
|
||||||
|
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function ExecuteDeletionPlan(array $aClasses): array
|
||||||
|
{
|
||||||
|
$oDeletionPlan = $this->GetDeletionPlan($aClasses);
|
||||||
|
|
||||||
|
if (count($oDeletionPlan->GetIssues()) > 0) {
|
||||||
|
throw new DataFeatureRemovalException("Deletion Plan cannot be executed due to issues");
|
||||||
|
}
|
||||||
|
|
||||||
|
$aSummary = [];
|
||||||
|
foreach ($oDeletionPlan->ListUpdates() as $sClass => $aToUpdate) {
|
||||||
|
$oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass);
|
||||||
|
|
||||||
|
foreach ($aToUpdate as $aData) {
|
||||||
|
$oToUpdate = $aData['to_reset'];
|
||||||
|
/** @var \DBObject $oToUpdate */
|
||||||
|
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef) {
|
||||||
|
$oToUpdate->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
|
||||||
|
}
|
||||||
|
$oToUpdate->DBUpdate();
|
||||||
|
$oDeletionPlanSummaryEntity->iUpdateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aSummary[$sClass] = $oDeletionPlanSummaryEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($oDeletionPlan->ListDeletes() as $sClass => $aDeletes) {
|
||||||
|
$oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass);
|
||||||
|
|
||||||
|
foreach ($aDeletes as $sId => $aDelete) {
|
||||||
|
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) {
|
||||||
|
$oFilter = DBObjectSearch::FromOQL_AllData("SELECT $sParentClass WHERE id=:id");
|
||||||
|
$sQuery = $oFilter->MakeDeleteQuery(['id' => $sId]);
|
||||||
|
CMDBSource::DeleteFrom($sQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMDBSource::Query('COMMIT');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\IssueLog::Exception(__METHOD__.': Cleanup failed', $e);
|
||||||
|
CMDBSource::Query('ROLLBACK');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
$oDeletionPlanSummaryEntity->iDeleteCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aSummary[$sClass] = $oDeletionPlanSummaryEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a deletion plan for all the objects of the classes
|
||||||
|
*
|
||||||
|
* @param array $aClasses array of class names to clean
|
||||||
|
*
|
||||||
|
* @return \DeletionPlan
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function GetDeletionPlan(array $aClasses): DeletionPlan
|
||||||
|
{
|
||||||
|
$oDeletionPlan = new DeletionPlan();
|
||||||
|
foreach ($aClasses as $sClass) {
|
||||||
|
$aObjects = $this->GetAllObjects($sClass);
|
||||||
|
foreach ($aObjects as $oObject) {
|
||||||
|
$oObject->CheckToDelete($oDeletionPlan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oDeletionPlan;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2026 Combodo SARL #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
{% UIPanel ForInformation { sTitle:'DataFeatureRemoval:DeletionPlan:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:DeletionPlan:SubTitle'|dict_format(iQueryCount) } %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
|
||||||
|
{% 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 %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% else %}
|
||||||
|
{{ 'DataFeatureRemoval:DeletionPlan:ToManyOperations'|dict_s }}
|
||||||
|
{% 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 %}
|
||||||
@@ -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,19 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2024 Combodo SAS #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
{% if bHasData %}
|
||||||
|
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Analysis:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:Analysis:SubTitle'|dict_format(iCount) } %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aAnalysisDataTable', aColumns:aAnalysisDataTable.Columns, aData:aAnalysisDataTable.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIPanel %}
|
||||||
|
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
{% UIInput ForHidden { sName:'operation', sValue:'DeletionPlan'} %}
|
||||||
|
{% for sKey, sClass in aClasses %}
|
||||||
|
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:PlanDeletion'|dict_s, sName:'btn_plandeletion', sId:'btn_plandeletion', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{# @copyright Copyright (C) 2010-2024 Combodo SAS #}
|
||||||
|
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||||
|
|
||||||
|
|
||||||
|
{% UIForm Standard {} %}
|
||||||
|
{% UIInput ForHidden {sName:'operation', sValue:'Analyze'} %}
|
||||||
|
{% UIInput ForHidden {sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
|
|
||||||
|
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:Features:Title'|dict_s} %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aExtensions', aColumns:aExtensions.Columns, aData:aExtensions.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIFieldSet %}
|
||||||
|
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Analyze'|dict_s, sName:'btn_apply', sId:'btn_apply', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{# @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 %}
|
||||||
|
|
||||||
|
{% 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>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% include 'Features.html.twig' %}
|
||||||
|
{% include 'ExtensionRemovalData.html.twig' %}
|
||||||
|
|
||||||
|
{% if not bHasData %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
<a href="{{ sSetupUrl }}">
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Setup'|dict_s, sName:'btn_setup', sId:'btn_setup', bIsSubmit:false} %}
|
||||||
|
</a>
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% endif %}
|
||||||
|
{% 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
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.
|
||||||
|
|
||||||
18
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_classmap.php
vendored
Normal file
18
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_classmap.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?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\\DeletionPlanSummaryEntity' => $baseDir . '/src/Entity/DeletionPlanSummaryEntity.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\\DataFeatureRemoverExtensionService' => $baseDir . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DeletionPlanService' => $baseDir . '/src/Service/DeletionPlanService.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(
|
||||||
|
);
|
||||||
11
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_psr4.php
vendored
Normal file
11
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\' => array($baseDir . '/src'),
|
||||||
|
'' => array($baseDir . '/src/NoNamespace'),
|
||||||
|
);
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_static.php
vendored
Normal file
49
datamodels/2.x/combodo-data-feature-removal/vendor/composer/autoload_static.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?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 $fallbackDirsPsr4 = array (
|
||||||
|
0 => __DIR__ . '/../..' . '/src/NoNamespace',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => __DIR__ . '/../..' . '/src/Controller/DataFeatureRemovalController.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DeletionPlanSummaryEntity' => __DIR__ . '/../..' . '/src/Entity/DeletionPlanSummaryEntity.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\\DataFeatureRemoverExtensionService' => __DIR__ . '/../..' . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DeletionPlanService' => __DIR__ . '/../..' . '/src/Service/DeletionPlanService.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->fallbackDirsPsr4 = ComposerStaticInit4f96a7199e2c0d90e547333758b26464::$fallbackDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit4f96a7199e2c0d90e547333758b26464::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -232,7 +232,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' => '数据库的磁盘空间',
|
||||||
|
|||||||
@@ -246,10 +246,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'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,32 +25,32 @@ 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;
|
$sBuildDir = APPROOT.'env-'.$sBuildEnv;
|
||||||
$sBuildDir = $sTargetDir.'-build';
|
$sBuildDir = $sBuildDir.'-build';
|
||||||
|
|
||||||
self::CheckDirectory($sTargetDir);
|
self::CheckDirectory($sBuildDir);
|
||||||
self::CheckDirectory($sBuildDir);
|
self::CheckDirectory($sBuildDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,12 +83,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 +104,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)) {
|
||||||
@@ -135,7 +135,7 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
|||||||
$aAvailableModules[$oModule->GetName()] = $oModule;
|
$aAvailableModules[$oModule->GetName()] = $oModule;
|
||||||
}
|
}
|
||||||
// TODO check the auto-selected modules here
|
// TODO check the auto-selected modules here
|
||||||
foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) {
|
foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) {
|
||||||
if ($oExtension->bMarkedAsChosen) {
|
if ($oExtension->bMarkedAsChosen) {
|
||||||
foreach ($oExtension->aModules as $sModuleName) {
|
foreach ($oExtension->aModules as $sModuleName) {
|
||||||
if (!isset($aRet[$sModuleName]) && isset($aAvailableModules[$sModuleName])) {
|
if (!isset($aRet[$sModuleName]) && isset($aAvailableModules[$sModuleName])) {
|
||||||
|
|||||||
@@ -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']} %}
|
||||||
|
|||||||
@@ -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,315 @@
|
|||||||
|
<?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
|
||||||
|
$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'); // 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(false, 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)
|
||||||
@@ -1983,18 +1983,6 @@
|
|||||||
<rank>3</rank>
|
<rank>3</rank>
|
||||||
<class>LogicalVolume</class>
|
<class>LogicalVolume</class>
|
||||||
</dashlet>
|
</dashlet>
|
||||||
<dashlet id="FiberChannelInterface" xsi:type="DashletBadge" _delta="define">
|
|
||||||
<rank>1.3</rank>
|
|
||||||
<class>FiberChannelInterface</class>
|
|
||||||
</dashlet>
|
|
||||||
<dashlet id="NASFileSystem" xsi:type="DashletBadge" _delta="define">
|
|
||||||
<rank>3.3</rank>
|
|
||||||
<class>NASFileSystem</class>
|
|
||||||
</dashlet>
|
|
||||||
<dashlet id="Tape" xsi:type="DashletBadge" _delta="define">
|
|
||||||
<rank>3.5</rank>
|
|
||||||
<class>Tape</class>
|
|
||||||
</dashlet>
|
|
||||||
</dashlets>
|
</dashlets>
|
||||||
</cell>
|
</cell>
|
||||||
</cells>
|
</cells>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized data
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||||
|
'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~~',
|
||||||
|
]);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user