ormcaselog.class.inc.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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. // Order by ascending date
  72. $aRet = array('entries' => array_reverse($this->GetAsArray()));
  73. return $aRet;
  74. }
  75. /**
  76. * Return all the data, in a format that is suitable for programmatic usages:
  77. * -> dates not formatted
  78. * -> to preserve backward compatibility, to the returned structure must grow (new array entries)
  79. *
  80. * Format:
  81. * array (
  82. * array (
  83. * 'date' => <yyyy-mm-dd hh:mm:ss>,
  84. * 'user_login' => <user friendly name>
  85. * 'user_id' => OPTIONAL <id of the user account (caution: the object might have been deleted since)>
  86. * 'message' => <message as plain text (CR/LF), empty if message_html is given>
  87. * 'message_html' => <message with HTML markup, empty if message is given>
  88. * )
  89. *
  90. * @return array
  91. * @throws DictExceptionMissingString
  92. */
  93. public function GetAsArray()
  94. {
  95. $aEntries = array();
  96. $iPos = 0;
  97. for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
  98. {
  99. $iPos += $this->m_aIndex[$index]['separator_length'];
  100. $sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
  101. $iPos += $this->m_aIndex[$index]['text_length'];
  102. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  103. // therefore we have changed the format. To preserve the compatibility with existing
  104. // installations of iTop, both format are allowed:
  105. // the 'date' item is either a DateTime object, or a unix timestamp
  106. if (is_int($this->m_aIndex[$index]['date']))
  107. {
  108. // Unix timestamp
  109. $sDate = date(AttributeDateTime::GetInternalFormat(),$this->m_aIndex[$index]['date']);
  110. }
  111. elseif (is_object($this->m_aIndex[$index]['date']))
  112. {
  113. if (version_compare(phpversion(), '5.3.0', '>='))
  114. {
  115. // DateTime
  116. $sDate = $this->m_aIndex[$index]['date']->format(AttributeDateTime::GetInternalFormat());
  117. }
  118. else
  119. {
  120. // No Warning... but the date is unknown
  121. $sDate = '';
  122. }
  123. }
  124. $sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : 'text';
  125. switch($sFormat)
  126. {
  127. case 'text':
  128. $sHtmlEntry = utils::TextToHtml($sTextEntry);
  129. break;
  130. case 'html':
  131. $sHtmlEntry = $sTextEntry;
  132. $sTextEntry = utils::HtmlToText($sHtmlEntry);
  133. break;
  134. }
  135. $aEntries[] = array(
  136. 'date' => $sDate,
  137. 'user_login' => $this->m_aIndex[$index]['user_name'],
  138. 'user_id' => $this->m_aIndex[$index]['user_id'],
  139. 'message' => $sTextEntry,
  140. 'message_html' => $sHtmlEntry,
  141. );
  142. }
  143. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  144. if ($iPos < (strlen($this->m_sLog) - 1))
  145. {
  146. $sTextEntry = substr($this->m_sLog, $iPos);
  147. $aEntries[] = array(
  148. 'date' => '',
  149. 'user_login' => '',
  150. 'message' => $sTextEntry,
  151. 'message_html' => utils::TextToHtml($sTextEntry),
  152. );
  153. }
  154. return $aEntries;
  155. }
  156. /**
  157. * 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
  158. * @return string
  159. */
  160. public function GetAsPlainText()
  161. {
  162. $sPlainText = '';
  163. $aJSON = $this->GetForJSON();
  164. foreach($aJSON['entries'] as $aData)
  165. {
  166. $sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
  167. $sPlainText .= $sSeparator.$aData['message'];
  168. }
  169. return $sPlainText;
  170. }
  171. public function GetIndex()
  172. {
  173. return $this->m_aIndex;
  174. }
  175. public function __toString()
  176. {
  177. return $this->m_sLog;
  178. }
  179. public function ClearModifiedFlag()
  180. {
  181. $this->m_bModified = false;
  182. }
  183. /**
  184. * Produces an HTML representation, aimed at being used within an email
  185. */
  186. public function GetAsEmailHtml()
  187. {
  188. $sStyleCaseLogHeader = '';
  189. $sStyleCaseLogEntry = '';
  190. $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
  191. $iPos = 0;
  192. $aIndex = $this->m_aIndex;
  193. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  194. {
  195. $iPos += $aIndex[$index]['separator_length'];
  196. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  197. $sCSSClass = 'caselog_entry_html';
  198. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  199. {
  200. $sCSSClass = 'caselog_entry';
  201. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  202. }
  203. else
  204. {
  205. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  206. }
  207. $iPos += $aIndex[$index]['text_length'];
  208. $sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  209. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  210. // therefore we have changed the format. To preserve the compatibility with existing
  211. // installations of iTop, both format are allowed:
  212. // the 'date' item is either a DateTime object, or a unix timestamp
  213. if (is_int($aIndex[$index]['date']))
  214. {
  215. // Unix timestamp
  216. $sDate = date(AttributeDateTime::GetFormat(), $aIndex[$index]['date']);
  217. }
  218. elseif (is_object($aIndex[$index]['date']))
  219. {
  220. if (version_compare(phpversion(), '5.3.0', '>='))
  221. {
  222. // DateTime
  223. $sDate = $aIndex[$index]['date']->format(AttributeDateTime::GetFormat());
  224. }
  225. else
  226. {
  227. // No Warning... but the date is unknown
  228. $sDate = '';
  229. }
  230. }
  231. $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>');
  232. $sEntry .= '</div>';
  233. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  234. $sEntry .= $sTextEntry;
  235. $sEntry .= '</div>';
  236. $sHtml = $sHtml.$sEntry;
  237. }
  238. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  239. if ($iPos < (strlen($this->m_sLog) - 1))
  240. {
  241. $sTextEntry = substr($this->m_sLog, $iPos);
  242. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  243. if (count($this->m_aIndex) == 0)
  244. {
  245. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
  246. $sHtml .= $sTextEntry;
  247. $sHtml .= '</div>';
  248. }
  249. else
  250. {
  251. $sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  252. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  253. $sHtml .= '</div>';
  254. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
  255. $sHtml .= $sTextEntry;
  256. $sHtml .= '</div>';
  257. }
  258. }
  259. $sHtml .= '</td></tr></table>';
  260. return $sHtml;
  261. }
  262. /**
  263. * Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
  264. */
  265. public function GetAsSimpleHtml()
  266. {
  267. $sStyleCaseLogHeader = '';
  268. $sStyleCaseLogEntry = '';
  269. $sHtml = '<ul class="case_log_simple_html">';
  270. $iPos = 0;
  271. $aIndex = $this->m_aIndex;
  272. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  273. {
  274. $iPos += $aIndex[$index]['separator_length'];
  275. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  276. $sCSSClass = 'case_log_simple_html_entry_html';
  277. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  278. {
  279. $sCSSClass = 'case_log_simple_html_entry';
  280. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  281. if (!is_null($aTransfoHandler))
  282. {
  283. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  284. }
  285. }
  286. else
  287. {
  288. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  289. }
  290. $iPos += $aIndex[$index]['text_length'];
  291. $sEntry = '<li>';
  292. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  293. // therefore we have changed the format. To preserve the compatibility with existing
  294. // installations of iTop, both format are allowed:
  295. // the 'date' item is either a DateTime object, or a unix timestamp
  296. if (is_int($aIndex[$index]['date']))
  297. {
  298. // Unix timestamp
  299. $sDate = date(AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
  300. }
  301. elseif (is_object($aIndex[$index]['date']))
  302. {
  303. if (version_compare(phpversion(), '5.3.0', '>='))
  304. {
  305. // DateTime
  306. $sDate = $aIndex[$index]['date']->format(AttributeDateTime::GetFormat());
  307. }
  308. else
  309. {
  310. // No Warning... but the date is unknown
  311. $sDate = '';
  312. }
  313. }
  314. $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>');
  315. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  316. $sEntry .= $sTextEntry;
  317. $sEntry .= '</div>';
  318. $sEntry .= '</li>';
  319. $sHtml = $sHtml.$sEntry;
  320. }
  321. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  322. if ($iPos < (strlen($this->m_sLog) - 1))
  323. {
  324. $sTextEntry = substr($this->m_sLog, $iPos);
  325. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  326. if (count($this->m_aIndex) == 0)
  327. {
  328. $sHtml .= '<li>';
  329. $sHtml .= $sTextEntry;
  330. $sHtml .= '</li>';
  331. }
  332. else
  333. {
  334. $sHtml .= '<li>';
  335. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  336. $sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
  337. $sHtml .= $sTextEntry;
  338. $sHtml .= '</div>';
  339. $sHtml .= '</li>';
  340. }
  341. }
  342. $sHtml .= '</ul>';
  343. return $sHtml;
  344. }
  345. /**
  346. * Produces an HTML representation, aimed at being used within the iTop framework
  347. */
  348. public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
  349. {
  350. $bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
  351. $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
  352. $iPos = 0;
  353. $aIndex = $this->m_aIndex;
  354. if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
  355. {
  356. // Don't display the first element, that is still considered as editable
  357. $iPos = $aIndex[0]['separator_length'] + $aIndex[0]['text_length'];
  358. array_shift($aIndex);
  359. }
  360. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  361. {
  362. if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
  363. {
  364. $sOpen = '';
  365. $sDisplay = 'style="display:none;"';
  366. }
  367. else
  368. {
  369. $sOpen = ' open';
  370. $sDisplay = '';
  371. }
  372. $iPos += $aIndex[$index]['separator_length'];
  373. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  374. $sCSSClass= 'caselog_entry_html';
  375. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  376. {
  377. $sCSSClass= 'caselog_entry';
  378. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  379. if (!is_null($aTransfoHandler))
  380. {
  381. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  382. }
  383. }
  384. else
  385. {
  386. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  387. }
  388. $iPos += $aIndex[$index]['text_length'];
  389. $sEntry = '<div class="caselog_header'.$sOpen.'">';
  390. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  391. // therefore we have changed the format. To preserve the compatibility with existing
  392. // installations of iTop, both format are allowed:
  393. // the 'date' item is either a DateTime object, or a unix timestamp
  394. if (is_int($aIndex[$index]['date']))
  395. {
  396. // Unix timestamp
  397. $sDate = date(AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
  398. }
  399. elseif (is_object($aIndex[$index]['date']))
  400. {
  401. if (version_compare(phpversion(), '5.3.0', '>='))
  402. {
  403. // DateTime
  404. $sDate = $aIndex[$index]['date']->format(AttributeDateTime::GetFormat());
  405. }
  406. else
  407. {
  408. // No Warning... but the date is unknown
  409. $sDate = '';
  410. }
  411. }
  412. $sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']);
  413. $sEntry .= '</div>';
  414. $sEntry .= '<div class="'.$sCSSClass.'"'.$sDisplay.'>';
  415. $sEntry .= $sTextEntry;
  416. $sEntry .= '</div>';
  417. $sHtml = $sHtml.$sEntry;
  418. }
  419. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  420. if ($iPos < (strlen($this->m_sLog) - 1))
  421. {
  422. // In this case the format is always "text"
  423. $sTextEntry = substr($this->m_sLog, $iPos);
  424. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  425. if (!is_null($aTransfoHandler))
  426. {
  427. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  428. }
  429. if (count($this->m_aIndex) == 0)
  430. {
  431. $sHtml .= '<div class="caselog_entry open">';
  432. $sHtml .= $sTextEntry;
  433. $sHtml .= '</div>';
  434. }
  435. else
  436. {
  437. if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
  438. {
  439. $sOpen = '';
  440. $sDisplay = 'style="display:none;"';
  441. }
  442. else
  443. {
  444. $sOpen = ' open';
  445. $sDisplay = '';
  446. }
  447. $sHtml .= '<div class="caselog_header'.$sOpen.'">';
  448. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  449. $sHtml .= '</div>';
  450. $sHtml .= '<div class="caselog_entry"'.$sDisplay.'>';
  451. $sHtml .= $sTextEntry;
  452. $sHtml .= '</div>';
  453. }
  454. }
  455. $sHtml .= '</td></tr></table>';
  456. return $sHtml;
  457. }
  458. /**
  459. * Add a new entry to the log or merge the given text into the currently modified entry
  460. * and updates the internal index
  461. * @param $sText string The text of the new entry
  462. */
  463. public function AddLogEntry($sText, $sOnBehalfOf = '')
  464. {
  465. $sText = HTMLSanitizer::Sanitize($sText);
  466. $bMergeEntries = false;
  467. $sDate = date(AttributeDateTime::GetInternalFormat());
  468. if ($sOnBehalfOf == '')
  469. {
  470. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  471. $iUserId = UserRights::GetUserId();
  472. }
  473. else
  474. {
  475. $iUserId = null;
  476. }
  477. if ($this->m_bModified)
  478. {
  479. $aLatestEntry = end($this->m_aIndex);
  480. if ($aLatestEntry['user_name'] != $sOnBehalfOf)
  481. {
  482. $bMergeEntries = false;
  483. }
  484. else
  485. {
  486. $bMergeEntries = true;
  487. }
  488. }
  489. if ($bMergeEntries)
  490. {
  491. $aLatestEntry = end($this->m_aIndex);
  492. $this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length']);
  493. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  494. $iSepLength = strlen($sSeparator);
  495. $iTextlength = strlen($sText."\n");
  496. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  497. $this->m_aIndex[] = array(
  498. 'user_name' => $sOnBehalfOf,
  499. 'user_id' => $iUserId,
  500. 'date' => time(),
  501. 'text_length' => $aLatestEntry['text_length'] + $iTextlength,
  502. 'separator_length' => $iSepLength,
  503. 'format' => 'html',
  504. );
  505. }
  506. else
  507. {
  508. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  509. $iSepLength = strlen($sSeparator);
  510. $iTextlength = strlen($sText);
  511. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  512. $this->m_aIndex[] = array(
  513. 'user_name' => $sOnBehalfOf,
  514. 'user_id' => $iUserId,
  515. 'date' => time(),
  516. 'text_length' => $iTextlength,
  517. 'separator_length' => $iSepLength,
  518. 'format' => 'html',
  519. );
  520. }
  521. $this->m_bModified = true;
  522. }
  523. public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
  524. {
  525. $sText = HTMLSanitizer::Sanitize(isset($oJson->message) ? $oJson->message : '');
  526. if (isset($oJson->user_id))
  527. {
  528. if (!UserRights::IsAdministrator())
  529. {
  530. throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
  531. }
  532. if ($bCheckUserId && ($oJson->user_id != 0))
  533. {
  534. try
  535. {
  536. $oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
  537. }
  538. catch(Exception $e)
  539. {
  540. throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
  541. }
  542. $iUserId = $oUser->GetKey();
  543. $sOnBehalfOf = $oUser->GetFriendlyName();
  544. }
  545. else
  546. {
  547. $iUserId = $oJson->user_id;
  548. $sOnBehalfOf = $oJson->user_login;
  549. }
  550. }
  551. else
  552. {
  553. $iUserId = UserRights::GetUserId();
  554. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  555. }
  556. if (isset($oJson->date))
  557. {
  558. $oDate = new DateTime($oJson->date);
  559. $iDate = (int) $oDate->format('U');
  560. }
  561. else
  562. {
  563. $iDate = time();
  564. }
  565. if (isset($oJson->format))
  566. {
  567. $sFormat = $oJson->format;
  568. }
  569. else
  570. {
  571. // TODO: what is the default format ? text ?
  572. $sFormat = 'html';
  573. }
  574. $sDate = date(AttributeDateTime::GetInternalFormat(), $iDate);
  575. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  576. $iSepLength = strlen($sSeparator);
  577. $iTextlength = strlen($sText);
  578. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  579. $this->m_aIndex[] = array(
  580. 'user_name' => $sOnBehalfOf,
  581. 'user_id' => $iUserId,
  582. 'date' => $iDate,
  583. 'text_length' => $iTextlength,
  584. 'separator_length' => $iSepLength,
  585. 'format' => $sFormat,
  586. );
  587. $this->m_bModified = true;
  588. }
  589. public function GetModifiedEntry()
  590. {
  591. $sModifiedEntry = '';
  592. if ($this->m_bModified)
  593. {
  594. $sModifiedEntry = $this->GetLatestEntry();
  595. }
  596. return $sModifiedEntry;
  597. }
  598. /**
  599. * Get the latest entry from the log
  600. * @return string
  601. */
  602. public function GetLatestEntry()
  603. {
  604. $aLastEntry = end($this->m_aIndex);
  605. $sRes = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
  606. return $sRes;
  607. }
  608. /**
  609. * Get the index of the latest entry from the log
  610. * @return integer
  611. */
  612. public function GetLatestEntryIndex()
  613. {
  614. $aKeys = array_keys($this->m_aIndex);
  615. $iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
  616. return $iLast;
  617. }
  618. /**
  619. * Get the text string corresponding to the given entry in the log (zero based index, older entries first)
  620. * @param integer $iIndex
  621. * @return string The text of the entry
  622. */
  623. public function GetEntryAt($iIndex)
  624. {
  625. $iPos = 0;
  626. $index = count($this->m_aIndex) - 1;
  627. $aIndex = $this->m_aIndex;
  628. while($index > $iIndex)
  629. {
  630. $iPos += $this->m_aIndex[$index]['separator_length'];
  631. $iPos += $this->m_aIndex[$index]['text_length'];
  632. $index--;
  633. }
  634. $iPos += $this->m_aIndex[$index]['separator_length'];
  635. $sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
  636. return $sText;
  637. }
  638. }
  639. ?>