N°7552 - Fix mentions not taking triggers filter into account

This commit is contained in:
Molkobain
2024-07-04 11:30:10 +02:00
parent 8c4c0ff5e1
commit c3582f0aff
6 changed files with 125 additions and 122 deletions

View File

@@ -107,7 +107,7 @@ class CKEditorHelper
'minimumCharacters' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
'feed_type' => 'ajax',
'feed_ajax_options' => [
'url' => utils::GetAbsoluteUrlAppRoot(). "pages/ajax.render.php?route=object.search&object_class=$sMentionClass&oql=SELECT $sMentionClass&search=",
'url' => utils::GetAbsoluteUrlAppRoot(). "pages/ajax.render.php?route=object.search_for_mentions&marker=".urlencode($sMentionMarker)."&needle=",
'throttle' => 500,
'marker' => $sMentionMarker,
],

View File

@@ -217,12 +217,23 @@ class CaseLogEntryForm extends UIContentBlock
$this->oTextInput = new RichText();
// Add the "host_class" to the mention endpoints so it can filter objects regarding the triggers
// Mind that `&needle=` must be ending the endpoint URL in order for the JS plugin to append the needle string
$aConfig = $this->oTextInput->GetConfig();
if (isset($aConfig['mentions'])) {
foreach ($aConfig['mentions'] as $iIdx => $aData) {
$sFeed = $aConfig['mentions'][$iIdx]['feed'];
if (isset($aConfig['mention']['feeds'])) {
foreach ($aConfig['mention']['feeds'] as $iIdx => $aData) {
$sFeed = $aConfig['mention']['feeds'][$iIdx]['feed_ajax_options']['url'];
// Remove existing "needle" parameter
$sFeed = str_replace('&needle=', '', $sFeed);
// Add new parameters
$sFeed = utils::AddParameterToUrl($sFeed, 'host_class', $this->GetObjectClass());
$aConfig['mentions'][$iIdx]['feed'] = utils::AddParameterToUrl($sFeed, 'host_id', $this->GetObjectId());
$sFeed = utils::AddParameterToUrl($sFeed, 'host_id', $this->GetObjectId());
// Re-append "needle" parameter
$sFeed = utils::AddParameterToUrl($sFeed, 'needle', '');
$aConfig['mention']['feeds'][$iIdx]['feed_ajax_options']['url'] = $sFeed;
}
}
$this->oTextInput->SetConfig($aConfig);

View File

@@ -21,6 +21,10 @@ use Combodo\iTop\Controller\AbstractController;
use Combodo\iTop\Service\Base\ObjectRepository;
use Combodo\iTop\Service\Router\Router;
use CoreCannotSaveObjectException;
use DBObjectSearch;
use DBObjectSet;
use DBSearch;
use DBUnionSearch;
use DeleteException;
use Dict;
use Exception;
@@ -833,6 +837,95 @@ JS;
]);
}
public function OperationSearchForMentions(): JsonPage
{
$oPage = new JsonPage();
$oPage->SetOutputDataOnly(true);
$sMarker = utils::ReadParam('marker', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$sNeedle = utils::ReadParam('needle', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$sHostClass = utils::ReadParam('host_class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
$iHostId = (int) utils::ReadParam('host_id', 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER);
// Check parameters
if (utils::IsNullOrEmptyString($sMarker)) {
throw new ApplicationException('Invalid parameters, marker must be specified.');
}
if (utils::IsNullOrEmptyString($sNeedle)) {
throw new ApplicationException('Invalid parameters, needle must be specified.');
}
$aMentionsAllowedClasses = MetaModel::GetConfig()->Get('mentions.allowed_classes');
if (isset($aMentionsAllowedClasses[$sMarker]) === false) {
throw new ApplicationException('Invalid marker "'.$sMarker.'"');
}
$aMatches = array();
// Retrieve mentioned class from marker
$sMentionedClass = $aMentionsAllowedClasses[$sMarker];
if (MetaModel::IsValidClass($sMentionedClass) === false) {
throw new ApplicationException('Invalid class "'.$sMentionedClass.'" for marker "'.$sMarker.'"');
}
// Base search used when no trigger configured
$oSearch = DBSearch::FromOQL("SELECT $sMentionedClass");
$aSearchParams = ['needle' => "%$sNeedle%"];
// Retrieve restricting scopes from triggers if any
if (utils::IsNotNullOrEmptyString($sHostClass) && ($iHostId > 0)) {
$oHostObj = MetaModel::GetObject($sHostClass, $iHostId);
$aSearchParams['this'] = $oHostObj;
$aTriggerMentionedSearches = [];
$aTriggerSetParams = array('class_list' => MetaModel::EnumParentClasses($sHostClass, ENUM_PARENT_CLASSES_ALL));
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aTriggerSetParams);
/** @var \TriggerOnObjectMention $oTrigger */
while ($oTrigger = $oTriggerSet->Fetch()) {
$sTriggerMentionedOQL = $oTrigger->Get('mentioned_filter');
// No filter on mentioned objects, don't restrict the scope at all, it can be any object of $sMentionedClass
if (utils::IsNullOrEmptyString($sTriggerMentionedOQL)) {
$aTriggerMentionedSearches = [$oSearch];
break;
}
$oTriggerMentionedSearch = DBSearch::FromOQL($sTriggerMentionedOQL);
$sTriggerMentionedClass = $oTriggerMentionedSearch->GetClass();
// Filter is not about the mentioned class, don't mind it
if (is_a($sMentionedClass, $sTriggerMentionedClass, true) === false) {
continue;
}
$aTriggerMentionedSearches[] = $oTriggerMentionedSearch;
}
if (count($aTriggerMentionedSearches) > 0) {
$oSearch = new DBUnionSearch($aTriggerMentionedSearches);
}
}
$sSearchMainClassName = $oSearch->GetClass();
$sSearchMainClassAlias = $oSearch->GetClassAlias();
$sObjectImageAttCode = MetaModel::GetImageAttributeCode($sSearchMainClassName);
// Optimize fields to load
$aObjectAttCodesToLoad = [];
if (MetaModel::IsValidAttCode($sSearchMainClassName, $sObjectImageAttCode)) {
$aObjectAttCodesToLoad[] = $sObjectImageAttCode;
}
$aResult = ObjectRepository::SearchFromOql($sSearchMainClassName, $aObjectAttCodesToLoad, $oSearch->ToOQL(), $sNeedle, $oHostObj, MetaModel::GetConfig()->Get('max_autocomplete_results'));
return $oPage->SetData([
'search_data' => $aResult,
'success' => $aResult !== null,
]);
}
/**
* OperationGet.
*

View File

@@ -79,10 +79,12 @@ class ObjectRepository
* @param string $sOql Oql expression
* @param string $sSearch Friendly name search string
* @param DBObject|null $oThisObject This object reference for oql
* @param int $iLimit Limit results to the $iLimit first elements
*
* @return array|null
* @since 3.2.0 Add $iLimit parameter
*/
public static function SearchFromOql(string $sObjectClass, array $aFieldsToLoad, string $sOql, string $sSearch, DBObject $oThisObject = null): ?array
public static function SearchFromOql(string $sObjectClass, array $aFieldsToLoad, string $sOql, string $sSearch, DBObject $oThisObject = null, int $iLimit = 0): ?array
{
try {
@@ -94,6 +96,11 @@ class ObjectRepository
// Create db set from db search
$oDbObjectSet = new DBObjectSet($oDbObjectSearch, [], ['this' => $oThisObject]);
// Limit results
if ($iLimit > 0) {
$oDbObjectSet->SetLimit($iLimit);
}
// return object array
return ObjectRepository::DBSetToObjectArray($oDbObjectSet, $sObjectClass, $aFieldsToLoad);
}