Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

N°7385 - Trigger on mention executed even if it's not a mention with @ #638

Open
wants to merge 5 commits into
base: support/3.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 79 additions & 69 deletions application/utils.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ protected static function Sanitize_Internal($value, $sSanitizationFilter)

// 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;

Expand Down Expand Up @@ -555,44 +555,44 @@ public static function ReadPostedDocument($sName, $sIndex = null)
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;

}
}
Expand Down Expand Up @@ -890,10 +890,10 @@ public static function StringToTime($sDate, $sFormat)
$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;
Expand All @@ -902,11 +902,11 @@ public static function StringToTime($sDate, $sFormat)
$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
}

/**
Expand Down Expand Up @@ -1335,16 +1335,16 @@ public static function GetSessionLog()
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 !
{
$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
Expand Down Expand Up @@ -1540,7 +1540,7 @@ public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenu
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
);
}

Expand Down Expand Up @@ -1962,7 +1962,7 @@ public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &
CURLOPT_HTTPHEADER => $aHTTPHeaders,
);

$aAllOptions = $aCurlOptions + $aOptions;
$aAllOptions = $aCurlOptions + $aOptions;
$ch = curl_init($sUrl);
curl_setopt_array($ch, $aAllOptions);
$response = curl_exec($ch);
Expand All @@ -1987,7 +1987,7 @@ public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &
/**
* 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())
Expand Down Expand Up @@ -2222,13 +2222,13 @@ public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight,
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)
{
Expand Down Expand Up @@ -2260,16 +2260,16 @@ public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight,
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();
Expand Down Expand Up @@ -2299,16 +2299,16 @@ public static function CreateUUID($sPrefix = '')
$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;
}

Expand All @@ -2320,7 +2320,7 @@ public static function CreateUUID($sPrefix = '')
*/
public static function GetCurrentModuleName($iCallDepth = 0)
{
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
}

/**
Expand Down Expand Up @@ -2367,7 +2367,7 @@ public static function GetCurrentModuleUrl()
*/
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
}

/**
Expand All @@ -2376,7 +2376,7 @@ public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
*/
public static function GetCompiledModuleVersion($sModuleName)
{
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
}

/**
Expand Down Expand Up @@ -3110,19 +3110,29 @@ public static function GetMentionedObjectsFromText(string $sText): array
$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];

// 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;
}
$sMatchedName = $aMentionMatches[5][$iMatchIdx];

$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;
Expand Down
5 changes: 2 additions & 3 deletions tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -520,14 +520,13 @@ protected function AssertArraysHaveSameItems(array $aExpected, array $aActual, s
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);
}
Expand Down
60 changes: 0 additions & 60 deletions tests/php-unit-tests/unitary-tests/application/utilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -645,66 +645,6 @@ public function ToAcronymProvider()
];
}

/**
* @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
Expand Down
Loading