mirror of
https://github.com/Combodo/iTop.git
synced 2026-08-26 16:08:22 +02:00
Compare commits
15 Commits
issue/9949
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a5b0b403e | ||
|
|
a346b71e9a | ||
|
|
8065343ea6 | ||
|
|
9c6087fd54 | ||
|
|
66073ba390 | ||
|
|
4756e2474c | ||
|
|
7dbbf742c4 | ||
|
|
2cd4fd1e0e | ||
|
|
fffff76c82 | ||
|
|
06d593631d | ||
|
|
54a0dfd06f | ||
|
|
e31492fe27 | ||
|
|
dfd7f4cd0a | ||
|
|
69624116db | ||
|
|
97364af71d |
@@ -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:ObjectAlreadyUpdated'));
|
||||
throw new Exception(Dict::S('UI:Error:InvalidToken'));
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
$id = static::GetUserPrefix().str_replace(['.', ' '], '', microtime()).'-'.bin2hex(random_bytes(24));
|
||||
Session::Set(['transactions', $id], true);
|
||||
// sem_release($rSemIdentified);
|
||||
|
||||
@@ -236,7 +236,7 @@ class privUITransactionFile
|
||||
|
||||
self::CleanupOldTransactions();
|
||||
|
||||
$sTransactionIdFullPath = tempnam(utils::GetDataPath().'transactions', static::GetUserPrefix());
|
||||
$sTransactionIdFullPath = static::CreateUniqueTransactionFilePath(utils::GetDataPath().'transactions', static::GetUserPrefix());
|
||||
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
|
||||
|
||||
$sTransactionIdFileName = basename($sTransactionIdFullPath);
|
||||
@@ -245,6 +245,33 @@ 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
|
||||
@@ -281,6 +308,18 @@ 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) {
|
||||
@@ -314,7 +353,7 @@ class privUITransactionFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old transactions which have been pending since more than 24 hours
|
||||
* Cleanup old transactions which have been pending since more than the lifetime defined in the configuration parameter 'transactions_file_lifetime'.
|
||||
* 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)
|
||||
@@ -327,7 +366,7 @@ class privUITransactionFile
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
$iLimit = time() - 24 * 3600;
|
||||
$iLimit = time() - (int) MetaModel::GetConfig()->Get('transactions_file_lifetime');
|
||||
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : utils::GetDataPath().'transactions/*';
|
||||
$aTransactions = glob($sPattern);
|
||||
foreach ($aTransactions as $sFileName) {
|
||||
|
||||
@@ -1288,6 +1288,14 @@ 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.',
|
||||
|
||||
@@ -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 completly to insert SCSS variables (not great as it will be outdated when updating the lib itself. e.g. jQuery UI)
|
||||
| # - 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)
|
||||
| |– _bulma-variables-overload.scss # Bulma CSS framework
|
||||
| |– _jquery-ui.scss # jQuery UI
|
||||
| ... # Etc…
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% else %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% EndUIColumn %}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
{% UIColumn Standard {} %}
|
||||
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
|
||||
{% if aExtension['installed'] %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% else %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% EndUIColumn %}
|
||||
|
||||
@@ -793,12 +793,7 @@ 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) {
|
||||
if ($this->oObject->IsNew()) {
|
||||
$sError = Dict::S('UI:Error:ObjectAlreadyCreated');
|
||||
} else {
|
||||
$sError = Dict::S('UI:Error:ObjectAlreadyUpdated');
|
||||
}
|
||||
|
||||
$sError = Dict::S('UI:Error:InvalidToken');
|
||||
$aData['messages']['error'] += [
|
||||
'_main' => [$sError],
|
||||
];
|
||||
|
||||
@@ -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' => 'Chyba: požadovaná operace byla již provedena (CSRF token nebyl nalezen)',
|
||||
'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:SMTP:UnknownVendor' => 'Poskytovatel OAuth SMTP %1$s neexistuje (email_transport_smtp.oauth.provider)',
|
||||
'UI:GroupBy:Count' => 'Množství',
|
||||
'UI:GroupBy:Count+' => 'Množství prvků',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
|
||||
'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: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',
|
||||
|
||||
@@ -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' => 'Fehler: The angeforderte Operation wurde bereits ausgeführt (CSRF-Token nicht gefunden)',
|
||||
'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: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',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)',
|
||||
'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:TwigController' => 'Internal error in form controller',
|
||||
|
||||
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)',
|
||||
'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:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)',
|
||||
|
||||
|
||||
@@ -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' => 'Error: La operación solicitada ya se habia realizado (CSRF token not found)',
|
||||
'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: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',
|
||||
|
||||
@@ -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' => 'Erreur: l\'opération a déjà été effectuée (CSRF token not found)',
|
||||
'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: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',
|
||||
|
||||
@@ -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' => 'Hiba: a kért művelet már végrehajtásra került (CSRF token nem található)',
|
||||
'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: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+' => '',
|
||||
|
||||
@@ -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' => 'Errore: l\'operazione richiesta è già stata eseguita (token CSRF non trovato)',
|
||||
'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:SMTP:UnknownVendor' => 'Il fornitore OAuth SMTP %1$s non esiste (email_transport_smtp.oauth.provider)',
|
||||
'UI:GroupBy:Count' => 'Conteggio',
|
||||
'UI:GroupBy:Count+' => '',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
|
||||
'UI:Error:InvalidToken' => '現在のセッションは無効です。ページを更新してもう一度お試しください。問題が解決しない場合は、一度ログアウトしてから再度ログインしてください。',
|
||||
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
|
||||
'UI:GroupBy:Count' => 'カウント',
|
||||
'UI:GroupBy:Count+' => '要素数',
|
||||
|
||||
@@ -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' => 'Fout: de gevraagde bewerking werd al uitgevoerd (CSRF token niet gevonden)',
|
||||
'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:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s bestaat niet (email_transport_smtp.oauth.provider)',
|
||||
'UI:GroupBy:Count' => 'Aantal',
|
||||
'UI:GroupBy:Count+' => 'Aantal objecten',
|
||||
|
||||
@@ -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' => 'Błąd: żądana operacja została już wykonana (nie znaleziono tokena CSRF)',
|
||||
'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:SMTP:UnknownVendor' => 'Dostawca OAuth SMTP %1$s nie istnieje (email_transport_smtp.oauth.provider)',
|
||||
'UI:GroupBy:Count' => 'Licznik',
|
||||
'UI:GroupBy:Count+' => 'Liczba elementów',
|
||||
|
||||
@@ -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' => 'Erro: A operação solicitada já foi executada (token CSRF não encontrado)',
|
||||
'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: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',
|
||||
|
||||
@@ -505,7 +505,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'UI:Error:InvalidDashboard' => 'Ошибка: недопустимый дашборд',
|
||||
'UI:Error:MaintenanceMode' => 'Приложение в режиме технического обслуживания',
|
||||
'UI:Error:MaintenanceTitle' => 'Техническое обслуживание',
|
||||
'UI:Error:InvalidToken' => 'Ошибка: запрошенная операция уже была выполнена (CSRF-токен не найден)',
|
||||
'UI:Error:InvalidToken' => 'Текущий сеанс недействителен. Обновите страницу и повторите попытку. Если проблема сохраняется, выйдите из системы и войдите снова.',
|
||||
'UI:Error:SMTP:UnknownVendor' => 'Провайдер OAuth SMTP %1$s не существует (email_transport_smtp.oauth.provider)',
|
||||
'UI:GroupBy:Count' => 'Количество',
|
||||
'UI:GroupBy:Count+' => 'Количество элементов',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
|
||||
'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: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~~',
|
||||
|
||||
@@ -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' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
|
||||
'UI:Error:InvalidToken' => 'Mevcut oturum geçersiz. Lütfen sayfayı yenileyip tekrar deneyin. Sorun devam ederse, oturumu kapatıp yeniden açın.',
|
||||
'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ı',
|
||||
|
||||
@@ -521,7 +521,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'UI:Error:InvalidDashboard' => '错误: 无效的仪表盘',
|
||||
'UI:Error:MaintenanceMode' => '应用正处于维护中',
|
||||
'UI:Error:MaintenanceTitle' => '维护',
|
||||
'UI:Error:InvalidToken' => '错误: 所请求的操作已执行 (没有CSRF token)',
|
||||
'UI:Error:InvalidToken' => '当前会话无效。请刷新页面后重试。如果问题仍然存在,请先退出登录再重新登录。',
|
||||
'UI:Error:TwigController' => '表单控制器内部错误',
|
||||
|
||||
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)',
|
||||
|
||||
@@ -924,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:ObjectAlreadyUpdated'));
|
||||
$oP->p(Dict::S('UI:Error:InvalidToken'));
|
||||
} else {
|
||||
// For archiving the modification
|
||||
$oFilter = DBObjectSearch::unserialize($sFilter);
|
||||
@@ -1105,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:ObjectAlreadyUpdated');
|
||||
$sMessage = Dict::S('UI:Error:InvalidToken');
|
||||
$sSeverity = 'info';
|
||||
} elseif ((get_class($aStimuli[$sStimulus]) !== 'StimulusUserAction') || (UserRights::IsStimulusAllowed($sClass, $sStimulus) === UR_ALLOWED_NO)) {
|
||||
$sUser = UserRights::GetUser();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class ModuleInstallationRepository
|
||||
{
|
||||
private static ModuleInstallationRepository $oInstance;
|
||||
private static ?ModuleInstallationRepository $oInstance;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
@@ -60,6 +60,7 @@ 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
|
||||
@@ -94,10 +95,7 @@ SQL;
|
||||
public function GetApplicationVersion(Config $oConfig)
|
||||
{
|
||||
try {
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
$tableWithPrefix = $this->GetTableWithPrefix($oConfig);
|
||||
$sSQLQuery = "SELECT * FROM $tableWithPrefix";
|
||||
$aSelectInstall = CMDBSource::QueryToArray($sSQLQuery);
|
||||
$aSelectInstall = $this->ReadFromDB($oConfig);
|
||||
} catch (MySQLException $e) {
|
||||
// No database or erroneous information
|
||||
SetupLog::Error(
|
||||
@@ -105,8 +103,6 @@ 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(),
|
||||
]
|
||||
|
||||
@@ -645,6 +645,7 @@ 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
|
||||
@@ -652,8 +653,9 @@ class RunTimeEnvironment
|
||||
}
|
||||
|
||||
if ($sShortComment === null) {
|
||||
$sShortComment = 'Done by the setup program';
|
||||
$sShortComment = $oParams->GetParameter('install_comment', 'Done by the setup program');
|
||||
}
|
||||
$oParams->SetParameter('install_comment', null);
|
||||
$sMainComment = $sShortComment."\nBuilt on ".ITOP_BUILD_DATE;
|
||||
|
||||
// Record datamodel version
|
||||
@@ -748,7 +750,6 @@ 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();
|
||||
@@ -1275,14 +1276,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 null $bUseSymLinks Whether to create symbolic links instead of copies
|
||||
* @param bool|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, $bUseSymLinks = null, $aAddedExtensions = [])
|
||||
public function CompileFrom($sSourceEnv, ?bool $bUseSymLinks = null, $aAddedExtensions = [])
|
||||
{
|
||||
$oConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$this->InitExtensionMap($oConfig);
|
||||
|
||||
@@ -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>';
|
||||
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span><span>'.$aChoice['extension_code'].'</span>';
|
||||
}
|
||||
$sChoiceDisabled = $aFlags['disabled'] && !$aFlags['checked'] ? 'choice-disabled' : '';
|
||||
|
||||
|
||||
@@ -158,15 +158,45 @@ HTML
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
|
||||
if ($this->DisplaySetupShortcutButton()) {
|
||||
$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;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
namespace Combodo\iTop\Controller\Base\Layout;
|
||||
|
||||
use Combodo\iTop\Application\WebPage\AjaxPage;
|
||||
use ApplicationContext;
|
||||
use ApplicationException;
|
||||
use cmdbAbstractObject;
|
||||
@@ -18,9 +17,13 @@ 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;
|
||||
@@ -31,11 +34,8 @@ 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:ObjectAlreadyCreated')];
|
||||
$aResult['data'] = ['error_message' => Dict::S('UI:Error:InvalidToken')];
|
||||
} else {
|
||||
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:ObjectAlreadyCreated'));
|
||||
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:InvalidToken'));
|
||||
$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:ObjectAlreadyUpdated')];
|
||||
$aResult['data'] = ['error_message' => Dict::S('UI:Error:InvalidToken')];
|
||||
} 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:ObjectAlreadyUpdated')."</strong>\n");
|
||||
$oPage->p("<strong>".Dict::S('UI:Error:InvalidToken')."</strong>\n");
|
||||
}
|
||||
|
||||
$sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||
$sMessage = Dict::Format('UI:Error:InvalidToken');
|
||||
$sSeverity = 'error';
|
||||
|
||||
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not updated (invalid transaction_id)', $sClass, [
|
||||
|
||||
@@ -7,19 +7,18 @@
|
||||
|
||||
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\Controller\AbstractController;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
use Combodo\iTop\Service\Router\Router;
|
||||
use Combodo\iTop\Service\Base\ObjectRepository;
|
||||
use Dict;
|
||||
use Exception;
|
||||
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\Router\Router;
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use UserRights;
|
||||
use utils;
|
||||
@@ -68,7 +67,7 @@ class LinkSetController extends AbstractController
|
||||
$sErrorMessage = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$sErrorMessage = 'invalid transaction id';
|
||||
$sErrorMessage = Dict::S('UI:Error:InvalidToken');
|
||||
}
|
||||
$oPage->SetData([
|
||||
'success' => $bOperationSuccess,
|
||||
@@ -111,7 +110,7 @@ class LinkSetController extends AbstractController
|
||||
$sErrorMessage = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$sErrorMessage = 'invalid transaction id';
|
||||
$sErrorMessage = Dict::S('UI:Error:InvalidToken');
|
||||
}
|
||||
$oPage->SetData([
|
||||
'success' => $bOperationSuccess,
|
||||
|
||||
@@ -20,7 +20,7 @@ class SymfonyPHPMailTransport extends AbstractTransport
|
||||
{
|
||||
$oHeaders = $oRawEmail->getHeaders();
|
||||
|
||||
return $oHeaders->get('To')->getBodyAsString();
|
||||
return $oHeaders->get('To') ? $oHeaders->get('To')->getBodyAsString() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +70,13 @@ 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();
|
||||
@@ -82,8 +89,9 @@ 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);
|
||||
$success = mail($sTo, $sSubject, $sBody, $sHeaders, $sAdditionalParameters);
|
||||
|
||||
if (!$success) {
|
||||
throw new \RuntimeException('The mail() function failed to send the message. Check server mail configuration.');
|
||||
|
||||
@@ -42,9 +42,10 @@ class privUITransactionFileTest extends ItopDataTestCase
|
||||
*/
|
||||
public function testCleanupOldTransactions($iCleanableCreated, $iPreservableCreated, $sCleanablePrefix, $sPreservablePrefix)
|
||||
{
|
||||
MetaModel::GetConfig()->Set('transactions_gc_threshold', 100);
|
||||
|
||||
$iBaseLimit = time() - 24 * 3600; //24h
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
$oConfig->Set('transactions_gc_threshold', 100);
|
||||
$iOriginalLifetime = (int) $oConfig->Get('transactions_file_lifetime');
|
||||
$iBaseLimit = time() - $iOriginalLifetime;
|
||||
|
||||
$sBaseDir = sys_get_temp_dir();
|
||||
$sDir = "$sBaseDir/privUITransactionFileTest/cleanupOldTransactions";
|
||||
@@ -185,4 +186,71 @@ 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-not-installed</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>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-installed</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>v1.2.3</span><span>Local extensions folder</span><span>itop-ext-installed</span>
|
||||
</div>
|
||||
<div class="ibo-extension-details--information--description">
|
||||
Do something
|
||||
|
||||
134
tests/php-unit-tests/unitary-tests/setup/WizStepWelcomeTest.php
Normal file
134
tests/php-unit-tests/unitary-tests/setup/WizStepWelcomeTest.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user