mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-07 10:04:14 +01:00
Compare commits
19 Commits
3.0.1-desi
...
feature/fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b87845c844 | ||
|
|
1f65ab5a92 | ||
|
|
7cfac7300b | ||
|
|
ede720c9b9 | ||
|
|
684efee5d2 | ||
|
|
e347989dcb | ||
|
|
37dd887cf1 | ||
|
|
b6cfb40f8f | ||
|
|
e1051e7a47 | ||
|
|
7e8eb26866 | ||
|
|
6775a3e928 | ||
|
|
faa38155e5 | ||
|
|
558bbc3357 | ||
|
|
cd7f9e478f | ||
|
|
e3586cff65 | ||
|
|
106127e6b7 | ||
|
|
2299983db3 | ||
|
|
9f27cf2b84 | ||
|
|
f78986009f |
@@ -251,7 +251,7 @@ class UIExtKeyWidget
|
||||
$aOption['picture_url'] = $oImage->GetDisplayURL($sClassAllowed, $oObj->GetKey(), $sObjectImageAttCode);
|
||||
$aOption['initials'] = '';
|
||||
} else {
|
||||
$aOption['initials'] = utils::ToAcronym($oObj->Get('friendlyname'));
|
||||
$aOption['initials'] = utils::FormatInitialsForMedallion(utils::ToAcronym($oObj->Get('friendlyname')));
|
||||
}
|
||||
}
|
||||
array_push($aOptions, $aOption);
|
||||
@@ -829,7 +829,7 @@ JS
|
||||
}
|
||||
|
||||
if (array_key_exists('initials', $aValue)) {
|
||||
$aElt['initials'] = $aValue['initials'];
|
||||
$aElt['initials'] = utils::FormatInitialsForMedallion($aValue['initials']);
|
||||
if (array_key_exists('picture_url', $aValue)) {
|
||||
$aElt['picture_url'] = $aValue['picture_url'];
|
||||
}
|
||||
|
||||
@@ -3050,6 +3050,20 @@ HTML;
|
||||
return $aMentionedObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method is not ideal, but other solutions seemed even less ideal:
|
||||
* * Add a "$sMaxLength" param. to utils::ToAcronym(): Does not work for every use cases (see corresponding ticket) as in some parts utils::ToAcronym isn't necessarly meant to be used in a medallion.
|
||||
*
|
||||
* @param string $sInitials
|
||||
*
|
||||
* @return string Truncates $sInitials so it can fit in medallions
|
||||
* @since 3.0.1 N°4913
|
||||
*/
|
||||
public static function FormatInitialsForMedallion(string $sInitials): string
|
||||
{
|
||||
return mb_substr($sInitials, 0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrl
|
||||
* @param string $sParamName
|
||||
|
||||
@@ -2936,52 +2936,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
// - TriggerOnObjectMention
|
||||
// 1 - Check if any caselog updated
|
||||
$aChanges = $this->m_aOrigValues;
|
||||
$aUpdatedLogAttCodes = array();
|
||||
foreach($aChanges as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if($oAttDef instanceof AttributeCaseLog && $value->GetModifiedEntry() !== '')
|
||||
{
|
||||
$aUpdatedLogAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = array();
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
// 3 - Trigger for those objects
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
$aTriggerArgs = $this->ToArgs('this') + array('mentioned->object()' => $oMentionedObject);
|
||||
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->ActivateOnMentionTriggers(true);
|
||||
|
||||
return $this->m_iKey;
|
||||
}
|
||||
@@ -3245,50 +3200,7 @@ abstract class DBObject implements iDisplay
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
// - TriggerOnObjectMention
|
||||
// 1 - Check if any caselog updated
|
||||
$aUpdatedLogAttCodes = array();
|
||||
foreach($aChanges as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$aUpdatedLogAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = array();
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
// 3 - Trigger for those objects
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
$aTriggerArgs = $this->ToArgs('this') + array('mentioned->object()' => $oMentionedObject);
|
||||
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->ActivateOnMentionTriggers(false);
|
||||
|
||||
$bHasANewExternalKeyValue = false;
|
||||
$aHierarchicalKeys = array();
|
||||
@@ -3501,6 +3413,74 @@ abstract class DBObject implements iDisplay
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate TriggerOnObjectMention triggers for the current object
|
||||
*
|
||||
* @param bool $bNewlyCreatedObject
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @since 3.0.1 N°4741
|
||||
*/
|
||||
private function ActivateOnMentionTriggers(bool $bNewlyCreatedObject): void
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$aChanges = $bNewlyCreatedObject ? $this->m_aOrigValues : $this->ListChanges();
|
||||
|
||||
// 1 - Check if any caselog updated
|
||||
$aUpdatedLogAttCodes = [];
|
||||
foreach ($aChanges as $sAttCode => $value) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeCaseLog) {
|
||||
// Skip empty log on creation
|
||||
if ($bNewlyCreatedObject && $value->GetModifiedEntry() === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aUpdatedLogAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = [];
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
|
||||
// 3 - Trigger for those objects
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
$aTriggerArgs = $this->ToArgs('this') + ['mentioned->object()' => $oMentionedObject];
|
||||
|
||||
$aParams = ['class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL)];
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), [], $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Save updated fields previous values for {@see DBObject::DBUpdate()} callbacks
|
||||
|
||||
@@ -455,6 +455,7 @@ class LogFileNameBuilderFactory
|
||||
class FileLog
|
||||
{
|
||||
protected $oFileNameBuilder;
|
||||
protected $bHasSQLite;
|
||||
|
||||
/**
|
||||
* FileLog constructor.
|
||||
@@ -467,6 +468,22 @@ class FileLog
|
||||
public function __construct($sFileName = '')
|
||||
{
|
||||
$this->oFileNameBuilder = LogFileNameBuilderFactory::GetInstance($sFileName);
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
$this->bHasSQLite = class_exists('SQLite3');
|
||||
if ($this->bHasSQLite) {
|
||||
if (empty($sLogFilePath)) {
|
||||
return;
|
||||
}
|
||||
$bCreate = false;
|
||||
$sDBFilePath = "$sLogFilePath.db";
|
||||
if (!is_file($sDBFilePath)) {
|
||||
$bCreate = true;
|
||||
}
|
||||
$this->DB = new SQLite3($sDBFilePath);
|
||||
if ($bCreate) {
|
||||
$this->DB->exec('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT, level TEXT, user TEXT, content LONGTEXT, context LONGTEXT, channel TEXT)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function Error($sText, $sChannel = '', $aContext = array())
|
||||
@@ -502,6 +519,17 @@ class FileLog
|
||||
|
||||
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
||||
{
|
||||
if ($this->bHasSQLite) {
|
||||
$stmt = $this->DB->prepare('INSERT INTO log (date, level, user, content, context, channel) VALUES (:date, :level, :user, :content, :context, :channel)');
|
||||
$stmt->bindValue(':date', date('Y-m-d H:i:s'));
|
||||
$stmt->bindValue(':level', $sLevel);
|
||||
$stmt->bindValue(':user', LogAPI::GetUserInfo());
|
||||
$stmt->bindValue(':content', $sText);
|
||||
$stmt->bindValue(':context', empty($aContext) ? '' : var_export($aContext, true));
|
||||
$stmt->bindValue(':channel', $sChannel);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7));
|
||||
$sTextPrefix .= ' | ';
|
||||
$sTextPrefix .= str_pad(LogAPI::GetUserInfo(), 5)." | ";
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2022 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-caselog-entry-in-collapsible-section--body--background-color: transparentize($ibo-color-grey-100,0.5) !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--padding: $ibo-spacing-300 !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--color: $ibo-color-grey-900 !default;
|
||||
$ibo-collapsible-section-within-alert--body--padding: $ibo-spacing-300 !default;
|
||||
$ibo-collapsible-section-within-alert--body--text-color: $ibo-color-grey-900 !default;
|
||||
|
||||
/* - caselog display in ormcaselog */
|
||||
.ibo-alert--body {
|
||||
.ibo-collapsible-section {
|
||||
margin: 0;
|
||||
min-width: 22em;
|
||||
|
||||
.ibo-collapsible-section--header .ibo-collapsible-section--title {
|
||||
@extend %ibo-font-size-100;
|
||||
.ibo-collapsible-section--header .ibo-collapsible-section--title,
|
||||
.ibo-collapsible-section--body {
|
||||
@extend %ibo-font-size-150;
|
||||
}
|
||||
|
||||
.ibo-collapsible-section--body {
|
||||
@extend %ibo-font-size-100;
|
||||
color: $ibo-caselog-entry-in-collapsible-section--body--color;
|
||||
padding: $ibo-caselog-entry-in-collapsible-section--body--padding;
|
||||
background-color: $ibo-caselog-entry-in-collapsible-section--body--background-color;
|
||||
color: $ibo-collapsible-section-within-alert--body--text-color;
|
||||
padding: $ibo-collapsible-section-within-alert--body--padding;
|
||||
}
|
||||
}
|
||||
> * + .ibo-collapsible-section {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -76,6 +76,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Anwendungsupgrade nicht möglich: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Vorsicht: App-Upgrade kann fehlerschlagen: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Einige angepasste Dateien wurden erkannt</b>, eine Teil-Update kann nicht ausgeführt werden.<br/>Befolgen Sie das <a href="%2$s">Verfahren</a>, um Ihr iTop manuell zu aktualisieren. Sie müssen das <a href="%1$s">Setup</a> benutzen, um Ihre Applikation zu aktualisieren.<br />',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Bereit zum Upgrade',
|
||||
|
||||
@@ -76,7 +76,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.',
|
||||
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check',
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'La aplicación no puede ser actualizada: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Advertencia: la actualización de la aplicación puede fallar: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Listo para empezar',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Attention : la mise à jour de l\'application peut échouer : %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Des fichiers modifiés ont été détectés</b>, une mise à jour partielle ne peut pas être effectuée.<br />Suivez la <a href="%2$s"> procedure</a> pour mettre à jour manuellement votre iTop. Vous devez utiliser la page <a href="%1$s">d\'installation</a> pour mettre à jour l\'application.',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Veuillez patienter pendant la vérification des fichiers',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Prêt pour l\\installation',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -78,6 +78,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Updaten van de toepassing is niet mogelijk: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Klaar om verder te gaan',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Nie można zaktualizować aplikacji: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Ostrzeżenie: aktualizacja aplikacji może się nie powieść: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Gotowy do startu',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Aplicação não pode ser atualizada: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Atenção: a atualização da aplicação pode falhar: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Pronto para começar',
|
||||
|
||||
@@ -64,6 +64,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Приложение не может быть обновлено: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Всё готово к началу',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~',
|
||||
|
||||
@@ -76,6 +76,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => '警告: 应用升级可能失败: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
|
||||
'iTopUpdate:UI:CheckInProgress' => 'Please wait during integrity check~~',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => '准备开始',
|
||||
|
||||
@@ -81,8 +81,10 @@ class FilesIntegrity
|
||||
* Check that files present in iTop folder corresponds to the manifest
|
||||
*
|
||||
* @param string $sRootPath
|
||||
* @param bool $bCheckNewModule
|
||||
*
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileIntegrityException
|
||||
* @since 3.0.1 Add $bCheckNewModule parameter
|
||||
*/
|
||||
public static function CheckInstallationIntegrity($sRootPath = APPROOT, $bCheckNewModule = false)
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -47,6 +47,3 @@ SetupWebPage::AddModule(
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -1428,7 +1428,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
'UI:About:InstallationOptions' => 'Options d\'installation',
|
||||
'UI:About:ManualExtensionSource' => 'Extension',
|
||||
'UI:About:Extension_Version' => 'Version: %1$s',
|
||||
'UI:About:RemoteExtensionSource' => 'iTop Hub',
|
||||
'UI:About:RemoteExtensionSource' => 'Data',
|
||||
|
||||
'UI:DisconnectedDlgMessage' => 'Vous êtes déconnecté(e). Vous devez vous identifier pour pouvoir continuer à utiliser l\'application.',
|
||||
'UI:DisconnectedDlgTitle' => 'Attention !',
|
||||
|
||||
@@ -1431,7 +1431,7 @@ Keď sú priradené spúštačom, každej akcii je dané číslo "príkazu", šp
|
||||
'UI:About:InstallationOptions' => 'Installation options~~',
|
||||
'UI:About:ManualExtensionSource' => 'Extension~~',
|
||||
'UI:About:Extension_Version' => 'Version: %1$s~~',
|
||||
'UI:About:RemoteExtensionSource' => 'iTop Hub~~',
|
||||
'UI:About:RemoteExtensionSource' => 'Data~~',
|
||||
|
||||
'UI:DisconnectedDlgMessage' => 'You are disconnected. You must identify yourself to continue using the application.~~',
|
||||
'UI:DisconnectedDlgTitle' => 'Warning!~~',
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
// Helpers
|
||||
function ShowAboutBox(sTitle)
|
||||
{
|
||||
var loadingDialog = $('<div id="block-about-iTop"></div>');
|
||||
var loadingDialog = $('<div id="ibo-about-box--loader"></div>');
|
||||
loadingDialog.dialog( {title:sTitle,autoOpen: true, modal: true, width: 700, height:350});
|
||||
$('#block-about-iTop').block();
|
||||
$('#ibo-about-box--loader').block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'about_box'}, function(data){
|
||||
$('#block-about-iTop').unblock();
|
||||
$('#ibo-about-box--loader').unblock();
|
||||
$('body').append(data);
|
||||
}).always(function() {
|
||||
loadingDialog.empty();
|
||||
|
||||
@@ -2515,7 +2515,7 @@ EOF
|
||||
);
|
||||
|
||||
$oSet = new DBObjectSet($oSearch, [], $aSearchParams);
|
||||
$oSet->OptimizeColumnLoad(array($oSearch->GetClassAlias() => array()));
|
||||
$oSet->OptimizeColumnLoad([$oSearch->GetClassAlias() => [$sObjectImageAttCode]]);
|
||||
$oSet->SetLimit(MetaModel::GetConfig()->Get('max_autocomplete_results'));
|
||||
// Note: We have to this manually because of a bug in DBSearch not checking the user prefs. by default.
|
||||
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
@@ -2540,7 +2540,7 @@ EOF
|
||||
} else {
|
||||
// If no image found, fallback on initials
|
||||
$aMatch['picture_style'] = '';
|
||||
$aMatch['initials'] = utils::ToAcronym($oObject->Get('friendlyname'));
|
||||
$aMatch['initials'] = utils::FormatInitialsForMedallion(utils::ToAcronym($oObject->Get('friendlyname')));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -773,12 +773,13 @@ class AjaxRenderController
|
||||
}
|
||||
|
||||
/**
|
||||
* display list of licenses in "About iTop" popup
|
||||
* Display list of licenses in "About iTop" popup
|
||||
* @param \AjaxPage $oPage
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.0.1
|
||||
*/
|
||||
private static function DisplayAboutLicenses( AjaxPage $oPage): void
|
||||
private static function DisplayAboutLicenses(AjaxPage $oPage): void
|
||||
{
|
||||
$sCurrEnv = utils::GetCurrentEnvironment();
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
@@ -804,6 +805,7 @@ JS
|
||||
$oPage->add('</fieldset>');
|
||||
$oPage->add("</div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Display about iTop for all user non admin
|
||||
* @param \AjaxPage $oPage
|
||||
@@ -829,7 +831,7 @@ EOF
|
||||
$oPage->add('<a href="http://www.combodo.com" title="www.combodo.com" target="_blank" style="background: none;"><img src="../images/logo-combodo.png?t='.utils::GetCacheBusterTimestamp().'"/></a>');
|
||||
$oPage->add('<div>'.$sVersionString.'</div>');
|
||||
$oPage->add("</div>");
|
||||
self::DisplayAboutLicenses( $oPage);
|
||||
self::DisplayAboutLicenses($oPage);
|
||||
$oPage->add("</div>");
|
||||
}
|
||||
|
||||
@@ -885,8 +887,6 @@ EOF
|
||||
}
|
||||
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation(MetaModel::GetConfig(), $aSearchDirs);
|
||||
|
||||
|
||||
|
||||
$aItopSettings = array('cron_max_execution_time', 'timezone');
|
||||
$aPHPSettings = array('memory_limit', 'max_execution_time', 'upload_max_filesize', 'post_max_size');
|
||||
$aMySQLSettings = array('max_allowed_packet', 'key_buffer_size', 'query_cache_size');
|
||||
|
||||
@@ -648,7 +648,7 @@ HTML
|
||||
// Open medallion from profile picture or first name letter
|
||||
$bEntryHasMedallionPicture = (empty($aContactPicturesCache[$iEntryUserId]) === false);
|
||||
$sEntryMedallionStyle = $bEntryHasMedallionPicture ? ' background-image: url(\''.$aContactPicturesCache[$iEntryUserId].'\');' : '';
|
||||
$sEntryMedallionContent = $bEntryHasMedallionPicture ? '' : UserRights::GetUserInitials($sEntryUserLogin);
|
||||
$sEntryMedallionContent = $bEntryHasMedallionPicture ? '' : utils::FormatInitialsForMedallion(UserRights::GetUserInitials($sEntryUserLogin));
|
||||
// - Entry tooltip
|
||||
$sEntryMedallionTooltip = utils::HtmlEntities($sEntryUserLogin);
|
||||
$sEntryMedallionTooltipPlacement = ($iEntryUserId === $iCurrentUserId) ? 'left' : 'right';
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{% if oUIBlock.GetAuthorPictureAbsUrl() is not empty %}
|
||||
<img class="ibo-activity-entry--author-picture" src="{{ oUIBlock.GetAuthorPictureAbsUrl() }}" alt="{{ oUIBlock.GetAuthorInitials() }}">
|
||||
{% else %}
|
||||
<div class="ibo-activity-entry--author-initials">{{ oUIBlock.GetAuthorInitials() }}</div>
|
||||
<div class="ibo-activity-entry--author-initials">{{ oUIBlock.GetAuthorInitials()|slice(0, 3) }}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
@@ -434,7 +434,7 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase
|
||||
public function testToAcronym(string $sInput, string $sExceptedAcronym)
|
||||
{
|
||||
$sTestedAcronym = utils::ToAcronym($sInput);
|
||||
$this->assertEquals($sTestedAcronym, $sExceptedAcronym, "Acronym for '$sInput' doesn't match. Got '$sTestedAcronym', expected '$sExceptedAcronym'.");
|
||||
$this->assertEquals($sExceptedAcronym, $sTestedAcronym, "Acronym for '$sInput' doesn't match. Got '$sTestedAcronym', expected '$sExceptedAcronym'.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,6 +479,10 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase
|
||||
'Jada Pinkett-smith',
|
||||
'JP',
|
||||
],
|
||||
'Several words, cyrillic alphabet' => [
|
||||
'Денис Александра',
|
||||
'ДА',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -550,6 +554,40 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider FormatInitialsForMedallionProvider
|
||||
* @covers utils::FormatInitialsForMedallion
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sExpected
|
||||
*/
|
||||
public function testFormatInitialsForMedallion(string $sInput, string $sExpected)
|
||||
{
|
||||
$sTested = utils::FormatInitialsForMedallion($sInput);
|
||||
$this->assertEquals($sExpected, $sTested);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public function FormatInitialsForMedallionProvider()
|
||||
{
|
||||
return [
|
||||
'All letters kept (2)' => [
|
||||
'AB',
|
||||
'AB',
|
||||
],
|
||||
'All letters kept (3)' => [
|
||||
'ABC',
|
||||
'ABC',
|
||||
],
|
||||
'Only 3 first letters kept (4)' => [
|
||||
'ABCD',
|
||||
'ABC',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sExpressionToConvert
|
||||
* @param int $iExpectedConvertedValue
|
||||
|
||||
@@ -36,30 +36,36 @@ class iTopModuleXmlInstallationChecklistTest extends ItopTestCase
|
||||
public function testAllModuleAreIncludedInInstallationXml()
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/2.x/installation.xml';
|
||||
if (!is_file($sInstallationXmlPath))
|
||||
{
|
||||
if (!is_file($sInstallationXmlPath)) {
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/1.x/installation.xml';
|
||||
}
|
||||
$this->assertTrue(is_file($sInstallationXmlPath), "$sInstallationXmlPath does not exist");
|
||||
|
||||
$sInstallationXmlContent = file_get_contents($sInstallationXmlPath);
|
||||
preg_match_all("|<module>(.*)</module>|", $sInstallationXmlContent, $aMatches);
|
||||
$aDeclaredModules = [] ;
|
||||
if (!empty($aMatches))
|
||||
{
|
||||
foreach ($aMatches[1] as $sModule)
|
||||
{
|
||||
if (!array_key_exists($sModule, $aDeclaredModules))
|
||||
{
|
||||
$aDeclaredModules = [];
|
||||
if (!empty($aMatches)) {
|
||||
foreach ($aMatches[1] as $sModule) {
|
||||
if (!array_key_exists($sModule, $aDeclaredModules)) {
|
||||
$aDeclaredModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertArraySubset($this->GetFilteredModulesFromDatamodels(APPROOT.'/datamodels'), $aDeclaredModules, false, "$sInstallationXmlPath does not refer to all provided modules. Refered modules:\n " . var_export($aDeclaredModules, true));
|
||||
$this->assertArraySubset(
|
||||
$this->GetFilteredModulesFromDatamodels(APPROOT.'/datamodels'),
|
||||
$aDeclaredModules,
|
||||
false,
|
||||
"{$sInstallationXmlPath} does not list all modules in /datamodels ! List of modules in installation.xml:\n ".var_export($aDeclaredModules, true)
|
||||
);
|
||||
|
||||
$aModulesFromDatamodels = $this->GetAllModules(APPROOT.'/datamodels');
|
||||
$this->assertArraySubset($aDeclaredModules, $aModulesFromDatamodels, false, "Not all modules are contained in $sInstallationXmlPath. Refered modules:\n " . var_export($aModulesFromDatamodels, true));
|
||||
$this->assertArraySubset(
|
||||
$aDeclaredModules,
|
||||
$aModulesFromDatamodels,
|
||||
false,
|
||||
"Not all modules are contained in {$sInstallationXmlPath}. List of modules in /datamodels:\n ".var_export($aModulesFromDatamodels, true)
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFilteredModulesFromDatamodels($sFolder)
|
||||
|
||||
Reference in New Issue
Block a user