ormcaselog.class.inc.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. <?php
  2. // Copyright (C) 2010-2016 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. define('CASELOG_VISIBLE_ITEMS', 2);
  19. define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
  20. /**
  21. * Class to store a "case log" in a structured way, keeping track of its successive entries
  22. *
  23. * @copyright Copyright (C) 2010-2016 Combodo SARL
  24. * @license http://opensource.org/licenses/AGPL-3.0
  25. */
  26. class ormCaseLog {
  27. protected $m_sLog;
  28. protected $m_aIndex;
  29. protected $m_bModified;
  30. /**
  31. * Initializes the log with the first (initial) entry
  32. * @param $sLog string The text of the whole case log
  33. * @param $aIndex hash The case log index
  34. */
  35. public function __construct($sLog = '', $aIndex = array())
  36. {
  37. $this->m_sLog = $sLog;
  38. $this->m_aIndex = $aIndex;
  39. $this->m_bModified = false;
  40. }
  41. public function GetText($bConvertToPlainText = false)
  42. {
  43. if ($bConvertToPlainText)
  44. {
  45. // Rebuild the log, but filtering any HTML markup for the all 'html' entries in the log
  46. return $this->GetAsPlainText();
  47. }
  48. else
  49. {
  50. return $this->m_sLog;
  51. }
  52. }
  53. public static function FromJSON($oJson)
  54. {
  55. if (!isset($oJson->items))
  56. {
  57. throw new Exception("Missing 'items' elements");
  58. }
  59. $oCaseLog = new ormCaseLog();
  60. foreach($oJson->items as $oItem)
  61. {
  62. $oCaseLog->AddLogEntryFromJSON($oItem);
  63. }
  64. return $oCaseLog;
  65. }
  66. /**
  67. * Return a value that will be further JSON encoded
  68. */
  69. public function GetForJSON()
  70. {
  71. $aEntries = array();
  72. $iPos = 0;
  73. for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
  74. {
  75. $iPos += $this->m_aIndex[$index]['separator_length'];
  76. $sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
  77. $iPos += $this->m_aIndex[$index]['text_length'];
  78. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  79. // therefore we have changed the format. To preserve the compatibility with existing
  80. // installations of iTop, both format are allowed:
  81. // the 'date' item is either a DateTime object, or a unix timestamp
  82. if (is_int($this->m_aIndex[$index]['date']))
  83. {
  84. // Unix timestamp
  85. $sDate = date(Dict::S('UI:CaseLog:DateFormat'),$this->m_aIndex[$index]['date']);
  86. }
  87. elseif (is_object($this->m_aIndex[$index]['date']))
  88. {
  89. if (version_compare(phpversion(), '5.3.0', '>='))
  90. {
  91. // DateTime
  92. $sDate = $this->m_aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
  93. }
  94. else
  95. {
  96. // No Warning... but the date is unknown
  97. $sDate = '';
  98. }
  99. }
  100. $sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : 'text';
  101. switch($sFormat)
  102. {
  103. case 'text':
  104. $sHtmlEntry = utils::TextToHtml($sTextEntry);
  105. break;
  106. case 'html':
  107. $sHtmlEntry = $sTextEntry;
  108. $sTextEntry = utils::HtmlToText($sHtmlEntry);
  109. break;
  110. }
  111. $aEntries[] = array(
  112. 'date' => $sDate,
  113. 'user_login' => $this->m_aIndex[$index]['user_name'],
  114. 'user_id' => $this->m_aIndex[$index]['user_id'],
  115. 'message' => $sTextEntry,
  116. 'message_html' => $sHtmlEntry,
  117. );
  118. }
  119. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  120. if ($iPos < (strlen($this->m_sLog) - 1))
  121. {
  122. $sTextEntry = substr($this->m_sLog, $iPos);
  123. $aEntries[] = array(
  124. 'date' => '',
  125. 'user_login' => '',
  126. 'message' => $sTextEntry,
  127. 'message_html' => utils::TextToHtml($sTextEntry),
  128. );
  129. }
  130. // Order by ascending date
  131. $aRet = array('entries' => array_reverse($aEntries));
  132. return $aRet;
  133. }
  134. /**
  135. * 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
  136. * @return string
  137. */
  138. public function GetAsPlainText()
  139. {
  140. $sPlainText = '';
  141. $aJSON = $this->GetForJSON();
  142. foreach($aJSON['entries'] as $aData)
  143. {
  144. $sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
  145. $sPlainText .= $sSeparator.$aData['message'];
  146. }
  147. return $sPlainText;
  148. }
  149. public function GetIndex()
  150. {
  151. return $this->m_aIndex;
  152. }
  153. public function __toString()
  154. {
  155. return $this->m_sLog;
  156. }
  157. public function ClearModifiedFlag()
  158. {
  159. $this->m_bModified = false;
  160. }
  161. /**
  162. * Produces an HTML representation, aimed at being used within an email
  163. */
  164. public function GetAsEmailHtml()
  165. {
  166. $sStyleCaseLogHeader = '';
  167. $sStyleCaseLogEntry = '';
  168. $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
  169. $iPos = 0;
  170. $aIndex = $this->m_aIndex;
  171. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  172. {
  173. $iPos += $aIndex[$index]['separator_length'];
  174. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  175. $sCSSClass = 'caselog_entry_html';
  176. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  177. {
  178. $sCSSClass = 'caselog_entry';
  179. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  180. }
  181. else
  182. {
  183. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  184. }
  185. $iPos += $aIndex[$index]['text_length'];
  186. $sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  187. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  188. // therefore we have changed the format. To preserve the compatibility with existing
  189. // installations of iTop, both format are allowed:
  190. // the 'date' item is either a DateTime object, or a unix timestamp
  191. if (is_int($aIndex[$index]['date']))
  192. {
  193. // Unix timestamp
  194. $sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
  195. }
  196. elseif (is_object($aIndex[$index]['date']))
  197. {
  198. if (version_compare(phpversion(), '5.3.0', '>='))
  199. {
  200. // DateTime
  201. $sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
  202. }
  203. else
  204. {
  205. // No Warning... but the date is unknown
  206. $sDate = '';
  207. }
  208. }
  209. $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>');
  210. $sEntry .= '</div>';
  211. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  212. $sEntry .= $sTextEntry;
  213. $sEntry .= '</div>';
  214. $sHtml = $sHtml.$sEntry;
  215. }
  216. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  217. if ($iPos < (strlen($this->m_sLog) - 1))
  218. {
  219. $sTextEntry = substr($this->m_sLog, $iPos);
  220. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  221. if (count($this->m_aIndex) == 0)
  222. {
  223. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
  224. $sHtml .= $sTextEntry;
  225. $sHtml .= '</div>';
  226. }
  227. else
  228. {
  229. $sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  230. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  231. $sHtml .= '</div>';
  232. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
  233. $sHtml .= $sTextEntry;
  234. $sHtml .= '</div>';
  235. }
  236. }
  237. $sHtml .= '</td></tr></table>';
  238. return $sHtml;
  239. }
  240. /**
  241. * Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
  242. */
  243. public function GetAsSimpleHtml()
  244. {
  245. $sStyleCaseLogHeader = '';
  246. $sStyleCaseLogEntry = '';
  247. $sHtml = '<ul class="case_log_simple_html">';
  248. $iPos = 0;
  249. $aIndex = $this->m_aIndex;
  250. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  251. {
  252. $iPos += $aIndex[$index]['separator_length'];
  253. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  254. $sCSSClass = 'case_log_simple_html_entry_html';
  255. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  256. {
  257. $sCSSClass = 'case_log_simple_html_entry';
  258. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  259. if (!is_null($aTransfoHandler))
  260. {
  261. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  262. }
  263. }
  264. else
  265. {
  266. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  267. }
  268. $iPos += $aIndex[$index]['text_length'];
  269. $sEntry = '<li>';
  270. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  271. // therefore we have changed the format. To preserve the compatibility with existing
  272. // installations of iTop, both format are allowed:
  273. // the 'date' item is either a DateTime object, or a unix timestamp
  274. if (is_int($aIndex[$index]['date']))
  275. {
  276. // Unix timestamp
  277. $sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
  278. }
  279. elseif (is_object($aIndex[$index]['date']))
  280. {
  281. if (version_compare(phpversion(), '5.3.0', '>='))
  282. {
  283. // DateTime
  284. $sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
  285. }
  286. else
  287. {
  288. // No Warning... but the date is unknown
  289. $sDate = '';
  290. }
  291. }
  292. $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>');
  293. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  294. $sEntry .= $sTextEntry;
  295. $sEntry .= '</div>';
  296. $sEntry .= '</li>';
  297. $sHtml = $sHtml.$sEntry;
  298. }
  299. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  300. if ($iPos < (strlen($this->m_sLog) - 1))
  301. {
  302. $sTextEntry = substr($this->m_sLog, $iPos);
  303. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  304. if (count($this->m_aIndex) == 0)
  305. {
  306. $sHtml .= '<li>';
  307. $sHtml .= $sTextEntry;
  308. $sHtml .= '</li>';
  309. }
  310. else
  311. {
  312. $sHtml .= '<li>';
  313. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  314. $sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
  315. $sHtml .= $sTextEntry;
  316. $sHtml .= '</div>';
  317. $sHtml .= '</li>';
  318. }
  319. }
  320. $sHtml .= '</ul>';
  321. return $sHtml;
  322. }
  323. /**
  324. * Produces an HTML representation, aimed at being used within the iTop framework
  325. */
  326. public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
  327. {
  328. $bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
  329. $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
  330. $iPos = 0;
  331. $aIndex = $this->m_aIndex;
  332. if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
  333. {
  334. // Don't display the first element, that is still considered as editable
  335. $iPos = $aIndex[0]['separator_length'] + $aIndex[0]['text_length'];
  336. array_shift($aIndex);
  337. }
  338. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  339. {
  340. if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
  341. {
  342. $sOpen = '';
  343. $sDisplay = 'style="display:none;"';
  344. }
  345. else
  346. {
  347. $sOpen = ' open';
  348. $sDisplay = '';
  349. }
  350. $iPos += $aIndex[$index]['separator_length'];
  351. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  352. $sCSSClass= 'caselog_entry_html';
  353. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  354. {
  355. $sCSSClass= 'caselog_entry';
  356. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  357. if (!is_null($aTransfoHandler))
  358. {
  359. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  360. }
  361. }
  362. else
  363. {
  364. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  365. }
  366. $iPos += $aIndex[$index]['text_length'];
  367. $sEntry = '<div class="caselog_header'.$sOpen.'">';
  368. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  369. // therefore we have changed the format. To preserve the compatibility with existing
  370. // installations of iTop, both format are allowed:
  371. // the 'date' item is either a DateTime object, or a unix timestamp
  372. if (is_int($aIndex[$index]['date']))
  373. {
  374. // Unix timestamp
  375. $sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
  376. }
  377. elseif (is_object($aIndex[$index]['date']))
  378. {
  379. if (version_compare(phpversion(), '5.3.0', '>='))
  380. {
  381. // DateTime
  382. $sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
  383. }
  384. else
  385. {
  386. // No Warning... but the date is unknown
  387. $sDate = '';
  388. }
  389. }
  390. $sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']);
  391. $sEntry .= '</div>';
  392. $sEntry .= '<div class="'.$sCSSClass.'"'.$sDisplay.'>';
  393. $sEntry .= $sTextEntry;
  394. $sEntry .= '</div>';
  395. $sHtml = $sHtml.$sEntry;
  396. }
  397. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  398. if ($iPos < (strlen($this->m_sLog) - 1))
  399. {
  400. // In this case the format is always "text"
  401. $sTextEntry = substr($this->m_sLog, $iPos);
  402. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  403. if (!is_null($aTransfoHandler))
  404. {
  405. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  406. }
  407. if (count($this->m_aIndex) == 0)
  408. {
  409. $sHtml .= '<div class="caselog_entry open">';
  410. $sHtml .= $sTextEntry;
  411. $sHtml .= '</div>';
  412. }
  413. else
  414. {
  415. if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
  416. {
  417. $sOpen = '';
  418. $sDisplay = 'style="display:none;"';
  419. }
  420. else
  421. {
  422. $sOpen = ' open';
  423. $sDisplay = '';
  424. }
  425. $sHtml .= '<div class="caselog_header'.$sOpen.'">';
  426. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  427. $sHtml .= '</div>';
  428. $sHtml .= '<div class="caselog_entry"'.$sDisplay.'>';
  429. $sHtml .= $sTextEntry;
  430. $sHtml .= '</div>';
  431. }
  432. }
  433. $sHtml .= '</td></tr></table>';
  434. return $sHtml;
  435. }
  436. /**
  437. * Add a new entry to the log or merge the given text into the currently modified entry
  438. * and updates the internal index
  439. * @param $sText string The text of the new entry
  440. */
  441. public function AddLogEntry($sText, $sOnBehalfOf = '')
  442. {
  443. $sText = HTMLSanitizer::Sanitize($sText);
  444. $bMergeEntries = false;
  445. $sDate = date(Dict::S('UI:CaseLog:DateFormat'));
  446. if ($sOnBehalfOf == '')
  447. {
  448. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  449. $iUserId = UserRights::GetUserId();
  450. }
  451. else
  452. {
  453. $iUserId = null;
  454. }
  455. if ($this->m_bModified)
  456. {
  457. $aLatestEntry = end($this->m_aIndex);
  458. if ($aLatestEntry['user_name'] != $sOnBehalfOf)
  459. {
  460. $bMergeEntries = false;
  461. }
  462. else
  463. {
  464. $bMergeEntries = true;
  465. }
  466. }
  467. if ($bMergeEntries)
  468. {
  469. $aLatestEntry = end($this->m_aIndex);
  470. $this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length']);
  471. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  472. $iSepLength = strlen($sSeparator);
  473. $iTextlength = strlen($sText."\n");
  474. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  475. $this->m_aIndex[] = array(
  476. 'user_name' => $sOnBehalfOf,
  477. 'user_id' => $iUserId,
  478. 'date' => time(),
  479. 'text_length' => $aLatestEntry['text_length'] + $iTextlength,
  480. 'separator_length' => $iSepLength,
  481. 'format' => 'html',
  482. );
  483. }
  484. else
  485. {
  486. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  487. $iSepLength = strlen($sSeparator);
  488. $iTextlength = strlen($sText);
  489. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  490. $this->m_aIndex[] = array(
  491. 'user_name' => $sOnBehalfOf,
  492. 'user_id' => $iUserId,
  493. 'date' => time(),
  494. 'text_length' => $iTextlength,
  495. 'separator_length' => $iSepLength,
  496. 'format' => 'html',
  497. );
  498. }
  499. $this->m_bModified = true;
  500. }
  501. public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
  502. {
  503. $sText = HTMLSanitizer::Sanitize(isset($oJson->message) ? $oJson->message : '');
  504. if (isset($oJson->user_id))
  505. {
  506. if (!UserRights::IsAdministrator())
  507. {
  508. throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
  509. }
  510. if ($bCheckUserId && ($oJson->user_id != 0))
  511. {
  512. try
  513. {
  514. $oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
  515. }
  516. catch(Exception $e)
  517. {
  518. throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
  519. }
  520. $iUserId = $oUser->GetKey();
  521. $sOnBehalfOf = $oUser->GetFriendlyName();
  522. }
  523. else
  524. {
  525. $iUserId = $oJson->user_id;
  526. $sOnBehalfOf = $oJson->user_login;
  527. }
  528. }
  529. else
  530. {
  531. $iUserId = UserRights::GetUserId();
  532. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  533. }
  534. if (isset($oJson->date))
  535. {
  536. $oDate = new DateTime($oJson->date);
  537. $iDate = (int) $oDate->format('U');
  538. }
  539. else
  540. {
  541. $iDate = time();
  542. }
  543. if (isset($oJson->format))
  544. {
  545. $sFormat = $oJson->format;
  546. }
  547. else
  548. {
  549. // TODO: what is the default format ? text ?
  550. $sFormat = 'html';
  551. }
  552. $sDate = date(Dict::S('UI:CaseLog:DateFormat'), $iDate);
  553. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  554. $iSepLength = strlen($sSeparator);
  555. $iTextlength = strlen($sText);
  556. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  557. $this->m_aIndex[] = array(
  558. 'user_name' => $sOnBehalfOf,
  559. 'user_id' => $iUserId,
  560. 'date' => $iDate,
  561. 'text_length' => $iTextlength,
  562. 'separator_length' => $iSepLength,
  563. 'format' => $sFormat,
  564. );
  565. $this->m_bModified = true;
  566. }
  567. public function GetModifiedEntry()
  568. {
  569. $sModifiedEntry = '';
  570. if ($this->m_bModified)
  571. {
  572. $sModifiedEntry = $this->GetLatestEntry();
  573. }
  574. return $sModifiedEntry;
  575. }
  576. /**
  577. * Get the latest entry from the log
  578. * @return string
  579. */
  580. public function GetLatestEntry()
  581. {
  582. $aLastEntry = end($this->m_aIndex);
  583. $sRes = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
  584. return $sRes;
  585. }
  586. /**
  587. * Get the index of the latest entry from the log
  588. * @return integer
  589. */
  590. public function GetLatestEntryIndex()
  591. {
  592. $aKeys = array_keys($this->m_aIndex);
  593. $iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
  594. return $iLast;
  595. }
  596. /**
  597. * Get the text string corresponding to the given entry in the log (zero based index, older entries first)
  598. * @param integer $iIndex
  599. * @return string The text of the entry
  600. */
  601. public function GetEntryAt($iIndex)
  602. {
  603. $iPos = 0;
  604. $index = count($this->m_aIndex) - 1;
  605. $aIndex = $this->m_aIndex;
  606. while($index > $iIndex)
  607. {
  608. $iPos += $this->m_aIndex[$index]['separator_length'];
  609. $iPos += $this->m_aIndex[$index]['text_length'];
  610. $index--;
  611. }
  612. $iPos += $this->m_aIndex[$index]['separator_length'];
  613. $sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
  614. return $sText;
  615. }
  616. }
  617. ?>