N°3531 - Activity panel: Restore possibility to load extra history entries asynchroniously

This commit is contained in:
Molkobain
2021-03-16 22:29:36 +01:00
parent 190ac1a65a
commit b97e2839c5
10 changed files with 394 additions and 63 deletions

View File

@@ -8,9 +8,17 @@ namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel;
use appUserPreferences;
use BinaryExpression;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EditsEntry;
use DBObjectSearch;
use DBObjectSet;
use Exception;
use FieldExpression;
use IssueLog;
use MetaModel;
use VariableExpression;
/**
* Class ActivityPanelHelper
@@ -75,4 +83,99 @@ class ActivityPanelHelper
$aStates[$sObjectClass.'::'.$sObjectMode] = $bIsClosed;
appUserPreferences::SetPref('activity_panel.is_closed', $aStates);
}
/**
* @param string $sObjectClass
* @param string $sObjectId
* @param string|null $sChangeOpIdToOffsetFrom Entries will be retrieved after this CMDBChangeOp ID. Typically used for pagination.
*
* @return array The 'max_history_length' edits entries from the CMDBChangeOp of the object, starting from $sChangeOpIdToOffsetFrom. Flag to know if more entries are available and the ID of the last returned entry are also provided.
*
* [
* 'entries' => EditsEntry[],
* 'last_loaded_entry_id' => null|int,
* 'more_entries_to_load' => bool,
* ]
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public static function GetCMDBChangeOpEditsEntriesForObject(string $sObjectClass, string $sObjectId, ?string $sChangeOpIdToOffsetFrom = null): array
{
$iMaxHistoryLength = MetaModel::GetConfig()->Get('max_history_length');
$aResults = [
'entries' => [],
'last_loaded_entry_id' => null,
'more_entries_to_load' => false,
];
// - Prepare query to retrieve changes
$oSearch = DBObjectSearch::FromOQL('SELECT CO FROM CMDBChangeOp AS CO WHERE CO.objclass = :obj_class AND CO.objkey = :obj_key AND CO.finalclass NOT IN (:excluded_optypes)');
$aArgs = ['obj_class' => $sObjectClass, 'obj_key' => $sObjectId, 'excluded_optypes' => ['CMDBChangeOpSetAttributeCaseLog']];
// - Optional offset condition
if (null !== $sChangeOpIdToOffsetFrom) {
$oSearch->AddConditionExpression(
new BinaryExpression(
new FieldExpression('id', 'CO'), '<', new VariableExpression('id')
)
);
$aArgs['id'] = $sChangeOpIdToOffsetFrom;
}
// Note: We can't order by date (only) as something multiple CMDBChangeOp rows are inserted at the same time (eg. Delivery model of the "Demo" Organization in the sample data).
// As the DB returns rows "chronologically", we get the older first and it messes with the processing. Ordering by the ID is way much simpler and less DB CPU consuming.
$oSet = new DBObjectSet($oSearch, ['id' => false], $aArgs);
// - Limit history entries to display
$bMoreEntriesToLoad = $oSet->CountExceeds($iMaxHistoryLength);
$oSet->SetLimit($iMaxHistoryLength);
// Prepare previous values to group edits within a same CMDBChange
$iPreviousChangeId = 0;
/** @var string|int $iPreviousChangeOpId Only used for pagination */
$iPreviousChangeOpId = 0;
$oPreviousEditsEntry = null;
/** @var \CMDBChangeOp $oChangeOp */
while ($oChangeOp = $oSet->Fetch()) {
// Skip case log changes as they are handled directly from the attributes themselves (most of them should have been excluded by the OQL above, but some derivated classes could still be retrieved)
if ($oChangeOp instanceof CMDBChangeOpSetAttributeCaseLog) {
continue;
}
// Make entry from CMDBChangeOp
$iChangeId = $oChangeOp->Get('change');
try {
$oEntry = ActivityEntryFactory::MakeFromCmdbChangeOp($oChangeOp);
}
catch (Exception $oException) {
IssueLog::Debug(static::class.': Could not create entry from CMDBChangeOp #'.$oChangeOp->GetKey().' related to '.$oChangeOp->Get('objclass').'::'.$oChangeOp->Get('objkey').': '.$oException->getMessage());
continue;
}
// If same CMDBChange and mergeable edits entry from the same author, we merge them
if (($iChangeId == $iPreviousChangeId) && ($oPreviousEditsEntry instanceof EditsEntry) && ($oEntry instanceof EditsEntry) && ($oPreviousEditsEntry->GetAuthorLogin() === $oEntry->GetAuthorLogin())) {
$oPreviousEditsEntry->Merge($oEntry);
} else {
$aResults['entries'][] = $oEntry;
// Set previous edits entry
if ($oEntry instanceof EditsEntry) {
$oPreviousEditsEntry = $oEntry;
}
}
$iPreviousChangeId = $iChangeId;
$iPreviousChangeOpId = $oChangeOp->GetKey();
}
unset($oSet);
// - Set last entry ID so the other can be loaded later
if ((true === $bMoreEntriesToLoad) && (0 !== $iPreviousChangeOpId)) {
$aResults['last_loaded_entry_id'] = $iPreviousChangeOpId;
$aResults['more_entries_to_load'] = true;
}
return $aResults;
}
}