Compare commits

...

20 Commits

Author SHA1 Message Date
Eric Espie
9fe705c70d N°9950 - Error loading structural data on upgrade 2026-08-21 10:15:50 +02:00
Eric Espie
3987785690 N°9950 - Error loading structural data on upgrade 2026-08-21 10:12:31 +02:00
Eric Espié
b8f02f48bd Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-21 10:00:20 +02:00
Eric Espié
ba426a9602 Update application/loginbasic.class.inc.php
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-08-21 09:59:55 +02:00
Eric Espie
285f64f8db N°9954 - Login-basic incompatible with OAuth2 server 2026-08-20 15:48:40 +02:00
Eric Espie
be5a5b0da1 N°9954 - Login-basic incompatible with OAuth2 server 2026-08-20 15:07:06 +02:00
odain
7583ede3b3 N°9949 - code style 2026-08-20 11:59:56 +02:00
odain-cbd
95cb21cff7 N°9949 - Cannot uninstall IPAM from setup wizard screen (#1015)
* N°9949 - Cannot uninstall IPAM from setup wizard screen

* N°9949 - add test coverage

* N°9949 - handle list in compilation
2026-08-20 10:03:25 +02:00
Molkobain
cdd7bbedcb N°9625 - Update welcome popup introduction message and add message for new portal look 2026-08-20 09:39:51 +02:00
Molkobain
ea1ed03d0c N°9948 - Fix core dashlets after AttributeDefinition split (#1014) 2026-08-20 08:53:09 +02:00
Lenaick
2073e8c10c N°9861 - Check user can go to audit step only when keep current choice button has been clicked (#1013)
* N°9861 - Check user can go to audit step only when keep current choice button has been clicked

* Rename variable to improve clarity
2026-08-19 16:03:25 +02:00
odain
1a41349d7b N°8806 - fix setup tests 2026-08-19 15:54:39 +02:00
odain
fdc1ef40e6 N°9875 - error msg typo 2026-08-19 15:27:49 +02:00
odain-cbd
4722551a16 N°9875 - Error deprecated on component version object (#1010)
* N°9875 - Error deprecated on component version object - propose boilerplate methods to handle ZipArchive

* N°9875 - Error deprecated on component version object - use ZipArchive boilerplate methods

* N°9875 - code style

* N°9875 - review feedbacks

* N°9875 - simplify return type of ZipArchiveCreateWithTempNam

* N°9875 - add @since
2026-08-19 15:14:32 +02:00
odain
a2934ef05b N°8806 - Installation multiple extension with dependence via ITSM Designer - fix check compatibity when new dictionaries not present yet in iTop core like SV-SV 2026-08-19 15:13:30 +02:00
Anne-Catherine
1cdc2c5648 N°9934 - OQL with magic field (#1011) 2026-08-19 11:46:28 +02:00
Lenaick
31eddad029 N°9876 - A non installed extension should not be selected (#992)
* N°9876 - A non installed extension should not be selected if it is not mandatory

* N°9876 - A non installed extension should not be selected if it is not mandatory

* N°9876 - A non installed extension should not be selected if it is not mandatory

* N°9876 Streamline and refactor ComputeChoiceFlags

* N°9876 - Fix several conditions :
* Installed extension with dependency issue must be checked and disabled without force-uninstall
* Mandatory extension with force-uninstall must be checked
* Mandatory or installed extension must keep unchecked status when move to another step

* N°9876 - remove mandatory condition for extensions not included in package

* N°9876 - Undo remove ComputeChoiceFlags method return type

---------

Co-authored-by: Timmy38 <timothee.callet@combodo.com>
2026-08-19 10:47:31 +02:00
Molkobain
b43d71f324 N°824 - Fix typo in notification filling 2026-08-19 09:54:23 +02:00
Anne-Catherine
26033774b8 N°9622 - Error in the user story life cycle (#915) 2026-08-19 08:22:35 +02:00
Lenaick
27c0fe191b N°9904 - Add explanation message when "Check compatibility" button is not available (#1003) 2026-08-18 16:40:18 +02:00
47 changed files with 1315 additions and 693 deletions

View File

@@ -3066,8 +3066,12 @@ EOF
}
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
if (is_array($aAllowedValues) && count($aAllowedValues) == 1) {
$aValues = array_keys($aAllowedValues);
$this->Set($sAttCode, $aValues[0]);
// linkset are ignored
// N°9622 - Error in the user story life cycle
if (!$oAttDef->IsLinkSet()) {
$aValues = array_keys($aAllowedValues);
$this->Set($sAttCode, $aValues[0]);
}
}
}
}

View File

@@ -25,6 +25,20 @@ use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\UIBlock;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Core\AttributeDefinition\AttributeDateTime;
use Combodo\iTop\Core\AttributeDefinition\AttributeDecimal;
use Combodo\iTop\Core\AttributeDefinition\AttributeDuration;
use Combodo\iTop\Core\AttributeDefinition\AttributeEncryptedString;
use Combodo\iTop\Core\AttributeDefinition\AttributeEnum;
use Combodo\iTop\Core\AttributeDefinition\AttributeExternalField;
use Combodo\iTop\Core\AttributeDefinition\AttributeFriendlyName;
use Combodo\iTop\Core\AttributeDefinition\AttributeInteger;
use Combodo\iTop\Core\AttributeDefinition\AttributeLinkedSet;
use Combodo\iTop\Core\AttributeDefinition\AttributeOneWayPassword;
use Combodo\iTop\Core\AttributeDefinition\AttributePassword;
use Combodo\iTop\Core\AttributeDefinition\AttributePercentage;
use Combodo\iTop\Core\AttributeDefinition\AttributeSubItem;
use Combodo\iTop\Core\AttributeDefinition\iAttributeNoGroupBy;
require_once(APPROOT.'application/forms.class.inc.php');
@@ -440,7 +454,7 @@ EOF
// For external fields, find the real type of the target
$sExtFieldAttCode = $sAttCode;
$sTargetClass = $sClass;
while (is_a($sAttType, 'AttributeExternalField', true)) {
while (is_a($sAttType, AttributeExternalField::class, true)) {
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'extkey_attcode');
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'target_attcode');
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtKeyAttCode, 'targetclass');
@@ -450,13 +464,13 @@ EOF
}
$aForbidenAttType = [
'AttributeLinkedSet',
'AttributeFriendlyName',
AttributeLinkedSet::class,
AttributeFriendlyName::class,
'iAttributeNoGroupBy', //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
'AttributeOneWayPassword',
'AttributeEncryptedString',
'AttributePassword',
iAttributeNoGroupBy::class, //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
AttributeOneWayPassword::class,
AttributeEncryptedString::class,
AttributePassword::class,
];
foreach ($aForbidenAttType as $sForbidenAttType) {
if (is_a($sAttType, $sForbidenAttType, true)) {
@@ -468,7 +482,7 @@ EOF
if (!in_array($sLabel, $aGroupBy)) {
$aGroupBy[$sAttCode] = $sLabel;
if (is_a($sAttType, 'AttributeDateTime', true)) {
if (is_a($sAttType, AttributeDateTime::class, true)) {
$aGroupBy[$sAttCode.':hour'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel);
$aGroupBy[$sAttCode.':month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month', $sLabel);
$aGroupBy[$sAttCode.':day_of_week'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek', $sLabel);
@@ -1102,7 +1116,7 @@ abstract class DashletGroupBy extends Dashlet
$aAttributeTypes = $this->oModelReflection->ListAttributes($this->sClass);
if (isset($aAttributeTypes[$this->sGroupByAttCode])) {
$sAttributeType = $aAttributeTypes[$this->sGroupByAttCode];
if (is_subclass_of($sAttributeType, 'AttributeDateTime') || $sAttributeType == 'AttributeDateTime') {
if (is_subclass_of($sAttributeType, AttributeDateTime::class) || $sAttributeType == AttributeDateTime::class) {
$this->sOrderDirection = 'asc';
} else {
$this->sOrderDirection = 'desc';
@@ -1258,7 +1272,7 @@ abstract class DashletGroupBy extends Dashlet
if ($this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) {
$aAttributeTypes = $this->oModelReflection->ListAttributes($sClass);
$sAttributeType = $aAttributeTypes[$this->sGroupByAttCode];
if (is_subclass_of($sAttributeType, 'AttributeDateTime') || $sAttributeType == 'AttributeDateTime') {
if (is_subclass_of($sAttributeType, AttributeDateTime::class) || $sAttributeType == AttributeDateTime::class) {
// Note: an alternative to this somewhat hardcoded way of doing things would be to implement...
//$oExpr = Expression::FromOQL($this->sGroupByExpr);
//$aTranslationData = array($oQuery->GetClassAlias() => array($this->sGroupByAttCode => new ScalarExpression(date('Y-m-d H:i:s', $iTime))));
@@ -1289,7 +1303,7 @@ abstract class DashletGroupBy extends Dashlet
foreach ($aValues as $sValue) {
$aDisplayValues[] = ['label' => $sValue, 'value' => (int)rand(1, 15)];
}
} elseif (is_subclass_of($sAttributeType, 'AttributeEnum') || $sAttributeType == 'AttributeEnum') {
} elseif (is_subclass_of($sAttributeType, AttributeEnum::class) || $sAttributeType == AttributeEnum::class) {
$aAllowed = $this->oModelReflection->GetAllowedValues_att($sClass, $this->sGroupByAttCode);
if ($aAllowed) { // null for non enums
foreach ($aAllowed as $sValue => $sValueLabel) {
@@ -1458,11 +1472,11 @@ abstract class DashletGroupBy extends Dashlet
}
foreach ($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
switch ($sAttType) {
case 'AttributeDecimal':
case 'AttributeDuration':
case 'AttributeInteger':
case 'AttributePercentage':
case 'AttributeSubItem': // TODO: Known limitation: no unit displayed (values in sec)
case AttributeDecimal::class:
case AttributeDuration::class:
case AttributeInteger::class:
case AttributePercentage::class:
case AttributeSubItem::class: // TODO: Known limitation: no unit displayed (values in sec)
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
$aFunctionAttributes[$sAttCode] = $sLabel;
break;

View File

@@ -5,7 +5,7 @@ use Combodo\iTop\Application\Helper\Session;
/**
* Class LoginBasic
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -24,11 +24,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode')) {
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
if (!is_null($this->GetAuthUserAndPassword())) {
Session::Set('login_mode', 'basic');
}
}
@@ -86,38 +82,49 @@ class LoginBasic extends AbstractLoginFSMExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
private function GetAuthUserAndPassword()
/**
* @return array|null [$sAuthUser, $sAuthPwd]
*/
private function GetAuthUserAndPassword(): ?array
{
$sAuthUser = '';
$sAuthPwd = null;
$sAuthorization = '';
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['HTTP_AUTHORIZATION'];
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!LoginWebPage::LooksLikeUTF8($sAuthUser)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
return [$sAuthUser, $sAuthPwd];
}
if (!empty($sAuthorization)) {
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($sAuthorization, 6)));
} else {
if (isset($_SERVER['PHP_AUTH_USER'])) {
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!LoginWebPage::LooksLikeUTF8($sAuthUser)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
}
if (empty($sAuthorization)) {
return null;
}
return [$sAuthUser, $sAuthPwd];
// Decode without Bearer
if (!utils::StartsWith($sAuthorization, 'Basic ')) {
return null;
}
$sDecodedAuthorisation = base64_decode(substr($sAuthorization, 6));
if (str_contains($sDecodedAuthorisation, ':')) {
return explode(':', $sDecodedAuthorisation);
}
return null;
}
}

View File

@@ -3249,4 +3249,48 @@ TXT
return (int)$sLimit;
}
}
/**
* @since 3.3.0 N°9875
* Open archive and raise appropriate exception.
* Warning: do not forget to close archive afterwhile
* @param string $sArchiveFilePath
* @param int|null $flags
* @return ZipArchive
* @throws \Exception
*/
public static function ZipArchiveOpen(string $sArchiveFilePath, int|null $flags = null): ZipArchive
{
$oZip = new ZipArchive();
if (is_null($flags)) {
$code = $oZip->open($sArchiveFilePath);
} else {
$code = $oZip->open($sArchiveFilePath, $flags);
}
if (true !== $code) {
//ZipArchive::ZIP_ER_NOZIP : 19
if ($code === 19) {
throw new \Exception(sprintf('Cannot open zip file due to inconsistent or empty content'));
}
throw new \Exception(sprintf('Cannot open zip file due to error code %s', $code));
}
return $oZip;
}
/**
* @since 3.3.0 N°9875
* @param string $sDirectory
* @param string $sPrefix
* @return ZipArchive
* @throws \Exception
*/
public static function ZipArchiveCreateWithTempNam(string $sDirectory, string $sPrefix): ZipArchive
{
$sTempnam = tempnam($sDirectory, $sPrefix);
unlink($sTempnam);
$sArchiveName = $sTempnam.'.zip';
return self::ZipArchiveOpen($sArchiveName, ZipArchive::CREATE);
}
}

View File

@@ -1642,10 +1642,10 @@ abstract class MetaModel
if ($sFilterCode == 'id') {
return true;
}
if (array_key_exists($sClass, self::$m_aMagicFields) && array_key_exists($sFilterCode, self::$m_aMagicFields[$sClass])) {
if (array_key_exists($sClass, self::$m_aMagicFields) && in_array($sFilterCode, self::$m_aMagicFields[$sClass])) {
return true;
}
if (array_key_exists($sClass, self::$m_aFilterForbiddenAttributes) && array_key_exists($sFilterCode, self::$m_aFilterForbiddenAttributes[$sClass])) {
if (array_key_exists($sClass, self::$m_aFilterForbiddenAttributes) && in_array($sFilterCode, self::$m_aFilterForbiddenAttributes[$sClass])) {
return false;
}
return self::IsValidAttCode($sClass, $sFilterCode);
@@ -2963,8 +2963,9 @@ abstract class MetaModel
self::$m_aMagicFields[] = $sClass;
}
self::$m_aMagicFields[$sClass][] = $sCode;
self::$m_aAttribOrigins[$sClass][$sCode] = self::$m_aAttribOrigins[$sClass][$sAttCode];
}
if (!$oAttDef->IsSearchable()) {
if (!$oAttDef->IsValidFilterAttribute()) {
if (!array_key_exists($sClass, self::$m_aFilterForbiddenAttributes)) {
self::$m_aFilterForbiddenAttributes[] = $sClass;
}

View File

@@ -60,15 +60,13 @@ class OQLActualClassTreeResolver
$aTranslateFields = [];
foreach ($aExpectedAttributes as $sAttCode => $oExpression) {
// 'id' is managed later
if ($sAttCode == 'id') {
if ($sAttCode === 'id') {
continue;
}
// Attributes can be stored in attributes list or for magic ones into filter codes list.
$sOriginClass = null;
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
if (MetaModel::IsValidAttCode($sClass, $sAttCode) || MetaModel::IsValidFilterCode($sClass, $sAttCode)) {
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
} elseif ($sAttCode == 'id') {
$sOriginClass = $sClass;
} else {
continue;
}

View File

@@ -217,8 +217,7 @@ final class CoreUpdater
throw new Exception(Dict::S('iTopUpdate:Error:BadFileFormat'));
}
$oArchive = new ZipArchive();
$oArchive->open($sArchiveFile);
$oArchive = utils::ZipArchiveOpen($sArchiveFile);
self::RRmdir(self::UPDATE_DIR);
SetupUtils::builddir(self::UPDATE_DIR);

View File

@@ -113,8 +113,10 @@ function DoLanding(WebPage $oPage)
file_put_contents($sZipArchiveFile, $sArchive);
// Expand the content of extension-x.zip into utils::GetDataPath().'downloaded-extensions/'
// where the installation will load the extension automatically
$oZip = new ZipArchive();
if (!$oZip->open($sZipArchiveFile)) {
try {
$oZip = utils::ZipArchiveOpen($sZipArchiveFile);
} catch (\Exception $e) {
throw new Exception('Unable to open "'.$sZipArchiveFile.'" for extraction. Make sure that the directory "'.'data/downloaded-extensions/'.'" is writable for the web server.');
}
for ($idx = 0; $idx < $oZip->numFiles; $idx++) {

View File

@@ -405,8 +405,8 @@ if (!class_exists('StructureInstaller')) {
$oAction->DBWrite();
}
//N°824 - Fill object_class in EventNotification from the Triggers target_class
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
// N°824 - Fill object_class in EventNotification from the Triggers target_class
if (version_compare($sPreviousVersion, '3.3.0', '<')) {
SetupLog::Info("Filling object_class in EventNotification from the Triggers target_class");
$iNbProcessed = 0;

View File

@@ -14,12 +14,12 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
// Message
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
// Message
Dict::Add('DA DA', 'Danish', 'Dansk', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -15,12 +15,12 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
// Message
Dict::Add('DE DE', 'German', 'Deutsch', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -39,4 +39,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -15,9 +15,9 @@ Dict::Add('EN US', 'English', 'English', [
// Message
Dict::Add('EN US', 'English', 'English', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
@@ -39,4 +39,10 @@ Dict::Add('EN US', 'English', 'English', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>',
]);

View File

@@ -15,12 +15,12 @@ Dict::Add('EN GB', 'British English', 'British English', [
// Message
Dict::Add('EN GB', 'British English', 'British English', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -39,4 +39,10 @@ Dict::Add('EN GB', 'British English', 'British English', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -17,12 +17,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
// Message
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -41,4 +41,10 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -43,4 +43,10 @@ Dict::Add('FR FR', 'French', 'Français', [
<div>La MFA, désormais intégrée directement à lexpérience coeur, renforce la sécurité globale tout en garantissant une expérience utilisateur plus fluide et cohérente.</div>
<br>
<div>Nous vous encourageons à vérifier vos paramètres et à activer la MFA afin de bénéficier dune meilleure protection de compte.</div>',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'Nouvelle expérience portail',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>Cette nouvelle version propose nativement une expérience plus moderne, flexible et responsive pour le portail, avec un look et une sensation alignés avec la console.</div>
<br>
<div>Nous avons repensé la navigation pour une expérience plus claire et plus cohérente, amélioré la flexibilité des menus et des tableaux de bord, et introduit des cartes responsives qui sadaptent à différentes tailles décran. "Mon profil" a également été amélioré, permettant aux utilisateurs de gérer plus facilement leur compte et les paramètres MFA.</div>
<br>
<div>Le portail peut également être personnalisé avec votre propre style, vous aidant à offrir une expérience plus cohérente et plus marquée à vos utilisateurs.</div>',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
// Message
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
// Message
Dict::Add('IT IT', 'Italian', 'Italiano', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
// Message
Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
// Message
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
// Message
Dict::Add('PL PL', 'Polish', 'Polski', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
// Message
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -38,4 +38,10 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
<div>MFA, встроенная прямо в основную функциональность, усиливает общую безопасность и обеспечивает более плавный и последовательный пользовательский опыт.</div>
<br>
<div>Рекомендуем проверить свои настройки и включить MFA для повышения защиты учётной записи.</div>',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
// Message
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -14,12 +14,12 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
// Message
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:WelcomePopup:Message:330_01_Welcome:Title' => 'Welcome to '.ITOP_APPLICATION_SHORT.' 3.3~~',
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>Were pleased to introduce this latest release, bringing you a range of updates and improvements!</div>
<br>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience.</div>
<div>Alongside new features like Multi-Factor Authentication, this version includes essential security updates, the first AI bricks, and various enhancements designed to deliver a smoother and more reliable experience. The CMDB also benefits from several improvements, including enhanced containerization and flow management, richer class descriptions, and lifecycle management. The portal has also received a new, more modern and flexible look, now natively integrated into the application.</div>
<br>
<div>We hope you enjoy using this version as much as we enjoyed building it.</div>
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
<div>Dont forget to adjust your iTop preferences to tailor the experience to your needs.</div>',
'UI:WelcomePopup:Message:330_02_AIFoundations:Title' => 'Laying the foundations for AI~~',
'UI:WelcomePopup:Message:330_02_AIFoundations:Description' => '<div>This release introduces the first building blocks for our upcoming AI-powered features.</div>
<br>
@@ -38,4 +38,10 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
<div>MFA, now built directly into the core experience, strengthens overall security while ensuring a more seamless and consistent user experience.</div>
<br>
<div>We encourage you to review your settings and enable MFA to benefit from enhanced account protection.</div>~~',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

View File

@@ -39,4 +39,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
<div>MFA, 现已直接集成至核心使用流程, 在强化整体安全防护能力的同时, 保障用户获得更流畅统一的使用体验.</div>
<br>
<div>我们建议您检查账户设置并开启多因子认证,以获得更强的账户安全防护.</div>',
'UI:WelcomePopup:Message:330_05_Portal:Title' => 'New Portal Experience~~',
'UI:WelcomePopup:Message:330_05_Portal:Description' => '<div>This new version natively brings a more modern, flexible, and responsive experience to the portal, with a look and feel aligned with the console.</div>
<br>
<div>Weve redesigned the navigation for a clearer and more consistent experience, enhanced the flexibility of menus and dashboards, and introduced responsive cards that adapt to different screen sizes. "My Profile" has also been enhanced, allowing users to manage their account and MFA settings more easily.</div>
<br>
<div>The portal can also be customized with your own look and feel, helping you deliver a more consistent and branded experience to your users.</div>~~',
]);

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -239,7 +239,7 @@ class iTopExtensionsMap
\IssueLog::Debug(__METHOD__.": remove extension from map", null, [$oExtension->sCode => $oExtension->sSourceDir]);
unset($this->aExtensions[$oExtension->sCode.'/'.$oExtension->sVersion]);
unset($this->aExtensionsByCode[$sCode]);
unset($this->aExtensionsByCode[$oExtension->sCode]);
}
/**
@@ -260,14 +260,17 @@ class iTopExtensionsMap
public function DeclareExtensionAsRemoved(array $aExtensionCodes): void
{
$aRemovedExtension = [];
foreach ($aExtensionCodes as $sCode) {
foreach ($aExtensionCodes as $sCode => $sLabel) {
$sRealCode = is_int($sCode) ? $sLabel : $sCode;
/** @var \iTopExtension $oExtension */
$oExtension = $this->GetFromExtensionCode($sCode);
$oExtension = $this->GetFromExtensionCode($sRealCode);
if (!is_null($oExtension)) {
$aRemovedExtension [] = $oExtension;
\IssueLog::Debug(__METHOD__.": remove extension locally", null, ['extension_code' => $oExtension->sCode]);
} else {
\IssueLog::Warning(__METHOD__." cannot find extensions", null, ['code' => $sCode]);
\IssueLog::Warning(__METHOD__." cannot find extensions", null, ['code' => $sRealCode]);
}
}
@@ -557,9 +560,6 @@ class iTopExtensionsMap
continue;
}
if (!$oExtension->bMandatory && $bRemoteExtensionsShouldBeMandatory) {
$oExtension->bMandatory = ($oExtension->sSource === iTopExtension::SOURCE_REMOTE);
}
$aRes[$oExtension->sCode] = $oExtension;
}

View File

@@ -19,7 +19,9 @@ if (is_null($sEnv)) {
$sConfFile = utils::GetConfigFilePath($sEnv);
try {
MetaModel::Startup($sConfFile, false /* $bModelOnly */, false /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
$oConfig = new Config($sConfFile);
$oConfig->Set('expression_cache_enabled', false);
MetaModel::Startup($oConfig, false /* $bModelOnly */, false /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
} catch (\Throwable $e) {
SetupLog::Enable(APPROOT.'log/setup.log');
\SetupLog::Error(

View File

@@ -230,6 +230,7 @@ class ModuleDiscovery
}
/**
* @since 3.3
* @param array<\iTopExtension> $aRemovedExtension
* @return void
*/
@@ -241,6 +242,19 @@ class ModuleDiscovery
self::$m_aRemovedExtensions = $aRemovedExtension;
}
/**
* @since 3.3
* @return string[]
*/
public static function GetRemovedExtensionCodes(): array
{
$aRes = [];
foreach (self::$m_aRemovedExtensions as $oExtension) {
$aRes[] = $oExtension->sCode;
}
return $aRes;
}
private static function Init($aSearchDirs): void
{
if (self::$m_aSearchDirs != $aSearchDirs) {

View File

@@ -57,9 +57,9 @@ class WizardController
/**
* Removes information about the previous step from the stack
* @return array{'class': string, 'state': string}
* @return null|array{'class': string, 'state': string}
*/
public function PopStep(): array
public function PopStep(): ?array
{
$aStep = array_pop($this->aWizardSteps);
$this->SetParameter('_steps', $this->aWizardSteps);
@@ -343,11 +343,8 @@ EOF
*/
public function GetLatestWizardStateFromStepClass(string $sStepClass): WizardState
{
$iCountModulesChoiceSteps = count(array_filter($this->aWizardSteps, fn (array $step): bool => $step['class'] === $sStepClass && $step['state'] !== ''));
$sStepState = $iCountModulesChoiceSteps > 0 ? (string) ($iCountModulesChoiceSteps - 1) : '';
// Pop the latest step from the stack, since we are going back to it
$this->PopStep();
$iStepStateOccurrences = count(array_filter($this->aWizardSteps, fn (array $step): bool => $step['class'] === $sStepClass && $step['state'] !== ''));
$sStepState = $iStepStateOccurrences > 0 ? (string) ($iStepStateOccurrences - 1) : '';
return new WizardState($sStepClass, $sStepState);
}

View File

@@ -82,15 +82,19 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
}
$bForceUninstall = (bool)$this->oWizard->GetParameter('force-uninstall', false);
if (!$bForceUninstall) {
if ($bSkipWizard && !$bForceUninstall) {
// Check if the current choices allow going to the data audit step
$aOptions = $this->oExtensionsMap->GetAllExtensionsOptionInfo();
foreach ($aOptions as $index => $aChoice) {
$sChoiceId = self::$SEP.$index;
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, false, false, true);
$oLatestWizardState = $this->oWizard->GetLatestWizardStateFromStepClass(WizStepModulesChoice::class);
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents[$oLatestWizardState->GetState()], false, false);
if (!$this->CanMoveForwardFromChoiceFlags($aFlags)) {
// Pop the latest step from the stack, since we are going back to it
$this->oWizard->PopStep();
// If the user has selected incompatible modules, we need to go back to the extension choices step, which is the last module choices step in the wizard
return $this->oWizard->GetLatestWizardStateFromStepClass(WizStepModulesChoice::class);
return $oLatestWizardState;
}
}
}

View File

@@ -51,6 +51,8 @@ class WizStepModulesChoice extends AbstractWizStepInstall
private array $aAnalyzeInstallationModules = [];
private ?MissingDependencyException $oMissingDependencyException = null;
private array $aFlagsByChoiceId = [];
public function __construct(WizardController $oWizard, $sCurrentState, bool $bOverWriteConfig = true)
{
parent::__construct($oWizard, $sCurrentState);
@@ -290,6 +292,34 @@ class WizStepModulesChoice extends AbstractWizStepInstall
$oPage->warning($sManualInstallError);
}
// Build the default choices
$aDefaults = $this->GetDefaults($aStepInfo, $this->aAnalyzeInstallationModules);
$index = $this->GetStepIndex();
// retrieve the saved selection
// use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id)
$aParameters = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true);
if (!isset($aParameters[$index])) {
$aParameters[$index] = $aDefaults;
}
$aSelectedComponents = $aParameters[$index];
$bDisableUninstallCheck = (bool)$this->oWizard->GetParameter('force-uninstall', false);
$aOptions = $aStepInfo['options'] ?? [];
foreach ($aOptions as $index => $aChoice) {
$sChoiceId = self::$SEP.$index;
$this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, false, $bDisableUninstallCheck);
}
if (!$this->bCanMoveForward) {
if (SetupUtils::IsConnectableToITopHub($this->aAnalyzeInstallationModules)) {
$oPage->error('Due to some inconsistencies the upgrade can\'t continue. You must deactivate "consistency protections" in the previous steps and restore a consistent environment.');
} else {
$oPage->error('Due to some inconsistencies the upgrade can\'t continue, please contact Combodo support.');
}
}
$oPage->add('<div class="module-selection-banner">');
$sBannerPath = isset($aStepInfo['banner']) ? $aStepInfo['banner'] : '';
if (!empty($sBannerPath)) {
@@ -308,18 +338,6 @@ class WizStepModulesChoice extends AbstractWizStepInstall
$oPage->add('<span>'.$sDescription.'</span>');
$oPage->add('</div>');
// Build the default choices
$aDefaults = $this->GetDefaults($aStepInfo, $this->aAnalyzeInstallationModules);
$index = $this->GetStepIndex();
// retrieve the saved selection
// use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id)
$aParameters = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true);
if (!isset($aParameters[$index])) {
$aParameters[$index] = $aDefaults;
}
$aSelectedComponents = $aParameters[$index];
$oPage->add('<div class="module-selection-body">');
$this->DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults);
$oPage->add('</div>');
@@ -742,8 +760,12 @@ EOF
return $this->aSteps[$index] ?? null;
}
public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck, bool $bUpgradeMode)
public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck): array
{
if (array_key_exists($sChoiceId, $this->aFlagsByChoiceId)) {
return $this->aFlagsByChoiceId[$sChoiceId];
}
$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();
@@ -752,51 +774,49 @@ EOF
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']);
$bInstalled = $bMissingFromDisk || $oITopExtension?->bInstalled ?? false;
$bDependencyIssue = $oITopExtension?->HasDependencyIssue() ?? false;
$bIsRemoteExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_REMOTE;
$bIsPackageExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_WIZARD;
$bDoNotUninstall = !$bCanBeUninstalled || $bIsRemoteExtension;
$bChecked = $bSelected;
$bDisabled = false;
if ($bMissingFromDisk) {
$bDisabled = true;
$bChecked = false;
} elseif ($bDependencyIssue && ($oITopExtension->sSource !== iTopExtension::SOURCE_WIZARD || !$bMandatory)) {
// If the extension is not installed, the user cannot select it
// If the extension is installed and mandatory or not uninstallable, the user cannot unselect it
// Unless the user uses the "force-uninstall" option
$bDisabled = (!$bInstalled || $bMandatory || !$bCanBeUninstalled) && !$bDisableUninstallCheck;
// If the extension is a remote extension and not be installed means the user previously uninstalled it
// Otherwise, it will be checked if it is mandatory or if it was selected by the user
if ($oITopExtension->sSource !== iTopExtension::SOURCE_REMOTE || $bInstalled) {
$bChecked = $bMandatory ?: $bSelected;
} elseif ($bMandatory && $bIsPackageExtension) {
$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
$bDisabled = !$bInstalled || !$bDisableUninstallCheck;
} elseif ($bInstalled && $bDoNotUninstall) {
// If the extension is uninstallable, it must be unchecked using the "force-uninstall" option
$bDisabled = !$bDisableUninstallCheck;
}
if (isset($aChoice['sub_options'])) {
$aOptions = $aChoice['sub_options']['options'] ?? [];
foreach ($aOptions as $index => $aSubChoice) {
$sSubChoiceId = $sChoiceId.self::$SEP.$index;
$aSubFlags = $this->ComputeChoiceFlags($aSubChoice, $sSubChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck);
if ($aSubFlags['checked']) {
$bChecked = true;
if ($aSubFlags['disabled']) {
// If some sub options are checked and cannot be unchecked, this choice also cannot be unchecked since it would uncheck all its sub options
$bDisabled = true;
}
}
}
}
} elseif ($bMandatory) {
$bDisabled = true;
$bChecked = true;
} elseif ($bInstalled && !$bCanBeUninstalled && !$bDisableUninstallCheck) {
$bChecked = true;
$bDisabled = true;
}
if ($bAllDisabled) {
$bDisabled = true;
}
if (isset($aChoice['sub_options'])) {
$aOptions = $aChoice['sub_options']['options'] ?? [];
foreach ($aOptions as $index => $aSubChoice) {
$sSubChoiceId = $sChoiceId.self::$SEP.$index;
$aSubFlags = $this->ComputeChoiceFlags($aSubChoice, $sSubChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $bUpgradeMode);
if ($aSubFlags['checked']) {
$bChecked = true;
if ($aSubFlags['disabled']) {
//If some sub options are enabled and cannot be disabled, this choice should also cannot be disabled since it would disable all its sub options
$bDisabled = true;
}
}
}
}
return [
$aFlags = [
'uninstallable' => $bCanBeUninstalled,
'dependency_issue' => $bDependencyIssue,
'mandatory' => $bMandatory,
@@ -805,6 +825,11 @@ EOF
'disabled' => $bDisabled,
'checked' => $bChecked,
];
$this->bCanMoveForward = $this->bCanMoveForward && $this->CanMoveForwardFromChoiceFlags($aFlags, $bDisableUninstallCheck);
$this->aFlagsByChoiceId[$sChoiceId] = $aFlags;
return $aFlags;
}
public function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false)
@@ -816,8 +841,7 @@ EOF
foreach ($aOptions as $index => $aChoice) {
$sChoiceId = $sParentId.self::$SEP.$index;
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade);
$this->bCanMoveForward = $this->bCanMoveForward && $this->CanMoveForwardFromChoiceFlags($aFlags, $bDisableUninstallCheck);
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck);
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $sChoiceId, $aFlags);
}
@@ -856,7 +880,7 @@ EOF
$bSelected = ($sChoiceId === $sChoiceIdNone);
}
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade);
$aFlags = $this->ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck);
//ComputeChoiceFlags does not completely compute alternative flags
$aFlags['disabled'] = $bDisabled;
$aFlags['checked'] = $bSelected;
@@ -881,10 +905,6 @@ EOF
if (!$aFlags['uninstallable']) {
return false;
}
// An extension cannot be uninstalled if it is mandatory
if ($aFlags['mandatory']) {
return false;
}
}
return true;
@@ -984,5 +1004,4 @@ EOF
return 'Next';
}
}

View File

@@ -646,9 +646,7 @@ abstract class Controller extends AbstractController
*/
final protected function ZipDownloadRemoveFile(array $aFiles, string $sDownloadArchiveName, bool $bUnlinkFiles = false): void
{
$sArchiveFileFullPath = tempnam(SetupUtils::GetTmpDir(), 'itop_download-').'.zip';
$oArchive = new ZipArchive();
$oArchive->open($sArchiveFileFullPath, ZipArchive::CREATE);
$oArchive = utils::ZipArchiveCreateWithTempNam(SetupUtils::GetTmpDir(), 'itop_download-');
foreach ($aFiles as $sFile) {
$oArchive->addFile($sFile, basename($sFile));
}
@@ -660,7 +658,7 @@ abstract class Controller extends AbstractController
}
}
$this->SendFileContent($sArchiveFileFullPath, $sDownloadArchiveName.'.zip', true, true);
$this->SendFileContent($oArchive->filename, $sDownloadArchiveName.'.zip', true, true);
}
final protected function SendFileContent($sFilePath, $sDownloadArchiveName = null, $bFileTransfer = true, $bRemoveFile = false, $aHeaders = []): void

View File

@@ -7,14 +7,11 @@
namespace Combodo\iTop\Application\WelcomePopup\Provider;
use Combodo\iTop\Controller\Newsroom\iTopNewsroomController;
use Combodo\iTop\Controller\Notifications\NotificationsCenterController;
use Combodo\iTop\Service\Router\Router;
use Dict;
use AbstractWelcomePopupExtension;
use Combodo\iTop\Application\WelcomePopup\MessageFactory;
use Dict;
use UserRights;
use utils;
use Combodo\iTop\Application\WelcomePopup\MessageFactory;
/**
* Implementation of the "default" Welcome Popup message
@@ -66,6 +63,13 @@ class DefaultProvider extends AbstractWelcomePopupExtension
Dict::S("UI:WelcomePopup:Message:330_04_MFA:Description"),
utils::GetAbsoluteUrlAppRoot()."images/illustrations/undraw_two_factor_authentication.svg"
);
$aMessages[] = MessageFactory::MakeForLeftTextsRightIllustrationAsSVGMarkup(
"330_05_Portal",
Dict::S("UI:WelcomePopup:Message:330_05_Portal:Title"),
Dict::S("UI:WelcomePopup:Message:330_05_Portal:Description"),
utils::GetAbsoluteUrlAppRoot()."images/illustrations/undraw_prototyping_process.svg"
);
}
return $aMessages;

View File

@@ -386,4 +386,8 @@ class AttributeBlob extends AttributeDefinition
{
return CMDBChangeOpSetAttributeBlob::class;
}
public function IsValidFilterAttribute(): bool
{
return false;
}
}

View File

@@ -193,6 +193,16 @@ abstract class AttributeDefinition
return $this->GetSearchType() != static::SEARCH_WIDGET_TYPE_RAW;
}
/**
* Indicates whether this attribute can be used in search filters in OQL queries
*
* @return bool
*/
public function IsValidFilterAttribute(): bool
{
return true;
}
/** @var string */
protected $m_sCode;
/** @var array */

View File

@@ -92,5 +92,8 @@ class AttributeEncryptedString extends AttributeString implements iAttributeNoGr
{
return CMDBChangeOpSetAttributeEncrypted::class;
}
public function IsValidFilterAttribute(): bool
{
return false;
}
}

View File

@@ -248,4 +248,9 @@ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeN
{
return CMDBChangeOpSetAttributeOneWayPassword::class;
}
public function IsValidFilterAttribute(): bool
{
return false;
}
}

View File

@@ -68,6 +68,10 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
return '******';
}
}
public function IsValidFilterAttribute(): bool
{
return false;
}
public function IsPartOfFingerprint()
{

View File

@@ -0,0 +1,93 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Application\LoginFSM;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use LoginBasic;
use LoginWebPage;
class LoginBasicTest extends ItopDataTestCase
{
public function testListSupportedLoginModes()
{
// Given
$oLoginBasic = new LoginBasic();
// When
$aActual = $oLoginBasic->ListSupportedLoginModes();
// Then
$this->assertEquals(['basic'], $aActual);
}
public function testOnModeDetectionForLoginPass()
{
// Given
$oLoginBasic = new LoginBasic();
$_SERVER['HTTP_AUTHORIZATION'] = 'Basic '.base64_encode("Login:BasicTest");
// When
$_SESSION = [];
$iErrorCode = 0;
$sActualRes = $oLoginBasic->LoginAction(LoginWebPage::LOGIN_STATE_MODE_DETECTION, $iErrorCode);
// Then
$this->assertEquals('basic', $_SESSION['login_mode'], 'Login mode should have detected basic mode');
$this->assertEquals(LoginWebPage::LOGIN_FSM_CONTINUE, $sActualRes, 'No Login FSM error should have been detected');
}
public function testOnModeDetectionForRedirect()
{
// Given
$oLoginBasic = new LoginBasic();
$_SERVER['REDIRECT_HTTP_AUTHORIZATION'] = 'Basic '.base64_encode('Login:BasicTest');
// When
$_SESSION = [];
$iErrorCode = 0;
$sActualRes = $oLoginBasic->LoginAction(LoginWebPage::LOGIN_STATE_MODE_DETECTION, $iErrorCode);
// Then
$this->assertEquals('basic', $_SESSION['login_mode'], 'Login mode should have detected basic mode');
$this->assertEquals(LoginWebPage::LOGIN_FSM_CONTINUE, $sActualRes, 'No Login FSM error should have been detected');
}
public function testOnModeDetectionForBasicLoginPassword()
{
// Given
$oLoginBasic = new LoginBasic();
$_SERVER['PHP_AUTH_USER'] = 'Login';
$_SERVER['PHP_AUTH_PW'] = 'Passwd';
// When
$_SESSION = [];
$iErrorCode = 0;
$sActualRes = $oLoginBasic->LoginAction(LoginWebPage::LOGIN_STATE_MODE_DETECTION, $iErrorCode);
// Then
$this->assertEquals('basic', $_SESSION['login_mode'], 'Login mode should have detected basic mode');
$this->assertEquals(LoginWebPage::LOGIN_FSM_CONTINUE, $sActualRes, 'No Login FSM error should have been detected');
}
public function testOnModeDetectionForBearerToken()
{
// Given
$oLoginBasic = new LoginBasic();
$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer '.base64_encode('LoginBasicTest');
// When
$_SESSION = [];
$iErrorCode = 0;
$sActualRes = $oLoginBasic->LoginAction(LoginWebPage::LOGIN_STATE_MODE_DETECTION, $iErrorCode);
// Then
$this->assertFalse(Session::IsSet('login_mode'), 'No basic login mode should have been detected');
$this->assertEquals(LoginWebPage::LOGIN_FSM_CONTINUE, $sActualRes, 'No Login FSM error should have been detected');
}
}

View File

@@ -1009,4 +1009,103 @@ INI;
utils::Unserialize($sData);
}
public static function ZipArchiveOpen_ValidZipFileProvider()
{
return [
"RDONLY" => [\ZipArchive::RDONLY],
"null" => [null],
];
}
/**
* @dataProvider ZipArchiveOpen_ValidZipFileProvider
*/
public function testZipArchiveOpen_ValidZipFile($flags)
{
$sArchiveName = tempnam(sys_get_temp_dir(), "testZipArchiveOpen_ValidZipFile_");
unlink($sArchiveName);
$oZip = new \ZipArchive();
$oZip->open($sArchiveName, \ZipArchive::CREATE);
$oZip->addFile(__FILE__);
$oZip->close();
$this->aFileToClean [] = $sArchiveName;
$oZip = utils::ZipArchiveOpen($sArchiveName, $flags);
self::assertNotNull($oZip);
$oZip->close();
}
public static function ZipArchiveOpen_EmptyExistingFileProvider()
{
return [
"RDONLY" => [\ZipArchive::RDONLY, 'Cannot open zip file due to inconsistent or empty content'],
"OVERWRITE" => [\ZipArchive::OVERWRITE],
];
}
/**
* @dataProvider ZipArchiveOpen_EmptyExistingFileProvider
*/
public function testZipArchiveOpen_EmptyExistingFile($flags, $sExpectedMessage = null)
{
$sFolderPath = tempnam(sys_get_temp_dir(), "testZipArchiveOpen_ZipFile_");
$this->aFileToClean [] = $sFolderPath;
if (! is_null($sExpectedMessage)) {
$this->expectExceptionMessage($sExpectedMessage);
}
$oZip = utils::ZipArchiveOpen($sFolderPath, $flags);
if (is_null($sExpectedMessage)) {
self::assertNotNull($oZip);
$oZip->close();
touch($sFolderPath);
}
}
public static function ZipArchiveOpen_NotyExistingFileProvider()
{
return [
"CREATE" => [\ZipArchive::CREATE],
"null" => [null, 'Cannot open zip file due to error code 9'],
];
}
/**
* @dataProvider ZipArchiveOpen_NotyExistingFileProvider
*/
public function testZipArchiveOpen_NotyExistingFile($flags, $sExpectedMessage = null)
{
$sFolderPath = tempnam(sys_get_temp_dir(), "testZipArchiveOpen_ZipFile_");
@unlink($sFolderPath);
if (! is_null($sExpectedMessage)) {
$this->expectExceptionMessage($sExpectedMessage);
}
$oZip = utils::ZipArchiveOpen($sFolderPath, $flags);
if (is_null($sExpectedMessage)) {
self::assertNotNull($oZip);
$oZip->close();
touch($sFolderPath);
$this->aFileToClean[] = $sFolderPath;
}
}
public function testZipArchiveCreateWithTempNam()
{
$oZip = utils::ZipArchiveCreateWithTempNam(sys_get_temp_dir(), "testZipArchiveOpenWithTempFile_");
self::assertNotNull($oZip);
$sFilePath = $oZip->filename;
self::assertFalse(is_file($sFilePath), $sFilePath);
$oZip->addEmptyDir('toto');
$oZip->addFile(__FILE__);
$oZip->close();
self::assertTrue(in_array($sFilePath, glob(sys_get_temp_dir().'/**')));
self::assertTrue(is_file($sFilePath), $sFilePath);
unlink($sFilePath);
}
}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<classes>
<class id="OQLResolverParent" _created_in="itop-structure" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>bizmodel</category>
<abstract>true</abstract>
<key_type>autoincrement</key_type>
<db_table>oqlresolverparent</db_table>
<db_key_field>id</db_key_field>
<fields_semantic>
<state_attribute>status</state_attribute>
</fields_semantic>
</properties>
<presentation/>
<methods/>
<fields>
<field id="cumulatedpending" xsi:type="AttributeStopWatch">
<states>
<state id="pending"/>
</states>
<working_time/>
<thresholds/>
<goal/>
</field><field id="status" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<sort_type>rank</sort_type>
<values>
<value id="new">
<code>new</code>
</value>
<value id="pending">
<code>pending</code>
</value>
</values>
<sql>status</sql>
<default_value>new</default_value>
<is_null_allowed>false</is_null_allowed>
</field>
</fields>
</class>
<class id="OQLResolverChild" _created_in="itop-structure" _delta="define">
<parent>OQLResolverParent</parent>
<properties>
<category>bizmodel</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>oqlresolverchild</db_table>
<db_key_field>id</db_key_field>
</properties>
<presentation/>
<methods/>
<fields>
<field id="name" xsi:type="AttributeString">
<sql>name</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
</fields>
</class>
</classes>
</itop_design>

View File

@@ -0,0 +1,41 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
use DBObjectSet;
use DBSearch;
use MetaModel;
class OQLResolverTest extends ItopCustomDatamodelTestCase
{
public function GetDatamodelDeltaAbsPath(): string
{
return __DIR__.'/Delta/delta_oql_resolver.xml';
}
public function testQueryOnMagicalFields()
{
// Given
$sObjectKey = $this->GivenObjectInDB('OQLResolverChild', ['name' => 'toto', 'status' => 'new']);
$oObject = MetaModel::GetObject('OQLResolverChild', $sObjectKey);
// When actions ApplyStimulus then next action fails
$sFilter = "SELECT OQLResolverChild WHERE ISNULL(cumulatedpending_laststart)";
$oSearch = DBSearch::FromOQL($sFilter);
$oSet = new DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad(['OQLResolverChild' => ['cumulatedpending']]);
$oActual = $oSet->Fetch();
// Then
// Check status...
$this->assertEquals($oObject->Get('name'), $oActual->Get('name'), 'The query should have returned the object');
}
}

View File

@@ -37,6 +37,40 @@ class ExtensionsMapTest extends ItopTestCase
$this->assertEquals($expected, array_keys($aExtensions));
}
public function testDeclareExtensionAsRemoved_WithDictPassed()
{
try {
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
//non existing extension
$aExtensionCodes = ['unexisting_extension_code' => 'label_unexisting_extension_code'];
//extension ok
$aExtensionCodes ['installed_ext1'] = $oExtensionsMap->GetFromExtensionCode('installed_ext1')->sLabel;
//search extension by label ko
$sLabel = $oExtensionsMap->GetFromExtensionCode('installed_ext_in_package')->sLabel;
$aExtensionCodes [$sLabel] = $sLabel;
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
$this->assertEquals(["installed_ext1"], ModuleDiscovery::GetRemovedExtensionCodes());
} finally {
ModuleDiscovery::DeclareRemovedExtensions([]);
}
}
public function testDeclareExtensionAsRemoved_WithListPassed()
{
try {
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$aExtensionCodes = ['unexisting_extension_code', 'installed_ext1'];
$aExtensionCodes [] = $oExtensionsMap->GetFromExtensionCode('installed_ext_in_package')->sLabel;
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
$this->assertEquals(["installed_ext1"], ModuleDiscovery::GetRemovedExtensionCodes());
} finally {
ModuleDiscovery::DeclareRemovedExtensions([]);
}
}
public function testGetAllExtensionsToDisplayInSetup_WithExtensionsHavingDependencyIssues()
{
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
@@ -137,6 +171,7 @@ class ExtensionsMapTest extends ItopTestCase
{
$oExt = new iTopExtension();
$oExt->sCode = $sCode;
$oExt->sLabel = "LABEL_".$sCode;
$oExt->sVersion = $sVersion;
$oExt->bVisible = $bVisible;
$oExt->sSource = $sSource;
@@ -147,6 +182,7 @@ class ExtensionsMapTest extends ItopTestCase
private function AddExtension(iTopExtensionsMap $oExtensionsMap, iTopExtension $oExt, string $mapKeyInItopExtensionMap)
{
$this->InvokeNonPublicMethod(iTopExtensionsMap::class, 'AddExtension', $oExtensionsMap, [$oExt]);
$aMap = $this->GetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap);
$aMap[$oExt->sCode.'/'.$oExt->sVersion] = $oExt;
$this->SetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap, $aMap);

View File

@@ -0,0 +1,33 @@
Removed from disk
Yes : Installed
No : Do not show extension .color_grey
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
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
Yes : Force uninstall
No : checked & disabled .color_orange #node13
Yes : Selected
No : unchecked & enabled .color_green #node14
Yes : checked & enabled .color_green #node15