17d559c7fSBen Coburn<?php 27d559c7fSBen Coburn/** 37d559c7fSBen Coburn * Changelog handling functions 47d559c7fSBen Coburn * 57d559c7fSBen Coburn * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 67d559c7fSBen Coburn * @author Andreas Gohr <andi@splitbrain.org> 77d559c7fSBen Coburn */ 87d559c7fSBen Coburn 91d11f1d3SSatoshi Saharause dokuwiki\ChangeLog\ChangeLog; 10*7fba736bSSatoshi Saharause dokuwiki\File\PageFile; 111d11f1d3SSatoshi Sahara 127d559c7fSBen Coburn/** 137d559c7fSBen Coburn * parses a changelog line into it's components 147d559c7fSBen Coburn * 157d559c7fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net> 164f1e2cb3SGerrit Uitslag * 174f1e2cb3SGerrit Uitslag * @param string $line changelog line 184f1e2cb3SGerrit Uitslag * @return array|bool parsed line or false 197d559c7fSBen Coburn */ 207d559c7fSBen Coburnfunction parseChangelogLine($line) { 211d11f1d3SSatoshi Sahara return ChangeLog::parseLogLine($line); 227d559c7fSBen Coburn} 237d559c7fSBen Coburn 247d559c7fSBen Coburn/** 2563f13cadSDamien Regad * Adds an entry to the changelog and saves the metadata for the page 267d559c7fSBen Coburn * 276527839fSSatoshi Sahara * Note: timestamp of the change might not be unique especially after very quick 286527839fSSatoshi Sahara * repeated edits (e.g. change checkbox via do plugin) 296527839fSSatoshi Sahara * 306527839fSSatoshi Sahara * @param int $date Timestamp of the change 31a365baeeSDominik Eckelmann * @param String $id Name of the affected page 32a365baeeSDominik Eckelmann * @param String $type Type of the change see DOKU_CHANGE_TYPE_* 33a365baeeSDominik Eckelmann * @param String $summary Summary of the change 34a365baeeSDominik Eckelmann * @param mixed $extra In case of a revert the revision (timestmp) of the reverted page 35a365baeeSDominik Eckelmann * @param array $flags Additional flags in a key value array. 364f1e2cb3SGerrit Uitslag * Available flags: 37a365baeeSDominik Eckelmann * - ExternalEdit - mark as an external edit. 38ac3ed4afSGerrit Uitslag * @param null|int $sizechange Change of filesize 39a365baeeSDominik Eckelmann * 407d559c7fSBen Coburn * @author Andreas Gohr <andi@splitbrain.org> 417d559c7fSBen Coburn * @author Esther Brunner <wikidesign@gmail.com> 427d559c7fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net> 437d559c7fSBen Coburn */ 445d9428a0SSatoshi Saharafunction addLogEntry( 455d9428a0SSatoshi Sahara $date, 465d9428a0SSatoshi Sahara $id, 475d9428a0SSatoshi Sahara $type = DOKU_CHANGE_TYPE_EDIT, 485d9428a0SSatoshi Sahara $summary = '', 495d9428a0SSatoshi Sahara $extra = '', 505d9428a0SSatoshi Sahara $flags = null, 515d9428a0SSatoshi Sahara $sizechange = null) 525d9428a0SSatoshi Sahara{ 537d559c7fSBen Coburn global $conf, $INFO; 54585bf44eSChristopher Smith /** @var Input $INPUT */ 55585bf44eSChristopher Smith global $INPUT; 567d559c7fSBen Coburn 575aa52fafSBen Coburn // check for special flags as keys 58252acce3SSatoshi Sahara if (!is_array($flags)) $flags = array(); 595aa52fafSBen Coburn $flagExternalEdit = isset($flags['ExternalEdit']); 605aa52fafSBen Coburn 617d559c7fSBen Coburn $id = cleanid($id); 627d559c7fSBen Coburn 637d559c7fSBen Coburn if (!$date) $date = time(); //use current time if none supplied 642f9daf16SAndreas Gohr $remote = (!$flagExternalEdit) ? clientIP(true) : '127.0.0.1'; 65585bf44eSChristopher Smith $user = (!$flagExternalEdit) ? $INPUT->server->str('REMOTE_USER') : ''; 661d11f1d3SSatoshi Sahara $sizechange = ($sizechange === null) ? '' : (int)$sizechange; 677d559c7fSBen Coburn 681d11f1d3SSatoshi Sahara // update changelog file and get the added entry that is also to be stored in metadata 69*7fba736bSSatoshi Sahara $pageFile = new PageFile($id); 70*7fba736bSSatoshi Sahara $logEntry = $pageFile->changelog->addLogEntry([ 717d559c7fSBen Coburn 'date' => $date, 727d559c7fSBen Coburn 'ip' => $remote, 73c7192766SSatoshi Sahara 'type' => $type, 747d559c7fSBen Coburn 'id' => $id, 757d559c7fSBen Coburn 'user' => $user, 76c7192766SSatoshi Sahara 'sum' => $summary, 77c7192766SSatoshi Sahara 'extra' => $extra, 78c7192766SSatoshi Sahara 'sizechange' => $sizechange, 791d11f1d3SSatoshi Sahara ]); 807d559c7fSBen Coburn 817d559c7fSBen Coburn // update metadata 82*7fba736bSSatoshi Sahara $pageFile->updateMetadata($logEntry); 837d559c7fSBen Coburn} 847d559c7fSBen Coburn 857d559c7fSBen Coburn/** 8699c8d7f2Smichael * Add's an entry to the media changelog 8799c8d7f2Smichael * 8899c8d7f2Smichael * @author Michael Hamann <michael@content-space.de> 8999c8d7f2Smichael * @author Andreas Gohr <andi@splitbrain.org> 9099c8d7f2Smichael * @author Esther Brunner <wikidesign@gmail.com> 9199c8d7f2Smichael * @author Ben Coburn <btcoburn@silicodon.net> 924f1e2cb3SGerrit Uitslag * 934f1e2cb3SGerrit Uitslag * @param int $date Timestamp of the change 944f1e2cb3SGerrit Uitslag * @param String $id Name of the affected page 954f1e2cb3SGerrit Uitslag * @param String $type Type of the change see DOKU_CHANGE_TYPE_* 964f1e2cb3SGerrit Uitslag * @param String $summary Summary of the change 974f1e2cb3SGerrit Uitslag * @param mixed $extra In case of a revert the revision (timestmp) of the reverted page 984f1e2cb3SGerrit Uitslag * @param array $flags Additional flags in a key value array. 994f1e2cb3SGerrit Uitslag * Available flags: 1004f1e2cb3SGerrit Uitslag * - (none, so far) 101ac3ed4afSGerrit Uitslag * @param null|int $sizechange Change of filesize 10299c8d7f2Smichael */ 10364159a61SAndreas Gohrfunction addMediaLogEntry( 10464159a61SAndreas Gohr $date, 10564159a61SAndreas Gohr $id, 10664159a61SAndreas Gohr $type = DOKU_CHANGE_TYPE_EDIT, 10764159a61SAndreas Gohr $summary = '', 10864159a61SAndreas Gohr $extra = '', 10964159a61SAndreas Gohr $flags = null, 11064159a61SAndreas Gohr $sizechange = null) 11164159a61SAndreas Gohr{ 112de3eb1d7SAdrian Lang global $conf; 113585bf44eSChristopher Smith /** @var Input $INPUT */ 114585bf44eSChristopher Smith global $INPUT; 11599c8d7f2Smichael 116facfe250SSatoshi Sahara // check for special flags as keys 117facfe250SSatoshi Sahara if (!is_array($flags)) $flags = array(); 118facfe250SSatoshi Sahara $flagExternalEdit = isset($flags['ExternalEdit']); 119facfe250SSatoshi Sahara 12099c8d7f2Smichael $id = cleanid($id); 12199c8d7f2Smichael 12299c8d7f2Smichael if (!$date) $date = time(); //use current time if none supplied 123facfe250SSatoshi Sahara $remote = (!$flagExternalEdit) ? clientIP(true) : '127.0.0.1'; 124facfe250SSatoshi Sahara $user = (!$flagExternalEdit) ? $INPUT->server->str('REMOTE_USER') : ''; 1251d11f1d3SSatoshi Sahara $sizechange = ($sizechange === null) ? '' : (int)$sizechange; 12699c8d7f2Smichael 1271d11f1d3SSatoshi Sahara // update changelog file and get the added entry 1281d11f1d3SSatoshi Sahara $logEntry = (new MediaChangeLog($id, 1024))->addLogEntry([ 12999c8d7f2Smichael 'date' => $date, 13099c8d7f2Smichael 'ip' => $remote, 131c7192766SSatoshi Sahara 'type' => $type, 13299c8d7f2Smichael 'id' => $id, 13399c8d7f2Smichael 'user' => $user, 134c7192766SSatoshi Sahara 'sum' => $summary, 135c7192766SSatoshi Sahara 'extra' => $extra, 136c7192766SSatoshi Sahara 'sizechange' => $sizechange, 1371d11f1d3SSatoshi Sahara ]); 13899c8d7f2Smichael} 13999c8d7f2Smichael 14099c8d7f2Smichael/** 141252acce3SSatoshi Sahara * returns an array of recently changed files using the changelog 1427d559c7fSBen Coburn * 1437d559c7fSBen Coburn * The following constants can be used to control which changes are 1447d559c7fSBen Coburn * included. Add them together as needed. 1457d559c7fSBen Coburn * 1467d559c7fSBen Coburn * RECENTS_SKIP_DELETED - don't include deleted pages 1477d559c7fSBen Coburn * RECENTS_SKIP_MINORS - don't include minor changes 14808e9b52fSPhy * RECENTS_ONLY_CREATION - only include new created pages and media 1497d559c7fSBen Coburn * RECENTS_SKIP_SUBSPACES - don't include subspaces 1500b926329SKate Arzamastseva * RECENTS_MEDIA_CHANGES - return media changes instead of page changes 1510b926329SKate Arzamastseva * RECENTS_MEDIA_PAGES_MIXED - return both media changes and page changes 1527d559c7fSBen Coburn * 1537d559c7fSBen Coburn * @param int $first number of first entry returned (for paginating 1547d559c7fSBen Coburn * @param int $num return $num entries 1557d559c7fSBen Coburn * @param string $ns restrict to given namespace 15659f20ea3SMichael Hamann * @param int $flags see above 15759f20ea3SMichael Hamann * @return array recently changed files 1587d559c7fSBen Coburn * 1597d559c7fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net> 16029778747SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1617d559c7fSBen Coburn */ 1627d559c7fSBen Coburnfunction getRecents($first, $num, $ns = '', $flags = 0) { 1637d559c7fSBen Coburn global $conf; 1647d559c7fSBen Coburn $recent = array(); 1657d559c7fSBen Coburn $count = 0; 1667d559c7fSBen Coburn 1677d559c7fSBen Coburn if (!$num) 1687d559c7fSBen Coburn return $recent; 1697d559c7fSBen Coburn 1707d559c7fSBen Coburn // read all recent changes. (kept short) 1710b926329SKate Arzamastseva if ($flags & RECENTS_MEDIA_CHANGES) { 1728e3e8693SAndreas Gohr $lines = @file($conf['media_changelog']) ?: []; 17399c8d7f2Smichael } else { 1748e3e8693SAndreas Gohr $lines = @file($conf['changelog']) ?: []; 17599c8d7f2Smichael } 1761b266025SPhy if (!is_array($lines)) { 1771b266025SPhy $lines = array(); 1781b266025SPhy } 17929778747SKate Arzamastseva $lines_position = count($lines) - 1; 18059f20ea3SMichael Hamann $media_lines_position = 0; 18159f20ea3SMichael Hamann $media_lines = array(); 18229778747SKate Arzamastseva 1830b926329SKate Arzamastseva if ($flags & RECENTS_MEDIA_PAGES_MIXED) { 1848e3e8693SAndreas Gohr $media_lines = @file($conf['media_changelog']) ?: []; 1851b266025SPhy if (!is_array($media_lines)) { 1861b266025SPhy $media_lines = array(); 1871b266025SPhy } 18829778747SKate Arzamastseva $media_lines_position = count($media_lines) - 1; 18929778747SKate Arzamastseva } 19029778747SKate Arzamastseva 19129778747SKate Arzamastseva $seen = array(); // caches seen lines, _handleRecent() skips them 1927d559c7fSBen Coburn 1937d559c7fSBen Coburn // handle lines 1940b926329SKate Arzamastseva while ($lines_position >= 0 || (($flags & RECENTS_MEDIA_PAGES_MIXED) && $media_lines_position >= 0)) { 19529778747SKate Arzamastseva if (empty($rec) && $lines_position >= 0) { 1961d901ab2SAndreas Gohr $rec = _handleRecent(@$lines[$lines_position], $ns, $flags, $seen); 19729778747SKate Arzamastseva if (!$rec) { 19829778747SKate Arzamastseva $lines_position --; 19929778747SKate Arzamastseva continue; 20029778747SKate Arzamastseva } 20129778747SKate Arzamastseva } 2020b926329SKate Arzamastseva if (($flags & RECENTS_MEDIA_PAGES_MIXED) && empty($media_rec) && $media_lines_position >= 0) { 20364159a61SAndreas Gohr $media_rec = _handleRecent( 20464159a61SAndreas Gohr @$media_lines[$media_lines_position], 20564159a61SAndreas Gohr $ns, 20664159a61SAndreas Gohr $flags | RECENTS_MEDIA_CHANGES, 20764159a61SAndreas Gohr $seen 20864159a61SAndreas Gohr ); 20929778747SKate Arzamastseva if (!$media_rec) { 21029778747SKate Arzamastseva $media_lines_position --; 21129778747SKate Arzamastseva continue; 21229778747SKate Arzamastseva } 21329778747SKate Arzamastseva } 2140b926329SKate Arzamastseva if (($flags & RECENTS_MEDIA_PAGES_MIXED) && @$media_rec['date'] >= @$rec['date']) { 21529778747SKate Arzamastseva $media_lines_position--; 21629778747SKate Arzamastseva $x = $media_rec; 217b5941dfaSKate Arzamastseva $x['media'] = true; 21829778747SKate Arzamastseva $media_rec = false; 21929778747SKate Arzamastseva } else { 22029778747SKate Arzamastseva $lines_position--; 22129778747SKate Arzamastseva $x = $rec; 222421ec38eSKate Arzamastseva if ($flags & RECENTS_MEDIA_CHANGES) $x['media'] = true; 22329778747SKate Arzamastseva $rec = false; 22429778747SKate Arzamastseva } 2257d559c7fSBen Coburn if (--$first >= 0) continue; // skip first entries 22629778747SKate Arzamastseva $recent[] = $x; 2277d559c7fSBen Coburn $count++; 2287d559c7fSBen Coburn // break when we have enough entries 2297d559c7fSBen Coburn if ($count >= $num) { break; } 2307d559c7fSBen Coburn } 2317d559c7fSBen Coburn return $recent; 2327d559c7fSBen Coburn} 2337d559c7fSBen Coburn 2347d559c7fSBen Coburn/** 23599c8d7f2Smichael * returns an array of files changed since a given time using the 23699c8d7f2Smichael * changelog 23799c8d7f2Smichael * 23899c8d7f2Smichael * The following constants can be used to control which changes are 23999c8d7f2Smichael * included. Add them together as needed. 24099c8d7f2Smichael * 24199c8d7f2Smichael * RECENTS_SKIP_DELETED - don't include deleted pages 24299c8d7f2Smichael * RECENTS_SKIP_MINORS - don't include minor changes 24308e9b52fSPhy * RECENTS_ONLY_CREATION - only include new created pages and media 24499c8d7f2Smichael * RECENTS_SKIP_SUBSPACES - don't include subspaces 2450b926329SKate Arzamastseva * RECENTS_MEDIA_CHANGES - return media changes instead of page changes 24699c8d7f2Smichael * 24799c8d7f2Smichael * @param int $from date of the oldest entry to return 24899c8d7f2Smichael * @param int $to date of the newest entry to return (for pagination, optional) 24999c8d7f2Smichael * @param string $ns restrict to given namespace (optional) 25059f20ea3SMichael Hamann * @param int $flags see above (optional) 25159f20ea3SMichael Hamann * @return array of files 25299c8d7f2Smichael * 25399c8d7f2Smichael * @author Michael Hamann <michael@content-space.de> 25499c8d7f2Smichael * @author Ben Coburn <btcoburn@silicodon.net> 25599c8d7f2Smichael */ 25699c8d7f2Smichaelfunction getRecentsSince($from, $to = null, $ns = '', $flags = 0) { 25799c8d7f2Smichael global $conf; 25899c8d7f2Smichael $recent = array(); 25999c8d7f2Smichael 26099c8d7f2Smichael if ($to && $to < $from) 26199c8d7f2Smichael return $recent; 26299c8d7f2Smichael 26399c8d7f2Smichael // read all recent changes. (kept short) 2640b926329SKate Arzamastseva if ($flags & RECENTS_MEDIA_CHANGES) { 26599c8d7f2Smichael $lines = @file($conf['media_changelog']); 26699c8d7f2Smichael } else { 26799c8d7f2Smichael $lines = @file($conf['changelog']); 26899c8d7f2Smichael } 269e920a0a1SAndreas Gohr if (!$lines) return $recent; 27099c8d7f2Smichael 27199c8d7f2Smichael // we start searching at the end of the list 27299c8d7f2Smichael $lines = array_reverse($lines); 27399c8d7f2Smichael 27499c8d7f2Smichael // handle lines 27599c8d7f2Smichael $seen = array(); // caches seen lines, _handleRecent() skips them 27699c8d7f2Smichael 27799c8d7f2Smichael foreach ($lines as $line) { 27899c8d7f2Smichael $rec = _handleRecent($line, $ns, $flags, $seen); 27999c8d7f2Smichael if ($rec !== false) { 28099c8d7f2Smichael if ($rec['date'] >= $from) { 28199c8d7f2Smichael if (!$to || $rec['date'] <= $to) { 28299c8d7f2Smichael $recent[] = $rec; 28399c8d7f2Smichael } 28499c8d7f2Smichael } else { 28599c8d7f2Smichael break; 28699c8d7f2Smichael } 28799c8d7f2Smichael } 28899c8d7f2Smichael } 28999c8d7f2Smichael 29099c8d7f2Smichael return array_reverse($recent); 29199c8d7f2Smichael} 29299c8d7f2Smichael 29399c8d7f2Smichael/** 2947d559c7fSBen Coburn * Internal function used by getRecents 2957d559c7fSBen Coburn * 2967d559c7fSBen Coburn * don't call directly 2977d559c7fSBen Coburn * 2987d559c7fSBen Coburn * @see getRecents() 2997d559c7fSBen Coburn * @author Andreas Gohr <andi@splitbrain.org> 3007d559c7fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net> 3014f1e2cb3SGerrit Uitslag * 3024f1e2cb3SGerrit Uitslag * @param string $line changelog line 3034f1e2cb3SGerrit Uitslag * @param string $ns restrict to given namespace 3044f1e2cb3SGerrit Uitslag * @param int $flags flags to control which changes are included 3054f1e2cb3SGerrit Uitslag * @param array $seen listing of seen pages 3064f1e2cb3SGerrit Uitslag * @return array|bool false or array with info about a change 3077d559c7fSBen Coburn */ 30899c8d7f2Smichaelfunction _handleRecent($line, $ns, $flags, &$seen) { 3097d559c7fSBen Coburn if (empty($line)) return false; //skip empty lines 3107d559c7fSBen Coburn 3117d559c7fSBen Coburn // split the line into parts 3121d11f1d3SSatoshi Sahara $recent = ChangeLog::parseLogLine($line); 313252acce3SSatoshi Sahara if ($recent === false) return false; 3147d559c7fSBen Coburn 3157d559c7fSBen Coburn // skip seen ones 3167d559c7fSBen Coburn if (isset($seen[$recent['id']])) return false; 3177d559c7fSBen Coburn 31808e9b52fSPhy // skip changes, of only new items are requested 31908e9b52fSPhy if ($recent['type'] !== DOKU_CHANGE_TYPE_CREATE && ($flags & RECENTS_ONLY_CREATION)) return false; 32068f43bcfSTero Kivinen 3217d559c7fSBen Coburn // skip minors 322ebf1501fSBen Coburn if ($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT && ($flags & RECENTS_SKIP_MINORS)) return false; 3237d559c7fSBen Coburn 3247d559c7fSBen Coburn // remember in seen to skip additional sights 3257d559c7fSBen Coburn $seen[$recent['id']] = 1; 3267d559c7fSBen Coburn 3277d559c7fSBen Coburn // check if it's a hidden page 3287d559c7fSBen Coburn if (isHiddenPage($recent['id'])) return false; 3297d559c7fSBen Coburn 3307d559c7fSBen Coburn // filter namespace 3317d559c7fSBen Coburn if (($ns) && (strpos($recent['id'], $ns.':') !== 0)) return false; 3327d559c7fSBen Coburn 3337d559c7fSBen Coburn // exclude subnamespaces 3347d559c7fSBen Coburn if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($recent['id']) != $ns)) return false; 3357d559c7fSBen Coburn 3367d559c7fSBen Coburn // check ACL 33742025dfdSMichael Hamann if ($flags & RECENTS_MEDIA_CHANGES) { 33842025dfdSMichael Hamann $recent['perms'] = auth_quickaclcheck(getNS($recent['id']).':*'); 33942025dfdSMichael Hamann } else { 34099c8d7f2Smichael $recent['perms'] = auth_quickaclcheck($recent['id']); 34142025dfdSMichael Hamann } 34299c8d7f2Smichael if ($recent['perms'] < AUTH_READ) return false; 3437d559c7fSBen Coburn 3447d559c7fSBen Coburn // check existance 3451d901ab2SAndreas Gohr if ($flags & RECENTS_SKIP_DELETED) { 34642025dfdSMichael Hamann $fn = (($flags & RECENTS_MEDIA_CHANGES) ? mediaFN($recent['id']) : wikiFN($recent['id'])); 34779e79377SAndreas Gohr if (!file_exists($fn)) return false; 3481d901ab2SAndreas Gohr } 3497d559c7fSBen Coburn 3507d559c7fSBen Coburn return $recent; 3517d559c7fSBen Coburn} 3525d9428a0SSatoshi Sahara