Compare commits

..

5 Commits

Author SHA1 Message Date
odain
467774deaa N°9949 - phpstan code cleanup 2026-08-24 19:31:42 +02:00
odain
992761f3f1 N°9949 - log enhancement in ext mgt 2026-08-24 19:31:16 +02:00
odain
794f10bb56 N°9949 - fix log level 2026-08-24 19:19:17 +02:00
odain
40e87b38a2 N°9949 - remove dupicated line 2026-08-24 18:04:28 +02:00
odain
de29d2e10c N°9949 - Cannot uninstall IPAM from setup wizard screen - fix extension mgt as well 2026-08-24 18:03:05 +02:00
39 changed files with 80 additions and 381 deletions

View File

@@ -4651,7 +4651,7 @@ EOF
// Not in preview mode, do the update for real
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
if (!utils::IsTransactionValid($sTransactionId, false)) {
throw new Exception(Dict::S('UI:Error:InvalidToken'));
throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated'));
}
utils::RemoveTransaction($sTransactionId);
}

View File

@@ -115,7 +115,7 @@ class privUITransactionSession
// Strictly speaking, the two lines below should be grouped together
// by a critical section
// sem_acquire($rSemIdentified);
$id = static::GetUserPrefix().str_replace(['.', ' '], '', microtime()).'-'.bin2hex(random_bytes(24));
$id = static::GetUserPrefix().str_replace(['.', ' '], '', microtime());
Session::Set(['transactions', $id], true);
// sem_release($rSemIdentified);
@@ -236,7 +236,7 @@ class privUITransactionFile
self::CleanupOldTransactions();
$sTransactionIdFullPath = static::CreateUniqueTransactionFilePath(utils::GetDataPath().'transactions', static::GetUserPrefix());
$sTransactionIdFullPath = tempnam(utils::GetDataPath().'transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
@@ -245,33 +245,6 @@ class privUITransactionFile
return $sTransactionIdFileName;
}
/**
* Create a transaction file path with a longer random suffix and create it atomically.
*
* @param string $sDir
* @param string $sPrefix
*
* @return string
*
* @throws CoreException
*/
private static function CreateUniqueTransactionFilePath(string $sDir, string $sPrefix): string
{
$iMaxAttempts = 10;
for ($i = 0; $i < $iMaxAttempts; $i++) {
$sFileName = $sPrefix.bin2hex(random_bytes(24)); // 48 random hex chars
$sFullPath = $sDir.'/'.$sFileName;
$hFile = @fopen($sFullPath, 'x');
if ($hFile !== false) {
fclose($hFile);
return $sFullPath;
}
}
throw new CoreException('Failed to allocate a unique transaction file name after multiple attempts.');
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
@@ -308,18 +281,6 @@ class privUITransactionFile
return false;
}
$iTime = filemtime($sFilepath);
$iLifetime = (int) MetaModel::GetConfig()->Get('transactions_file_lifetime');
$iLimit = time() - $iLifetime;
if ($iTime < $iLimit) {
self::Info("IsTransactionValid: Transaction '$id' expired.");
if ($iLifetime < 3600) {
IssueLog::Warning("IsTransactionValid: Transaction '$id' expired after only $iLifetime seconds. Consider increasing the 'transactions_file_lifetime' configuration parameter.");
}
@unlink($sFilepath);
return false;
}
if ($bRemoveTransaction) {
$bResult = @unlink($sFilepath);
if (!$bResult) {
@@ -353,7 +314,7 @@ class privUITransactionFile
}
/**
* Cleanup old transactions which have been pending since more than the lifetime defined in the configuration parameter 'transactions_file_lifetime'.
* Cleanup old transactions which have been pending since more than 24 hours
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
*/
protected static function CleanupOldTransactions($sTransactionDir = null)
@@ -366,7 +327,7 @@ class privUITransactionFile
}
clearstatcache();
$iLimit = time() - (int) MetaModel::GetConfig()->Get('transactions_file_lifetime');
$iLimit = time() - 24 * 3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : utils::GetDataPath().'transactions/*';
$aTransactions = glob($sPattern);
foreach ($aTransactions as $sFileName) {

View File

@@ -1288,14 +1288,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'transactions_file_lifetime' => [
'type' => 'integer',
'description' => 'Value in seconds for the lifetime of a transaction file (after this duration, the transaction will be invalid and the garbage collector could delete it). This only works if the "transaction_storage" parameter is set to "File".',
'default' => 3600,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_transactions' => [
'type' => 'bool',
'description' => 'Whether or not to enable the debug log for the transactions.',

View File

@@ -42,7 +42,7 @@ css/backoffice/
| vendors/ # Third-party libs, should be either:
| # - Overload of the lib SCSS variables (BEST way, but possible only if the lib exposes them. e.g. Bulma)
| # - Overload of the lib necessary CSS classes only (not great as it duplicates some rules in the browser, which add weight and computation. e.g. dataTables)
| # - Duplicate the lib CSS completely to insert SCSS variables (not great as it will be outdated when updating the lib itself. e.g. jQuery UI)
| # - Duplicate the lib CSS completly to insert SCSS variables (not great as it will be outdated when updating the lib itself. e.g. jQuery UI)
| | _bulma-variables-overload.scss # Bulma CSS framework
| | _jquery-ui.scss # jQuery UI
| ... # Etc…

View File

@@ -12,7 +12,7 @@ class DeletionPlanEntity
public readonly DeletionPlanItem $oDelete;
public readonly DeletionPlanItem $oUpdate;
public readonly DeletionPlanItem $oIssue;
public function __construct()
{
$this->oDelete = new DeletionPlanItem();

View File

@@ -32,9 +32,9 @@
{% UIColumn Standard {} %}
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
{% if aExtension['installed'] %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% else %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% endif %}
{% endfor %}
{% EndUIColumn %}

View File

@@ -12,9 +12,9 @@
{% UIColumn Standard {} %}
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
{% if aExtension['installed'] %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% else %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% endif %}
{% endfor %}
{% EndUIColumn %}

View File

@@ -521,7 +521,7 @@ Par exemple : disques durs externes, scanners, dispositifs d\'entrée (trackball
Dict::Add('FR FR', 'French', 'Français', [
'Class:Enclosure' => 'Châssis',
'Class:Enclosure+' => 'Un châssis monté à l\'intérieur d\'une Baie qui permet d\'installer des équipements informatiques, comme des Serveurs lames ou des équipements réseau.',
'Class:Enclosure+' => 'Un châssis montée à l\'intérieur d\'une Baie qui permet d\'installer des équipements informatiques, comme des Serveurs lames ou des équipements réseau.',
'Class:Enclosure/ComplementaryName' => '%1$s - %2$s - %3$s',
'Class:Enclosure/Attribute:rack_id' => 'Baie',
'Class:Enclosure/Attribute:rack_id+' => '',

View File

@@ -793,7 +793,12 @@ class ObjectFormManager extends FormManager
{
$isTransactionValid = \utils::IsTransactionValid($this->oForm->GetTransactionId(), false); //The transaction token is kept in order to preserve BC with ajax forms (the second call would fail if the token is deleted). (The GC will take care of cleaning the token for us later on)
if (!$isTransactionValid) {
$sError = Dict::S('UI:Error:InvalidToken');
if ($this->oObject->IsNew()) {
$sError = Dict::S('UI:Error:ObjectAlreadyCreated');
} else {
$sError = Dict::S('UI:Error:ObjectAlreadyUpdated');
}
$aData['messages']['error'] += [
'_main' => [$sError],
];

View File

@@ -503,7 +503,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:Error:InvalidDashboard' => 'Chyba: neplatná nástěnka',
'UI:Error:MaintenanceMode' => 'Aktuálně probíhá údržba systému',
'UI:Error:MaintenanceTitle' => 'Údržba aplikace',
'UI:Error:InvalidToken' => 'Aktuální relace je neplatná. Obnovte stránku a zkuste to znovu. Pokud problém přetrvává, odhlaste se a znovu se přihlaste.',
'UI:Error:InvalidToken' => 'Chyba: požadovaná operace byla již provedena (CSRF token nebyl nalezen)',
'UI:Error:SMTP:UnknownVendor' => 'Poskytovatel OAuth SMTP %1$s neexistuje (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Množství',
'UI:GroupBy:Count+' => 'Množství prvků',

View File

@@ -503,7 +503,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => 'Den nuværende session er ugyldig. Opdater siden, og prøv igen. Hvis problemet fortsætter, skal du logge ud og logge ind igen.',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Antal',
'UI:GroupBy:Count+' => 'Antal af elementer',

View File

@@ -506,7 +506,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
'UI:Error:InvalidDashboard' => 'Fehler: Ungültiges Dashboard',
'UI:Error:MaintenanceMode' => 'Die Anwendung befindet sich derzeit im Wartungsmodus.',
'UI:Error:MaintenanceTitle' => 'Wartung',
'UI:Error:InvalidToken' => 'Die aktuelle Sitzung ist ungültig. Bitte aktualisieren Sie die Seite und versuchen Sie es erneut. Wenn das Problem weiterhin besteht, melden Sie sich bitte ab und wieder an.',
'UI:Error:InvalidToken' => 'Fehler: The angeforderte Operation wurde bereits ausgeführt (CSRF-Token nicht gefunden)',
'UI:Error:SMTP:UnknownVendor' => 'Der oAuth-SMTP-Provider %1$s existiert nicht (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Anzahl',
'UI:GroupBy:Count+' => 'Anzahl der Elemente',

View File

@@ -521,7 +521,7 @@ Dict::Add('EN US', 'English', 'English', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance',
'UI:Error:MaintenanceTitle' => 'Maintenance',
'UI:Error:InvalidToken' => 'The current session is invalid. Please refresh the page and try again. If the problem persists, please log out and log in again.',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)',
'UI:Error:TwigController' => 'Internal error in form controller',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)',

View File

@@ -521,7 +521,7 @@ Dict::Add('EN GB', 'British English', 'British English', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance',
'UI:Error:MaintenanceTitle' => 'Maintenance',
'UI:Error:InvalidToken' => 'The current session is invalid. Please refresh the page and try again. If the problem persists, please log out and log in again.',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)',

View File

@@ -501,7 +501,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'UI:Error:InvalidDashboard' => 'Error: Dashboard inválido',
'UI:Error:MaintenanceMode' => 'La aplicación se encuentra actualmente en mantenimiento',
'UI:Error:MaintenanceTitle' => 'Mantenimiento',
'UI:Error:InvalidToken' => 'La sesión actual no es válida. Actualice la página e inténtelo de nuevo. Si el problema persiste, cierre sesión y vuelva a iniciarla.',
'UI:Error:InvalidToken' => 'Error: La operación solicitada ya se habia realizado (CSRF token not found)',
'UI:Error:SMTP:UnknownVendor' => 'El proveedor SMTP de OAuth %1$s no existe (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Cuenta',
'UI:GroupBy:Count+' => 'Número de Elementos',

View File

@@ -517,7 +517,7 @@ Nous espérons que vous aimerez cette version autant que nous avons eu du plaisi
'UI:Error:InvalidDashboard' => 'Erreur: Le tableau de bord est invalide',
'UI:Error:MaintenanceMode' => 'L\'application est en maintenance',
'UI:Error:MaintenanceTitle' => 'Maintenance',
'UI:Error:InvalidToken' => 'La session actuelle est invalide. Veuillez actualiser la page et réessayer. Si le problème persiste, veuillez vous déconnecter puis vous reconnecter.',
'UI:Error:InvalidToken' => 'Erreur: l\'opération a déjà été effectuée (CSRF token not found)',
'UI:Error:TwigController' => 'Erreur interne dans le contrôleur de formulaire',
'UI:Error:SMTP:UnknownVendor' => 'Le provider SMTP OAuth 2.0 %1$s n\'existe pas',
'UI:GroupBy:Count' => 'Nombre',

View File

@@ -505,7 +505,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'UI:Error:InvalidDashboard' => 'Hiba: Érvénytelen műszerfal',
'UI:Error:MaintenanceMode' => 'Az alkalmazás jelenleg karbantartás alatt van',
'UI:Error:MaintenanceTitle' => 'Karbantartás',
'UI:Error:InvalidToken' => 'Az aktuális munkamenet érvénytelen. Frissítse az oldalt, majd próbálja újra. Ha a probléma továbbra is fennáll, jelentkezzen ki, majd jelentkezzen be újra.',
'UI:Error:InvalidToken' => 'Hiba: a kért művelet már végrehajtásra került (CSRF token nem található)',
'UI:Error:SMTP:UnknownVendor' => 'A %1$s OAuth SMTP szolgáltató nem létezik (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Mennyiség',
'UI:GroupBy:Count+' => '',

View File

@@ -507,7 +507,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
'UI:Error:InvalidDashboard' => 'Errore: cruscotto non valido',
'UI:Error:MaintenanceMode' => 'L\'applicazione è attualmente in manutenzione',
'UI:Error:MaintenanceTitle' => 'Manutenzione',
'UI:Error:InvalidToken' => 'La sessione corrente non è valida. Aggiorna la pagina e riprova. Se il problema persiste, esegui la disconnessione e accedi di nuovo.',
'UI:Error:InvalidToken' => 'Errore: l\'operazione richiesta è già stata eseguita (token CSRF non trovato)',
'UI:Error:SMTP:UnknownVendor' => 'Il fornitore OAuth SMTP %1$s non esiste (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Conteggio',
'UI:GroupBy:Count+' => '',

View File

@@ -506,7 +506,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => '現在のセッションは無効です。ページを更新してもう一度お試しください。問題が解決しない場合は、一度ログアウトしてから再度ログインしてください。',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'カウント',
'UI:GroupBy:Count+' => '要素数',

View File

@@ -504,7 +504,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'UI:Error:InvalidDashboard' => 'Fout: ongeldig dashboard',
'UI:Error:MaintenanceMode' => 'Toepassing is momenteel in onderhoud',
'UI:Error:MaintenanceTitle' => 'Onderhoud',
'UI:Error:InvalidToken' => 'De huidige sessie is ongeldig. Vernieuw de pagina en probeer het opnieuw. Als het probleem aanhoudt, log dan uit en weer in.',
'UI:Error:InvalidToken' => 'Fout: de gevraagde bewerking werd al uitgevoerd (CSRF token niet gevonden)',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s bestaat niet (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Aantal',
'UI:GroupBy:Count+' => 'Aantal objecten',

View File

@@ -506,7 +506,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
'UI:Error:InvalidDashboard' => 'Błąd: nieprawidłowy pulpit nawigacyjny',
'UI:Error:MaintenanceMode' => 'Aplikacja jest obecnie w trakcie konserwacji',
'UI:Error:MaintenanceTitle' => 'Konserwacja',
'UI:Error:InvalidToken' => 'Bieżąca sesja jest nieprawidłowa. Odśwież stronę i spróbuj ponownie. Jeśli problem będzie się powtarzał, wyloguj się i zaloguj ponownie.',
'UI:Error:InvalidToken' => 'Błąd: żądana operacja została już wykonana (nie znaleziono tokena CSRF)',
'UI:Error:SMTP:UnknownVendor' => 'Dostawca OAuth SMTP %1$s nie istnieje (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Licznik',
'UI:GroupBy:Count+' => 'Liczba elementów',

View File

@@ -503,7 +503,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'UI:Error:InvalidDashboard' => 'Erro: painel inválido',
'UI:Error:MaintenanceMode' => 'A aplicação está em manutenção',
'UI:Error:MaintenanceTitle' => 'Manutenção',
'UI:Error:InvalidToken' => 'A sessão atual é inválida. Atualize a página e tente novamente. Se o problema persistir, saia e entre novamente.',
'UI:Error:InvalidToken' => 'Erro: A operação solicitada já foi executada (token CSRF não encontrado)',
'UI:Error:SMTP:UnknownVendor' => 'O provedor de autenticação SMTP OAuth %1$s não existe (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Número',
'UI:GroupBy:Count+' => 'Número de elementos',

View File

@@ -505,7 +505,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
'UI:Error:InvalidDashboard' => 'Ошибка: недопустимый дашборд',
'UI:Error:MaintenanceMode' => 'Приложение в режиме технического обслуживания',
'UI:Error:MaintenanceTitle' => 'Техническое обслуживание',
'UI:Error:InvalidToken' => 'Текущий сеанс недействителен. Обновите страницу и повторите попытку. Если проблема сохраняется, выйдите из системы и войдите снова.',
'UI:Error:InvalidToken' => 'Ошибка: запрошенная операция уже была выполнена (CSRF-токен не найден)',
'UI:Error:SMTP:UnknownVendor' => 'Провайдер OAuth SMTP %1$s не существует (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Количество',
'UI:GroupBy:Count+' => 'Количество элементов',

View File

@@ -509,7 +509,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => 'Aktuálna relácia je neplatná. Obnovte stránku a skúste to znova. Ak problém pretrváva, odhláste sa a znova sa prihláste.',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Počet',
'UI:GroupBy:Count+' => 'Number of elements~~',

View File

@@ -506,7 +506,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => 'Mevcut oturum geçersiz. Lütfen sayfayı yenileyip tekrar deneyin. Sorun devam ederse, oturumu kapatıp yeniden açın.',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Say',
'UI:GroupBy:Count+' => 'Eleman sayısı',

View File

@@ -521,7 +521,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:Error:InvalidDashboard' => '错误: 无效的仪表盘',
'UI:Error:MaintenanceMode' => '应用正处于维护中',
'UI:Error:MaintenanceTitle' => '维护',
'UI:Error:InvalidToken' => '当前会话无效。请刷新页面后重试。如果问题仍然存在,请先退出登录再重新登录。',
'UI:Error:InvalidToken' => '错误: 所请求的操作已执行 (没有CSRF token)',
'UI:Error:TwigController' => '表单控制器内部错误',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)',

View File

@@ -12,7 +12,6 @@ use Combodo\iTop\Application\Helper\SynchroReplicaHelper;
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchHelper;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
@@ -925,7 +924,7 @@ try {
if (!utils::IsTransactionValid($sTransactionId)) {
$sUser = UserRights::GetUser();
IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser'");
$oP->p(Dict::S('UI:Error:InvalidToken'));
$oP->p(Dict::S('UI:Error:ObjectAlreadyUpdated'));
} else {
// For archiving the modification
$oFilter = DBObjectSearch::unserialize($sFilter);
@@ -1106,7 +1105,7 @@ try {
if (!utils::IsTransactionValid($sTransactionId)) {
$sUser = UserRights::GetUser();
IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'");
$sMessage = Dict::S('UI:Error:InvalidToken');
$sMessage = Dict::S('UI:Error:ObjectAlreadyUpdated');
$sSeverity = 'info';
} elseif ((get_class($aStimuli[$sStimulus]) !== 'StimulusUserAction') || (UserRights::IsStimulusAllowed($sClass, $sStimulus) === UR_ALLOWED_NO)) {
$sUser = UserRights::GetUser();

View File

@@ -2,7 +2,7 @@
class ModuleInstallationRepository
{
private static ?ModuleInstallationRepository $oInstance;
private static ModuleInstallationRepository $oInstance;
protected function __construct()
{
@@ -60,7 +60,6 @@ class ModuleInstallationRepository
CMDBSource::InitFromConfig($oConfig);
//read db module installations
$tableWithPrefix = $this->GetTableWithPrefix($oConfig);
$iRootId = CMDBSource::QueryToScalar("SELECT max(parent_id) FROM $tableWithPrefix");
// Get the latest installed modules, without the "root" ones (iTop version and datamodel version)
$sSQL = <<<SQL
@@ -95,7 +94,10 @@ SQL;
public function GetApplicationVersion(Config $oConfig)
{
try {
$aSelectInstall = $this->ReadFromDB($oConfig);
CMDBSource::InitFromConfig($oConfig);
$tableWithPrefix = $this->GetTableWithPrefix($oConfig);
$sSQLQuery = "SELECT * FROM $tableWithPrefix";
$aSelectInstall = CMDBSource::QueryToArray($sSQLQuery);
} catch (MySQLException $e) {
// No database or erroneous information
SetupLog::Error(
@@ -103,6 +105,8 @@ SQL;
null,
[
'host' => $oConfig->Get('db_host'),
'user' => $oConfig->Get('db_user'),
'pwd:' => $oConfig->Get('db_pwd'),
'db name' => $oConfig->Get('db_name'),
'msg' => $e->getMessage(),
]

View File

@@ -645,7 +645,6 @@ class RunTimeEnvironment
$iPrevAccessMode = $oConfig->Get('access_mode');
$oConfig->Set('access_mode', ACCESS_FULL);
$this->InitDataModel($oConfig, true); // load data model and connect to the database
$oParams = new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME);
if (CMDBSource::DBName() == '') {
// In case this has not yet been done
@@ -653,9 +652,8 @@ class RunTimeEnvironment
}
if ($sShortComment === null) {
$sShortComment = $oParams->GetParameter('install_comment', 'Done by the setup program');
$sShortComment = 'Done by the setup program';
}
$oParams->SetParameter('install_comment', null);
$sMainComment = $sShortComment."\nBuilt on ".ITOP_BUILD_DATE;
// Record datamodel version
@@ -750,6 +748,7 @@ class RunTimeEnvironment
}
}
$oParams = new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME);
if (class_exists('DesignerUpdate') && $oParams->GetParameter('return_application') === 'designer') {
// Now keep track of this update
$oLog = new DesignerUpdate();
@@ -1276,14 +1275,14 @@ class RunTimeEnvironment
* - plus the list of modules present in the "extra" directory of the build environment: data/<build_environment>-modules/
*
* @param string $sSourceEnv The name of the source environment to 'imitate'
* @param bool|null $bUseSymLinks Whether to create symbolic links instead of copies
* @param null $bUseSymLinks Whether to create symbolic links instead of copies
* @param array $aAddedExtensions List of additional extensions to add to the build environment
*
* @return string[]
* @throws \ConfigException
* @throws \CoreException
*/
public function CompileFrom($sSourceEnv, ?bool $bUseSymLinks = null, $aAddedExtensions = [])
public function CompileFrom($sSourceEnv, $bUseSymLinks = null, $aAddedExtensions = [])
{
$oConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
$this->InitExtensionMap($oConfig);

View File

@@ -70,7 +70,7 @@ $("[data-role=\"setup-collapsable-options--toggler\"").on('click', function() {
$("#force-uninstall").on("click", function() {
let $this = $(this);
let bForceUninstall = $this.prop("checked");
if( bForceUninstall && !confirm('Beware, uninstalling extensions flagged as non uninstallable may result in data corruption and application crashes. Are you sure you want to continue?')){
if( bForceUninstall && !confirm('Beware, uninstalling extensions flagged as non uninstallable may result in data corruption and application crashes. Are you sure you want to continue ?')){
$this.prop("checked",false);
}
});

View File

@@ -792,7 +792,7 @@ EOF
// 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 not uninstallable, it must be unchecked using the "force-uninstall" option
// If the extension is uninstallable, it must be unchecked using the "force-uninstall" option
$bDisabled = !$bDisableUninstallCheck;
}
@@ -944,7 +944,7 @@ EOF
$sMetadata = '';
if (isset($aChoice['version']) && isset($aChoice['source_label'])) {
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span><span>'.$aChoice['extension_code'].'</span>';
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span>';
}
$sChoiceDisabled = $aFlags['disabled'] && !$aFlags['checked'] ? 'choice-disabled' : '';
@@ -987,7 +987,7 @@ EOF
public function CanMoveForward()
{
return $this->bCanMoveForward;
return true;
}
public function JSCanMoveForward()

View File

@@ -158,45 +158,15 @@ HTML
</form>
HTML
);
if ($this->DisplaySetupShortcutButton()) {
$oPage->add_ready_script(
<<<JS
$oPage->add_ready_script(
<<<JS
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button class="ibo-button ibo-is-alternative ibo-is-neutral" form="fast_setup"><span class="ibo-button--label">Keep current choices</span></button></td>');
JS
);
}
);
}
}
}
public function DisplaySetupShortcutButton(): bool
{
if ('install' === $this->oWizard->GetParameter('mode', 'install')) {
//fresh install
return false;
}
$oConfig = utils::GetConfig();
$res = ModuleInstallationRepository::GetInstance()->GetApplicationVersion($oConfig);
if (false === $res) {
return false;
}
$sProductName = $res['product_name'] ?? null;
$sProductVersion = $res['product_version'] ?? null;
if (is_null($sProductName) || is_null($sProductVersion)) {
\SetupLog::Error(__METHOD__.": cannot fetch itop version", null, $res);
return false;
}
if (ITOP_VERSION_FULL !== $sProductVersion) {
return false;
}
return (ITOP_APPLICATION === $sProductName);
}
public function CanMoveForward()
{
return $this->bCanMoveForward;

View File

@@ -7,6 +7,7 @@
namespace Combodo\iTop\Controller\Base\Layout;
use Combodo\iTop\Application\WebPage\AjaxPage;
use ApplicationContext;
use ApplicationException;
use cmdbAbstractObject;
@@ -17,13 +18,9 @@ use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreateHelper;
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary;
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
use Combodo\iTop\Application\WebPage\AjaxPage;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\JsonPage;
use Combodo\iTop\Controller\AbstractController;
use Combodo\iTop\Service\Base\ObjectRepository;
use Combodo\iTop\Service\Router\Router;
use Combodo\iTop\Service\SummaryCard\SummaryCardService;
use CoreCannotSaveObjectException;
use DBObjectSearch;
use DBObjectSet;
@@ -34,8 +31,11 @@ use Dict;
use Exception;
use IssueLog;
use iTopOwnershipLock;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\JsonPage;
use MetaModel;
use SecurityException;
use Combodo\iTop\Service\SummaryCard\SummaryCardService;
use UserRights;
use utils;
@@ -361,9 +361,9 @@ JS;
IssueLog::Error(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
if ($this->IsHandlingXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::S('UI:Error:InvalidToken')];
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyCreated')];
} else {
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:InvalidToken'));
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:ObjectAlreadyCreated'));
$oErrorAlert->SetIsClosable(false)
->SetIsCollapsible(false);
$oPage->AddUiBlock($oErrorAlert);
@@ -552,13 +552,13 @@ JS;
IssueLog::Error(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
if ($this->IsHandlingXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::S('UI:Error:InvalidToken')];
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyUpdated')];
} else {
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
$oPage->p("<strong>".Dict::S('UI:Error:InvalidToken')."</strong>\n");
$oPage->p("<strong>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</strong>\n");
}
$sMessage = Dict::Format('UI:Error:InvalidToken');
$sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'error';
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not updated (invalid transaction_id)', $sClass, [

View File

@@ -7,18 +7,19 @@
namespace Combodo\iTop\Controller\Links;
use Combodo\iTop\Application\WebPage\AjaxPage;
use cmdbAbstractObject;
use Combodo\iTop\Application\Helper\FormHelper;
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
use Combodo\iTop\Application\WebPage\AjaxPage;
use Combodo\iTop\Application\WebPage\JsonPage;
use Combodo\iTop\Controller\AbstractController;
use Combodo\iTop\Service\Base\ObjectRepository;
use Combodo\iTop\Service\Links\LinkSetModel;
use Combodo\iTop\Service\Router\Router;
use CoreException;
use DBObject;
use Combodo\iTop\Service\Base\ObjectRepository;
use Dict;
use Exception;
use Combodo\iTop\Application\WebPage\JsonPage;
use CoreException;
use DBObject;
use MetaModel;
use UserRights;
use utils;
@@ -67,7 +68,7 @@ class LinkSetController extends AbstractController
$sErrorMessage = $e->getMessage();
}
} else {
$sErrorMessage = Dict::S('UI:Error:InvalidToken');
$sErrorMessage = 'invalid transaction id';
}
$oPage->SetData([
'success' => $bOperationSuccess,
@@ -110,7 +111,7 @@ class LinkSetController extends AbstractController
$sErrorMessage = $e->getMessage();
}
} else {
$sErrorMessage = Dict::S('UI:Error:InvalidToken');
$sErrorMessage = 'invalid transaction id';
}
$oPage->SetData([
'success' => $bOperationSuccess,

View File

@@ -20,7 +20,7 @@ class SymfonyPHPMailTransport extends AbstractTransport
{
$oHeaders = $oRawEmail->getHeaders();
return $oHeaders->get('To') ? $oHeaders->get('To')->getBodyAsString() : '';
return $oHeaders->get('To')->getBodyAsString();
}
/**
@@ -70,13 +70,6 @@ class SymfonyPHPMailTransport extends AbstractTransport
return $sHeaders;
}
public function prepareAdditionalParameters(SentMessage $message): string
{
$sender = $message->getEnvelope()->getSender()->getEncodedAddress();
return '-f'.escapeshellarg($sender);
}
protected function doSend(SentMessage $message): void
{
$oRawEmail = $message->getOriginalMessage();
@@ -89,9 +82,8 @@ class SymfonyPHPMailTransport extends AbstractTransport
$sSubject = $this->prepareSubject($oRawEmail);
$sBody = $this->prepareBody($oRawEmail);
$sHeaders = $this->prepareHeaders($oRawEmail);
$sAdditionalParameters = $this->prepareAdditionalParameters($message);
$success = mail($sTo, $sSubject, $sBody, $sHeaders, $sAdditionalParameters);
$success = mail($sTo, $sSubject, $sBody, $sHeaders);
if (!$success) {
throw new \RuntimeException('The mail() function failed to send the message. Check server mail configuration.');

View File

@@ -42,10 +42,9 @@ class privUITransactionFileTest extends ItopDataTestCase
*/
public function testCleanupOldTransactions($iCleanableCreated, $iPreservableCreated, $sCleanablePrefix, $sPreservablePrefix)
{
$oConfig = MetaModel::GetConfig();
$oConfig->Set('transactions_gc_threshold', 100);
$iOriginalLifetime = (int) $oConfig->Get('transactions_file_lifetime');
$iBaseLimit = time() - $iOriginalLifetime;
MetaModel::GetConfig()->Set('transactions_gc_threshold', 100);
$iBaseLimit = time() - 24 * 3600; //24h
$sBaseDir = sys_get_temp_dir();
$sDir = "$sBaseDir/privUITransactionFileTest/cleanupOldTransactions";
@@ -186,71 +185,4 @@ class privUITransactionFileTest extends ItopDataTestCase
$bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUnauthenticatedUser);
$this->assertTrue($bResult, 'Token created by unauthenticated user must be removed when no user logged');
}
/**
* Validate that transactions_file_lifetime drives transaction expiration.
*
* @throws \Exception
*/
public function testIsTransactionValidUsesConfiguredFileLifetime()
{
$this->CreateUser(static::USER1_TEST_LOGIN, self::SAMPLE_DATA_SUPPORT_PROFILE_ID);
$bUserLogin = UserRights::Login(self::USER1_TEST_LOGIN);
$this->assertTrue($bUserLogin, 'Login with test user throw an error');
$oConfig = MetaModel::GetConfig();
$iOriginalLifetime = (int) $oConfig->Get('transactions_file_lifetime');
$iTestAgeInSeconds = 30;
try {
$oConfig->Set('transactions_file_lifetime', 3600);
$sLongLifetimeTransactionId = privUITransactionFile::GetNewTransactionId();
$sLongLifetimeTransactionFilePath = \utils::GetDataPath().'transactions/'.$sLongLifetimeTransactionId;
$bTouchSuccess = touch($sLongLifetimeTransactionFilePath, time() - $iTestAgeInSeconds);
$this->assertTrue($bTouchSuccess, 'Unable to age transaction file for long-lifetime scenario');
$bResult = privUITransactionFile::IsTransactionValid($sLongLifetimeTransactionId, false);
$this->assertTrue($bResult, 'Transaction should still be valid when its age is below transactions_file_lifetime');
privUITransactionFile::RemoveTransaction($sLongLifetimeTransactionId);
$oConfig->Set('transactions_file_lifetime', 5);
$sShortLifetimeTransactionId = privUITransactionFile::GetNewTransactionId();
$sShortLifetimeTransactionFilePath = \utils::GetDataPath().'transactions/'.$sShortLifetimeTransactionId;
$bTouchSuccess = touch($sShortLifetimeTransactionFilePath, time() - $iTestAgeInSeconds);
$this->assertTrue($bTouchSuccess, 'Unable to age transaction file for short-lifetime scenario');
$bResult = privUITransactionFile::IsTransactionValid($sShortLifetimeTransactionId, false);
$this->assertFalse($bResult, 'Transaction should be invalid when its age is above transactions_file_lifetime');
} finally {
$oConfig->Set('transactions_file_lifetime', $iOriginalLifetime);
}
}
/**
* @throws \SecurityException
* @throws \Exception
*/
public function testTransactionIdsContain48RandomHexCharsForSessionAndFileStorage()
{
$this->CreateUser(static::USER1_TEST_LOGIN, self::SAMPLE_DATA_SUPPORT_PROFILE_ID);
$bUserLogin = UserRights::Login(self::USER1_TEST_LOGIN);
$this->assertTrue($bUserLogin, 'Login with test user throw an error');
$sSessionTransactionId = \privUITransactionSession::GetNewTransactionId();
$this->assertTransactionIdHas48HexSuffix($sSessionTransactionId);
\privUITransactionSession::RemoveTransaction($sSessionTransactionId);
$sFileTransactionId = privUITransactionFile::GetNewTransactionId();
$this->assertTransactionIdHas48HexSuffix($sFileTransactionId);
privUITransactionFile::RemoveTransaction($sFileTransactionId);
}
private function assertTransactionIdHas48HexSuffix(string $sTransactionId): void
{
$aParts = explode('-', $sTransactionId);
$sHexSuffix = end($aParts);
$this->assertNotFalse($sHexSuffix, 'Transaction ID suffix is missing');
$this->assertSame(48, strlen($sHexSuffix), "Transaction ID '$sTransactionId' must end with 48 hex chars");
$this->assertMatchesRegularExpression('/^[a-f0-9]{48}$/', $sHexSuffix, "Transaction ID '$sTransactionId' suffix must be lowercase hexadecimal");
}
}

View File

@@ -1295,7 +1295,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
<div id="badge--itop-ext-not-installed--to-be-installed" class="ibo-badge ibo-block checked ibo-is-cyan" title="This extension will be installed during the setup." >to be installed</div><div id="badge--itop-ext-not-installed--not-installed" class="ibo-badge ibo-block unchecked ibo-is-blue-grey" title="This extension is not part of the current installation." >not installed</div>
</div>
<div class="ibo-extension-details--information--metadata">
<span>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-not-installed</span>
<span>v1.2.3</span><span>Local extensions folder</span>
</div>
<div class="ibo-extension-details--information--description">
Do something
@@ -1338,7 +1338,7 @@ HTML,
<div id="badge--itop-ext-installed--installed" class="ibo-badge ibo-block checked ibo-is-green" title="This extension is part of the current installation." >installed</div><div id="badge--itop-ext-installed--to-be-uninstalled" class="ibo-badge ibo-block unchecked ibo-is-red" title="This extension will be uninstalled during the setup." >to be uninstalled</div>
</div>
<div class="ibo-extension-details--information--metadata">
<span>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-installed</span>
<span>v1.2.3</span><span>Local extensions folder</span>
</div>
<div class="ibo-extension-details--information--description">
Do something
@@ -1382,7 +1382,7 @@ HTML,
<div id="badge--itop-ext-installed--installed" class="ibo-badge ibo-block checked ibo-is-green" title="This extension is part of the current installation." >installed</div><div id="badge--itop-ext-installed--to-be-uninstalled" class="ibo-badge ibo-block unchecked ibo-is-red" title="This extension will be uninstalled during the setup." >to be uninstalled</div><div id="badge--itop-ext-installed--not-uninstallable" class="ibo-badge ibo-block ibo-is-yellow" title="Once this extension has been installed, it should not be uninstalled." >cannot be uninstalled</div>
</div>
<div class="ibo-extension-details--information--metadata">
<span>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-installed</span>
<span>v1.2.3</span><span>Local extensions folder</span>
</div>
<div class="ibo-extension-details--information--description">
Do something

View File

@@ -1,134 +0,0 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Integration;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use WizardController;
use ModuleInstallationRepository;
class WizStepWelcomeTest extends ItopDataTestCase
{
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
require_once(APPROOT.'/setup/wizardsteps_autoload.php');
require_once(APPROOT.'/setup/moduleinstallation/ModuleInstallationRepository.php');
}
protected function tearDown(): void
{
parent::tearDown(); // TODO: Change the autogenerated stub
ModuleInstallationRepository::SetInstance(null);
}
public function testDisplaySetupShortcutButton_NoButtonWhenFreshInstall()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_NoButtonWhenNoItopVersionFetched()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$aApplicationVersion = [
'product_name' => ITOP_APPLICATION,
];
$this->GivenGetApplicationVersion($aApplicationVersion);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_NoButtonWhenNoApplicationVersionFetched()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$aApplicationVersion = [
'product_version' => ITOP_VERSION_FULL,
];
$this->GivenGetApplicationVersion($aApplicationVersion);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_NoButtonWhenApplicationChangeDuringUpgrade()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$aApplicationVersion = [
'product_version' => ITOP_VERSION_FULL,
'product_name' => 'toto',
];
$this->GivenGetApplicationVersion($aApplicationVersion);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_NoButtonWhenVersionChangeDuringUpgrade()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$aApplicationVersion = [
'product_version' => '6.6.6',
'product_name' => ITOP_APPLICATION,
];
$this->GivenGetApplicationVersion($aApplicationVersion);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_NoButtonWhenMySQLException()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$this->GivenGetApplicationVersion(false);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(false, $oWiz->DisplaySetupShortcutButton());
}
public function testDisplaySetupShortcutButton_ButtonDisplayed()
{
$oWizard = $this->createMock(WizardController::class);
$this->GivenSetupMode($oWizard, 'upgrade');
$aApplicationVersion = [
'product_version' => ITOP_VERSION_FULL,
'product_name' => ITOP_APPLICATION,
];
$this->GivenGetApplicationVersion($aApplicationVersion);
$oWiz = new \WizStepWelcome($oWizard, "");
$this->assertEquals(true, $oWiz->DisplaySetupShortcutButton());
}
private function GivenSetupMode(WizardController $oWizardMock, $sMode = 'install'): void
{
$oWizardMock->expects($this->once())
->method('GetParameter')
->with('mode')
->willReturn($sMode);
}
private function GivenGetApplicationVersion($sExpectedReturnedValue): void
{
$oModuleInstallationRepository = $this->createMock(ModuleInstallationRepository::class);
ModuleInstallationRepository::SetInstance($oModuleInstallationRepository);
$oModuleInstallationRepository->expects($this->once())
->method('GetApplicationVersion')
->willReturn($sExpectedReturnedValue);
}
}

View File

@@ -1,22 +0,0 @@
<?php
use Combodo\iTop\Core\Email\Transport\SymfonyPHPMailTransport;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use Symfony\Component\Mime\Email;
class SymfonyPHPMailTransportTest extends ItopTestCase
{
public function testPrepareMustNotThrowErrorWhenToHeaderIsMissing(): void
{
$oEmail = (new Email())
->from('sender@example.com')
->cc('cc1@example.com', 'cc2@example.com')
->text('Body');
$oTransport = new SymfonyPHPMailTransport();
$oTransport->prepareTo($oEmail);
$this->assertTrue(true); // if no error is thrown, the test passes
}
}