1<?php 2 3namespace dokuwiki\ChangeLog; 4 5/** 6 * methods for handling of changelog of pages or media files 7 */ 8abstract class ChangeLog 9{ 10 11 /** @var string */ 12 protected $id; 13 /** @var int */ 14 protected $chunk_size; 15 /** @var array */ 16 protected $cache; 17 18 /** 19 * Constructor 20 * 21 * @param string $id page id 22 * @param int $chunk_size maximum block size read from file 23 */ 24 public function __construct($id, $chunk_size = 8192) 25 { 26 global $cache_revinfo; 27 28 $this->cache =& $cache_revinfo; 29 if (!isset($this->cache[$id])) { 30 $this->cache[$id] = array(); 31 } 32 33 $this->id = $id; 34 $this->setChunkSize($chunk_size); 35 36 } 37 38 /** 39 * Set chunk size for file reading 40 * Chunk size zero let read whole file at once 41 * 42 * @param int $chunk_size maximum block size read from file 43 */ 44 public function setChunkSize($chunk_size) 45 { 46 if (!is_numeric($chunk_size)) $chunk_size = 0; 47 48 $this->chunk_size = (int)max($chunk_size, 0); 49 } 50 51 /** 52 * Returns path to changelog 53 * 54 * @return string path to file 55 */ 56 abstract protected function getChangelogFilename(); 57 58 /** 59 * Returns path to current page/media 60 * 61 * @return string path to file 62 */ 63 abstract protected function getFilename(); 64 65 /** 66 * Get the changelog information for a specific page id and revision (timestamp) 67 * 68 * Adjacent changelog lines are optimistically parsed and cached to speed up 69 * consecutive calls to getRevisionInfo. For large changelog files, only the chunk 70 * containing the requested changelog line is read. 71 * 72 * @param int $rev revision timestamp 73 * @return bool|array false or array with entries: 74 * - date: unix timestamp 75 * - ip: IPv4 address (127.0.0.1) 76 * - type: log line type 77 * - id: page id 78 * - user: user name 79 * - sum: edit summary (or action reason) 80 * - extra: extra data (varies by line type) 81 * 82 * @author Ben Coburn <btcoburn@silicodon.net> 83 * @author Kate Arzamastseva <pshns@ukr.net> 84 */ 85 public function getRevisionInfo($rev) 86 { 87 $rev = max(0, $rev); 88 if (!$rev) return false; 89 90 // check if it's already in the memory cache 91 if (isset($this->cache[$this->id]) && isset($this->cache[$this->id][$rev])) { 92 return $this->cache[$this->id][$rev]; 93 } 94 95 //read lines from changelog 96 list($fp, $lines) = $this->readloglines($rev); 97 if ($fp) { 98 fclose($fp); 99 } 100 if (empty($lines)) return false; 101 102 // parse and cache changelog lines 103 foreach ($lines as $value) { 104 $tmp = parseChangelogLine($value); 105 if ($tmp !== false) { 106 $this->cache[$this->id][$tmp['date']] = $tmp; 107 } 108 } 109 if (!isset($this->cache[$this->id][$rev])) { 110 return false; 111 } 112 return $this->cache[$this->id][$rev]; 113 } 114 115 /** 116 * Return a list of page revisions numbers 117 * 118 * Does not guarantee that the revision exists in the attic, 119 * only that a line with the date exists in the changelog. 120 * By default the current revision is skipped. 121 * 122 * The current revision is automatically skipped when the page exists. 123 * See $INFO['meta']['last_change'] for the current revision. 124 * A negative $first let read the current revision too. 125 * 126 * For efficiency, the log lines are parsed and cached for later 127 * calls to getRevisionInfo. Large changelog files are read 128 * backwards in chunks until the requested number of changelog 129 * lines are recieved. 130 * 131 * @param int $first skip the first n changelog lines 132 * @param int $num number of revisions to return 133 * @return array with the revision timestamps 134 * 135 * @author Ben Coburn <btcoburn@silicodon.net> 136 * @author Kate Arzamastseva <pshns@ukr.net> 137 */ 138 public function getRevisions($first, $num) 139 { 140 $revs = array(); 141 $lines = array(); 142 $count = 0; 143 144 $num = max($num, 0); 145 if ($num == 0) { 146 return $revs; 147 } 148 149 if ($first < 0) { 150 $first = 0; 151 } else { 152 if (file_exists($this->getFilename())) { 153 // skip current revision if the page exists 154 $first = max($first + 1, 0); 155 } 156 } 157 158 $file = $this->getChangelogFilename(); 159 160 if (!file_exists($file)) { 161 return $revs; 162 } 163 if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) { 164 // read whole file 165 $lines = file($file); 166 if ($lines === false) { 167 return $revs; 168 } 169 } else { 170 // read chunks backwards 171 $fp = fopen($file, 'rb'); // "file pointer" 172 if ($fp === false) { 173 return $revs; 174 } 175 fseek($fp, 0, SEEK_END); 176 $tail = ftell($fp); 177 178 // chunk backwards 179 $finger = max($tail - $this->chunk_size, 0); 180 while ($count < $num + $first) { 181 $nl = $this->getNewlinepointer($fp, $finger); 182 183 // was the chunk big enough? if not, take another bite 184 if ($nl > 0 && $tail <= $nl) { 185 $finger = max($finger - $this->chunk_size, 0); 186 continue; 187 } else { 188 $finger = $nl; 189 } 190 191 // read chunk 192 $chunk = ''; 193 $read_size = max($tail - $finger, 0); // found chunk size 194 $got = 0; 195 while ($got < $read_size && !feof($fp)) { 196 $tmp = @fread($fp, max(min($this->chunk_size, $read_size - $got), 0)); 197 if ($tmp === false) { 198 break; 199 } //error state 200 $got += strlen($tmp); 201 $chunk .= $tmp; 202 } 203 $tmp = explode("\n", $chunk); 204 array_pop($tmp); // remove trailing newline 205 206 // combine with previous chunk 207 $count += count($tmp); 208 $lines = array_merge($tmp, $lines); 209 210 // next chunk 211 if ($finger == 0) { 212 break; 213 } else { // already read all the lines 214 $tail = $finger; 215 $finger = max($tail - $this->chunk_size, 0); 216 } 217 } 218 fclose($fp); 219 } 220 221 // skip parsing extra lines 222 $num = max(min(count($lines) - $first, $num), 0); 223 if ($first > 0 && $num > 0) { 224 $lines = array_slice($lines, max(count($lines) - $first - $num, 0), $num); 225 } else { 226 if ($first > 0 && $num == 0) { 227 $lines = array_slice($lines, 0, max(count($lines) - $first, 0)); 228 } elseif ($first == 0 && $num > 0) { 229 $lines = array_slice($lines, max(count($lines) - $num, 0)); 230 } 231 } 232 233 // handle lines in reverse order 234 for ($i = count($lines) - 1; $i >= 0; $i--) { 235 $tmp = parseChangelogLine($lines[$i]); 236 if ($tmp !== false) { 237 $this->cache[$this->id][$tmp['date']] = $tmp; 238 $revs[] = $tmp['date']; 239 } 240 } 241 242 return $revs; 243 } 244 245 /** 246 * Get the nth revision left or right handside for a specific page id and revision (timestamp) 247 * 248 * For large changelog files, only the chunk containing the 249 * reference revision $rev is read and sometimes a next chunck. 250 * 251 * Adjacent changelog lines are optimistically parsed and cached to speed up 252 * consecutive calls to getRevisionInfo. 253 * 254 * @param int $rev revision timestamp used as startdate (doesn't need to be revisionnumber) 255 * @param int $direction give position of returned revision with respect to $rev; positive=next, negative=prev 256 * @return bool|int 257 * timestamp of the requested revision 258 * otherwise false 259 */ 260 public function getRelativeRevision($rev, $direction) 261 { 262 $rev = max($rev, 0); 263 $direction = (int)$direction; 264 265 //no direction given or last rev, so no follow-up 266 if (!$direction || ($direction > 0 && $this->isCurrentRevision($rev))) { 267 return false; 268 } 269 270 //get lines from changelog 271 list($fp, $lines, $head, $tail, $eof) = $this->readloglines($rev); 272 if (empty($lines)) return false; 273 274 // look for revisions later/earlier than $rev, when founded count till the wanted revision is reached 275 // also parse and cache changelog lines for getRevisionInfo(). 276 $revcounter = 0; 277 $relativerev = false; 278 $checkotherchunck = true; //always runs once 279 while (!$relativerev && $checkotherchunck) { 280 $tmp = array(); 281 //parse in normal or reverse order 282 $count = count($lines); 283 if ($direction > 0) { 284 $start = 0; 285 $step = 1; 286 } else { 287 $start = $count - 1; 288 $step = -1; 289 } 290 for ($i = $start; $i >= 0 && $i < $count; $i = $i + $step) { 291 $tmp = parseChangelogLine($lines[$i]); 292 if ($tmp !== false) { 293 $this->cache[$this->id][$tmp['date']] = $tmp; 294 //look for revs older/earlier then reference $rev and select $direction-th one 295 if (($direction > 0 && $tmp['date'] > $rev) || ($direction < 0 && $tmp['date'] < $rev)) { 296 $revcounter++; 297 if ($revcounter == abs($direction)) { 298 $relativerev = $tmp['date']; 299 } 300 } 301 } 302 } 303 304 //true when $rev is found, but not the wanted follow-up. 305 $checkotherchunck = $fp 306 && ($tmp['date'] == $rev || ($revcounter > 0 && !$relativerev)) 307 && !(($tail == $eof && $direction > 0) || ($head == 0 && $direction < 0)); 308 309 if ($checkotherchunck) { 310 list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, $direction); 311 312 if (empty($lines)) break; 313 } 314 } 315 if ($fp) { 316 fclose($fp); 317 } 318 319 return $relativerev; 320 } 321 322 /** 323 * Returns revisions around rev1 and rev2 324 * When available it returns $max entries for each revision 325 * 326 * @param int $rev1 oldest revision timestamp 327 * @param int $rev2 newest revision timestamp (0 looks up last revision) 328 * @param int $max maximum number of revisions returned 329 * @return array with two arrays with revisions surrounding rev1 respectively rev2 330 */ 331 public function getRevisionsAround($rev1, $rev2, $max = 50) 332 { 333 $max = intval(abs($max) / 2) * 2 + 1; 334 $rev1 = max($rev1, 0); 335 $rev2 = max($rev2, 0); 336 337 if ($rev2) { 338 if ($rev2 < $rev1) { 339 $rev = $rev2; 340 $rev2 = $rev1; 341 $rev1 = $rev; 342 } 343 } else { 344 //empty right side means a removed page. Look up last revision. 345 $revs = $this->getRevisions(-1, 1); 346 $rev2 = $revs[0]; 347 } 348 //collect revisions around rev2 349 list($revs2, $allrevs, $fp, $lines, $head, $tail) = $this->retrieveRevisionsAround($rev2, $max); 350 351 if (empty($revs2)) return array(array(), array()); 352 353 //collect revisions around rev1 354 $index = array_search($rev1, $allrevs); 355 if ($index === false) { 356 //no overlapping revisions 357 list($revs1, , , , ,) = $this->retrieveRevisionsAround($rev1, $max); 358 if (empty($revs1)) $revs1 = array(); 359 } else { 360 //revisions overlaps, reuse revisions around rev2 361 $lastrev = array_pop($allrevs); //keep last entry that could be external edit 362 $revs1 = $allrevs; 363 while ($head > 0) { 364 for ($i = count($lines) - 1; $i >= 0; $i--) { 365 $tmp = parseChangelogLine($lines[$i]); 366 if ($tmp !== false) { 367 $this->cache[$this->id][$tmp['date']] = $tmp; 368 $revs1[] = $tmp['date']; 369 $index++; 370 371 if ($index > intval($max / 2)) break 2; 372 } 373 } 374 375 list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1); 376 } 377 sort($revs1); 378 $revs1[] = $lastrev; //push back last entry 379 380 //return wanted selection 381 $revs1 = array_slice($revs1, max($index - intval($max / 2), 0), $max); 382 } 383 384 return array(array_reverse($revs1), array_reverse($revs2)); 385 } 386 387 388 /** 389 * Checks if the ID has old revisons 390 * @return boolean 391 */ 392 public function hasRevisions() { 393 $file = $this->getChangelogFilename(); 394 return file_exists($file); 395 } 396 397 /** 398 * Returns lines from changelog. 399 * If file larger than $chuncksize, only chunck is read that could contain $rev. 400 * 401 * @param int $rev revision timestamp 402 * @return array|false 403 * if success returns array(fp, array(changeloglines), $head, $tail, $eof) 404 * where fp only defined for chuck reading, needs closing. 405 * otherwise false 406 */ 407 protected function readloglines($rev) 408 { 409 $file = $this->getChangelogFilename(); 410 411 if (!file_exists($file)) { 412 return false; 413 } 414 415 $fp = null; 416 $head = 0; 417 $tail = 0; 418 $eof = 0; 419 420 if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) { 421 // read whole file 422 $lines = file($file); 423 if ($lines === false) { 424 return false; 425 } 426 } else { 427 // read by chunk 428 $fp = fopen($file, 'rb'); // "file pointer" 429 if ($fp === false) { 430 return false; 431 } 432 $head = 0; 433 fseek($fp, 0, SEEK_END); 434 $eof = ftell($fp); 435 $tail = $eof; 436 437 // find chunk 438 while ($tail - $head > $this->chunk_size) { 439 $finger = $head + intval(($tail - $head) / 2); 440 $finger = $this->getNewlinepointer($fp, $finger); 441 $tmp = fgets($fp); 442 if ($finger == $head || $finger == $tail) { 443 break; 444 } 445 $tmp = parseChangelogLine($tmp); 446 $finger_rev = $tmp['date']; 447 448 if ($finger_rev > $rev) { 449 $tail = $finger; 450 } else { 451 $head = $finger; 452 } 453 } 454 455 if ($tail - $head < 1) { 456 // cound not find chunk, assume requested rev is missing 457 fclose($fp); 458 return false; 459 } 460 461 $lines = $this->readChunk($fp, $head, $tail); 462 } 463 return array( 464 $fp, 465 $lines, 466 $head, 467 $tail, 468 $eof, 469 ); 470 } 471 472 /** 473 * Read chunk and return array with lines of given chunck. 474 * Has no check if $head and $tail are really at a new line 475 * 476 * @param resource $fp resource filepointer 477 * @param int $head start point chunck 478 * @param int $tail end point chunck 479 * @return array lines read from chunck 480 */ 481 protected function readChunk($fp, $head, $tail) 482 { 483 $chunk = ''; 484 $chunk_size = max($tail - $head, 0); // found chunk size 485 $got = 0; 486 fseek($fp, $head); 487 while ($got < $chunk_size && !feof($fp)) { 488 $tmp = @fread($fp, max(min($this->chunk_size, $chunk_size - $got), 0)); 489 if ($tmp === false) { //error state 490 break; 491 } 492 $got += strlen($tmp); 493 $chunk .= $tmp; 494 } 495 $lines = explode("\n", $chunk); 496 array_pop($lines); // remove trailing newline 497 return $lines; 498 } 499 500 /** 501 * Set pointer to first new line after $finger and return its position 502 * 503 * @param resource $fp filepointer 504 * @param int $finger a pointer 505 * @return int pointer 506 */ 507 protected function getNewlinepointer($fp, $finger) 508 { 509 fseek($fp, $finger); 510 $nl = $finger; 511 if ($finger > 0) { 512 fgets($fp); // slip the finger forward to a new line 513 $nl = ftell($fp); 514 } 515 return $nl; 516 } 517 518 /** 519 * Check whether given revision is the current page 520 * 521 * @param int $rev timestamp of current page 522 * @return bool true if $rev is current revision, otherwise false 523 */ 524 public function isCurrentRevision($rev) 525 { 526 return $rev == @filemtime($this->getFilename()); 527 } 528 529 /** 530 * Return an existing revision for a specific date which is 531 * the current one or younger or equal then the date 532 * 533 * @param number $date_at timestamp 534 * @return string revision ('' for current) 535 */ 536 public function getLastRevisionAt($date_at) 537 { 538 //requested date_at(timestamp) younger or equal then modified_time($this->id) => load current 539 if (file_exists($this->getFilename()) && $date_at >= @filemtime($this->getFilename())) { 540 return ''; 541 } else { 542 if ($rev = $this->getRelativeRevision($date_at + 1, -1)) { //+1 to get also the requested date revision 543 return $rev; 544 } else { 545 return false; 546 } 547 } 548 } 549 550 /** 551 * Returns the next lines of the changelog of the chunck before head or after tail 552 * 553 * @param resource $fp filepointer 554 * @param int $head position head of last chunk 555 * @param int $tail position tail of last chunk 556 * @param int $direction positive forward, negative backward 557 * @return array with entries: 558 * - $lines: changelog lines of readed chunk 559 * - $head: head of chunk 560 * - $tail: tail of chunk 561 */ 562 protected function readAdjacentChunk($fp, $head, $tail, $direction) 563 { 564 if (!$fp) return array(array(), $head, $tail); 565 566 if ($direction > 0) { 567 //read forward 568 $head = $tail; 569 $tail = $head + intval($this->chunk_size * (2 / 3)); 570 $tail = $this->getNewlinepointer($fp, $tail); 571 } else { 572 //read backward 573 $tail = $head; 574 $head = max($tail - $this->chunk_size, 0); 575 while (true) { 576 $nl = $this->getNewlinepointer($fp, $head); 577 // was the chunk big enough? if not, take another bite 578 if ($nl > 0 && $tail <= $nl) { 579 $head = max($head - $this->chunk_size, 0); 580 } else { 581 $head = $nl; 582 break; 583 } 584 } 585 } 586 587 //load next chunck 588 $lines = $this->readChunk($fp, $head, $tail); 589 return array($lines, $head, $tail); 590 } 591 592 /** 593 * Collect the $max revisions near to the timestamp $rev 594 * 595 * @param int $rev revision timestamp 596 * @param int $max maximum number of revisions to be returned 597 * @return bool|array 598 * return array with entries: 599 * - $requestedrevs: array of with $max revision timestamps 600 * - $revs: all parsed revision timestamps 601 * - $fp: filepointer only defined for chuck reading, needs closing. 602 * - $lines: non-parsed changelog lines before the parsed revisions 603 * - $head: position of first readed changelogline 604 * - $lasttail: position of end of last readed changelogline 605 * otherwise false 606 */ 607 protected function retrieveRevisionsAround($rev, $max) 608 { 609 $lastChangelogRev = 0; 610 $externaleditRevinfo = $this->getExternalEditRevInfo(); 611 612 if ($externaleditRevinfo) { 613 $revisions = $this->getRevisions(-1, 1); 614 $lastChangelogRev = $revisions[0]; 615 if ($externaleditRevinfo['date'] == $rev) { 616 $rev = $lastChangelogRev; //replace by an existing changelog line 617 } 618 } 619 620 $revs = array(); 621 $aftercount = $beforecount = 0; 622 623 //get lines from changelog 624 list($fp, $lines, $starthead, $starttail, /* $eof */) = $this->readloglines($rev); 625 if (empty($lines)) { 626 if ($externaleditRevinfo) { 627 $revs[] = $externaleditRevinfo['date']; 628 return array($revs, $revs, false, [], 0, 0); 629 } else { 630 return false; 631 } 632 } 633 634 //parse chunk containing $rev, and read forward more chunks until $max/2 is reached 635 $head = $starthead; 636 $tail = $starttail; 637 while (count($lines) > 0) { 638 foreach ($lines as $line) { 639 $tmp = parseChangelogLine($line); 640 if ($tmp !== false) { 641 $this->cache[$this->id][$tmp['date']] = $tmp; 642 $revs[] = $tmp['date']; 643 if ($tmp['date'] >= $rev) { 644 //count revs after reference $rev 645 $aftercount++; 646 if ($aftercount == 1) $beforecount = count($revs); 647 } 648 //enough revs after reference $rev? 649 if ($aftercount > intval($max / 2)) break 2; 650 } 651 } 652 //retrieve next chunk 653 list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, 1); 654 } 655 if ($aftercount == 0) return false; 656 657 $lasttail = $tail; 658 659 //read additional chuncks backward until $max/2 is reached and total number of revs is equal to $max 660 $lines = array(); 661 $i = 0; 662 if ($aftercount > 0) { 663 $head = $starthead; 664 $tail = $starttail; 665 while ($head > 0) { 666 list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1); 667 668 for ($i = count($lines) - 1; $i >= 0; $i--) { 669 $tmp = parseChangelogLine($lines[$i]); 670 if ($tmp !== false) { 671 $this->cache[$this->id][$tmp['date']] = $tmp; 672 $revs[] = $tmp['date']; 673 $beforecount++; 674 //enough revs before reference $rev? 675 if ($beforecount > max(intval($max / 2), $max - $aftercount)) break 2; 676 } 677 } 678 } 679 } 680 sort($revs); 681 682 //add external edit when the last value is identical with the last revision in the changelog 683 if ($externaleditRevinfo && $revs[count($revs)-1] == $lastChangelogRev) { 684 $revs[] = $externaleditRevinfo['date']; 685 } 686 687 //keep only non-parsed lines 688 $lines = array_slice($lines, 0, $i); 689 //trunk desired selection 690 $requestedrevs = array_slice($revs, -$max, $max); 691 692 return array($requestedrevs, $revs, $fp, $lines, $head, $lasttail); 693 } 694 695 /** 696 * Returns revision logline in same format as @see ChangeLog::getRevisionInfo() 697 * 698 * @return bool|array false if not external edit/deletion, otherwise array with entries: 699 * - date: unix timestamp for external edit or 'unknown' for external deletion 700 * - ip: IPv4 address (127.0.0.1) 701 * - type: log line type 702 * - id: page id 703 * - user: user name 704 * - sum: edit summary (or action reason) 705 * - extra: extra data (varies by line type) 706 * 707 * @author Gerrit Uitslag <klapinklapin@gmail.com> 708 */ 709 public function getExternalEditRevInfo() 710 { 711 global $lang; 712 global $cache_externaledit; //caches external edits per page 713 714 // check if it's already in the memory cache 715 if (isset($cache_externaledit[$this->id])) { 716 if ($cache_externaledit[$this->id] === false) { 717 return false; 718 } else { 719 return $this->cache[$this->id][$cache_externaledit[$this->id]]; 720 } 721 } 722 $externaleditRevinfo = false; 723 $cache_externaledit[$this->id] = false; 724 725 //in attic no revision of current existing wiki page, so external edit occurred 726 $fileLastMod = $this->getFilename(); 727 $lastMod = @filemtime($fileLastMod); // from wiki page, suppresses warning in case the file not exists 728 $lastRev = $this->getRevisions(-1, 1); // from changelog 729 $lastRev = (int) (empty($lastRev) ? 0 : $lastRev[0]); 730 if (!file_exists($this->getFilename($lastMod)) && file_exists($fileLastMod) && $lastRev < $lastMod) { 731 $cache_externaledit[$this->id] = $lastMod; 732 $fileLastRev = wikiFN($this->id, $lastRev); //returns current wikipage path if $lastRev==false 733 $revinfo = $this->getRevisionInfo($lastRev); 734 if (empty($lastRev) || !file_exists($fileLastRev) || $revinfo['type'] == DOKU_CHANGE_TYPE_DELETE) { 735 $filesize_old = 0; 736 } else { 737 $filesize_old = io_getSizeFile($fileLastRev); 738 } 739 $filesize_new = filesize($fileLastMod); 740 $sizechange = $filesize_new - $filesize_old; 741 $isJustCreated = empty($lastRev) || !file_exists($fileLastRev); 742 743 $externaleditRevinfo = [ 744 'date' => $lastMod, 745 'ip' => '127.0.0.1', 746 'type' => $isJustCreated ? DOKU_CHANGE_TYPE_CREATE : DOKU_CHANGE_TYPE_EDIT, 747 'id' => $this->id, 748 'user' => '', 749 'sum' => ($isJustCreated ? $lang['created'] .' - ' : '') . $lang['external_edit'], 750 'extra' => '', 751 'sizechange' => $sizechange 752 ]; 753 $cache_externaledit[$this->id] = $externaleditRevinfo['date']; 754 $this->cache[$this->id][$externaleditRevinfo['date']] = $externaleditRevinfo; 755 } 756 757 $revinfo = $this->getRevisionInfo($lastRev); 758 //deleted wiki page, but not registered in changelog 759 if (!file_exists($fileLastMod) // there is no current page=>true 760 && !empty($lastRev) && $revinfo['type'] !== DOKU_CHANGE_TYPE_DELETE 761 ) { 762 $fileLastRev = $this->getFilename($lastRev); 763 $externaleditRevinfo = [ 764 'date' => time(), //unknown deletion date, always higher as latest rev 765 'ip' => '127.0.0.1', 766 'type' => DOKU_CHANGE_TYPE_EXTERNAL_DELETE, 767 'id' => $this->id, 768 'user' => '', 769 'sum' => $lang['deleted']. ' - ' . $lang['external_edit'], 770 'extra' => '', 771 'sizechange' => -io_getSizeFile($fileLastRev) 772 ]; 773 $cache_externaledit[$this->id] = $externaleditRevinfo['date']; 774 $this->cache[$this->id][$externaleditRevinfo['date']] = $externaleditRevinfo; 775 } 776 777 return $externaleditRevinfo; 778 } 779} 780