mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
739 lines
22 KiB
PHP
739 lines
22 KiB
PHP
<?php
|
|
// Copyright (C) 2010-2021 Combodo SARL
|
|
//
|
|
// This file is part of iTop.
|
|
//
|
|
// 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.
|
|
//
|
|
// iTop is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSectionUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
|
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
|
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
|
use Combodo\iTop\Renderer\BlockRenderer;
|
|
|
|
define('CASELOG_VISIBLE_ITEMS', 2);
|
|
define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
|
|
|
|
|
|
/**
|
|
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
|
*
|
|
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
|
* @license http://opensource.org/licenses/AGPL-3.0
|
|
*/
|
|
class ormCaseLog {
|
|
protected $m_sLog;
|
|
protected $m_aIndex;
|
|
protected $m_bModified;
|
|
|
|
/**
|
|
* 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())
|
|
{
|
|
$this->m_sLog = $sLog;
|
|
$this->m_aIndex = $aIndex;
|
|
$this->m_bModified = false;
|
|
}
|
|
|
|
public function GetText($bConvertToPlainText = false)
|
|
{
|
|
if ($bConvertToPlainText)
|
|
{
|
|
// Rebuild the log, but filtering any HTML markup for the all 'html' entries in the log
|
|
return $this->GetAsPlainText();
|
|
}
|
|
else
|
|
{
|
|
return $this->m_sLog;
|
|
}
|
|
}
|
|
|
|
public static function FromJSON($oJson)
|
|
{
|
|
if (!isset($oJson->items))
|
|
{
|
|
throw new Exception("Missing 'items' elements");
|
|
}
|
|
$oCaseLog = new ormCaseLog();
|
|
foreach($oJson->items as $oItem)
|
|
{
|
|
$oCaseLog->AddLogEntryFromJSON($oItem);
|
|
}
|
|
return $oCaseLog;
|
|
}
|
|
|
|
/**
|
|
* Return a value that will be further JSON encoded
|
|
*/
|
|
public function GetForJSON()
|
|
{
|
|
// Order by ascending date
|
|
$aRet = array('entries' => array_reverse($this->GetAsArray()));
|
|
return $aRet;
|
|
}
|
|
|
|
/**
|
|
* Return all the data, in a format that is suitable for programmatic usages:
|
|
* -> dates not formatted
|
|
* -> to preserve backward compatibility, to the returned structure must grow (new array entries)
|
|
*
|
|
* Format:
|
|
* array (
|
|
* array (
|
|
* 'date' => <yyyy-mm-dd hh:mm:ss>,
|
|
* 'user_login' => <user friendly name>
|
|
* 'user_id' => OPTIONAL <id of the user account (caution: the object might have been deleted since)>
|
|
* 'message' => <message as plain text (CR/LF), empty if message_html is given>
|
|
* 'message_html' => <message with HTML markup, empty if message is given>
|
|
* )
|
|
*
|
|
* @return array
|
|
* @throws DictExceptionMissingString
|
|
*/
|
|
public function GetAsArray()
|
|
{
|
|
$aEntries = array();
|
|
$iPos = 0;
|
|
for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
|
|
{
|
|
$iPos += $this->m_aIndex[$index]['separator_length'];
|
|
$sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
|
$iPos += $this->m_aIndex[$index]['text_length'];
|
|
|
|
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
|
// therefore we have changed the format. To preserve the compatibility with existing
|
|
// installations of iTop, both format are allowed:
|
|
// the 'date' item is either a DateTime object, or a unix timestamp
|
|
if (is_int($this->m_aIndex[$index]['date']))
|
|
{
|
|
// Unix timestamp
|
|
$sDate = date(AttributeDateTime::GetInternalFormat(),$this->m_aIndex[$index]['date']);
|
|
}
|
|
elseif (is_object($this->m_aIndex[$index]['date']))
|
|
{
|
|
if (version_compare(phpversion(), '5.3.0', '>='))
|
|
{
|
|
// DateTime
|
|
$sDate = $this->m_aIndex[$index]['date']->format(AttributeDateTime::GetInternalFormat());
|
|
}
|
|
else
|
|
{
|
|
// No Warning... but the date is unknown
|
|
$sDate = '';
|
|
}
|
|
}
|
|
$sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : 'text';
|
|
switch($sFormat)
|
|
{
|
|
case 'text':
|
|
$sHtmlEntry = utils::TextToHtml($sTextEntry);
|
|
break;
|
|
|
|
case 'html':
|
|
$sHtmlEntry = $sTextEntry;
|
|
$sTextEntry = utils::HtmlToText($sHtmlEntry);
|
|
break;
|
|
}
|
|
$aEntries[] = array(
|
|
'date' => $sDate,
|
|
'user_login' => $this->m_aIndex[$index]['user_name'],
|
|
'user_id' => $this->m_aIndex[$index]['user_id'],
|
|
'message' => $sTextEntry,
|
|
'message_html' => $sHtmlEntry,
|
|
);
|
|
}
|
|
|
|
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
|
if ($iPos < (strlen($this->m_sLog) - 1))
|
|
{
|
|
$sTextEntry = substr($this->m_sLog, $iPos);
|
|
|
|
$aEntries[] = array(
|
|
'date' => '',
|
|
'user_login' => '',
|
|
'user_id' => 0,
|
|
'message' => $sTextEntry,
|
|
'message_html' => utils::TextToHtml($sTextEntry),
|
|
);
|
|
}
|
|
|
|
return $aEntries;
|
|
}
|
|
|
|
/**
|
|
* Returns a "plain text" version of the log (equivalent to $this->m_sLog) where all the HTML markup from the 'html' entries have been removed
|
|
* @return string
|
|
*/
|
|
public function GetAsPlainText()
|
|
{
|
|
$sPlainText = '';
|
|
$aJSON = $this->GetForJSON();
|
|
foreach($aJSON['entries'] as $aData)
|
|
{
|
|
$sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
|
|
$sPlainText .= $sSeparator.$aData['message'];
|
|
}
|
|
return $sPlainText;
|
|
}
|
|
|
|
public function GetIndex()
|
|
{
|
|
return $this->m_aIndex;
|
|
}
|
|
|
|
public function __toString()
|
|
{
|
|
if($this->IsEmpty()) return '';
|
|
|
|
return $this->m_sLog;
|
|
}
|
|
|
|
public function IsEmpty()
|
|
{
|
|
return ($this->m_sLog === null);
|
|
}
|
|
|
|
/**
|
|
* @return int The number of entries in this log
|
|
* @since 3.0.0
|
|
*/
|
|
public function GetEntryCount(): int
|
|
{
|
|
return count($this->m_aIndex);
|
|
}
|
|
|
|
public function ClearModifiedFlag()
|
|
{
|
|
$this->m_bModified = false;
|
|
}
|
|
|
|
/**
|
|
* Produces an HTML representation, aimed at being used within an email
|
|
*/
|
|
public function GetAsEmailHtml()
|
|
{
|
|
$sStyleCaseLogHeader = '';
|
|
$sStyleCaseLogEntry = '';
|
|
|
|
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
|
$iPos = 0;
|
|
$aIndex = $this->m_aIndex;
|
|
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
|
{
|
|
$iPos += $aIndex[$index]['separator_length'];
|
|
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
|
$sCSSClass = 'caselog_entry_html';
|
|
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
|
{
|
|
$sCSSClass = 'caselog_entry';
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
}
|
|
else
|
|
{
|
|
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
|
}
|
|
$iPos += $aIndex[$index]['text_length'];
|
|
|
|
$sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
|
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
|
// therefore we have changed the format. To preserve the compatibility with existing
|
|
// installations of iTop, both format are allowed:
|
|
// the 'date' item is either a DateTime object, or a unix timestamp
|
|
if (is_int($aIndex[$index]['date']))
|
|
{
|
|
// Unix timestamp
|
|
$sDate = date((string)AttributeDateTime::GetFormat(), $aIndex[$index]['date']);
|
|
}
|
|
elseif (is_object($aIndex[$index]['date']))
|
|
{
|
|
if (version_compare(phpversion(), '5.3.0', '>='))
|
|
{
|
|
// DateTime
|
|
$sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
|
|
}
|
|
else
|
|
{
|
|
// No Warning... but the date is unknown
|
|
$sDate = '';
|
|
}
|
|
}
|
|
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
|
$sEntry .= '</div>';
|
|
$sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
|
|
$sEntry .= $sTextEntry;
|
|
$sEntry .= '</div>';
|
|
$sHtml = $sHtml.$sEntry;
|
|
}
|
|
|
|
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
|
if ($iPos < (strlen($this->m_sLog) - 1))
|
|
{
|
|
$sTextEntry = substr($this->m_sLog, $iPos);
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
|
|
if (count($this->m_aIndex) == 0)
|
|
{
|
|
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
|
|
$sHtml .= $sTextEntry;
|
|
$sHtml .= '</div>';
|
|
}
|
|
else
|
|
{
|
|
$sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
|
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
|
$sHtml .= '</div>';
|
|
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
|
|
$sHtml .= $sTextEntry;
|
|
$sHtml .= '</div>';
|
|
}
|
|
}
|
|
$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 = '';
|
|
|
|
$sHtml = '<ul class="case_log_simple_html">';
|
|
$iPos = 0;
|
|
$aIndex = $this->m_aIndex;
|
|
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
|
{
|
|
$iPos += $aIndex[$index]['separator_length'];
|
|
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
|
$sCSSClass = 'case_log_simple_html_entry_html';
|
|
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
|
{
|
|
$sCSSClass = 'case_log_simple_html_entry';
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
if (!is_null($aTransfoHandler))
|
|
{
|
|
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!is_null($aTransfoHandler))
|
|
{
|
|
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
|
|
}
|
|
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
|
}
|
|
$iPos += $aIndex[$index]['text_length'];
|
|
|
|
$sEntry = '<li>';
|
|
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
|
// therefore we have changed the format. To preserve the compatibility with existing
|
|
// installations of iTop, both format are allowed:
|
|
// the 'date' item is either a DateTime object, or a unix timestamp
|
|
if (is_int($aIndex[$index]['date']))
|
|
{
|
|
// Unix timestamp
|
|
$sDate = date((string)AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
|
|
}
|
|
elseif (is_object($aIndex[$index]['date']))
|
|
{
|
|
if (version_compare(phpversion(), '5.3.0', '>='))
|
|
{
|
|
// DateTime
|
|
$sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
|
|
}
|
|
else
|
|
{
|
|
// No Warning... but the date is unknown
|
|
$sDate = '';
|
|
}
|
|
}
|
|
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
|
$sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
|
|
$sEntry .= $sTextEntry;
|
|
$sEntry .= '</div>';
|
|
$sEntry .= '</li>';
|
|
$sHtml = $sHtml.$sEntry;
|
|
}
|
|
|
|
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
|
if ($iPos < (strlen($this->m_sLog) - 1))
|
|
{
|
|
$sTextEntry = substr($this->m_sLog, $iPos);
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
|
|
if (count($this->m_aIndex) == 0)
|
|
{
|
|
$sHtml .= '<li>';
|
|
$sHtml .= $sTextEntry;
|
|
$sHtml .= '</li>';
|
|
}
|
|
else
|
|
{
|
|
$sHtml .= '<li>';
|
|
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
|
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
|
$sHtml .= $sTextEntry;
|
|
$sHtml .= '</div>';
|
|
$sHtml .= '</li>';
|
|
}
|
|
}
|
|
$sHtml .= '</ul>';
|
|
return $sHtml;
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
|
|
$oBlock = UIContentBlockUIBlockFactory::MakeStandard(null, ['ibo-caselog-list']);
|
|
$iPos = 0;
|
|
$aIndex = $this->m_aIndex;
|
|
if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
|
|
{
|
|
// Don't display the first element, that is still considered as editable
|
|
$aLastEntry = end($aIndex);
|
|
$iPos = $aLastEntry['separator_length'] + $aLastEntry['text_length'];
|
|
array_pop($aIndex);
|
|
}
|
|
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
|
{
|
|
if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
|
|
{
|
|
$bIsOpen = false;
|
|
}
|
|
else
|
|
{
|
|
$bIsOpen = true;
|
|
}
|
|
$iPos += $aIndex[$index]['separator_length'];
|
|
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
|
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
|
{
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
if (!is_null($aTransfoHandler))
|
|
{
|
|
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!is_null($aTransfoHandler))
|
|
{
|
|
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
|
|
}
|
|
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
|
}
|
|
$iPos += $aIndex[$index]['text_length'];
|
|
|
|
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
|
// therefore we have changed the format. To preserve the compatibility with existing
|
|
// installations of iTop, both format are allowed:
|
|
// the 'date' item is either a DateTime object, or a unix timestamp
|
|
if (is_int($aIndex[$index]['date']))
|
|
{
|
|
// Unix timestamp
|
|
$sDate = date((string)AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
|
|
}
|
|
elseif (is_object($aIndex[$index]['date']))
|
|
{
|
|
if (version_compare(phpversion(), '5.3.0', '>='))
|
|
{
|
|
// DateTime
|
|
$sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
|
|
}
|
|
else
|
|
{
|
|
// No Warning... but the date is unknown
|
|
$sDate = '';
|
|
}
|
|
}
|
|
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']));
|
|
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
|
$oCollapsibleBlock->SetOpenedByDefault($bIsOpen);
|
|
$oBlock->AddSubBlock($oCollapsibleBlock);
|
|
}
|
|
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
|
if ($iPos < (strlen($this->m_sLog) - 1))
|
|
{
|
|
// In this case the format is always "text"
|
|
$sTextEntry = substr($this->m_sLog, $iPos);
|
|
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
|
if (!is_null($aTransfoHandler))
|
|
{
|
|
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
|
}
|
|
|
|
if (count($this->m_aIndex) == 0)
|
|
{
|
|
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( '');
|
|
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
|
$oCollapsibleBlock->SetOpenedByDefault(true);
|
|
$oBlock->AddSubBlock($oCollapsibleBlock);
|
|
}
|
|
else
|
|
{
|
|
if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
|
|
{
|
|
$bIsOpen = false;
|
|
}
|
|
else
|
|
{
|
|
$bIsOpen = true;
|
|
}
|
|
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( Dict::S('UI:CaseLog:InitialValue'));
|
|
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
|
$oCollapsibleBlock->SetOpenedByDefault($bIsOpen);
|
|
}
|
|
}
|
|
$oBlockRenderer = new BlockRenderer($oBlock);
|
|
$sHtml = $oBlockRenderer->RenderHtml();
|
|
$sScript = $oBlockRenderer->RenderJsInlineRecursively($oBlock,iUIBlock::ENUM_JS_TYPE_ON_READY);
|
|
if ($sScript!=''){
|
|
if ($oP == null) {
|
|
$sScript = '<script>'.$sScript.'</script>';
|
|
$sHtml .= $sScript;
|
|
} else {
|
|
$oP->add_ready_script($sScript);
|
|
}
|
|
}
|
|
return $sHtml;
|
|
}
|
|
|
|
/**
|
|
* Add a new entry to the log or merge the given text into the currently modified entry
|
|
* and updates the internal index
|
|
*
|
|
* @param string $sText The text of the new entry
|
|
* @param string $sOnBehalfOf Display this name instead of current user name
|
|
* @param null|int $iOnBehalfOfId Use this UserId to author this Entry. If $sOnBehalfOf equals '', it'll be replaced by this User friendlyname
|
|
*
|
|
* @throws \ArchivedObjectException
|
|
* @throws \CoreException
|
|
* @throws \OQLException
|
|
*
|
|
* @since 3.0.0 New $iOnBehalfOfId parameter
|
|
* @since 3.0.0 May throw \ArchivedObjectException exception
|
|
*/
|
|
public function AddLogEntry(string $sText, $sOnBehalfOf = '', $iOnBehalfOfId = null)
|
|
{
|
|
$sText = HTMLSanitizer::Sanitize($sText);
|
|
$sDate = date(AttributeDateTime::GetInternalFormat());
|
|
if ($sOnBehalfOf == '' && $iOnBehalfOfId === null) {
|
|
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
|
$iUserId = UserRights::GetUserId();
|
|
}
|
|
elseif ($iOnBehalfOfId !== null) {
|
|
$iUserId = $iOnBehalfOfId;
|
|
/* @var User $oUser */
|
|
$oUser = MetaModel::GetObject('User', $iUserId, false, true);
|
|
if ($oUser !== null && $sOnBehalfOf === '') {
|
|
$sOnBehalfOf = $oUser->GetFriendlyName();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$iUserId = null;
|
|
}
|
|
if ($this->m_bModified)
|
|
{
|
|
$aLatestEntry = end($this->m_aIndex);
|
|
if ($aLatestEntry['user_name'] == $sOnBehalfOf)
|
|
{
|
|
// Append the new text to the previous one
|
|
$sPreviousText = substr($this->m_sLog, $aLatestEntry['separator_length'], $aLatestEntry['text_length']);
|
|
$sText = $sPreviousText."\n".$sText;
|
|
|
|
// Cleanup the previous entry
|
|
array_pop($this->m_aIndex);
|
|
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length'] + $aLatestEntry['text_length']);
|
|
}
|
|
}
|
|
|
|
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
|
$iSepLength = strlen($sSeparator);
|
|
$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' => time(),
|
|
'text_length' => $iTextlength,
|
|
'separator_length' => $iSepLength,
|
|
'format' => 'html',
|
|
);
|
|
$this->m_bModified = true;
|
|
}
|
|
|
|
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
|
{
|
|
if (isset($oJson->user_id))
|
|
{
|
|
if (!UserRights::IsAdministrator())
|
|
{
|
|
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
|
}
|
|
if ($bCheckUserId && ($oJson->user_id != 0))
|
|
{
|
|
try
|
|
{
|
|
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
|
}
|
|
$iUserId = $oUser->GetKey();
|
|
$sOnBehalfOf = $oUser->GetFriendlyName();
|
|
}
|
|
else
|
|
{
|
|
$iUserId = $oJson->user_id;
|
|
$sOnBehalfOf = $oJson->user_login;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$iUserId = UserRights::GetUserId();
|
|
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
|
}
|
|
|
|
if (isset($oJson->date))
|
|
{
|
|
$oDate = new DateTime($oJson->date);
|
|
$iDate = (int) $oDate->format('U');
|
|
}
|
|
else
|
|
{
|
|
$iDate = time();
|
|
}
|
|
if (isset($oJson->format))
|
|
{
|
|
$sFormat = $oJson->format;
|
|
}
|
|
else
|
|
{
|
|
// The default is HTML
|
|
$sFormat = 'html';
|
|
}
|
|
|
|
$sText = isset($oJson->message) ? $oJson->message : '';
|
|
if ($sFormat == 'html')
|
|
{
|
|
$sText = HTMLSanitizer::Sanitize($sText);
|
|
}
|
|
|
|
$sDate = date(AttributeDateTime::GetInternalFormat(), $iDate);
|
|
|
|
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
|
$iSepLength = strlen($sSeparator);
|
|
$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,
|
|
'separator_length' => $iSepLength,
|
|
'format' => $sFormat,
|
|
);
|
|
|
|
$this->m_bModified = true;
|
|
}
|
|
|
|
public function GetModifiedEntry($sFormat = 'text')
|
|
{
|
|
$sModifiedEntry = '';
|
|
if ($this->m_bModified)
|
|
{
|
|
$sModifiedEntry = $this->GetLatestEntry($sFormat);
|
|
}
|
|
return $sModifiedEntry;
|
|
}
|
|
|
|
/**
|
|
* Get the latest entry from the log
|
|
* @param string The expected output format text|html
|
|
* @return string
|
|
*/
|
|
public function GetLatestEntry($sFormat = 'text')
|
|
{
|
|
$sRes = '';
|
|
$aLastEntry = end($this->m_aIndex);
|
|
$sRaw = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
|
|
switch($sFormat)
|
|
{
|
|
case 'text':
|
|
if ($aLastEntry['format'] == 'text')
|
|
{
|
|
$sRes = $sRaw;
|
|
}
|
|
else
|
|
{
|
|
$sRes = utils::HtmlToText($sRaw);
|
|
}
|
|
break;
|
|
|
|
case 'html':
|
|
if ($aLastEntry['format'] == 'text')
|
|
{
|
|
$sRes = utils::TextToHtml($sRaw);
|
|
}
|
|
else
|
|
{
|
|
$sRes = $sRaw;
|
|
}
|
|
break;
|
|
}
|
|
return $sRes;
|
|
}
|
|
|
|
/**
|
|
* Get the index of the latest entry from the log
|
|
* @return integer
|
|
*/
|
|
public function GetLatestEntryIndex()
|
|
{
|
|
$aKeys = array_keys($this->m_aIndex);
|
|
$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
|
|
* @return string The text of the entry
|
|
*/
|
|
public function GetEntryAt($iIndex)
|
|
{
|
|
$iPos = 0;
|
|
$index = count($this->m_aIndex) - 1;
|
|
while($index > $iIndex)
|
|
{
|
|
$iPos += $this->m_aIndex[$index]['separator_length'];
|
|
$iPos += $this->m_aIndex[$index]['text_length'];
|
|
$index--;
|
|
}
|
|
$iPos += $this->m_aIndex[$index]['separator_length'];
|
|
$sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
|
return $sText;
|
|
}
|
|
}
|