diff --git a/application/utils.inc.php b/application/utils.inc.php index 37a013608..14e3face3 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -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('!^(?
)'.$sDateRegexp.'(?".print_r($aLightTrace, true)."\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('/]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"/Ui', $sText, $aMentionMatches); + $aMentionAllowedClasses = MetaModel::GetConfig()->Get('mentions.allowed_classes'); + preg_match_all('/]*)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; diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php index 5b13815d6..dabe84d58 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php @@ -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); } diff --git a/tests/php-unit-tests/unitary-tests/application/utilsTest.php b/tests/php-unit-tests/unitary-tests/application/utilsTest.php index 824cbcccb..a7a7f08a9 100644 --- a/tests/php-unit-tests/unitary-tests/application/utilsTest.php +++ b/tests/php-unit-tests/unitary-tests/application/utilsTest.php @@ -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' => [ - <<Beginning
Before link #Test TicketAfter link
End
-HTML, - [ - 'UserRequest' => ['12345'], - ], - ], - '2 UserRequests' => [ - <<Beginning
Before link #Test Ticket After link
And #Test Ticket
End
-HTML, - [ - 'UserRequest' => ['12345', '987654'], - ], - ], - '1 UserRequest, 1 Person' => [ - << -HTML, - [ - 'UserRequest' => ['12345'], - 'Person' => ['3'], - ], - ], - ]; - } - /** * @dataProvider FormatInitialsForMedallionProvider * @covers utils::FormatInitialsForMedallion diff --git a/tests/php-unit-tests/unitary-tests/application/utilsTestWithDataModel.php b/tests/php-unit-tests/unitary-tests/application/utilsTestWithDataModel.php new file mode 100644 index 000000000..5fd4068d8 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/application/utilsTestWithDataModel.php @@ -0,0 +1,85 @@ +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' => [ + <<BeginningBefore link @Test PersonAfter link
End
+HTML, + [ + 'Person' => ['12345'], + ], + ], + 'Should not match 1 Object if the mention prefix is missing' => [ + <<Beginning
Before link #Test Ticket After link
+HTML, + [], + ], + 'Should return 2 Objects' => [ + << +HTML, + [ + 'Person' => ['12345', '3'], + ], + ], + 'Should process objects of different classes' => [ + <<😊#R-012345 After link + And @Claude Monet + End +HTML, + [ + 'Team' => ['12345'], + 'Person' => ['3'], + ], + ], + ]; + } +}