diff options
Diffstat (limited to 'inc')
-rw-r--r-- | inc/changelog.php | 284 | ||||
-rw-r--r-- | inc/html.php | 47 | ||||
-rw-r--r-- | inc/lang/en/lang.php | 4 | ||||
-rw-r--r-- | inc/lang/nl/lang.php | 4 |
4 files changed, 288 insertions, 51 deletions
diff --git a/inc/changelog.php b/inc/changelog.php index 6ff1e0eca..f7b07ae57 100644 --- a/inc/changelog.php +++ b/inc/changelog.php @@ -360,59 +360,13 @@ function getRevisionInfo($id, $rev, $chunk_size=8192, $media=false) { } else { $file = metaFN($id, '.changes'); } - if (!@file_exists($file)) { return false; } - if (filesize($file)<$chunk_size || $chunk_size==0) { - // read whole file - $lines = file($file); - if ($lines===false) { return false; } - } else { - // read by chunk - $fp = fopen($file, 'rb'); // "file pointer" - if ($fp===false) { return false; } - $head = 0; - fseek($fp, 0, SEEK_END); - $tail = ftell($fp); - $finger = 0; - $finger_rev = 0; - // find chunk - while ($tail-$head>$chunk_size) { - $finger = $head+floor(($tail-$head)/2.0); - fseek($fp, $finger); - fgets($fp); // slip the finger forward to a new line - $finger = ftell($fp); - $tmp = fgets($fp); // then read at that location - $tmp = parseChangelogLine($tmp); - $finger_rev = $tmp['date']; - if ($finger==$head || $finger==$tail) { break; } - if ($finger_rev>$rev) { - $tail = $finger; - } else { - $head = $finger; - } - } - - if ($tail-$head<1) { - // cound not find chunk, assume requested rev is missing - fclose($fp); - return false; - } - - // read chunk - $chunk = ''; - $chunk_size = max($tail-$head, 0); // found chunk size - $got = 0; - fseek($fp, $head); - while ($got<$chunk_size && !feof($fp)) { - $tmp = @fread($fp, max($chunk_size-$got, 0)); - if ($tmp===false) { break; } //error state - $got += strlen($tmp); - $chunk .= $tmp; - } - $lines = explode("\n", $chunk); - array_pop($lines); // remove trailing newline + //read lines from changelog + list($fp, $lines) = _readloglines($file, $rev, $chunk_size); + if($fp) { fclose($fp); } + if(empty($lines)) return false; // parse and cache changelog lines foreach ($lines as $value) { @@ -545,4 +499,234 @@ function getRevisions($id, $first, $num, $chunk_size=8192, $media=false) { return $revs; } +/** + * Get the nth revision left or right handside for a specific page id + * and revision (timestamp). For large changelog files, only the chunk containing the + * reference revision $rev is read and sometimes a next chunck. + * + * Adjacent changelog lines are optimistically parsed and cached to speed up + * consecutive calls to getRevisionInfo. + * + * @author Gerrit Uitslag <klapinklapin@gmail.com> + * + * based on getRevisionInfo by + * @author Ben Coburn <btcoburn@silicodon.net> + * @author Kate Arzamastseva <pshns@ukr.net> + * + * @param string $id pageid + * @param int $rev revision timestamp used as startdate (doesn't need to be revisionnumber) + * @param int $direction give position of returned revision with respect to $rev; positive=next, negative=prev + * @param int $chunk_size maximum block size + * @param bool $media + * @return bool|string + */ +function getRelativeRevision($id, $rev, $direction, $chunk_size = 8192, $media = false) { + global $cache_revinfo; + global $INFO; + $cache =& $cache_revinfo; + if(!isset($cache[$id])) { + $cache[$id] = array(); + } + $rev = max($rev, 0); + $direction = (int) $direction; + + //no direction given or last rev, so no follow-up + if(!$direction || + ($direction > 0 + && isset($INFO['meta']['last_change']['date']) + && $rev == $INFO['meta']['last_change']['date'])) { + return false; + } + + if($media) { + $file = mediaMetaFN($id, '.changes'); + } else { + $file = metaFN($id, '.changes'); + } + + //get lines from changelog + list($fp, $lines, $head, $tail, $eof) = _readloglines($file, $rev, $chunk_size); + if(empty($lines)) return false; + + // look for revisions later/earlier then $rev, when founded count till the wanted revision is reached + // also parse and cache changelog lines for getRevisionInfo(). + $revcounter = 0; + $relativerev = false; + $checkotherchunck = true; //always runs once + while(!$relativerev && $checkotherchunck) { + $tmp = array(); + //parse in normal or reverse order + $count = count($lines); + if($direction > 0) { + $start = 0; + $step = 1; + } else { + $start = $count - 1; + $step = -1; + } + for($i = $start; $i >= 0 && $i < $count; $i = $i + $step) { + $tmp = parseChangelogLine($lines[$i]); + if($tmp !== false) { + $cache[$id][$tmp['date']] = $tmp; + //look for revs older/earlier then reference $rev and select $direction-th one + if(($direction > 0 && $tmp['date'] > $rev) || ($direction < 0 && $tmp['date'] < $rev)) { + $revcounter++; + if($revcounter == abs($direction)) { + $relativerev = $tmp['date']; + } + } + } + } + + //true when $rev is found, but not the wanted follow-up. + $checkotherchunck = $fp + && ($tmp['date'] == $rev || ($revcounter > 0 && !$relativerev)) + && !(($tail == $eof && $direction > 0) || ($head == 0 && $direction < 0)); + + if($checkotherchunck) { + //search bounds of chunck, rounded on new line, but smaller than $chunck_size + if($direction > 0) { + $head = $tail; + $lookpointer = true; + $tail = $head + floor($chunk_size * (2 / 3)); + while($lookpointer) { + $tail = min($tail, $eof); + $tail = _getNewlinepointer($fp, $tail); + $lookpointer = $tail - $head > $chunk_size; + if($lookpointer) { + $tail = $head + floor(($tail - $head) / 2); + } + if($tail == $head) break; + } + } else { + $tail = $head; + $head = max($tail - $chunk_size, 0); + $head = _getNewlinepointer($fp, $head); + } + + //load next chunck + $lines = _readChunk($fp, $head, $tail); + if(empty($lines)) break; + } + } + if($fp) { + fclose($fp); + } + + if(isset($INFO['meta']['last_change']) && $relativerev == $INFO['meta']['last_change']['date']) { + return 'current'; + } + return $relativerev; +} + +/** + * get lines from changelog. + * If file larger than $chuncksize, only chunck is read that could contain $rev. + * + * @param int $file path to changelog file + * @param int $rev revision timestamp + * @param int $chunk_size maximum block size read from file + * @return array(fp, array(changeloglines), $head, $tail, $eof)|bool + * returns false when not succeed. fp only defined for chuck reading, needs closing. + */ +function _readloglines($file, $rev, $chunk_size) { + if(!@file_exists($file)) { + return false; + } + + $fp = null; + $head = 0; + $tail = 0; + $eof = 0; + if(filesize($file) < $chunk_size || $chunk_size == 0) { + // read whole file + $lines = file($file); + if($lines === false) { + return false; + } + } else { + // read by chunk + $fp = fopen($file, 'rb'); // "file pointer" + if($fp === false) { + return false; + } + $head = 0; + fseek($fp, 0, SEEK_END); + $eof = ftell($fp); + $tail = $eof; + $finger = 0; + $finger_rev = 0; + + // find chunk + while($tail - $head > $chunk_size) { + $finger = $head + floor(($tail - $head) / 2.0); + $finger = _getNewlinepointer($fp, $finger); + $tmp = fgets($fp); + $tmp = parseChangelogLine($tmp); + $finger_rev = $tmp['date']; + if($finger == $head || $finger == $tail) { + break; + } + if($finger_rev > $rev) { + $tail = $finger; + } else { + $head = $finger; + } + } + + if($tail - $head < 1) { + // cound not find chunk, assume requested rev is missing + fclose($fp); + return false; + } + + $lines = _readChunk($fp, $head, $tail); + } + return array( + $fp, + $lines, + $head, + $tail, + $eof + ); +} + +/** + * Read chunk and return array with lines of given chunck. + * Has no check if $head and $tail are really at a new line + * + * @param $fp resource filepointer + * @param $head int start point chunck + * @param $tail int end point chunck + * @return array lines read from chunck + */ +function _readChunk($fp, $head, $tail) { + $chunk = ''; + $chunk_size = max($tail - $head, 0); // found chunk size + $got = 0; + fseek($fp, $head); + while($got < $chunk_size && !feof($fp)) { + $tmp = @fread($fp, max($chunk_size - $got, 0)); + if($tmp === false) { //error state + break; + } + $got += strlen($tmp); + $chunk .= $tmp; + } + $lines = explode("\n", $chunk); + array_pop($lines); // remove trailing newline + return $lines; +} +/** + * Set pointer to first new line after $finger and return its position + * + * @param $fp resource filepointer + * @param $finger int a pointer + * @return int pointer + */ +function _getNewlinepointer($fp, $finger) { + fseek($fp, $finger); + fgets($fp); // slip the finger forward to a new line + return ftell($fp); +} diff --git a/inc/html.php b/inc/html.php index 7f473cdb6..91d8c1833 100644 --- a/inc/html.php +++ b/inc/html.php @@ -1153,6 +1153,18 @@ function html_diff($text='',$intro=true,$type=null){ } $r_text = rawWiki($ID,$r_rev); + //look for previous/next revision + if($r_rev) { + $next_rev = getRelativeRevision($ID, $r_rev, 1); + } else { + $next_rev = false; + } + if($l_rev) { + $prev_rev = getRelativeRevision($ID, $l_rev, -1); + } else { + $prev_rev = false; + } + list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev, null, false, $type == 'inline'); } @@ -1192,7 +1204,40 @@ function html_diff($text='',$intro=true,$type=null){ 'rev2[1]' => $r_rev, 'difftype' => $type, )); - ptln('<p><a class="wikilink1" href="'.$diffurl.'">'.$lang['difflink'].'</a></p>'); + ptln('<p><a class="wikilink1" href="'.$diffurl.'">'.$lang['difflink'].'</a><br />'); + if($prev_rev){ + $diffurlprev = wl($ID, array( + 'do' => 'diff', + 'rev2[0]' => $prev_rev, + 'rev2[1]' => $l_rev, + 'difftype' => $type, + )); + ptln('<a class="wikilink1" href="'.$diffurlprev.'">← '.$lang['diffpreviousedit'].'</a> - '); + } + $recenturl = wl($ID, array( + 'do' => 'revisions' + )); + ptln('<a class="wikilink1" href="'.$recenturl.'">'.$lang['overviewrevs'].'</a>'); + if($next_rev){ + if($next_rev=='current') { + $diffurlnextparam = array( + 'do' => 'diff', + 'rev' => $r_rev, + 'difftype' => $type, + ); + $navnexttitle = $lang['difflastedit']; + } else { + $diffurlnextparam = array( + 'do' => 'diff', + 'rev2[0]' => $r_rev, + 'rev2[1]' => $next_rev, + 'difftype' => $type, + ); + $navnexttitle = $lang['diffnextedit']; + } + ptln(' - <a class="wikilink1" href="'.wl($ID, $diffurlnextparam).'">'.$navnexttitle.' →</a>'); + } + ptln('</p>'); ptln('</div>'); } ?> diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 17a75803f..73b9981d2 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -191,6 +191,10 @@ $lang['difflink'] = 'Link to this comparison view'; $lang['diff_type'] = 'View differences:'; $lang['diff_inline'] = 'Inline'; $lang['diff_side'] = 'Side by Side'; +$lang['diffpreviousedit'] = 'Previous edit'; +$lang['diffnextedit'] = 'Next edit'; +$lang['difflastedit'] = 'Last edit'; +$lang['overviewrevs'] = 'Overview of revisions'; $lang['line'] = 'Line'; $lang['breadcrumb'] = 'Trace'; $lang['youarehere'] = 'You are here'; diff --git a/inc/lang/nl/lang.php b/inc/lang/nl/lang.php index 5f95a99bf..cf5c5dae5 100644 --- a/inc/lang/nl/lang.php +++ b/inc/lang/nl/lang.php @@ -196,6 +196,10 @@ $lang['difflink'] = 'Link naar deze vergelijking'; $lang['diff_type'] = 'Bekijk verschillen:'; $lang['diff_inline'] = 'Inline'; $lang['diff_side'] = 'Zij aan zij'; +$lang['diffpreviousedit'] = 'Vorige bewerking'; +$lang['diffnextedit'] = 'Volgende bewerking'; +$lang['difflastedit'] = 'Laatste bewerking'; +$lang['overviewrevs'] = 'Overzicht van revisies'; $lang['line'] = 'Regel'; $lang['breadcrumb'] = 'Spoor'; $lang['youarehere'] = 'Je bent hier'; |