Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion css/backoffice/blocks-integrations/_all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
@import "field-badge-within-datatable";
@import "jquery-blockui-within-dialog";
@import "jquery-blockui-within-datatable";
@import "badge-with-badge";
@import "badge-with-badge";
@import "extension-details-with-extension-details";
2 changes: 1 addition & 1 deletion css/backoffice/blocks-integrations/_badge-with-badge.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
15 changes: 13 additions & 2 deletions css/backoffice/layout/extension/_extension-details.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,21 @@ $ibo-extension-details--actions--button--padding-x: $ibo-button--padding-x !defa
padding-right: $ibo-extension-details--information--metadata--padding;
}

.ibo-extension-details:has(input:checked) .ibo-badge.unchecked, .ibo-extension-details:has(input:not(:checked)) .ibo-badge.checked {
display: none;
//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;
}
Expand Down
4 changes: 2 additions & 2 deletions css/setup.css

Large diffs are not rendered by default.

48 changes: 34 additions & 14 deletions css/setup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -316,29 +316,34 @@ fieldset {
background-color: #F7FAFC;
padding: 10px;
.wiz-choice{
&:checked ~ .description {
#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{
&:not(:checked) ~ label .checked{
display:none;
}
&:checked ~ label .setup-extension-tag.unchecked{
&:checked ~ label .unchecked{
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 {
font-size: 1.17rem;
Expand Down Expand Up @@ -633,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 {
margin-left: 0.5rem;
}
Expand Down
2 changes: 1 addition & 1 deletion datamodels/2.x/installation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
<choice>
<extension_code>itop-problem-mgmt</extension_code>
<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">
<module>itop-problem-mgmt</module>
</modules>
Expand Down
13 changes: 3 additions & 10 deletions setup/extensionsmap.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ public function GetAllExtensionsOptionInfo(bool $bRemoteExtensionsShouldBeMandat
'source_label' => $this->GetExtensionSourceLabel($oExtension->sSource),
'uninstallable' => $oExtension->CanBeUninstalled(),
'missing' => $oExtension->bRemovedFromDisk,
'version' => $oExtension->sVersion,
];
}

Expand All @@ -416,26 +417,18 @@ public function GetAllExtensionsOptionInfo(bool $bRemoteExtensionsShouldBeMandat

protected function GetExtensionSourceLabel($sSource)
{
$sDecorationClass = '';
$sResult = '';
switch ($sSource) {
case iTopExtension::SOURCE_MANUAL:
$sResult = 'Local extensions folder';
$sDecorationClass = 'fas fa-folder';
break;

case iTopExtension::SOURCE_REMOTE:
$sResult = (ITOP_APPLICATION == 'iTop') ? 'iTop Hub' : 'ITSM Designer';
$sDecorationClass = (ITOP_APPLICATION == 'iTop') ? 'fc fc-chameleon-icon' : 'fa pencil-ruler';
break;

default:
$sResult = '';
}
if ($sResult == '') {
return '';
}

return '<i class="setup-extension--icon '.$sDecorationClass.'" data-tooltip-content="'.$sResult.'"></i>';
return $sResult;
}

/**
Expand Down
139 changes: 77 additions & 62 deletions setup/wizardsteps/WizStepModulesChoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ protected function DisplayStep($oPage)
$oPage->add_style("div.choice { margin: 0.5em;}");
$oPage->add_style("div.choice a { text-decoration:none; font-weight: bold; color: #1C94C4 }");
$oPage->add_style("div.description { margin-left: 2em; }");
$oPage->add_style(".choice-disabled { color: #999; }");
$oPage->add_style("input.unremovable { accent-color: orangered;}");

$sManualInstallError = SetupUtils::CheckManualInstallDirEmpty(
Expand Down Expand Up @@ -638,7 +637,6 @@ protected function GetStepInfo($idx = null)
if ($index + 1 >= count($this->aSteps)) {
//make sure we also cache next step as well
$aOptions = $this->oExtensionsMap->GetAllExtensionsOptionInfo($bRemoteExtensionsShouldBeMandatory);

// Display this step of the wizard only if there is something to display
if (count($aOptions) > 0) {
$this->aSteps[] = [
Expand Down Expand Up @@ -672,7 +670,7 @@ public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSe
$oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']);
//If the extension is missing from disk, it won't exist in the ExtensionsMap, thus returning null
$bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] === true || $aChoice['uninstallable'] === 'yes' : $oITopExtension->CanBeUninstalled();
$bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId);
$bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] === $sChoiceId);
$bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true;
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']);
$bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled;
Expand Down Expand Up @@ -719,7 +717,7 @@ public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSe
];
}

protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false)
public function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false)
{
$aOptions = $aStepInfo['options'] ?? [];
$aAlternatives = $aStepInfo['alternatives'] ?? [];
Expand All @@ -728,107 +726,124 @@ protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDe

foreach ($aOptions as $index => $aChoice) {
$sChoiceId = $sParentId.self::$SEP.$index;
$sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"';
$sId = utils::EscapeHtml($aChoice['extension_code']);
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade);

$sTooltip = '';
$sUnremovable = '';
if ($aFlags['missing']) {
$sTooltip .= '<div class="setup-extension-tag removed">source removed</div>';
}
if ($aFlags['installed']) {
$sTooltip .= '<div class="setup-extension-tag checked installed">installed</div>';
$sTooltip .= '<div class="setup-extension-tag unchecked tobeuninstalled">to be uninstalled</div>';
} else {
$sTooltip .= '<div class="setup-extension-tag checked tobeinstalled">to be installed</div>';
$sTooltip .= '<div class="setup-extension-tag unchecked notinstalled">not installed</div>';
}
if (!$aFlags['uninstallable']) {
$sTooltip .= '<div class="setup-extension-tag notuninstallable">cannot be uninstalled</div>';
}
if ($aFlags['disabled'] && !$aFlags['checked'] && !$aFlags['uninstallable'] && !$bDisableUninstallCheck) {
$this->bCanMoveForward = false;//Disable "Next"
}
$sChecked = $aFlags['checked'] ? ' checked ' : '';
$sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled" ' : '';
$sMissingModule = $aFlags['missing'] ? 'setup-extension--missing' : '';

$sHiddenInput = $aFlags['disabled'] && $aFlags['checked'] ? '<input type="hidden" name="choice['.$sChoiceId.']" value="'.$sChoiceId.'"/>' : '';
$oPage->add('<div class="choice '.$sMissingModule.'" '.$sDataId.'><input class="wiz-choice '.$sUnremovable.'" id="'.$sId.'" name="choice['.$sChoiceId.']" type="checkbox" value="'.$sChoiceId.'" '.$sDisabled.$sChecked.'/>'.$sHiddenInput.'&nbsp;');
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $aFlags['disabled'], $sTooltip);
$oPage->add('</div>');
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $sChoiceId, $aFlags);
}

$sChoiceName = null;
$sDisabled = '';
$bDisabled = false;
$sChoiceIdNone = null;
foreach ($aAlternatives as $index => $aChoice) {
$sChoiceId = $sParentId.self::$SEP.$index;
if ($sChoiceName == null) {
$sChoiceName = $sChoiceId; // All radios share the same name
}
$bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId);
//Defaults contains previous installation choices during upgrade
$bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] === $sChoiceId);
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault);
if ($bMandatory || $bAllDisabled) {
// One choice is mandatory, all alternatives are disabled
$sDisabled = ' disabled data-disabled="disabled"';
$bDisabled = true;
}
if ((!isset($aChoice['sub_options']) || (count($aChoice['sub_options']) == 0)) && (!isset($aChoice['modules']) || (count($aChoice['modules']) == 0))) {
if ((!isset($aChoice['sub_options']) || (count($aChoice['sub_options']) === 0)) && (!isset($aChoice['modules']) || (count($aChoice['modules']) === 0))) {
//If there is no modules in the choice AND it has no sub choices, it is an empty choice.
$sChoiceIdNone = $sChoiceId; // the "None" / empty choice
}
}

if (!array_key_exists($sChoiceName, $aDefaults) || ($aDefaults[$sChoiceName] == $sChoiceIdNone)) {
if (!array_key_exists($sChoiceName, $aDefaults) || ($aDefaults[$sChoiceName] === $sChoiceIdNone)) {
// The "none" choice does not disable the selection !!
$sDisabled = '';
$bDisabled = false;
}

foreach ($aAlternatives as $index => $aChoice) {
$sAttributes = '';
$sChoiceId = $sParentId.self::$SEP.$index;
$sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"';
$sId = utils::EscapeHtml($aChoice['extension_code']);
if ($sChoiceName == null) {
if ($sChoiceName === null) {
$sChoiceName = $sChoiceId; // All radios share the same name
}
$bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId);
$bSelected = isset($aSelectedComponents[$sChoiceName]) && ($aSelectedComponents[$sChoiceName] == $sChoiceId);
if (!isset($aSelectedComponents[$sChoiceName]) && ($sChoiceIdNone != null)) {
$bSelected = isset($aSelectedComponents[$sChoiceName]) && ($aSelectedComponents[$sChoiceName] === $sChoiceId);
if (!isset($aSelectedComponents[$sChoiceName]) && ($sChoiceIdNone !== null)) {
// No choice selected, select the "None" option
$bSelected = ($sChoiceId == $sChoiceIdNone);
$bSelected = ($sChoiceId === $sChoiceIdNone);
}
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault);

if ($bSelected) {
$sAttributes = ' checked ';
}
$sHidden = '';
if ($bMandatory && $bDisabled) {
$sAttributes = ' checked ';
$sHidden = '<input type="hidden" name="choice['.$sChoiceName.']" value="'.$sChoiceId.'"/>';
}
$oPage->add('<div class="choice" '.$sDataId.'><input class="wiz-choice" id="'.$sId.'" name="choice['.$sChoiceName.']" type="radio"'.$sAttributes.' value="'.$sChoiceId.'"'.$sDisabled.'/>'.$sHidden.'&nbsp;');
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled && !$bSelected);
$oPage->add('</div>');
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade);
//ComputeChoiceFlags does not completely compute alternative flags
$aFlags['disabled'] = $bDisabled;
$aFlags['checked'] = $bSelected;
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceName, $sChoiceId, $aFlags, 'radio');
}
}

protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled = false, $sTooltip = '')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method could be tests by providing a mock and having different display usecases... that would help next developers that will have to enhance this rendering

protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceName, $sChoiceId, $aFlags, $sInputType = 'checkbox')
{
$sMoreInfo = (isset($aChoice['more_info']) && ($aChoice['more_info'] != '')) ? '<a class="setup--wizard-choice--more-info" target="_blank" href="'.$aChoice['more_info'].'">More information</a>' : '';
$sSourceLabel = $aChoice['source_label'] ?? '';
$sMoreInfo = (isset($aChoice['more_info']) && ($aChoice['more_info'] != '')) ? '
<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']) : '';
$sId = utils::EscapeHtml($aChoice['extension_code']);
$sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"';
$sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled"' : '';
$sChecked = $aFlags['checked'] ? ' checked ' : '';
$sHiddenInput = $aFlags['disabled'] && $aFlags['checked'] ? '<input type="hidden" name="choice['.$sChoiceName.']" value="'.$sChoiceId.'"/>' : '';

$oPage->add('<label class="setup--wizard-choice--label" for="'.$sId.'">'.$sSourceLabel.'<b>'.utils::EscapeHtml($aChoice['title']).'</b>'.'&nbsp;'.$sTooltip.'</label> '.$sMoreInfo.'');
$sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : '';
$oPage->add('<div class="setup--wizard-choice--description description">'.$sDescription.'<span id="sub_choices'.$sId.'">');
$sTooltip = '';
if ($aFlags['missing']) {
$sTooltip .= '<span class="ibo-badge ibo-block ibo-is-red" title="The local extension folder has been removed from the disk. This will force the uninstallation of this extension." >source removed</span>';
}
if ($aFlags['installed']) {
$sTooltip .= '<span class="ibo-badge ibo-block checked ibo-is-green" title="This extension is part of the current installation." >installed</span>';

$sTooltip .= '<span class="ibo-badge ibo-block unchecked ibo-is-red" title="This extension will be uninstalled during the setup." >to be uninstalled</span>';
} else {
$sTooltip .= '<span class="ibo-badge ibo-block checked ibo-is-cyan" title="This extension will be installed during the setup." >to be installed</span>';
$sTooltip .= '<span class="ibo-badge ibo-block unchecked ibo-is-blue-grey" title="This extension is not part of the current installation." >not installed</span>';
}
if (!$aFlags['uninstallable']) {
$sTooltip .= '<span class="ibo-badge ibo-block ibo-is-orange" title="Once this extension has been installed, it should not be uninstalled." >cannot be uninstalled</span>';
}

$sMetadata = '';
if (isset($aChoice['version']) && isset($aChoice['source_label'])) {
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span>';
}
$sChoiceDisabled = $aFlags['disabled'] && !$aFlags['checked'] ? 'choice-disabled' : '';

$oPage->add('
<div class="ibo-extension-details ibo-content-block ibo-block '.$sChoiceDisabled.'" '.$sDataId.'>
<div class="ibo-extension-details--actions">
<input class="wiz-choice" id="'.$sId.'" name="choice['.$sChoiceName.']" type="'.$sInputType.'" value="'.$sChoiceId.'" '.$sDisabled.$sChecked.'/>
'.$sHiddenInput.'
</div>
<div class="ibo-extension-details--information">
<div class="ibo-extension-details--information--label">
<label for="'.$sId.'"><b>'.utils::EscapeHtml($aChoice['title']).'</b></label>
'.$sMoreInfo.'
'.$sTooltip.'
</div>
<div class="ibo-extension-details--information--metadata">
'.$sMetadata.'
</div>
<div class="ibo-extension-details--information--description">
'.$sDescription.'
');
$bSubOptionsDisabled = $aFlags['disabled'] && (!$aFlags['installed'] || $sInputType === 'checkbox');
if (isset($aChoice['sub_options'])) {
$this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled);
$oPage->add('<div id="sub_choices'.$sId.'">');
$this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $aDefaults, $sChoiceId, $bSubOptionsDisabled);
$oPage->add('</div>');
}
$oPage->add('</span></div>');
$oPage->add('
</div>
</div>
</div>
');
}

protected function GetSourceFilePath()
Expand Down
Loading