mirror of
https://github.com/Combodo/iTop.git
synced 2026-07-18 20:56:38 +02:00
N°6275 - Propose CaseLog extensibility API to manage history rotate
This commit is contained in:
@@ -2246,3 +2246,25 @@ interface iModuleExtension
|
||||
*/
|
||||
public function __construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to manipulate ormCaseLog objects after initialization/edition
|
||||
*
|
||||
* @api
|
||||
* @private
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
interface iOrmCaseLogExtension
|
||||
{
|
||||
public function __construct();
|
||||
|
||||
/**
|
||||
* Rebuild API to be able manipulate ormCaseLog after its initialization/modifications
|
||||
* Examples of use: fix ormcase log broken index/shrink huge histories/....
|
||||
* @param string $sLog: ormcaselog description
|
||||
* @param array|null $aIndex: ormcaselog index
|
||||
*
|
||||
* @return bool: indicate whether current ormCaseLog fields were touched
|
||||
*/
|
||||
public function Rebuild(&$sLog, &$aIndex) : bool;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -25,10 +25,11 @@ use Combodo\iTop\Renderer\BlockRenderer;
|
||||
define('CASELOG_VISIBLE_ITEMS', 2);
|
||||
define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
|
||||
|
||||
require_once('ormcaselogservice.inc.php');
|
||||
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
@@ -47,19 +48,22 @@ class ormCaseLog {
|
||||
protected $m_sLog;
|
||||
protected $m_aIndex;
|
||||
protected $m_bModified;
|
||||
|
||||
protected \ormCaseLogService $oOrmCaseLogService;
|
||||
|
||||
/**
|
||||
* Initializes the log with the first (initial) entry
|
||||
* @param $sLog string The text of the whole case log
|
||||
* @param $aIndex array The case log index
|
||||
*/
|
||||
public function __construct($sLog = '', $aIndex = array())
|
||||
public function __construct($sLog = '', $aIndex = [], \ormCaseLogService $oOrmCaseLogService=null)
|
||||
{
|
||||
$this->m_sLog = $sLog;
|
||||
$this->m_aIndex = $aIndex;
|
||||
$this->m_bModified = false;
|
||||
$this->oOrmCaseLogService = (is_null($oOrmCaseLogService)) ? new \ormCaseLogService() : $oOrmCaseLogService;
|
||||
$this->RebuildIndex();
|
||||
}
|
||||
|
||||
|
||||
public function GetText($bConvertToPlainText = false)
|
||||
{
|
||||
if ($bConvertToPlainText)
|
||||
@@ -72,7 +76,7 @@ class ormCaseLog {
|
||||
return $this->m_sLog;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function FromJSON($oJson)
|
||||
{
|
||||
if (!isset($oJson->items))
|
||||
@@ -88,8 +92,8 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value that will be further JSON encoded
|
||||
*/
|
||||
* Return a value that will be further JSON encoded
|
||||
*/
|
||||
public function GetForJSON()
|
||||
{
|
||||
// Order by ascending date
|
||||
@@ -199,9 +203,9 @@ class ormCaseLog {
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
|
||||
$sPlainText .= $sSeparator.$aData['message'];
|
||||
}
|
||||
return $sPlainText;
|
||||
return $sPlainText;
|
||||
}
|
||||
|
||||
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->m_aIndex;
|
||||
@@ -227,7 +231,7 @@ class ormCaseLog {
|
||||
{
|
||||
return count($this->m_aIndex);
|
||||
}
|
||||
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
$this->m_bModified = false;
|
||||
@@ -235,7 +239,7 @@ class ormCaseLog {
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within an email
|
||||
*/
|
||||
*/
|
||||
public function GetAsEmailHtml()
|
||||
{
|
||||
$sStyleCaseLogHeader = '';
|
||||
@@ -312,10 +316,10 @@ class ormCaseLog {
|
||||
$sHtml .= '</td></tr></table>';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
|
||||
*/
|
||||
*/
|
||||
public function GetAsSimpleHtml($aTransfoHandler = null)
|
||||
{
|
||||
$sStyleCaseLogEntry = '';
|
||||
@@ -338,7 +342,7 @@ class ormCaseLog {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
|
||||
}
|
||||
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
||||
}
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<li>';
|
||||
@@ -396,7 +400,7 @@ class ormCaseLog {
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within the iTop framework
|
||||
*/
|
||||
*/
|
||||
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||
{
|
||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
||||
@@ -519,7 +523,7 @@ class ormCaseLog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -534,7 +538,7 @@ class ormCaseLog {
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
*
|
||||
* @since 3.0.0 New $iOnBehalfOfId parameter
|
||||
* @since 3.0.0 May throw \ArchivedObjectException exception
|
||||
*/
|
||||
@@ -585,6 +589,7 @@ class ormCaseLog {
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => static::ENUM_FORMAT_HTML,
|
||||
);
|
||||
$this->RebuildIndex();
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
@@ -620,7 +625,7 @@ class ormCaseLog {
|
||||
$iUserId = UserRights::GetUserId();
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
}
|
||||
|
||||
|
||||
if (isset($oJson->date))
|
||||
{
|
||||
$oDate = new DateTime($oJson->date);
|
||||
@@ -653,14 +658,14 @@ class ormCaseLog {
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => $sFormat,
|
||||
);
|
||||
|
||||
$this->RebuildIndex();
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
@@ -716,7 +721,7 @@ class ormCaseLog {
|
||||
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
||||
return $iLast;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text string corresponding to the given entry in the log (zero based index, older entries first)
|
||||
* @param integer $iIndex
|
||||
@@ -736,4 +741,16 @@ class ormCaseLog {
|
||||
$sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
||||
return InlineImage::FixUrls($sText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
public function RebuildIndex(): void
|
||||
{
|
||||
$oNewOrmCaseLog = $this->oOrmCaseLogService->Rebuild($this->m_sLog, $this->m_aIndex);
|
||||
if (! is_null($oNewOrmCaseLog)) {
|
||||
$this->m_aIndex = $oNewOrmCaseLog->m_aIndex;
|
||||
$this->m_sLog = $oNewOrmCaseLog->m_sLog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
core/ormcaselogservice.inc.php
Normal file
58
core/ormcaselogservice.inc.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service dedicated to ormCaseLog rebuild
|
||||
*
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
class ormCaseLogService
|
||||
{
|
||||
/**
|
||||
* Array of "providers" of welcome popup messages
|
||||
* @var iOrmCaseLogExtension[]
|
||||
*/
|
||||
protected $aOrmCaseLogExtension = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
protected function LoadCaseLogExtensions()
|
||||
{
|
||||
if ($this->aOrmCaseLogExtension !== null) return;
|
||||
|
||||
$aOrmCaseLogExtension = [];
|
||||
$aProviderClasses = \utils::GetClassesForInterface(iOrmCaseLogExtension::class, '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
|
||||
foreach($aProviderClasses as $sProviderClass) {
|
||||
$aOrmCaseLogExtension[] = new $sProviderClass();
|
||||
}
|
||||
$this->aOrmCaseLogExtension = $aOrmCaseLogExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sLog
|
||||
* @param array|null $aIndex
|
||||
*
|
||||
* @return \ormCaseLog|null: returns rebuilt ormCaseLog. null if not touched
|
||||
*/
|
||||
public function Rebuild($sLog, $aIndex) : ?\ormCaseLog
|
||||
{
|
||||
$this->LoadCaseLogExtensions();
|
||||
|
||||
$bTouched = false;
|
||||
foreach ($this->aOrmCaseLogExtension as $oOrmCaseLogExtension){
|
||||
/** var iOrmCaseLogExtension $oOrmCaseLogExtension */
|
||||
$bTouched = $bTouched || $oOrmCaseLogExtension->Rebuild($sLog, $aIndex);
|
||||
}
|
||||
|
||||
if ($bTouched){
|
||||
return new \ormCaseLog($sLog, $aIndex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2970,6 +2970,7 @@ return array(
|
||||
'lnkAuditCategoryToAuditDomain' => $baseDir . '/application/audit.domain.class.inc.php',
|
||||
'lnkTriggerAction' => $baseDir . '/core/trigger.class.inc.php',
|
||||
'ormCaseLog' => $baseDir . '/core/ormcaselog.class.inc.php',
|
||||
'ormCaseLogService' => $baseDir . '/core/ormcaselogservice.inc.php',
|
||||
'ormCustomFieldsValue' => $baseDir . '/core/ormcustomfieldsvalue.class.inc.php',
|
||||
'ormDocument' => $baseDir . '/core/ormdocument.class.inc.php',
|
||||
'ormLinkSet' => $baseDir . '/core/ormlinkset.class.inc.php',
|
||||
|
||||
@@ -3335,6 +3335,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'lnkAuditCategoryToAuditDomain' => __DIR__ . '/../..' . '/application/audit.domain.class.inc.php',
|
||||
'lnkTriggerAction' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
|
||||
'ormCaseLog' => __DIR__ . '/../..' . '/core/ormcaselog.class.inc.php',
|
||||
'ormCaseLogService' => __DIR__ . '/../..' . '/core/ormcaselogservice.inc.php',
|
||||
'ormCustomFieldsValue' => __DIR__ . '/../..' . '/core/ormcustomfieldsvalue.class.inc.php',
|
||||
'ormDocument' => __DIR__ . '/../..' . '/core/ormdocument.class.inc.php',
|
||||
'ormLinkSet' => __DIR__ . '/../..' . '/core/ormlinkset.class.inc.php',
|
||||
|
||||
@@ -22,6 +22,32 @@ use ormCaseLog;
|
||||
*/
|
||||
class ormCaseLogTest extends ItopDataTestCase
|
||||
{
|
||||
const USE_TRANSACTION = false;
|
||||
private $sLogin;
|
||||
private $sPassword = "Iuytrez9876543ç_è-(";
|
||||
|
||||
public function setUp() :void{
|
||||
parent::setUp(); // TODO: Change the autogenerated stub
|
||||
//require_once APPROOT . "core/CaseLogService.php";
|
||||
|
||||
$oAdminProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true);
|
||||
|
||||
if (is_object($oAdminProfile)) {
|
||||
$this->sLogin = "admin-".date('dmYHis');
|
||||
|
||||
$this->CreateTestOrganization();
|
||||
|
||||
/** @var \Person $oPerson */
|
||||
$oPerson = $this->createObject('Person', array(
|
||||
'name' => $this->sLogin,
|
||||
'first_name' => 'Test',
|
||||
'org_id' => $this->getTestOrgId(),
|
||||
));
|
||||
|
||||
$this->CreateUser($this->sLogin, $oAdminProfile->GetKey(), $this->sPassword, $oPerson->GetKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \ormCaseLog::GetEntryCount()
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -38,4 +64,141 @@ class ormCaseLogTest extends ItopDataTestCase
|
||||
$oLog->AddLogEntry('First entry');
|
||||
$this->assertEquals($oLog->GetEntryCount(), 1, 'Should be 1 entry, returned '.$oLog->GetEntryCount());
|
||||
}
|
||||
|
||||
public function testConstructorWithoutRebuild(){
|
||||
$oOrmCaseLogService = $this->createMock(\ormCaseLogService::class);
|
||||
$sLog = "aaaaa";
|
||||
$aInitialIndex = ['a' => 'b'];
|
||||
|
||||
$oOrmCaseLogService->expects($this->exactly(1))
|
||||
->method('Rebuild')
|
||||
->with($sLog, $aInitialIndex)
|
||||
->willReturn(null);
|
||||
|
||||
$oLog = new ormCaseLog($sLog, $aInitialIndex, $oOrmCaseLogService);
|
||||
$this->assertEquals($aInitialIndex, $oLog->GetIndex());
|
||||
$this->assertEquals($sLog, $oLog->GetText());
|
||||
}
|
||||
|
||||
public function testConstructorWithRebuild(){
|
||||
$oOrmCaseLogService = $this->createMock(\ormCaseLogService::class);
|
||||
$sLog = "aaaaa";
|
||||
$sRebuiltLog = "bbbb";
|
||||
$aInitialIndex = ['a' => 'b'];
|
||||
$aRebuiltIndex = ['c' => 'd'];
|
||||
|
||||
$oOrmCaseLogService->expects($this->exactly(1))
|
||||
->method('Rebuild')
|
||||
->with($sLog, $aInitialIndex)
|
||||
->willReturn(new ormCaseLog($sRebuiltLog, $aRebuiltIndex));
|
||||
|
||||
$oLog = new ormCaseLog($sLog, $aInitialIndex, $oOrmCaseLogService);
|
||||
$this->assertEquals($aRebuiltIndex, $oLog->GetIndex());
|
||||
$this->assertEquals($sRebuiltLog, $oLog->GetText());
|
||||
}
|
||||
|
||||
public function AddLogEntryProvider(){
|
||||
return [
|
||||
'AddLogEntry' => [
|
||||
'bTestAddLogEntry' => 'true'
|
||||
],
|
||||
'AddLogEntryFromJSON' => [
|
||||
'bTestAddLogEntry' => 'false'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider AddLogEntryProvider
|
||||
*/
|
||||
public function testAddLogEntryNoRebuild($bTestAddLogEntry=true){
|
||||
$_SESSION = array();
|
||||
$this->assertTrue(\UserRights::Login($this->sLogin));
|
||||
|
||||
$sLog = "aaaaa";
|
||||
$sDate = date(\AttributeDateTime::GetInternalFormat());
|
||||
$iUserId = \UserRights::GetUserId();
|
||||
$sOnBehalfOf = \UserRights::GetUserFriendlyName();
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$sExpectedLog = $sSeparator."<p>$sLog</p>";
|
||||
|
||||
$oOrmCaseLogService = $this->createMock(\ormCaseLogService::class);
|
||||
$aExpectedIndex = [
|
||||
[
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => 12,
|
||||
'separator_length' => strlen($sSeparator),
|
||||
'format' => 'html',
|
||||
]
|
||||
];
|
||||
|
||||
$oOrmCaseLogService->expects($this->exactly(2))
|
||||
->method('Rebuild')
|
||||
->withConsecutive(['', []], [$sExpectedLog, $aExpectedIndex])
|
||||
->willReturnOnConsecutiveCalls(null, null);
|
||||
|
||||
$oLog = new ormCaseLog('', [], $oOrmCaseLogService);
|
||||
if ($bTestAddLogEntry){
|
||||
$oLog->AddLogEntry($sLog);
|
||||
} else {
|
||||
$sJson = json_encode(
|
||||
[
|
||||
'user_login' => 'gabuzmeu',
|
||||
'message' => $sLog,
|
||||
]
|
||||
);
|
||||
$oJson = json_decode($sJson);
|
||||
$oLog->AddLogEntryFromJSON($oJson, false);
|
||||
}
|
||||
|
||||
$this->assertEquals($sExpectedLog, $oLog->GetText());
|
||||
$this->assertEquals($aExpectedIndex, $oLog->GetIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider AddLogEntryProvider
|
||||
*/
|
||||
public function testAddLogEntry($bTestAddLogEntry=true){
|
||||
$_SESSION = array();
|
||||
$this->assertTrue(\UserRights::Login($this->sLogin));
|
||||
|
||||
$sLog = "aaaaa";
|
||||
$sDate = date(\AttributeDateTime::GetInternalFormat());
|
||||
$iUserId = \UserRights::GetUserId();
|
||||
$sOnBehalfOf = \UserRights::GetUserFriendlyName();
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$sExpectedLog = $sSeparator."<p>$sLog</p>";
|
||||
|
||||
$aRebuiltIndex = ['c' => 'd'];
|
||||
$sRebuiltLog = "bbbb";
|
||||
|
||||
$oOrmCaseLogService = $this->createMock(\ormCaseLogService::class);
|
||||
|
||||
$aExpectedIndex = [
|
||||
[
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => 12,
|
||||
'separator_length' => strlen($sSeparator),
|
||||
'format' => 'html',
|
||||
]
|
||||
];
|
||||
$oOrmCaseLogService->expects($this->exactly(2))
|
||||
->method('Rebuild')
|
||||
->withConsecutive(['', []], [$sExpectedLog, $aExpectedIndex])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
null,
|
||||
new ormCaseLog($sRebuiltLog, $aRebuiltIndex)
|
||||
);
|
||||
|
||||
$oLog = new ormCaseLog('', [], $oOrmCaseLogService);
|
||||
$oLog->AddLogEntry($sLog);
|
||||
|
||||
$this->assertEquals($sRebuiltLog, $oLog->GetText());
|
||||
$this->assertEquals($aRebuiltIndex, $oLog->GetIndex());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user