mirror of
https://github.com/Combodo/iTop.git
synced 2026-10-12 19:39:01 +02:00
Compare commits
14 Commits
feature/10
...
feature/89
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0ef950664 | ||
|
|
8bdb7dd03a | ||
|
|
f1d0432220 | ||
|
|
34d26cde6c | ||
|
|
0c4f208ca8 | ||
|
|
538cf46d5f | ||
|
|
f9b66e88ec | ||
|
|
a81c9a0be4 | ||
|
|
4e1bf80b9f | ||
|
|
2ac4229ee0 | ||
|
|
e6d485855e | ||
|
|
ab9d270b62 | ||
|
|
4a6af15237 | ||
|
|
05e585ced3 |
1
.github/ISSUE_TEMPLATE/enhancement.yml
vendored
1
.github/ISSUE_TEMPLATE/enhancement.yml
vendored
@@ -9,6 +9,7 @@ body:
|
||||
- Please describe what's your improvement proposition.
|
||||
- Then tell us if you're willing to create a PR for this enhancement ? If so, we'll indicate in the issue if we're interested in it.
|
||||
- The more users are affected and the greater is the impact, the more likely this improvement will be accepted. If you identified a bug and have an improvement proposition, please use the 'Bug report' template instead."
|
||||
Please note we're not willing to accept code improvement (e.g. refactoring) that won't have a "functional" change for the user.
|
||||
|
||||
- type: textarea
|
||||
id: enhancement_details
|
||||
|
||||
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -2,8 +2,11 @@
|
||||
IMPORTANT: Before creating your PR, please create an issue first to know if Combodo is interested in your contribution (not needed for translations PR).
|
||||
Since we may refuse a PR, it's preferable to create an issue first, to avoid spending time coding something that won't be accepted.
|
||||
PR that will benefit the most users and has a greater impact on the product are more likely to be accepted, so please explain your use case and why you think it would be useful for the community.
|
||||
We're not willing to receive PR concerning codes improvements (e.g. code refactoring), except if it's part of a code that brings/corrects a feature for users.
|
||||
Please note that we're working with sprints, so we reserve the right to decline a PR if we do not plan to work on that subject in the 6 coming months.
|
||||
Thank you for your comprehension 😊
|
||||
|
||||
Once you've done it, and we confirmed we're interested in it, please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||
Once you've created the issue, and we confirmed we're interested in it, please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||
|
||||
Any PRs not following the guidelines or with missing information will not be considered.
|
||||
-->
|
||||
|
||||
@@ -61,7 +61,7 @@ foreach ($aTcpdfFontsDirContent as $sTcpdfFontResourceName) {
|
||||
* 2) Then adding the DroidSansFallback font (useful for CJK data for example)
|
||||
*/
|
||||
echo $sCurrentScriptFileName.": ---2) Copying font files to TCPDF ($sTcPdfFontsFolder)...\n";
|
||||
$aFontFilesToCopy = glob(__DIR__.'\droidsansfallback.*');
|
||||
$aFontFilesToCopy = glob(__DIR__.DIRECTORY_SEPARATOR.'droidsansfallback.*');
|
||||
foreach ($aFontFilesToCopy as $sFontFileToCopy) {
|
||||
$sFontFileName = basename($sFontFileToCopy);
|
||||
echo $sCurrentScriptFileName.': copying '.$sFontFileName."\n";
|
||||
|
||||
@@ -38,6 +38,7 @@ class DataFeatureRemovalController extends Controller
|
||||
private array $aCountClassesToCleanup = [];
|
||||
private array $aAnalysisDataTable = [];
|
||||
private array $aDeletionExecutionSummary = [];
|
||||
private ?array $aBasePackageModules = null;
|
||||
|
||||
private int $iCount = 0;
|
||||
private int $iColumnCount = 2;
|
||||
@@ -371,18 +372,23 @@ class DataFeatureRemovalController extends Controller
|
||||
|
||||
foreach ($aExtensionsRef as $oExtension) {
|
||||
/** @var \iTopExtension $oExtension */
|
||||
$aMetaData = [$oExtension->sVersion, $oExtension->GetExtensionSourceLabel(), $oExtension->sCode];
|
||||
$bIsPackageExtension = $this->IsIncludedInPackage($oExtension);
|
||||
|
||||
$aExtensionsData[$oExtension->sCode] = [
|
||||
'version' => $oExtension->sVersion,
|
||||
'label' => $oExtension->sLabel,
|
||||
'code' => $oExtension->sCode,
|
||||
'description' => $oExtension->sDescription,
|
||||
'source' => $oExtension->GetExtensionSourceLabel(),
|
||||
'metadata' => $aMetaData,
|
||||
'installed' => $oExtension->bInstalled,
|
||||
'extra_flags' => [
|
||||
'uninstallable' => $oExtension->CanBeUninstalled(),
|
||||
'remote' => $oExtension->IsRemote(),
|
||||
'missing' => $oExtension->bRemovedFromDisk,
|
||||
'dependency_issue' => $oExtension->HasDependencyIssue(),
|
||||
'already_part_of_itop' => $bIsPackageExtension,
|
||||
],
|
||||
|
||||
];
|
||||
@@ -391,6 +397,52 @@ class DataFeatureRemovalController extends Controller
|
||||
return $aExtensionsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when all modules of a non-package extension are already included in base package modules.
|
||||
*/
|
||||
public function IsIncludedInPackage(iTopExtension $oExtension): bool
|
||||
{
|
||||
if ($oExtension->sSource === iTopExtension::SOURCE_WIZARD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$aModules = $oExtension->aModules ?? [];
|
||||
if (!is_array($aModules) || empty($aModules)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$aBasePackageModules = $this->GetBasePackageModules();
|
||||
foreach ($aModules as $sModuleId) {
|
||||
if (!in_array($sModuleId, $aBasePackageModules)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetBasePackageModules(): array
|
||||
{
|
||||
if ($this->aBasePackageModules !== null) {
|
||||
return $this->aBasePackageModules;
|
||||
}
|
||||
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
$oExtensionsMap = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap();
|
||||
$aSelectedExtensions = $oExtensionsMap->GetSelectedExtensions($oConfig);
|
||||
foreach ($aSelectedExtensions as $index => $sExtensionCode) {
|
||||
$oExtension = $oExtensionsMap->GetFromExtensionCode($sExtensionCode);
|
||||
if ($oExtension->sSource !== iTopExtension::SOURCE_WIZARD) {
|
||||
unset($aSelectedExtensions[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
$sSourceEnv = MetaModel::GetEnvironment();
|
||||
$oRuntimeEnvironment = new RunTimeEnvironment($sSourceEnv, false);
|
||||
|
||||
$this->aBasePackageModules = $oRuntimeEnvironment->GetModulesToLoadFromSelectedExtensions($oConfig, $aSelectedExtensions);
|
||||
return $this->aBasePackageModules;
|
||||
}
|
||||
|
||||
private function GetExtensionsDiff(array $aAddedExtensions, array $aRemovedExtensions): array
|
||||
{
|
||||
$aExtensions = [];
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
{% UIColumn Standard {} %}
|
||||
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||
{% if aExtension['installed'] %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% else %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% EndUIColumn %}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
{% UIColumn Standard {} %}
|
||||
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||
{% if aExtension['installed'] %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% else %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% EndUIColumn %}
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Diese Extension wird während des Setups deinstalliert.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'nicht deinstallierbar',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Sobald diese Extension installiert ist, sollte sie nicht mehr deinstalliert werden.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'auf dem Datenträger nicht vorhanden',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Das lokale Verzeichnis der Extension wurde vom Datenträger entfernt. Dadurch wird die Deinstallation dieser Extension erzwungen.',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'Über %1$s',
|
||||
|
||||
@@ -21,6 +21,8 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('EN GB', 'British English', 'British English', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Cette extension sera désinstallée lors de l\'installation.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'non désinstallable',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Une fois cette extension installée, elle ne devrait pas être désinstallée.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'déjà dans iTop',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'Tous les modules de cette extension font déjà partie du package iTop. Pour désinstaller cette extension, vous devez supprimer son dossier.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'supprimée du disque',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Le dossier local de l\'extension a été supprimé du disque. Cela forcera la désinstallation de cette extension.',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'À propos de %1$s',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Это расширение будет удалено в ходе установки.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'нельзя удалить',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'После установки это расширение не должно удаляться.',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'отсутствует на диске',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Локальная папка расширения удалена с диска. Это приведёт к принудительному удалению расширения.',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'О расширении %1$s',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
|
||||
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',
|
||||
|
||||
19
js/pages/backoffice/dialog.js
Normal file
19
js/pages/backoffice/dialog.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Overload of the default dialog widget
|
||||
$.widget('ui.dialog', $.ui.dialog, {
|
||||
_allowInteraction: function (oEvent) {
|
||||
const oTarget = $(oEvent.target);
|
||||
// If we interact with a CKEditor instance in fullscreen mode, we need to allow it
|
||||
// We could check if the current instance is in the dialog, but it's easier to always allow it in fullscreen
|
||||
if (oTarget.closest('.ck.ck-fullscreen__main-wrapper, .ck-body-wrapper').length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If that's not a specific case, fall back to the default behavior
|
||||
return this._super(oEvent);
|
||||
}
|
||||
});
|
||||
BIN
lib/tecnickcom/tcpdf/fonts/droidsansfallback.ctg.z
Normal file
BIN
lib/tecnickcom/tcpdf/fonts/droidsansfallback.ctg.z
Normal file
Binary file not shown.
2975
lib/tecnickcom/tcpdf/fonts/droidsansfallback.php
Normal file
2975
lib/tecnickcom/tcpdf/fonts/droidsansfallback.php
Normal file
File diff suppressed because it is too large
Load Diff
BIN
lib/tecnickcom/tcpdf/fonts/droidsansfallback.z
Normal file
BIN
lib/tecnickcom/tcpdf/fonts/droidsansfallback.z
Normal file
Binary file not shown.
@@ -753,7 +753,7 @@ class iTopExtensionsMap
|
||||
* @param string[] $aRemovedExtensions
|
||||
* @return string[] :
|
||||
*/
|
||||
public function GetSelectedExtensions(Config $oConfig, array $aAddedExtensions, array $aRemovedExtensions): array
|
||||
public function GetSelectedExtensions(Config $oConfig, array $aAddedExtensions = [], array $aRemovedExtensions = []): array
|
||||
{
|
||||
$aDbChoices = $this->GetChoicesFromDatabase($oConfig);
|
||||
foreach ($aDbChoices as $i => $sChoice) {
|
||||
|
||||
@@ -1670,20 +1670,7 @@ JS
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV));
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes);
|
||||
|
||||
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad);
|
||||
|
||||
$bRemoteExtensionsShouldBeMandatory = !$oWizard->GetParameter('force-uninstall', false);
|
||||
if ($bRemoteExtensionsShouldBeMandatory) {
|
||||
foreach ($aAvailableModules as $key => $aModule) {
|
||||
$bIsExtra = (array_key_exists('root_dir', $aModule) && (strpos($aModule['root_dir'], $sExtraDir) !== false)); // Some modules (root, datamodel) have no 'root_dir'
|
||||
if ($bIsExtra) {
|
||||
// Modules in data/production-modules/ are considered as mandatory and always installed
|
||||
$aAvailableModules[$key]['visible'] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aAvailableModules;
|
||||
return $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,6 +169,36 @@ class WizStepModulesChoice extends AbstractWizStepInstall
|
||||
return new WizardState(WizStepModulesChoice::class, (string)($index - 1));
|
||||
}
|
||||
|
||||
protected ?array $aSelectedModules = null;
|
||||
|
||||
public function GetAllSelectedModulesUntilNow(): array
|
||||
{
|
||||
if (!is_null($this->aSelectedModules)) {
|
||||
return $this->aSelectedModules;
|
||||
}
|
||||
|
||||
$this->aSelectedModules = [];
|
||||
$aSelectedChoices = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true);
|
||||
$iNextStep = $this->GetStepIndex();
|
||||
$index = $iNextStep;
|
||||
while (isset($aSelectedChoices[$iNextStep])) {
|
||||
//Let's empty the next steps, we only want what has been chosen before
|
||||
$aSelectedChoices[$iNextStep] = [];
|
||||
$iNextStep++;
|
||||
}
|
||||
|
||||
$aPreviousSteps = [];
|
||||
for ($i = 0; $i < $index; $i++) {
|
||||
|
||||
$aStepInfo = $this->GetStepInfo($i);
|
||||
$this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $this->aSelectedModules);
|
||||
|
||||
$aPreviousSteps = array_merge($aPreviousSteps, $this->aSelectedModules);
|
||||
}
|
||||
|
||||
return $this->aSelectedModules;
|
||||
}
|
||||
|
||||
public function GetWizardSteps(): array
|
||||
{
|
||||
$aSteps = [
|
||||
@@ -761,6 +791,22 @@ EOF
|
||||
return $this->aSteps[$index] ?? null;
|
||||
}
|
||||
|
||||
public function ExtensionIsAlreadyIncludedInPreviousChoices(?iTopExtension $oITopExtension): bool
|
||||
{
|
||||
|
||||
if (is_null($oITopExtension) || empty($oITopExtension->aModules)) {
|
||||
return false;
|
||||
}
|
||||
$aAllPreviousChoicesModules = $this->GetAllSelectedModulesUntilNow();
|
||||
|
||||
foreach ($oITopExtension->aModules as $sModuleId) {
|
||||
if (!isset($aAllPreviousChoicesModules[$sModuleId])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck): array
|
||||
{
|
||||
if (array_key_exists($sChoiceId, $this->aFlagsByChoiceId)) {
|
||||
@@ -777,6 +823,7 @@ EOF
|
||||
$bDependencyIssue = $oITopExtension?->HasDependencyIssue() ?? false;
|
||||
$bIsRemoteExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_REMOTE;
|
||||
$bIsPackageExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_WIZARD;
|
||||
$bAlreadyIncluded = $this->ExtensionIsAlreadyIncludedInPreviousChoices($oITopExtension);
|
||||
$bDoNotUninstall = !$bCanBeUninstalled || $bIsRemoteExtension;
|
||||
|
||||
$bChecked = $bSelected;
|
||||
@@ -788,6 +835,9 @@ EOF
|
||||
} elseif ($bMandatory && $bIsPackageExtension) {
|
||||
$bDisabled = true;
|
||||
$bChecked = true;
|
||||
} elseif ($bAlreadyIncluded) {
|
||||
$bDisabled = true;
|
||||
$bChecked = true;
|
||||
} else {
|
||||
if ($bDependencyIssue) {
|
||||
// If the extension has a dependency issue, it cannot be checked and must be unchecked using the "force-uninstall" option
|
||||
@@ -823,6 +873,7 @@ EOF
|
||||
|
||||
$aFlags = [
|
||||
'uninstallable' => $bCanBeUninstalled,
|
||||
'already_included' => $bAlreadyIncluded,
|
||||
'dependency_issue' => $bDependencyIssue,
|
||||
'mandatory' => $bMandatory,
|
||||
'missing' => $bMissingFromDisk,
|
||||
@@ -922,7 +973,7 @@ EOF
|
||||
<a class="setup--wizard-choice--more-info" target="_blank" href="'.$aChoice['more_info'].'">
|
||||
<i class="setup-extension--icon fas fa-external-link-alt" title="More information"></i>
|
||||
</a>' : '';
|
||||
$sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : '';
|
||||
$sDescription = isset($aChoice['description']) ? trim(utils::EscapeHtml($aChoice['description'])) : '';
|
||||
$sId = utils::EscapeHtml($aChoice['extension_code']);
|
||||
$sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"';
|
||||
$sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled"' : '';
|
||||
@@ -947,11 +998,15 @@ EOF
|
||||
if ($aFlags['dependency_issue']) {
|
||||
$sTooltip .= '<div id="badge--'.$sId.'--cannot-be-installed" class="ibo-badge ibo-block ibo-is-orange" title="This extension cannot be installed because one or more dependencies are not satisfied." >cannot be installed</div>';
|
||||
}
|
||||
if ($aFlags['already_included']) {
|
||||
$sTooltip .= '<div id="badge--'.$sId.'--already-part-of-itop" class="ibo-badge ibo-block ibo-is-yellow" title="All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder." >already part of iTop</div>';
|
||||
}
|
||||
|
||||
$sMetadata = '';
|
||||
if (isset($aChoice['version']) && isset($aChoice['source_label'])) {
|
||||
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span><span>'.$aChoice['extension_code'].'</span>';
|
||||
}
|
||||
|
||||
$sChoiceDisabled = $aFlags['disabled'] && !$aFlags['checked'] ? 'choice-disabled' : '';
|
||||
|
||||
$oPage->add('
|
||||
|
||||
@@ -18,6 +18,7 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
private const BADGE_ID_TO_BE_INSTALLED = 'to-be-installed';
|
||||
private const BADGE_ID_TO_BE_UNINSTALLED = 'to-be-uninstalled';
|
||||
private const BADGE_ID_NOT_UNINSTALLABLE = 'not-uninstallable';
|
||||
private const BADGE_ID_ALREADY_PART_OF_ITOP = 'already-part-of-itop';
|
||||
private const BADGE_ID_MISSING_FROM_DISK = 'missing-from-disk';
|
||||
private const BADGE_ID_CANNOT_BE_INSTALLED = 'cannot-be-installed';
|
||||
|
||||
@@ -27,10 +28,11 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
$bUninstallable = $aExtraFlags['uninstallable'] ?? true;
|
||||
$bMissingFromDisk = $aExtraFlags['missing'] ?? false;
|
||||
$bDependencyIssue = $aExtraFlags['dependency_issue'] ?? false;
|
||||
$bAlreadyPartOfITop = $aExtraFlags['already_part_of_itop'] ?? false;
|
||||
$bSelected = $aExtraFlags['selected'] ?? true;
|
||||
$bDisabled = $aExtraFlags['disabled'] ?? false;
|
||||
$bRemote = $aExtraFlags['remote'] ?? false;
|
||||
self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk, $bDependencyIssue, $sCode);
|
||||
self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk, $bDependencyIssue, $bAlreadyPartOfITop, $sCode);
|
||||
$oBadgeInstalled = BadgeUIBlockFactory::MakeGreen(
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled'),
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled+'),
|
||||
@@ -51,6 +53,10 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
if ($bMissingFromDisk || $bDependencyIssue) {
|
||||
$oExtensionDetails->GetToggler()->SetIsToggled(false);
|
||||
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
|
||||
} elseif ($bAlreadyPartOfITop) {
|
||||
$bSelected = true;
|
||||
$oExtensionDetails->GetToggler()->SetIsToggled(true);
|
||||
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
|
||||
} elseif ((!$bUninstallable || $bRemote) && !$bDisabled) {
|
||||
$oExtensionDetails->AllowForceUninstall();
|
||||
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
|
||||
@@ -72,9 +78,10 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
$aBadges = [];
|
||||
$bUninstallable = $aExtraFlags['uninstallable'] ?? true;
|
||||
$bDependencyIssue = $aExtraFlags['dependency_issue'] ?? false;
|
||||
$bAlreadyPartOfITop = $aExtraFlags['already_part_of_itop'] ?? false;
|
||||
$bSelected = $aExtraFlags['selected'] ?? false;
|
||||
$bDisabled = $aExtraFlags['disabled'] ?? false;
|
||||
self::AddExtraBadges($aBadges, $bUninstallable, false, $bDependencyIssue, $sCode);
|
||||
self::AddExtraBadges($aBadges, $bUninstallable, false, $bDependencyIssue, $bAlreadyPartOfITop, $sCode);
|
||||
$oBadgeInstalled = BadgeUIBlockFactory::MakeGrey(
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled'),
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled+'),
|
||||
@@ -94,6 +101,9 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
if ($bDependencyIssue) {
|
||||
$oExtensionDetails->GetToggler()->SetIsToggled(false);
|
||||
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
|
||||
} elseif ($bAlreadyPartOfITop) {
|
||||
$oExtensionDetails->GetToggler()->SetIsToggled(true);
|
||||
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
|
||||
}
|
||||
|
||||
if ($bSelected) {
|
||||
@@ -107,7 +117,7 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
return $oExtensionDetails;
|
||||
}
|
||||
|
||||
private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk, bool $bDependencyIssue, string $sCode)
|
||||
private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk, bool $bDependencyIssue, bool $bAlreadyPartOfITop, string $sCode)
|
||||
{
|
||||
if (!$bUninstallable) {
|
||||
$aBadges[] = BadgeUIBlockFactory::MakeYellow(
|
||||
@@ -130,6 +140,13 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
|
||||
self::GetBadgeId($sCode, self::BADGE_ID_CANNOT_BE_INSTALLED)
|
||||
);
|
||||
}
|
||||
if ($bAlreadyPartOfITop) {
|
||||
$aBadges[] = BadgeUIBlockFactory::MakeYellow(
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop'),
|
||||
Dict::S('UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+'),
|
||||
self::GetBadgeId($sCode, self::BADGE_ID_ALREADY_PART_OF_ITOP)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,6 +209,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
$this->LinkScriptFromAppRoot('js/pages/backoffice/keyboard-shortcuts.js');
|
||||
|
||||
// Used throughout the app.
|
||||
$this->LinkScriptFromAppRoot('js/pages/backoffice/dialog.js');
|
||||
$this->LinkScriptFromAppRoot('js/pages/backoffice/toolbox.js');
|
||||
$this->LinkScriptFromAppRoot('js/pages/backoffice/on-ready.js');
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\DotMake\Dependencies\Composer\Tcpdf;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @coversNothing
|
||||
*/
|
||||
class TcpdfUpdateFontsTest extends ItopTestCase
|
||||
{
|
||||
public function testThatDroidSansFallbackFilesAreCopiedToTcpdfFontsFolderAfterLibraryUpdate(): void
|
||||
{
|
||||
$sSourcePattern = APPROOT
|
||||
.'.make'.DIRECTORY_SEPARATOR.'dependencies'.DIRECTORY_SEPARATOR.'composer'.DIRECTORY_SEPARATOR.'tcpdf'.DIRECTORY_SEPARATOR.'droidsansfallback.*';
|
||||
$aSourceFiles = glob($sSourcePattern);
|
||||
$this->assertIsArray($aSourceFiles, 'Unable to read source TCPDF custom font files.');
|
||||
$this->assertNotEmpty($aSourceFiles, 'No source files found for pattern droidsansfallback.*');
|
||||
|
||||
foreach ($aSourceFiles as $sSourceFilePath) {
|
||||
$sFontFileName = basename($sSourceFilePath);
|
||||
$sDestinationFilePath = APPROOT
|
||||
.'lib'.DIRECTORY_SEPARATOR.'tecnickcom'.DIRECTORY_SEPARATOR.'tcpdf'.DIRECTORY_SEPARATOR.'fonts'.DIRECTORY_SEPARATOR.$sFontFileName;
|
||||
|
||||
$this->assertFileExists($sDestinationFilePath, "Missing copied font file: {$sFontFileName}");
|
||||
$this->assertSame(
|
||||
hash_file('sha256', $sSourceFilePath),
|
||||
hash_file('sha256', $sDestinationFilePath),
|
||||
"Copied font file content mismatch: {$sFontFileName}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval\Service;
|
||||
|
||||
use Combodo\iTop\DataFeatureRemoval\Controller\DataFeatureRemovalController;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use iTopExtension;
|
||||
|
||||
/**
|
||||
* @see DataFeatureController
|
||||
@@ -32,4 +33,49 @@ class DataFeatureControllerTest extends ItopDataTestCase
|
||||
$expected = '{"0":"itop-container-mgmt","1":"combodo-monitoring"}';
|
||||
$this->assertEquals($expected, $this->InvokeNonPublicMethod(DataFeatureRemovalController::class, 'ConvertIntoSetupFormat', $oController, [ $aExtensions]));
|
||||
}
|
||||
|
||||
public function testGetBasePackageModules()
|
||||
{
|
||||
$oController = new DataFeatureRemovalController();
|
||||
$aModules = $oController->GetBasePackageModules();
|
||||
$this->assertNotEmpty($aModules);
|
||||
$this->assertContains('itop-structure', $aModules);
|
||||
$this->assertContains('authent-local', $aModules);
|
||||
$this->assertContains('itop-backup', $aModules);
|
||||
$this->assertContains('itop-tickets', $aModules);
|
||||
|
||||
}
|
||||
|
||||
public function testIsIncludedInPackage()
|
||||
{
|
||||
$oController = $this->getMockBuilder(DataFeatureRemovalController::class)
|
||||
->onlyMethods(['GetBasePackageModules'])
|
||||
->getMock();
|
||||
$oController->method('GetBasePackageModules')->willReturn([
|
||||
'itop-structure',
|
||||
'authent-local',
|
||||
'itop-backup',
|
||||
'itop-tickets',
|
||||
]);
|
||||
|
||||
$oIncludedExtension = new iTopExtension();
|
||||
$oIncludedExtension->sSource = iTopExtension::SOURCE_MANUAL;
|
||||
$oIncludedExtension->aModules = ['itop-structure', 'authent-local'];
|
||||
$this->assertTrue($oController->IsIncludedInPackage($oIncludedExtension), 'IsIncludedInPackage should return true if all modules are included in the base package');
|
||||
|
||||
$oNotIncludedExtension = new iTopExtension();
|
||||
$oNotIncludedExtension->sSource = iTopExtension::SOURCE_MANUAL;
|
||||
$oNotIncludedExtension->aModules = ['itop-structure', 'combodo-non-existing-module'];
|
||||
$this->assertFalse($oController->IsIncludedInPackage($oNotIncludedExtension), 'IsIncludedInPackage should return false if at least one of its modules is not included in the base package');
|
||||
|
||||
$oEmptyExtension = new iTopExtension();
|
||||
$oEmptyExtension->sSource = iTopExtension::SOURCE_MANUAL;
|
||||
$oEmptyExtension->aModules = [];
|
||||
$this->assertFalse($oController->IsIncludedInPackage($oEmptyExtension), 'IsIncludedInPackage should return false if the extension has no modules');
|
||||
|
||||
$oPackageExtension = new iTopExtension();
|
||||
$oPackageExtension->sSource = iTopExtension::SOURCE_WIZARD;
|
||||
$oPackageExtension->aModules = ['itop-structure', 'authent-local'];
|
||||
$this->assertFalse($oController->IsIncludedInPackage($oPackageExtension), 'IsIncludedInPackage should return false if the extension is a package extension');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup;
|
||||
|
||||
use AnalyzeInstallation;
|
||||
use CheckResult;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\ModelReflectionSerializer;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use Config;
|
||||
use ModuleDiscovery;
|
||||
use WizardController;
|
||||
use SetupUtils;
|
||||
|
||||
/**
|
||||
@@ -28,6 +32,13 @@ class SetupUtilsTest extends ItopTestCase
|
||||
|
||||
$this->RequireOnceItopFile('setup/setuputils.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/setuppage.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/wizardcontroller.class.inc.php');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
ModuleDiscovery::ResetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,6 +230,30 @@ OUTPUT;
|
||||
$this->ValidateCheckResults($expected, $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug N°10045
|
||||
*/
|
||||
public function testAnalyzeInstallationDoesNotAutomaticallySetModulesInProductionModulesVisibilityToFalse()
|
||||
{
|
||||
$this->RequireOnceItopFile('setup/moduleinstallation/AnalyzeInstallation.php');
|
||||
$this->SetNonPublicProperty(AnalyzeInstallation::GetInstance(), 'aAvailableModules', null);
|
||||
|
||||
$sRemoteEnv = 'production-temp';
|
||||
$sExtraDir = \utils::GetDataPath().$sRemoteEnv.'-modules/';
|
||||
$sExtraModuleName = 'extra-module';
|
||||
|
||||
$this->createModule($sExtraDir, $sExtraModuleName, '1.0.0');
|
||||
|
||||
$oWizard = new WizardController('WizStepWelcome');
|
||||
$oWizard->SetParameter('source_dir', APPROOT.'datamodels/2.x');
|
||||
$oWizard->SetParameter('remote_env', $sRemoteEnv);
|
||||
|
||||
$aModules = SetupUtils::AnalyzeInstallation($oWizard);
|
||||
|
||||
$this->assertContains($sExtraModuleName, array_keys($aModules), 'Module discovery should have found the extra module');
|
||||
$this->assertTrue($aModules[$sExtraModuleName]['visible'], 'AnalyzeInstallation should not have automatically set the extra module visibility to false');
|
||||
}
|
||||
|
||||
private function ValidateCheckResults(array $expected, array $aActualCheckResults)
|
||||
{
|
||||
$aActual = [];
|
||||
@@ -232,4 +267,37 @@ OUTPUT;
|
||||
self::assertEquals($expected, $aActual);
|
||||
}
|
||||
|
||||
protected function createModule($sDirectory, $sModuleName, $sModuleVersion, $bMandatory = false, $bVisible = true)
|
||||
{
|
||||
$sModuleDir = $sDirectory.'/'.$sModuleName;
|
||||
SetupUtils::builddir($sModuleDir);
|
||||
$this->aFileToClean[] = $sDirectory;
|
||||
|
||||
$sModuleFileName = $sModuleDir.'/module.'.$sModuleName.'.php';
|
||||
|
||||
$sMandatory = var_export($bMandatory, true);
|
||||
$sVisible = var_export($bVisible, true);
|
||||
file_put_contents(
|
||||
$sModuleFileName,
|
||||
<<<PHP
|
||||
<?php
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__,
|
||||
"$sModuleName/$sModuleVersion",
|
||||
[
|
||||
'label' => "$sModuleName",
|
||||
'dependencies' => [],
|
||||
'mandatory' => $sMandatory,
|
||||
'visible' => $sVisible,
|
||||
'datamodel' => [],
|
||||
'data.struct' => [],
|
||||
'data.sample' => [],
|
||||
'doc.manual_setup' => '',
|
||||
'doc.more_information' => '',
|
||||
]
|
||||
);
|
||||
PHP
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node1 - A missing extension should always be disabled and unchecked, even a mandatory extension included in package' => [
|
||||
@@ -81,6 +82,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => true,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node2 - A mandatory extension included in package should be checked and disabled even if the "disable uninstallation check" flag is set' => [
|
||||
@@ -106,6 +108,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => true,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node3 - An installed non uninstallable sub extension should force its parent to be checked and disabled' => [
|
||||
@@ -140,6 +143,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node3 - An installed remote sub extension should force its parent to be checked and disabled' => [
|
||||
@@ -175,6 +179,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node4 - An installed uninstallable sub extension should force its parent to be checked but not disabled' => [
|
||||
@@ -209,6 +214,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node4 - An installed non uninstallable sub extension should force its parent to be checked but not disabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -243,6 +249,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node4 - An installed remote sub extension should force its parent to be checked but not disabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -278,6 +285,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node5 - A non installed extension with missing dependencies should be not checked and disabled' => [
|
||||
@@ -305,6 +313,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node5 - A non installed extension with missing dependencies should be not checked and disabled even with force uninstall' => [
|
||||
@@ -332,6 +341,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node6 - An installed extension with missing dependencies and without force uninstall should be checked and disabled' => [
|
||||
@@ -359,6 +369,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node7 - An installed extension with missing dependencies and with force uninstall should be checked and enabled' => [
|
||||
@@ -386,6 +397,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node8 - An installed but not selected extension with missing dependencies and with force uninstall should be unchecked and enabled' => [
|
||||
@@ -413,6 +425,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node9 - A not selected, not installed extension should not be checked and be enabled' => [
|
||||
@@ -436,6 +449,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node10 - A selected but not installed extension should be checked and enabled' => [
|
||||
@@ -459,6 +473,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node11 - An installed but not selected extension should not be checked and be enabled' => [
|
||||
@@ -482,6 +497,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node12 - An installed and selected extension should be checked and enabled' => [
|
||||
@@ -506,6 +522,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node13 - An installed non uninstallable extension should be checked and disabled' => [
|
||||
@@ -529,6 +546,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node13 - An installed remote extension should be checked and disabled' => [
|
||||
@@ -553,6 +571,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => true,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node14 - An installed but not selected non uninstallable extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -576,6 +595,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node14 - An installed but not selected remote extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -600,6 +620,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => true,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node15 - An installed non uninstallable extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -623,6 +644,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node15 - An installed remote extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
|
||||
@@ -647,9 +669,10 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => true,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node16 - A non installed non uninstallable sub extension should not force its parent flags' => [
|
||||
'#node16 - A non installed sub extension should not force its parent flags' => [
|
||||
'aExtensionsOnDiskOrDb' => [
|
||||
'itop-ext1' => [
|
||||
'installed' => true,
|
||||
@@ -681,6 +704,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node16 - A non installed remote sub extension should not force its parent flags' => [
|
||||
@@ -716,6 +740,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node13 - An installed non uninstallable extension should be checked and disabled when the "disable uninstallation check" flag is not set, even if it is not selected' => [
|
||||
@@ -739,6 +764,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
'#node6 - An installed extension with missing dependencies without force uninstall should be checked and disabled, even if it is not selected' => [
|
||||
@@ -766,6 +792,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
'dependency_issue' => true,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -781,6 +808,52 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
$this->assertEquals($aExpectedFlags, $aFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* #node17 an extension with all of its modules already selected in previous steps should be checked and disabled, with the "already_included" flag set to true
|
||||
*/
|
||||
public function testComputeChoiceFlagsShouldReturnAlreadyIncludedWhenAllModulesAreInPreviousSteps(): void
|
||||
{
|
||||
$this->oWizard->SetParameter('source_dir', __DIR__.'/ressources');
|
||||
$this->oWizard->SetParameter('selected_components', json_encode([
|
||||
['_0' => '_0'],//Selecting itop-config-mgmt-core
|
||||
]));
|
||||
|
||||
$oWizStepModulesChoice = new WizStepModulesChoiceFake($this->oWizard, '1');
|
||||
$oWizStepModulesChoice->setExtensionMap(iTopExtensionsMapFake::createFromArray([
|
||||
'itop-ext1' => [
|
||||
'installed' => false,
|
||||
'modules' => [
|
||||
'itop-config-mgmt',
|
||||
'itop-attachments',
|
||||
],
|
||||
],
|
||||
]));
|
||||
|
||||
$aFlags = $oWizStepModulesChoice->ComputeChoiceFlags(
|
||||
[
|
||||
'extension_code' => 'itop-ext1',
|
||||
'uninstallable' => true,
|
||||
],
|
||||
'_0',
|
||||
[],
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
$aExpectedFlags = [
|
||||
'uninstallable' => true,
|
||||
'missing' => false,
|
||||
'installed' => false,
|
||||
'disabled' => true,
|
||||
'checked' => true,
|
||||
'dependency_issue' => false,
|
||||
'mandatory' => false,
|
||||
'remote' => false,
|
||||
'already_included' => true,
|
||||
];
|
||||
$this->assertEquals($aExpectedFlags, $aFlags);
|
||||
}
|
||||
|
||||
public function ProviderCanMoveForwardFromChoiceFlags(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -4,30 +4,30 @@ Removed from disk
|
||||
Yes : Forced uninstallation, unchecked & disabled .color_red #node1
|
||||
No : Mandatory & part of package
|
||||
Yes : Forced installation, checked & disabled .color_orange #node2
|
||||
No : Has sub options
|
||||
Yes : Sub option checked
|
||||
No : Modules included in package
|
||||
Yes : Forced installation, checked & disabled .color_orange #node17
|
||||
No : Has sub options & Sub option checked
|
||||
Yes : Sub option disabled
|
||||
Yes : checked & disabled .color_orange #node3
|
||||
No : force checked .color_yellow #node4
|
||||
#node16 No : (Dependency issue)
|
||||
No : Dependency issue
|
||||
Yes : Installed
|
||||
No : unchecked & disabled .color_orange #node5
|
||||
Yes : Force uninstall
|
||||
No : checked & disabled .color_orange #node6
|
||||
Yes : Selected
|
||||
Yes : checked & enabled .color_green #node7
|
||||
No : unchecked & enabled .color_green #node8
|
||||
No : Installed
|
||||
No : Selected
|
||||
No : unchecked & enabled .color_green #node9
|
||||
Yes : checked & enabled .color_green #node10
|
||||
Yes : Remote | cannot_be_uninstalled
|
||||
No : Selected
|
||||
No : unchecked & enabled .color_green #node11
|
||||
Yes : checked & enabled .color_green #node12
|
||||
No : checked & enabled .color_green #node4
|
||||
No : Dependency issue #node16
|
||||
Yes : Installed
|
||||
No : unchecked & disabled .color_orange #node5
|
||||
Yes : Force uninstall
|
||||
No : checked & disabled .color_orange #node13
|
||||
No : checked & disabled .color_orange #node6
|
||||
Yes : Selected
|
||||
No : unchecked & enabled .color_green #node14
|
||||
Yes : checked & enabled .color_green #node15
|
||||
Yes : checked & enabled .color_green #node7
|
||||
No : unchecked & enabled .color_green #node8
|
||||
No : Installed
|
||||
No : Selected
|
||||
No : unchecked & enabled .color_green #node9
|
||||
Yes : checked & enabled .color_green #node10
|
||||
Yes : Remote | cannot_be_uninstalled
|
||||
No : Selected
|
||||
No : unchecked & enabled .color_green #node11
|
||||
Yes : checked & enabled .color_green #node12
|
||||
Yes : Force uninstall
|
||||
No : checked & disabled .color_orange #node13
|
||||
Yes : Selected
|
||||
No : unchecked & enabled .color_green #node14
|
||||
Yes : checked & enabled .color_green #node15
|
||||
|
||||
Reference in New Issue
Block a user