ormcaselog.class.inc.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. <?php
  2. // Copyright (C) 2010-2017 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-2017 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. if($this->IsEmpty()) return '';
  178. return $this->m_sLog;
  179. }
  180. public function IsEmpty()
  181. {
  182. return ($this->m_sLog === null);
  183. }
  184. public function ClearModifiedFlag()
  185. {
  186. $this->m_bModified = false;
  187. }
  188. /**
  189. * Produces an HTML representation, aimed at being used within an email
  190. */
  191. public function GetAsEmailHtml()
  192. {
  193. $sStyleCaseLogHeader = '';
  194. $sStyleCaseLogEntry = '';
  195. $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
  196. $iPos = 0;
  197. $aIndex = $this->m_aIndex;
  198. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  199. {
  200. $iPos += $aIndex[$index]['separator_length'];
  201. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  202. $sCSSClass = 'caselog_entry_html';
  203. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  204. {
  205. $sCSSClass = 'caselog_entry';
  206. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  207. }
  208. else
  209. {
  210. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  211. }
  212. $iPos += $aIndex[$index]['text_length'];
  213. $sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  214. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  215. // therefore we have changed the format. To preserve the compatibility with existing
  216. // installations of iTop, both format are allowed:
  217. // the 'date' item is either a DateTime object, or a unix timestamp
  218. if (is_int($aIndex[$index]['date']))
  219. {
  220. // Unix timestamp
  221. $sDate = date((string)AttributeDateTime::GetFormat(), $aIndex[$index]['date']);
  222. }
  223. elseif (is_object($aIndex[$index]['date']))
  224. {
  225. if (version_compare(phpversion(), '5.3.0', '>='))
  226. {
  227. // DateTime
  228. $sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
  229. }
  230. else
  231. {
  232. // No Warning... but the date is unknown
  233. $sDate = '';
  234. }
  235. }
  236. $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>');
  237. $sEntry .= '</div>';
  238. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  239. $sEntry .= $sTextEntry;
  240. $sEntry .= '</div>';
  241. $sHtml = $sHtml.$sEntry;
  242. }
  243. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  244. if ($iPos < (strlen($this->m_sLog) - 1))
  245. {
  246. $sTextEntry = substr($this->m_sLog, $iPos);
  247. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  248. if (count($this->m_aIndex) == 0)
  249. {
  250. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
  251. $sHtml .= $sTextEntry;
  252. $sHtml .= '</div>';
  253. }
  254. else
  255. {
  256. $sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
  257. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  258. $sHtml .= '</div>';
  259. $sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
  260. $sHtml .= $sTextEntry;
  261. $sHtml .= '</div>';
  262. }
  263. }
  264. $sHtml .= '</td></tr></table>';
  265. return $sHtml;
  266. }
  267. /**
  268. * Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
  269. */
  270. public function GetAsSimpleHtml($aTransfoHandler = null)
  271. {
  272. $sStyleCaseLogHeader = '';
  273. $sStyleCaseLogEntry = '';
  274. $sHtml = '<ul class="case_log_simple_html">';
  275. $iPos = 0;
  276. $aIndex = $this->m_aIndex;
  277. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  278. {
  279. $iPos += $aIndex[$index]['separator_length'];
  280. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  281. $sCSSClass = 'case_log_simple_html_entry_html';
  282. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  283. {
  284. $sCSSClass = 'case_log_simple_html_entry';
  285. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  286. if (!is_null($aTransfoHandler))
  287. {
  288. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  289. }
  290. }
  291. else
  292. {
  293. if (!is_null($aTransfoHandler))
  294. {
  295. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
  296. }
  297. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  298. }
  299. $iPos += $aIndex[$index]['text_length'];
  300. $sEntry = '<li>';
  301. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  302. // therefore we have changed the format. To preserve the compatibility with existing
  303. // installations of iTop, both format are allowed:
  304. // the 'date' item is either a DateTime object, or a unix timestamp
  305. if (is_int($aIndex[$index]['date']))
  306. {
  307. // Unix timestamp
  308. $sDate = date((string)AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
  309. }
  310. elseif (is_object($aIndex[$index]['date']))
  311. {
  312. if (version_compare(phpversion(), '5.3.0', '>='))
  313. {
  314. // DateTime
  315. $sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
  316. }
  317. else
  318. {
  319. // No Warning... but the date is unknown
  320. $sDate = '';
  321. }
  322. }
  323. $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>');
  324. $sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
  325. $sEntry .= $sTextEntry;
  326. $sEntry .= '</div>';
  327. $sEntry .= '</li>';
  328. $sHtml = $sHtml.$sEntry;
  329. }
  330. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  331. if ($iPos < (strlen($this->m_sLog) - 1))
  332. {
  333. $sTextEntry = substr($this->m_sLog, $iPos);
  334. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  335. if (count($this->m_aIndex) == 0)
  336. {
  337. $sHtml .= '<li>';
  338. $sHtml .= $sTextEntry;
  339. $sHtml .= '</li>';
  340. }
  341. else
  342. {
  343. $sHtml .= '<li>';
  344. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  345. $sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
  346. $sHtml .= $sTextEntry;
  347. $sHtml .= '</div>';
  348. $sHtml .= '</li>';
  349. }
  350. }
  351. $sHtml .= '</ul>';
  352. return $sHtml;
  353. }
  354. /**
  355. * Produces an HTML representation, aimed at being used within the iTop framework
  356. */
  357. public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
  358. {
  359. $bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
  360. $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
  361. $iPos = 0;
  362. $aIndex = $this->m_aIndex;
  363. if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
  364. {
  365. // Don't display the first element, that is still considered as editable
  366. $aLastEntry = end($aIndex);
  367. $iPos = $aLastEntry['separator_length'] + $aLastEntry['text_length'];
  368. array_pop($aIndex);
  369. }
  370. for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
  371. {
  372. if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
  373. {
  374. $sOpen = '';
  375. $sDisplay = 'style="display:none;"';
  376. }
  377. else
  378. {
  379. $sOpen = ' open';
  380. $sDisplay = '';
  381. }
  382. $iPos += $aIndex[$index]['separator_length'];
  383. $sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
  384. $sCSSClass= 'caselog_entry_html';
  385. if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
  386. {
  387. $sCSSClass= 'caselog_entry';
  388. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  389. if (!is_null($aTransfoHandler))
  390. {
  391. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  392. }
  393. }
  394. else
  395. {
  396. if (!is_null($aTransfoHandler))
  397. {
  398. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
  399. }
  400. $sTextEntry = InlineImage::FixUrls($sTextEntry);
  401. }
  402. $iPos += $aIndex[$index]['text_length'];
  403. $sEntry = '<div class="caselog_header'.$sOpen.'">';
  404. // Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
  405. // therefore we have changed the format. To preserve the compatibility with existing
  406. // installations of iTop, both format are allowed:
  407. // the 'date' item is either a DateTime object, or a unix timestamp
  408. if (is_int($aIndex[$index]['date']))
  409. {
  410. // Unix timestamp
  411. $sDate = date((string)AttributeDateTime::GetFormat(),$aIndex[$index]['date']);
  412. }
  413. elseif (is_object($aIndex[$index]['date']))
  414. {
  415. if (version_compare(phpversion(), '5.3.0', '>='))
  416. {
  417. // DateTime
  418. $sDate = $aIndex[$index]['date']->format((string)AttributeDateTime::GetFormat());
  419. }
  420. else
  421. {
  422. // No Warning... but the date is unknown
  423. $sDate = '';
  424. }
  425. }
  426. $sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']);
  427. $sEntry .= '</div>';
  428. $sEntry .= '<div class="'.$sCSSClass.'"'.$sDisplay.'>';
  429. $sEntry .= $sTextEntry;
  430. $sEntry .= '</div>';
  431. $sHtml = $sHtml.$sEntry;
  432. }
  433. // Process the case of an eventual remainder (quick migration of AttributeText fields)
  434. if ($iPos < (strlen($this->m_sLog) - 1))
  435. {
  436. // In this case the format is always "text"
  437. $sTextEntry = substr($this->m_sLog, $iPos);
  438. $sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
  439. if (!is_null($aTransfoHandler))
  440. {
  441. $sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
  442. }
  443. if (count($this->m_aIndex) == 0)
  444. {
  445. $sHtml .= '<div class="caselog_entry open">';
  446. $sHtml .= $sTextEntry;
  447. $sHtml .= '</div>';
  448. }
  449. else
  450. {
  451. if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
  452. {
  453. $sOpen = '';
  454. $sDisplay = 'style="display:none;"';
  455. }
  456. else
  457. {
  458. $sOpen = ' open';
  459. $sDisplay = '';
  460. }
  461. $sHtml .= '<div class="caselog_header'.$sOpen.'">';
  462. $sHtml .= Dict::S('UI:CaseLog:InitialValue');
  463. $sHtml .= '</div>';
  464. $sHtml .= '<div class="caselog_entry"'.$sDisplay.'>';
  465. $sHtml .= $sTextEntry;
  466. $sHtml .= '</div>';
  467. }
  468. }
  469. $sHtml .= '</td></tr></table>';
  470. return $sHtml;
  471. }
  472. /**
  473. * Add a new entry to the log or merge the given text into the currently modified entry
  474. * and updates the internal index
  475. * @param $sText string The text of the new entry
  476. */
  477. public function AddLogEntry($sText, $sOnBehalfOf = '')
  478. {
  479. $sText = HTMLSanitizer::Sanitize($sText);
  480. $bMergeEntries = false;
  481. $sDate = date(AttributeDateTime::GetInternalFormat());
  482. if ($sOnBehalfOf == '')
  483. {
  484. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  485. $iUserId = UserRights::GetUserId();
  486. }
  487. else
  488. {
  489. $iUserId = null;
  490. }
  491. if ($this->m_bModified)
  492. {
  493. $aLatestEntry = end($this->m_aIndex);
  494. if ($aLatestEntry['user_name'] == $sOnBehalfOf)
  495. {
  496. // Append the new text to the previous one
  497. $sPreviousText = substr($this->m_sLog, $aLatestEntry['separator_length'], $aLatestEntry['text_length']);
  498. $sText = $sPreviousText."\n".$sText;
  499. // Cleanup the previous entry
  500. array_pop($this->m_aIndex);
  501. $this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length'] + $aLatestEntry['text_length']);
  502. }
  503. }
  504. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  505. $iSepLength = strlen($sSeparator);
  506. $iTextlength = strlen($sText);
  507. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  508. $this->m_aIndex[] = array(
  509. 'user_name' => $sOnBehalfOf,
  510. 'user_id' => $iUserId,
  511. 'date' => time(),
  512. 'text_length' => $iTextlength,
  513. 'separator_length' => $iSepLength,
  514. 'format' => 'html',
  515. );
  516. $this->m_bModified = true;
  517. }
  518. public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
  519. {
  520. if (isset($oJson->user_id))
  521. {
  522. if (!UserRights::IsAdministrator())
  523. {
  524. throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
  525. }
  526. if ($bCheckUserId && ($oJson->user_id != 0))
  527. {
  528. try
  529. {
  530. $oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
  531. }
  532. catch(Exception $e)
  533. {
  534. throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
  535. }
  536. $iUserId = $oUser->GetKey();
  537. $sOnBehalfOf = $oUser->GetFriendlyName();
  538. }
  539. else
  540. {
  541. $iUserId = $oJson->user_id;
  542. $sOnBehalfOf = $oJson->user_login;
  543. }
  544. }
  545. else
  546. {
  547. $iUserId = UserRights::GetUserId();
  548. $sOnBehalfOf = UserRights::GetUserFriendlyName();
  549. }
  550. if (isset($oJson->date))
  551. {
  552. $oDate = new DateTime($oJson->date);
  553. $iDate = (int) $oDate->format('U');
  554. }
  555. else
  556. {
  557. $iDate = time();
  558. }
  559. if (isset($oJson->format))
  560. {
  561. $sFormat = $oJson->format;
  562. }
  563. else
  564. {
  565. // The default is HTML
  566. $sFormat = 'html';
  567. }
  568. $sText = isset($oJson->message) ? $oJson->message : '';
  569. if ($sFormat == 'html')
  570. {
  571. $sText = HTMLSanitizer::Sanitize($sText);
  572. }
  573. $sDate = date(AttributeDateTime::GetInternalFormat(), $iDate);
  574. $sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
  575. $iSepLength = strlen($sSeparator);
  576. $iTextlength = strlen($sText);
  577. $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
  578. $this->m_aIndex[] = array(
  579. 'user_name' => $sOnBehalfOf,
  580. 'user_id' => $iUserId,
  581. 'date' => $iDate,
  582. 'text_length' => $iTextlength,
  583. 'separator_length' => $iSepLength,
  584. 'format' => $sFormat,
  585. );
  586. $this->m_bModified = true;
  587. }
  588. public function GetModifiedEntry($sFormat = 'text')
  589. {
  590. $sModifiedEntry = '';
  591. if ($this->m_bModified)
  592. {
  593. $sModifiedEntry = $this->GetLatestEntry($sFormat);
  594. }
  595. return $sModifiedEntry;
  596. }
  597. /**
  598. * Get the latest entry from the log
  599. * @param string The expected output format text|html
  600. * @return string
  601. */
  602. public function GetLatestEntry($sFormat = 'text')
  603. {
  604. $sRes = '';
  605. $aLastEntry = end($this->m_aIndex);
  606. $sRaw = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
  607. switch($sFormat)
  608. {
  609. case 'text':
  610. if ($aLastEntry['format'] == 'text')
  611. {
  612. $sRes = $sRaw;
  613. }
  614. else
  615. {
  616. $sRes = utils::HtmlToText($sRaw);
  617. }
  618. break;
  619. case 'html':
  620. if ($aLastEntry['format'] == 'text')
  621. {
  622. $sRes = utils::TextToHtml($sRaw);
  623. }
  624. else
  625. {
  626. $sRes = $sRaw;
  627. }
  628. break;
  629. }
  630. return $sRes;
  631. }
  632. /**
  633. * Get the index of the latest entry from the log
  634. * @return integer
  635. */
  636. public function GetLatestEntryIndex()
  637. {
  638. $aKeys = array_keys($this->m_aIndex);
  639. $iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
  640. return $iLast;
  641. }
  642. /**
  643. * Get the text string corresponding to the given entry in the log (zero based index, older entries first)
  644. * @param integer $iIndex
  645. * @return string The text of the entry
  646. */
  647. public function GetEntryAt($iIndex)
  648. {
  649. $iPos = 0;
  650. $index = count($this->m_aIndex) - 1;
  651. $aIndex = $this->m_aIndex;
  652. while($index > $iIndex)
  653. {
  654. $iPos += $this->m_aIndex[$index]['separator_length'];
  655. $iPos += $this->m_aIndex[$index]['text_length'];
  656. $index--;
  657. }
  658. $iPos += $this->m_aIndex[$index]['separator_length'];
  659. $sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
  660. return $sText;
  661. }
  662. }
  663. ?>