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