mirror of
https://github.com/Combodo/iTop.git
synced 2026-08-21 21:58:20 +02:00
Compare commits
20 Commits
feature/91
...
issue/9954
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fe705c70d | ||
|
|
3987785690 | ||
|
|
b8f02f48bd | ||
|
|
ba426a9602 | ||
|
|
285f64f8db | ||
|
|
be5a5b0da1 | ||
|
|
7583ede3b3 | ||
|
|
95cb21cff7 | ||
|
|
cdd7bbedcb | ||
|
|
ea1ed03d0c | ||
|
|
2073e8c10c | ||
|
|
1a41349d7b | ||
|
|
fdc1ef40e6 | ||
|
|
4722551a16 | ||
|
|
a2934ef05b | ||
|
|
1cdc2c5648 | ||
|
|
31eddad029 | ||
|
|
b43d71f324 | ||
|
|
26033774b8 | ||
|
|
27c0fe191b |
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t 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>We’ve 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>',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -43,4 +43,10 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
<div>La MFA, désormais intégrée directement à l’expé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 d’une 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 s’adaptent à 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>',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’re pleased to introduce this latest release, bringing you a range of updates and improvements !</div>
|
||||
'UI:WelcomePopup:Message:330_01_Welcome:Description' => '<div>We’re 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>Don’t forget to adjust your iTop preferences to tailor the experience to your needs.</div>~~',
|
||||
<div>Don’t 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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
@@ -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>We’ve 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>~~',
|
||||
]);
|
||||
|
||||
1
images/illustrations/undraw_prototyping_process.svg
Normal file
1
images/illustrations/undraw_prototyping_process.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 25 KiB |
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -386,4 +386,8 @@ class AttributeBlob extends AttributeDefinition
|
||||
{
|
||||
return CMDBChangeOpSetAttributeBlob::class;
|
||||
}
|
||||
public function IsValidFilterAttribute(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -92,5 +92,8 @@ class AttributeEncryptedString extends AttributeString implements iAttributeNoGr
|
||||
{
|
||||
return CMDBChangeOpSetAttributeEncrypted::class;
|
||||
}
|
||||
|
||||
public function IsValidFilterAttribute(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,4 +248,9 @@ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeN
|
||||
{
|
||||
return CMDBChangeOpSetAttributeOneWayPassword::class;
|
||||
}
|
||||
|
||||
public function IsValidFilterAttribute(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,10 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
return '******';
|
||||
}
|
||||
}
|
||||
public function IsValidFilterAttribute(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
41
tests/php-unit-tests/unitary-tests/core/OQLResolverTest.php
Normal file
41
tests/php-unit-tests/unitary-tests/core/OQLResolverTest.php
Normal 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');
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
Reference in New Issue
Block a user