function handle_approve(&$event, $param)
 {
     global $ID, $REV, $INFO;
     if ($event->data == 'show' && isset($_GET['approve'])) {
         if (!$this->can_approve()) {
             return;
         }
         //change last commit comment to Approved
         $meta = p_read_metadata($ID);
         $meta[current][last_change][sum] = $meta[persistent][last_change][sum] = APPROVED;
         $meta[current][last_change][user] = $meta[persistent][last_change][user] = $INFO[client];
         if (!array_key_exists($INFO[client], $meta[current][contributor])) {
             $meta[current][contributor][$INFO[client]] = $INFO[userinfo][name];
             $meta[persistent][contributor][$INFO[client]] = $INFO[userinfo][name];
         }
         p_save_metadata($ID, $meta);
         //update changelog
         //remove last line from file
         $changelog_file = metaFN($ID, '.changes');
         $changes = file($changelog_file, FILE_SKIP_EMPTY_LINES);
         $lastLogLine = array_pop($changes);
         $info = parseChangelogLine($lastLogLine);
         $info[user] = $INFO[client];
         $info[sum] = APPROVED;
         $logline = implode("\t", $info) . "\n";
         array_push($changes, $logline);
         io_saveFile($changelog_file, implode('', $changes));
         header('Location: ?id=' . $ID);
     }
 }
Esempio n. 2
0
/**
 * Trims the recent changes cache (or imports the old changelog) as needed.
 *
 * @param media_changes If the media changelog shall be trimmed instead of
 * the page changelog
 *
 * @author Ben Coburn <*****@*****.**>
 */
function runTrimRecentChanges($media_changes = false)
{
    global $conf;
    $fn = $media_changes ? $conf['media_changelog'] : $conf['changelog'];
    // Trim the Recent Changes
    // Trims the recent changes cache to the last $conf['changes_days'] recent
    // changes or $conf['recent'] items, which ever is larger.
    // The trimming is only done once a day.
    if (@file_exists($fn) && filectime($fn) + 86400 < time() && !@file_exists($fn . '_tmp')) {
        io_lock($fn);
        $lines = file($fn);
        if (count($lines) <= $conf['recent']) {
            // nothing to trim
            io_unlock($fn);
            return false;
        }
        io_saveFile($fn . '_tmp', '');
        // presave tmp as 2nd lock
        $trim_time = time() - $conf['recent_days'] * 86400;
        $out_lines = array();
        for ($i = 0; $i < count($lines); $i++) {
            $log = parseChangelogLine($lines[$i]);
            if ($log === false) {
                continue;
            }
            // discard junk
            if ($log['date'] < $trim_time) {
                $old_lines[$log['date'] . ".{$i}"] = $lines[$i];
                // keep old lines for now (append .$i to prevent key collisions)
            } else {
                $out_lines[$log['date'] . ".{$i}"] = $lines[$i];
                // definitely keep these lines
            }
        }
        // sort the final result, it shouldn't be necessary,
        //   however the extra robustness in making the changelog cache self-correcting is worth it
        ksort($out_lines);
        $extra = $conf['recent'] - count($out_lines);
        // do we need extra lines do bring us up to minimum
        if ($extra > 0) {
            ksort($old_lines);
            $out_lines = array_merge(array_slice($old_lines, -$extra), $out_lines);
        }
        // save trimmed changelog
        io_saveFile($fn . '_tmp', implode('', $out_lines));
        @unlink($fn);
        if (!rename($fn . '_tmp', $fn)) {
            // rename failed so try another way...
            io_unlock($fn);
            io_saveFile($fn, implode('', $out_lines));
            @unlink($fn . '_tmp');
        } else {
            io_unlock($fn);
        }
        return true;
    }
    // nothing done
    return false;
}
Esempio n. 3
0
function allRevisions($id)
{
    $ret = array();
    $lines = @file(metaFN($id, '.changes'));
    if (!$lines) {
        return $ret;
    }
    foreach ($lines as $line) {
        $tmp = parseChangelogLine($line);
        $ret[] = $tmp['date'];
    }
    return $ret;
}
Esempio n. 4
0
 function _getChanges($user)
 {
     global $conf;
     function globr($dir, $pattern)
     {
         $files = glob($dir . '/' . $pattern);
         foreach (glob($dir . '/*', GLOB_ONLYDIR) as $subdir) {
             $subfiles = globr($subdir, $pattern);
             $files = array_merge($files, $subfiles);
         }
         return $files;
     }
     $changes = array();
     $alllist = globr($conf['metadir'], '*.changes');
     $skip = array('_comments.changes', '_dokuwiki.changes');
     for ($i = 0; $i < count($alllist); $i++) {
         $fullname = $alllist[$i];
         $filepart = basename($fullname);
         if (in_array($filepart, $skip)) {
             continue;
         }
         $f = file($fullname);
         for ($j = 0; $j < count($f); $j++) {
             $line = $f[$j];
             $change = parseChangelogLine($line);
             if ($change['user'] == $user) {
                 $changes[] = $change;
             }
         }
         /* for all lines */
     }
     /* for all files */
     function cmp($a, $b)
     {
         $time1 = $a['date'];
         $time2 = $b['date'];
         if ($time1 == $time2) {
             return 0;
         }
         return $time1 < $time2 ? 1 : -1;
     }
     uasort($changes, 'cmp');
     return $changes;
 }
Esempio n. 5
0
/**
 * Return a list of page revisions numbers
 * Does not guarantee that the revision exists in the attic,
 * only that a line with the date exists in the changelog.
 * By default the current revision is skipped.
 *
 * id:    the page of interest
 * first: skip the first n changelog lines
 * num:   number of revisions to return
 *
 * The current revision is automatically skipped when the page exists.
 * See $INFO['meta']['last_change'] for the current revision.
 *
 * For efficiency, the log lines are parsed and cached for later
 * calls to getRevisionInfo. Large changelog files are read
 * backwards in chunks until the requested number of changelog
 * lines are recieved.
 *
 * @author Ben Coburn <*****@*****.**>
 * @author Kate Arzamastseva <*****@*****.**>
 */
function getRevisions($id, $first, $num, $chunk_size = 8192, $media = false)
{
    global $cache_revinfo;
    $cache =& $cache_revinfo;
    if (!isset($cache[$id])) {
        $cache[$id] = array();
    }
    $revs = array();
    $lines = array();
    $count = 0;
    if ($media) {
        $file = mediaMetaFN($id, '.changes');
    } else {
        $file = metaFN($id, '.changes');
    }
    $num = max($num, 0);
    $chunk_size = max($chunk_size, 0);
    if ($first < 0) {
        $first = 0;
    } else {
        if (!$media && @file_exists(wikiFN($id)) || $media && @file_exists(mediaFN($id))) {
            // skip current revision if the page exists
            $first = max($first + 1, 0);
        }
    }
    if (!@file_exists($file)) {
        return $revs;
    }
    if (filesize($file) < $chunk_size || $chunk_size == 0) {
        // read whole file
        $lines = file($file);
        if ($lines === false) {
            return $revs;
        }
    } else {
        // read chunks backwards
        $fp = fopen($file, 'rb');
        // "file pointer"
        if ($fp === false) {
            return $revs;
        }
        fseek($fp, 0, SEEK_END);
        $tail = ftell($fp);
        // chunk backwards
        $finger = max($tail - $chunk_size, 0);
        while ($count < $num + $first) {
            fseek($fp, $finger);
            $nl = $finger;
            if ($finger > 0) {
                fgets($fp);
                // slip the finger forward to a new line
                $nl = ftell($fp);
            }
            // was the chunk big enough? if not, take another bite
            if ($nl > 0 && $tail <= $nl) {
                $finger = max($finger - $chunk_size, 0);
                continue;
            } else {
                $finger = $nl;
            }
            // read chunk
            $chunk = '';
            $read_size = max($tail - $finger, 0);
            // found chunk size
            $got = 0;
            while ($got < $read_size && !feof($fp)) {
                $tmp = @fread($fp, max($read_size - $got, 0));
                if ($tmp === false) {
                    break;
                }
                //error state
                $got += strlen($tmp);
                $chunk .= $tmp;
            }
            $tmp = explode("\n", $chunk);
            array_pop($tmp);
            // remove trailing newline
            // combine with previous chunk
            $count += count($tmp);
            $lines = array_merge($tmp, $lines);
            // next chunk
            if ($finger == 0) {
                break;
            } else {
                $tail = $finger;
                $finger = max($tail - $chunk_size, 0);
            }
        }
        fclose($fp);
    }
    // skip parsing extra lines
    $num = max(min(count($lines) - $first, $num), 0);
    if ($first > 0 && $num > 0) {
        $lines = array_slice($lines, max(count($lines) - $first - $num, 0), $num);
    } else {
        if ($first > 0 && $num == 0) {
            $lines = array_slice($lines, 0, max(count($lines) - $first, 0));
        } else {
            if ($first == 0 && $num > 0) {
                $lines = array_slice($lines, max(count($lines) - $num, 0));
            }
        }
    }
    // handle lines in reverse order
    for ($i = count($lines) - 1; $i >= 0; $i--) {
        $tmp = parseChangelogLine($lines[$i]);
        if ($tmp !== false) {
            $cache[$id][$tmp['date']] = $tmp;
            $revs[] = $tmp['date'];
        }
    }
    return $revs;
}
Esempio n. 6
0
 /**
  * Based on _handleRecent() from inc/changelog.php
  *
  * @param string $line
  * @param array  $ns
  * @param array  $excludedpages
  * @param array  $type
  * @param array  $user
  * @param int    $maxage
  * @param array  $seen
  * @return array|bool
  */
 protected function handleChangelogLine($line, $ns, $excludedpages, $type, $user, $maxage, &$seen)
 {
     // split the line into parts
     $change = parseChangelogLine($line);
     if ($change === false) {
         return false;
     }
     // skip seen ones
     if (isset($seen[$change['id']])) {
         return false;
     }
     // filter type
     if (!empty($type) && !in_array($change['type'], $type)) {
         return false;
     }
     // filter user
     if (!empty($user) && (empty($change['user']) || !in_array($change['user'], $user))) {
         return false;
     }
     // remember in seen to skip additional sights
     $seen[$change['id']] = 1;
     // show only not existing pages for delete
     if ($change['type'] != 'D' && !page_exists($change['id'])) {
         return false;
     }
     // filter maxage
     if ($maxage && $change['date'] < time() - $maxage) {
         return false;
     }
     // check if it's a hidden page
     if (isHiddenPage($change['id'])) {
         return false;
     }
     // filter included namespaces
     if (isset($ns['include'])) {
         if (!$this->isInNamespace($ns['include'], $change['id'])) {
             return false;
         }
     }
     // filter excluded namespaces
     if (isset($ns['exclude'])) {
         if ($this->isInNamespace($ns['exclude'], $change['id'])) {
             return false;
         }
     }
     // exclude pages
     if (!empty($excludedpages)) {
         foreach ($excludedpages as $page) {
             if ($change['id'] == $page) {
                 return false;
             }
         }
     }
     // check ACL
     $change['perms'] = auth_quickaclcheck($change['id']);
     if ($change['perms'] < AUTH_READ) {
         return false;
     }
     return $change;
 }
 /**
  * sometimes chuncksize is set to true
  */
 function test_chuncksizetrue()
 {
     $rev = 1362525899;
     $infoexpected = parseChangelogLine($this->logline);
     $pagelog = new PageChangeLog($this->pageid, true);
     $info = $pagelog->getRevisionInfo($rev);
     $this->assertEquals($infoexpected, $info);
 }
Esempio n. 8
0
 /**
  * Internal function used by $this->getComments()
  *
  * don't call directly
  *
  * @see getRecentComments()
  * @author Andreas Gohr <*****@*****.**>
  * @author Ben Coburn <*****@*****.**>
  * @author Esther Brunner <*****@*****.**>
  *
  * @param string $line
  * @param string $ns
  * @param array  $seen
  * @return array|bool
  */
 function _handleRecentComment($line, $ns, &$seen)
 {
     if (empty($line)) {
         return false;
     }
     //skip empty lines
     // split the line into parts
     $recent = parseChangelogLine($line);
     if ($recent === false) {
         return false;
     }
     $cid = $recent['extra'];
     $fullcid = $recent['id'] . '#' . $recent['extra'];
     // skip seen ones
     if (isset($seen[$fullcid])) {
         return false;
     }
     // skip 'show comment' log entries
     if ($recent['type'] === 'sc') {
         return false;
     }
     // remember in seen to skip additional sights
     $seen[$fullcid] = 1;
     // check if it's a hidden page or comment
     if (isHiddenPage($recent['id'])) {
         return false;
     }
     if ($recent['type'] === 'hc') {
         return false;
     }
     // filter namespace or id
     if ($ns && strpos($recent['id'] . ':', $ns . ':') !== 0) {
         return false;
     }
     // check ACL
     $recent['perm'] = auth_quickaclcheck($recent['id']);
     if ($recent['perm'] < AUTH_READ) {
         return false;
     }
     // check existance
     $recent['file'] = wikiFN($recent['id']);
     $recent['exists'] = @file_exists($recent['file']);
     if (!$recent['exists']) {
         return false;
     }
     if ($recent['type'] === 'dc') {
         return false;
     }
     // get discussion meta file name
     $data = unserialize(io_readFile(metaFN($recent['id'], '.comments'), false));
     // check if discussion is turned off
     if ($data['status'] === 0) {
         return false;
     }
     $parent_id = $cid;
     // Check for the comment and all parents if they exist and are visible.
     do {
         $tcid = $parent_id;
         // check if the comment still exists
         if (!isset($data['comments'][$tcid])) {
             return false;
         }
         // check if the comment is visible
         if ($data['comments'][$tcid]['show'] != 1) {
             return false;
         }
         $parent_id = $data['comments'][$tcid]['parent'];
     } while ($parent_id && $parent_id != $tcid);
     // okay, then add some additional info
     if (is_array($data['comments'][$cid]['user'])) {
         $recent['name'] = $data['comments'][$cid]['user']['name'];
     } else {
         $recent['name'] = $data['comments'][$cid]['name'];
     }
     $recent['desc'] = strip_tags($data['comments'][$cid]['xhtml']);
     $recent['anchor'] = 'comment_' . $cid;
     return $recent;
 }
Esempio n. 9
0
 /**
  * Based on _handleRecent() from inc/changelog.php
  */
 function handleChangelogLine($line, $ns, $type, $user, &$seen)
 {
     // split the line into parts
     $change = parseChangelogLine($line);
     if ($change === false) {
         return false;
     }
     // skip seen ones
     if (isset($seen[$change['id']])) {
         return false;
     }
     // filter type
     if (!empty($type) && !in_array($change['type'], $type)) {
         return false;
     }
     // filter user
     if (!empty($user) && (empty($change['user']) || !in_array($change['user'], $user))) {
         return false;
     }
     // remember in seen to skip additional sights
     $seen[$change['id']] = 1;
     // check if it's a hidden page
     if (isHiddenPage($change['id'])) {
         return false;
     }
     // filter included namespaces
     if (isset($ns['include'])) {
         if (!$this->isInNamespace($ns['include'], $change['id'])) {
             return false;
         }
     }
     // filter excluded namespaces
     if (isset($ns['exclude'])) {
         if ($this->isInNamespace($ns['exclude'], $change['id'])) {
             return false;
         }
     }
     // check ACL
     $change['perms'] = auth_quickaclcheck($change['id']);
     if ($change['perms'] < AUTH_READ) {
         return false;
     }
     return $change;
 }
Esempio n. 10
0
 /**
  * Trims the recent comments cache to the last $conf['changes_days'] recent
  * changes or $conf['recent'] items, which ever is larger.
  * The trimming is only done once a day.
  *
  * @author Ben Coburn <*****@*****.**>
  */
 function _trimRecentCommentsLog($changelog)
 {
     global $conf;
     if (@file_exists($changelog) && filectime($changelog) + 86400 < time() && !@file_exists($changelog . '_tmp')) {
         io_lock($changelog);
         $lines = file($changelog);
         if (count($lines) < $conf['recent']) {
             // nothing to trim
             io_unlock($changelog);
             return true;
         }
         io_saveFile($changelog . '_tmp', '');
         // presave tmp as 2nd lock
         $trim_time = time() - $conf['recent_days'] * 86400;
         $out_lines = array();
         $num = count($lines);
         for ($i = 0; $i < $num; $i++) {
             $log = parseChangelogLine($lines[$i]);
             if ($log === false) {
                 continue;
             }
             // discard junk
             if ($log['date'] < $trim_time) {
                 $old_lines[$log['date'] . ".{$i}"] = $lines[$i];
                 // keep old lines for now (append .$i to prevent key collisions)
             } else {
                 $out_lines[$log['date'] . ".{$i}"] = $lines[$i];
                 // definitely keep these lines
             }
         }
         // sort the final result, it shouldn't be necessary,
         // however the extra robustness in making the changelog cache self-correcting is worth it
         ksort($out_lines);
         $extra = $conf['recent'] - count($out_lines);
         // do we need extra lines do bring us up to minimum
         if ($extra > 0) {
             ksort($old_lines);
             $out_lines = array_merge(array_slice($old_lines, -$extra), $out_lines);
         }
         // save trimmed changelog
         io_saveFile($changelog . '_tmp', implode('', $out_lines));
         @unlink($changelog);
         if (!rename($changelog . '_tmp', $changelog)) {
             // rename failed so try another way...
             io_unlock($changelog);
             io_saveFile($changelog, implode('', $out_lines));
             @unlink($changelog . '_tmp');
         } else {
             io_unlock($changelog);
         }
         return true;
     }
 }
Esempio n. 11
0
 /**
  * Collect the $max revisions near to the timestamp $rev
  *
  * @param int $rev revision timestamp
  * @param int $max maximum number of revisions to be returned
  * @return bool|array
  *     return array with entries:
  *       - $requestedrevs: array of with $max revision timestamps
  *       - $revs: all parsed revision timestamps
  *       - $fp: filepointer only defined for chuck reading, needs closing.
  *       - $lines: non-parsed changelog lines before the parsed revisions
  *       - $head: position of first readed changelogline
  *       - $lasttail: position of end of last readed changelogline
  *     otherwise false
  */
 protected function retrieveRevisionsAround($rev, $max)
 {
     //get lines from changelog
     list($fp, $lines, $starthead, $starttail, ) = $this->readloglines($rev);
     if (empty($lines)) {
         return false;
     }
     //parse chunk containing $rev, and read forward more chunks until $max/2 is reached
     $head = $starthead;
     $tail = $starttail;
     $revs = array();
     $aftercount = $beforecount = 0;
     while (count($lines) > 0) {
         foreach ($lines as $line) {
             $tmp = parseChangelogLine($line);
             if ($tmp !== false) {
                 $this->cache[$this->id][$tmp['date']] = $tmp;
                 $revs[] = $tmp['date'];
                 if ($tmp['date'] >= $rev) {
                     //count revs after reference $rev
                     $aftercount++;
                     if ($aftercount == 1) {
                         $beforecount = count($revs);
                     }
                 }
                 //enough revs after reference $rev?
                 if ($aftercount > floor($max / 2)) {
                     break 2;
                 }
             }
         }
         //retrieve next chunk
         list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, 1);
     }
     if ($aftercount == 0) {
         return false;
     }
     $lasttail = $tail;
     //read additional chuncks backward until $max/2 is reached and total number of revs is equal to $max
     $lines = array();
     $i = 0;
     if ($aftercount > 0) {
         $head = $starthead;
         $tail = $starttail;
         while ($head > 0) {
             list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1);
             for ($i = count($lines) - 1; $i >= 0; $i--) {
                 $tmp = parseChangelogLine($lines[$i]);
                 if ($tmp !== false) {
                     $this->cache[$this->id][$tmp['date']] = $tmp;
                     $revs[] = $tmp['date'];
                     $beforecount++;
                     //enough revs before reference $rev?
                     if ($beforecount > max(floor($max / 2), $max - $aftercount)) {
                         break 2;
                     }
                 }
             }
         }
     }
     sort($revs);
     //keep only non-parsed lines
     $lines = array_slice($lines, 0, $i);
     //trunk desired selection
     $requestedrevs = array_slice($revs, -$max, $max);
     return array($requestedrevs, $revs, $fp, $lines, $head, $lasttail);
 }
Esempio n. 12
0
 /**
  * Internal function used by $this->getLinkbacks()
  *
  * don't call directly
  *
  * @see getRecentComments()
  * @author Andreas Gohr <*****@*****.**>
  * @author Ben Coburn <*****@*****.**>
  * @author Esther Brunner <*****@*****.**>
  * @author Gina Haeussge <*****@*****.**>
  */
 function _handleRecentLinkback($line, $ns)
 {
     static $seen = array();
     //caches seen pages and skip them
     if (empty($line)) {
         return false;
     }
     //skip empty lines
     // split the line into parts
     $recent = parseChangelogLine($line);
     if ($recent === false) {
         return false;
     }
     $lid = $recent['extra'];
     $fulllid = $recent['id'] . '#' . $recent['extra'];
     // skip seen ones
     if (isset($seen[$fulllid])) {
         return false;
     }
     // skip 'show comment' log entries
     if ($recent['type'] === 'sc') {
         return false;
     }
     // remember in seen to skip additional sights
     $seen[$fulllid] = 1;
     // check if it's a hidden page or comment
     if (isHiddenPage($recent['id'])) {
         return false;
     }
     if ($recent['type'] === 'hl') {
         return false;
     }
     // filter namespace or id
     if ($ns && strpos($recent['id'] . ':', $ns . ':') !== 0) {
         return false;
     }
     // check ACL
     $recent['perm'] = auth_quickaclcheck($recent['id']);
     if ($recent['perm'] < AUTH_READ) {
         return false;
     }
     // check existance
     $recent['file'] = wikiFN($recent['id']);
     $recent['exists'] = @file_exists($recent['file']);
     if (!$recent['exists']) {
         return false;
     }
     if ($recent['type'] === 'dc') {
         return false;
     }
     // get linkback meta file name
     $data = unserialize(io_readFile(metaFN($recent['id'], '.linkbacks'), false));
     // check if discussion is turned off
     if (!$data['display']) {
         return false;
     }
     // okay, then add some additional info
     $recent['name'] = $data['receivedpings'][$lid]['url'];
     $recent['desc'] = $data['receivedpings'][$lid]['excerpt'];
     return $recent;
 }
 /**
  * request existing rev and check cache
  */
 function test_requestrev_checkcache()
 {
     $rev = 1362525359;
     $dir = 1;
     $revexpected = 1362525899;
     $infoexpected = parseChangelogLine($this->logline);
     $pagelog = new PageChangeLog($this->pageid, $chunk_size = 8192);
     $revfound = $pagelog->getRelativeRevision($rev, $dir);
     $this->assertEquals($revexpected, $revfound);
     //checked info returned from cache
     $info = $pagelog->getRevisionInfo($revfound);
     $this->assertEquals($infoexpected, $info);
 }
 /**
  * Returns change log entries within the target period
  */
 function _getLogEntries($start, $end, $logfile, $class = '')
 {
     $loglines = (array) @file($logfile);
     $entries = array();
     for ($i = count($loglines) - 1; $i >= 0; $i--) {
         $entry = parseChangelogLine($loglines[$i]);
         if ($entry === false) {
             break;
         }
         if ($entry['date'] < $start) {
             break;
         }
         if ($entry['date'] > $end) {
             continue;
         }
         // some fixes for plain text outputs
         $entry['sum'] = trim(strtr($entry['sum'], "\n\v\r", '    '));
         $entry['extra'] = trim(strtr($entry['extra'], "\n\v\r", '    '));
         if ($class) {
             $entry['class'] = $class;
         }
         $entries[] = $entry;
     }
     return $entries;
 }