mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°7385 - Trigger on mention executed even if it's not a mention with @ (#638)
This commit is contained in:
@@ -521,7 +521,7 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
// N°6350 - returns only valid URLs
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
break;
|
||||
|
||||
@@ -554,44 +554,44 @@ class utils
|
||||
switch($sError)
|
||||
{
|
||||
case UPLOAD_ERR_OK:
|
||||
$sTmpName = is_null($sIndex) ? $aFileInfo['tmp_name'] : $aFileInfo['tmp_name'][$sIndex];
|
||||
$sMimeType = is_null($sIndex) ? $aFileInfo['type'] : $aFileInfo['type'][$sIndex];
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
$sTmpName = is_null($sIndex) ? $aFileInfo['tmp_name'] : $aFileInfo['tmp_name'][$sIndex];
|
||||
$sMimeType = is_null($sIndex) ? $aFileInfo['type'] : $aFileInfo['type'][$sIndex];
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
|
||||
$doc_content = file_get_contents($sTmpName);
|
||||
$doc_content = file_get_contents($sTmpName);
|
||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||
break;
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
break;
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:CannotWriteToTmp_Dir', ini_get('upload_tmp_dir')));
|
||||
break;
|
||||
throw new FileUploadException(Dict::Format('UI:Error:CannotWriteToTmp_Dir', ini_get('upload_tmp_dir')));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||
break;
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||
break;
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -889,10 +889,10 @@ class utils
|
||||
$aDateRegexps = array_values($aSpec);
|
||||
}
|
||||
|
||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||
|
||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||
{
|
||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||
{
|
||||
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
||||
$sMonth = isset($aMatches['month']) ? $aMatches['month'] : 1;
|
||||
$sDay = isset($aMatches['day']) ? $aMatches['day'] : 1;
|
||||
@@ -901,11 +901,11 @@ class utils
|
||||
$sSecond = isset($aMatches['second']) ? $aMatches['second'] : 0;
|
||||
return strtotime("$sYear-$sMonth-$sDay $sHour:$sMinute:$sSecond");
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// http://www.spaweditor.com/scripts/regex/index.php
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// http://www.spaweditor.com/scripts/regex/index.php
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1334,8 +1334,8 @@ class utils
|
||||
return Session::GetLog();
|
||||
}
|
||||
|
||||
static function DebugBacktrace($iLimit = 5)
|
||||
{
|
||||
static function DebugBacktrace($iLimit = 5)
|
||||
{
|
||||
$aFullTrace = debug_backtrace();
|
||||
$aLightTrace = array();
|
||||
for($i=1; ($i<=$iLimit && $i < count($aFullTrace)); $i++) // Skip the last function call... which is the call to this function !
|
||||
@@ -1343,7 +1343,7 @@ class utils
|
||||
$aLightTrace[$i] = $aFullTrace[$i]['function'].'(), called from line '.$aFullTrace[$i]['line'].' in '.$aFullTrace[$i]['file'];
|
||||
}
|
||||
echo "<p><pre>".print_r($aLightTrace, true)."</pre></p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given iTop PHP script, passing it the current credentials
|
||||
@@ -1539,7 +1539,7 @@ class utils
|
||||
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH) {
|
||||
// Static menus: Email this page, CSV Export & Add to Dashboard
|
||||
$aResult[] = new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'),
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1961,7 +1961,7 @@ SQL;
|
||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||
);
|
||||
|
||||
$aAllOptions = $aCurlOptions + $aOptions;
|
||||
$aAllOptions = $aCurlOptions + $aOptions;
|
||||
$ch = curl_init($sUrl);
|
||||
curl_setopt_array($ch, $aAllOptions);
|
||||
$response = curl_exec($ch);
|
||||
@@ -1986,7 +1986,7 @@ SQL;
|
||||
/**
|
||||
* Get a standard list of character sets
|
||||
*
|
||||
* @param array $aAdditionalEncodings Additional values
|
||||
* @param array $aAdditionalEncodings Additional values
|
||||
* @return array of iconv code => english label, sorted by label
|
||||
*/
|
||||
public static function GetPossibleEncodings($aAdditionalEncodings = array())
|
||||
@@ -2221,13 +2221,13 @@ SQL;
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
case 'image/png':
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
return $oImage;
|
||||
// Unsupported image type, return the image as-is
|
||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
return $oImage;
|
||||
}
|
||||
if ($img === false)
|
||||
{
|
||||
@@ -2259,16 +2259,16 @@ SQL;
|
||||
switch ($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
imagegif($new); // send image to output buffer
|
||||
break;
|
||||
imagegif($new); // send image to output buffer
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
}
|
||||
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||
@ob_end_clean();
|
||||
@@ -2298,16 +2298,16 @@ SQL;
|
||||
$data .= mt_rand();
|
||||
$hash = strtoupper(hash('ripemd128', $uid . md5($data)));
|
||||
$sUUID = '{' .
|
||||
substr($hash, 0, 8) .
|
||||
'-' .
|
||||
substr($hash, 8, 4) .
|
||||
'-' .
|
||||
substr($hash, 12, 4) .
|
||||
'-' .
|
||||
substr($hash, 16, 4) .
|
||||
'-' .
|
||||
substr($hash, 20, 12) .
|
||||
'}';
|
||||
substr($hash, 0, 8) .
|
||||
'-' .
|
||||
substr($hash, 8, 4) .
|
||||
'-' .
|
||||
substr($hash, 12, 4) .
|
||||
'-' .
|
||||
substr($hash, 16, 4) .
|
||||
'-' .
|
||||
substr($hash, 20, 12) .
|
||||
'}';
|
||||
return $sUUID;
|
||||
}
|
||||
|
||||
@@ -2319,7 +2319,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2366,7 +2366,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2375,7 +2375,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3109,19 +3109,29 @@ TXT
|
||||
$aMentionMatches = [];
|
||||
$sText = html_entity_decode($sText);
|
||||
|
||||
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"/Ui', $sText, $aMentionMatches);
|
||||
$aMentionAllowedClasses = MetaModel::GetConfig()->Get('mentions.allowed_classes');
|
||||
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"\s*([^>]*)>(.*)<\/a>/Ui', $sText, $aMentionMatches);
|
||||
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
$sMatchedName = $aMentionMatches[5][$iMatchIdx];
|
||||
|
||||
// Prepare array for matched class if not already present
|
||||
if (!array_key_exists($sMatchedClass, $aMentionedObjects)) {
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if (!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass])) {
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
$sMentionPrefix = array_search($sMatchedClass, $aMentionAllowedClasses);
|
||||
if ($sMentionPrefix === false) {
|
||||
continue;
|
||||
}
|
||||
//tests if the name starts with $sMentionPrefix (e.g. '@' for 'Contact' class)
|
||||
if (str_starts_with($sMatchedName, $sMentionPrefix) === false) {
|
||||
continue;
|
||||
}
|
||||
// Prepare array for matched class if not already present
|
||||
if (!array_key_exists($sMatchedClass, $aMentionedObjects)) {
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if (!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass])) {
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
}
|
||||
|
||||
return $aMentionedObjects;
|
||||
|
||||
@@ -521,14 +521,13 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
sort($aActual);
|
||||
sort($aExpected);
|
||||
|
||||
$sExpected = implode("\n", $aExpected);
|
||||
$sActual = implode("\n", $aActual);
|
||||
$sExpected = var_export($aExpected, true);
|
||||
$sActual = var_export($aActual, true);
|
||||
if ($sExpected === $sActual) {
|
||||
$this->assertTrue(true);
|
||||
return;
|
||||
}
|
||||
$sMessage .= "\nExpected:\n$sExpected\nActual:\n$sActual";
|
||||
var_export($aActual);
|
||||
|
||||
$this->fail($sMessage);
|
||||
}
|
||||
|
||||
@@ -645,66 +645,6 @@ class utilsTest extends ItopTestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider GetMentionedObjectsFromTextProvider
|
||||
* @covers utils::GetMentionedObjectsFromText
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testGetMentionedObjectsFromText($sInput, $aExceptedMentionedObjects)
|
||||
{
|
||||
// Emulate the "Case provider mechanism" (reason: the data provider requires utils constants not available before the application startup)
|
||||
echo "testGetMentionedObjectsFromText: input = $sInput\n";
|
||||
$aTestedMentionedObjects = utils::GetMentionedObjectsFromText($sInput);
|
||||
|
||||
$sExpectedAsString = print_r($aExceptedMentionedObjects, true);
|
||||
$sTestedAsString = print_r($aTestedMentionedObjects, true);
|
||||
|
||||
$this->assertEquals($sExpectedAsString, $sTestedAsString, "Found mentioned objects don't match. Got: $sTestedAsString, expected $sExpectedAsString");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetMentionedObjectsFromTextProvider(): array
|
||||
{
|
||||
$sAbsUrlAppRoot = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
return [
|
||||
'No object' => [
|
||||
"Begining
|
||||
Second line
|
||||
End",
|
||||
[],
|
||||
],
|
||||
'1 UserRequest' => [
|
||||
<<<HTML
|
||||
<p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="UserRequest" data-object-key="12345" data-object-id="#Test Ticket" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=UserRequest&id=12345">#Test Ticket</a>After link</p><p>End</p>
|
||||
HTML,
|
||||
[
|
||||
'UserRequest' => ['12345'],
|
||||
],
|
||||
],
|
||||
'2 UserRequests' => [
|
||||
<<<HTML
|
||||
<div class="ibo-activity-entry--main-information-content"><p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="UserRequest" data-object-key="12345" data-object-id="#Test Ticket 1" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=UserRequest&id=12345">#Test Ticket</a> After link</p><p>And <a data-role="object-mention" data-object-class="UserRequest" data-object-key="987654" data-object-id="#Test Ticket 2" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=UserRequest&id=987654">#Test Ticket</a></p><p>End</p></div>
|
||||
HTML,
|
||||
[
|
||||
'UserRequest' => ['12345', '987654'],
|
||||
],
|
||||
],
|
||||
'1 UserRequest, 1 Person' => [
|
||||
<<<HTML
|
||||
<div class="ibo-activity-entry--main-information-content"><div class="ibo-activity-entry--main-information-content"><p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="UserRequest" data-object-key="12345" data-object-id="#Test Ticket" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=UserRequest&id=12345">#Test Ticket</a> After link</p><p>And <a data-role="object-mention" data-object-class="Person" data-object-id="@Agatha Christie" data-object-key="3" data-object-id="@Agatha Christie" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=Person&id=3">@Agatha Christie</a></p><p>End</p></div></div>
|
||||
HTML,
|
||||
[
|
||||
'UserRequest' => ['12345'],
|
||||
'Person' => ['3'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider FormatInitialsForMedallionProvider
|
||||
* @covers utils::FormatInitialsForMedallion
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* @covers utils
|
||||
*/
|
||||
class utilsTestWithDataModel extends ItopDataTestCase
|
||||
{
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
/**
|
||||
* @dataProvider GetMentionedObjectsFromTextProvider
|
||||
* @covers utils::GetMentionedObjectsFromText
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testGetMentionedObjectsFromText($sInput, $aExceptedMentionedObjects)
|
||||
{
|
||||
MetaModel::GetConfig()->Set('mentions.allowed_classes', ['@' => 'Person', '😊#' => 'Team']);
|
||||
// Emulate the "Case provider mechanism" (reason: the data provider requires utils constants not available before the application startup)
|
||||
$aTestedMentionedObjects = utils::GetMentionedObjectsFromText($sInput);
|
||||
|
||||
$this->AssertArraysHaveSameItems($aExceptedMentionedObjects, $aTestedMentionedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetMentionedObjectsFromTextProvider(): array
|
||||
{
|
||||
$sAbsUrlAppRoot = 'https://myitop.com/itop/';
|
||||
|
||||
return [
|
||||
'No object' => [
|
||||
"Begining
|
||||
Second line
|
||||
End",
|
||||
[],
|
||||
],
|
||||
'1 Object' => [
|
||||
<<<HTML
|
||||
<p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="Person" data-object-key="12345" data-object-id="#Test Person" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=Person&id=12345">@Test Person</a>After link</p><p>End</p>
|
||||
HTML,
|
||||
[
|
||||
'Person' => ['12345'],
|
||||
],
|
||||
],
|
||||
'Should not match 1 Object if the mention prefix is missing' => [
|
||||
<<<HTML
|
||||
<div class="ibo-activity-entry--main-information-content"><p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="Person" data-object-key="12345" data-object-id="#Test Person 1" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=Person&id=12345">#Test Ticket</a> After link</p></div>
|
||||
HTML,
|
||||
[],
|
||||
],
|
||||
'Should return 2 Objects' => [
|
||||
<<<HTML
|
||||
<div class="ibo-activity-entry--main-information-content"><div class="ibo-activity-entry--main-information-content"><p>Beginning</p><p>Before link <a data-role="object-mention" data-object-class="Person" data-object-key="12345" data-object-id="#Test Person" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=UserRequest&id=12345">@Test Person</a> After link</p><p>And <a data-role="object-mention" data-object-class="Person" data-object-id="@Agatha Christie" data-object-key="3" data-object-id="@Agatha Christie" href="$sAbsUrlAppRoot/pages/UI.php?operation=details&class=Person&id=3">@Agatha Christie</a></p><p>End</p></div></div>
|
||||
HTML,
|
||||
[
|
||||
'Person' => ['12345', '3'],
|
||||
],
|
||||
],
|
||||
'Should process objects of different classes' => [
|
||||
<<<HTML
|
||||
Begining
|
||||
Before link <a data-object-class="Team" data-object-key="12345" href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=Team&id=12345&foo=bar\">😊#R-012345</a> After link
|
||||
And <a data-object-class="Person" data-object-key="3" href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=Person&id=3&foo=bar\">@Claude Monet</a>
|
||||
End
|
||||
HTML,
|
||||
[
|
||||
'Team' => ['12345'],
|
||||
'Person' => ['3'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user