/** * Start the reversion process */ function _revert($revert, $filter) { global $conf; echo '<hr /><br />'; echo '<p>' . $this->getLang('revstart') . '</p>'; echo '<ul>'; foreach ($revert as $id) { global $REV; // find the last non-spammy revision $data = ''; $old = getRevisions($id, 0, $this->max_revs); if (count($old)) { foreach ($old as $REV) { $data = rawWiki($id, $REV); if (strpos($data, $filter) === false) { break; } } } if ($data) { saveWikiText($id, $data, 'old revision restored', false); printf('<li><div class="li">' . $this->getLang('reverted') . '</div></li>', $id, $REV); } else { saveWikiText($id, '', '', false); printf('<li><div class="li">' . $this->getLang('removed') . '</div></li>', $id); } @set_time_limit(10); flush(); } echo '</ul>'; echo '<p>' . $this->getLang('revstop') . '</p>'; }
/** * @param Doku_Event $event * @param array $param */ function remove(&$event, $param) { if (!$this->hlp->isActive()) { return; } if (!$this->getConf('delete attic on first approve')) { return; } if ($this->hlp->getPreviousApprovedRevision()) { return; // previous version exist } global $ID; $revisions = getRevisions($ID, 0, 0, 0); foreach ($revisions as $revision) { $fn = wikiFN($ID, $revision); if (file_exists($fn)) { @unlink($fn); } } }
/** * Returns a list of available revisions of a given wiki page * * @author Michael Klier <*****@*****.**> */ function pageVersions($id, $first) { global $conf; $versions = array(); if (empty($id)) { return new IXR_Error(1, 'Empty page ID'); } require_once DOKU_INC . 'inc/changelog.php'; $revisions = getRevisions($id, $first, $conf['recent'] + 1); if (count($revisions) == 0 && $first != 0) { $first = 0; $revisions = getRevisions($id, $first, $conf['recent'] + 1); } if (count($revisions) > 0 && $first == 0) { array_unshift($revisions, ''); // include current revision array_pop($revisions); // remove extra log entry } $hasNext = false; if (count($revisions) > $conf['recent']) { $hasNext = true; array_pop($revisions); // remove extra log entry } if (!empty($revisions)) { foreach ($revisions as $rev) { $file = wikiFN($id, $rev); $time = @filemtime($file); // we check if the page actually exists, if this is not the // case this can lead to less pages being returned than // specified via $conf['recent'] if ($time) { $info = getRevisionInfo($id, $time, 1024); if (!empty($info)) { $data['user'] = $info['user']; $data['ip'] = $info['ip']; $data['type'] = $info['type']; $data['sum'] = $info['sum']; $data['modified'] = new IXR_Date($info['date']); $data['version'] = $info['date']; array_push($versions, $data); } } } return $versions; } else { return array(); } }
public function handle_action_act_preprocess(Doku_Event &$event, $param) { global $ID; global $TEXT; global $ACT; global $SUM; global $RANGE; global $REV; $act = $event->data; if ($act != 'dokutranslate_review') { $act = act_clean($act); $act = act_permcheck($act); } # Ignore drafts if the page is being translated # FIXME: Find a way to save $_REQUEST['parid'] into the draft if (@file_exists(metaFN($ID, '.translate')) && in_array($act, array('draft', 'recover'))) { act_draftdel('draftdel'); $ACT = $act = 'edit'; } if ($act == 'save') { # Take over save action if translation is in progress # or we're starting it if (!@file_exists(metaFN($ID, '.translate')) && empty($_REQUEST['translate'])) { return; } if (!checkSecurityToken()) { return; } # We're starting a translation if (!@file_exists(metaFN($ID, '.translate')) && !empty($_REQUEST['translate'])) { # Check if the user has permission to start # translation in this namespace if (!isModerator($ID)) { return; } # Take the event over $event->stopPropagation(); $event->preventDefault(); # Save the data but exit if it fails $ACT = act_save($act); if ($ACT != 'show') { return; } # Page was deleted, exit if (!@file_exists(wikiFN($ID))) { return; } # Prepare data path $datapath = dataPath($ID); io_mkdir_p($datapath, 0755, true); # Backup the original page io_rename(wikiFN($ID), $datapath . '/orig.txt'); # Backup old revisions $revisions = allRevisions($ID); foreach ($revisions as $rev) { $tmp = wikiFN($ID, $rev); io_rename($tmp, $datapath . '/' . basename($tmp)); } # Backup meta files $metas = metaFiles($ID); foreach ($metas as $f) { io_rename($f, $datapath . '/' . basename($f)); } # Generate empty page to hold translated text $data = getCleanInstructions($datapath . '/orig.txt'); saveWikiText($ID, genTranslateFile($data), $SUM, $_REQUEST['minor']); $translateMeta = genMeta(count($data)); # create meta file for current translation state io_saveFile(metaFN($ID, '.translate'), serialize($translateMeta)); # create separate meta file for translation history io_saveFile(metaFN($ID, '.translateHistory'), serialize(array('current' => $translateMeta))); } else { # Translation in progress, take the event over $event->preventDefault(); # Save the data but exit if it fails $ACT = act_save($act); # Save failed, exit if ($ACT != 'show') { return; } # Save successful, update translation metadata $lastrev = getRevisions($ID, 0, 1, 1024); updateMeta($ID, getParID(), $lastrev[0]); } } else { if ($act == 'revert') { # Take over save action if translation is in progress if (!@file_exists(metaFN($ID, '.translate'))) { return; } if (!checkSecurityToken()) { return; } # Translation in progress, take the event over $event->preventDefault(); # Save the data but exit if it fails $revert = $REV; $ACT = act_revert($act); # Revert failed, exit if ($ACT != 'show') { return; } # Revert successful, update translation metadata $lastrev = getRevisions($ID, 0, 1, 1024); updateMeta($ID, getParID(), $lastrev[0], $revert); } else { if (in_array($act, array('edit', 'preview'))) { if (!@file_exists(metaFN($ID, '.translate')) || isset($TEXT)) { return; } $parid = getParID(); $instructions = p_cached_instructions(wikiFN($ID)); $separators = array(); # Build array of paragraph separators foreach ($instructions as $ins) { if ($ins[0] == 'plugin' && $ins[1][0] == 'dokutranslate' && in_array($ins[1][1][0], array(DOKU_LEXER_ENTER, DOKU_LEXER_SPECIAL, DOKU_LEXER_EXIT))) { $separators[] = $ins[1][1]; } } # Validate paragraph ID if ($parid >= count($separators) - 1) { $parid = 0; } # Build range for paragraph $RANGE = strval($separators[$parid][2] + 1) . '-' . strval($separators[$parid + 1][1] - 1); } else { if ($act == 'dokutranslate_review') { # This action is mine $event->stopPropagation(); $event->preventDefault(); # Show the page when done $ACT = 'show'; # Load data $meta = unserialize(io_readFile(metaFN($ID, '.translateHistory'), false)); $parid = getParID(); $writeRev = empty($REV) ? 'current' : intval($REV); $writeRev = empty($meta[$writeRev][$parid]['changed']) ? $writeRev : $meta[$writeRev][$parid]['changed']; $user = $_SERVER['REMOTE_USER']; # Check for permission to write reviews if (!canReview($ID, $meta[$writeRev], $parid)) { return; } # Add review to meta array $data['message'] = $_REQUEST['review']; $data['quality'] = intval($_REQUEST['quality']); $data['incomplete'] = !empty($_REQUEST['incomplete']); $meta[$writeRev][$parid]['reviews'][$user] = $data; # Review applies to latest revision as well if (empty($REV) || $meta['current'][$parid]['changed'] == $writeRev) { $meta['current'][$parid]['reviews'][$user] = $data; io_saveFile(metaFN($ID, '.translate'), serialize($meta['current'])); } # Save metadata io_saveFile(metaFN($ID, '.translateHistory'), serialize($meta)); } } } } }
/** * Convenience function to get just the status tags from a page revision. * * @param array $params The parameters given by the user * @return array An updated $params array with data filled in */ function handleActivityByUserReport($params) { global $cache_revinfo; $params = validateNamespace($params); $namespace = $params["namespace"]; $params = validateStartDate($params); $start_timestamp = $params["start_timestamp"]; $params = validateEndDate($params); $params["report_title"] = "Activity by User for " . $namespace . " from " . $params["start_date"] . " to " . $params["end_date"]; // Find all pages in the namespace $pages = getAllPagesInNamespace($namespace); $params["debug_num_pages_in_ns"] = count($pages); // Build an array of the last update by user by page $num_revisions = 0; $num_revisions_within_dates = 0; $num_revisions_with_matching_users = 0; $last_revision_by_user_by_page = array(); $page_current_revisions = array(); foreach ($pages as $page) { // Ignore any pages that haven't been changed since the begin date. if ($page["rev"] < $start_timestamp) { continue; } // Clear the Dokuwiki revision cache. This is potentially fragile, but // we don't do this, the DokuWiki cache for this script will grow until // it blows out the memory for this thread. $cache_revinfo = array(); // Get all revisions for this page. $page_id = $page["id"]; $revision_ids = getRevisions($page_id, 0, 10000); // Reverse the array so that it goes least-recent to most-recent $revision_ids = array_reverse($revision_ids); // Push the current revision onto the stack. array_push($revision_ids, $page["rev"]); $page_current_revisions[$page_id] = $page["rev"]; // Count number of revisions for debugging $num_revisions += count($revision_ids); // Consider each revision foreach ($revision_ids as $revision_id) { if ($revision_id < $start_timestamp or $revision_id > $params["end_timestamp"]) { // Ignore revisions that fall outside the date window continue; } // Count number of revisions for debugging $num_revisions_within_dates += 1; // Get info for this revision $user = getPageUser($page_id, $revision_id); // Filter on users. if ($user == "") { // Ignore empty users. continue; } elseif ($params["users"] != "" and in_array($user, $params["users"]) == false) { // This user isn't in the list, ignore this revision continue; } $num_revisions_with_matching_users += 1; // Remember the most recent revision by user if (array_key_exists($user, $last_revision_by_user_by_page) == false) { $last_revision_by_user_by_page[$user] = array(); } $last_revision_by_user_by_page[$user][$page_id] = $revision_id; } } $params["debug_num_revisions_in_ns"] = $num_revisions; $params["debug_num_revisions_within_dates"] = $num_revisions_within_dates; $params["debug_num_revisions_with_matching_users"] = $num_revisions_with_matching_users; // Identify status of last change per user $last_status_by_user_by_page = array(); foreach ($last_revision_by_user_by_page as $user => $page_ids) { foreach ($page_ids as $page_id => $last_revision_id) { // $statuses = getStatusTags($page_id, $last_revision_id); // Get status tags (we have to make the call differently based on // whether or not this is the current revision) if ($last_revision_id == $page_current_revisions[$page_id]) { $statuses = getStatusTags($page_id, ""); } else { $statuses = getStatusTags($page_id, $last_revision_id); } if (count($statuses) > 0) { if (array_key_exists($user, $last_status_by_user_by_page) == false) { $last_status_by_user_by_page[$user] = array(); } $last_status_by_user_by_page[$user][$page_id] = implode(", ", $statuses); } } } // Create count of status by user $count_by_user_then_status = array(); $count_by_user_then_status["TOTAL"] = array(); foreach ($last_status_by_user_by_page as $user => $page_ids) { foreach ($page_ids as $page_id => $last_status) { if (array_key_exists($user, $count_by_user_then_status) == false) { $count_by_user_then_status[$user] = array(); } if (array_key_exists($last_status, $count_by_user_then_status[$user]) == false) { $count_by_user_then_status[$user][$last_status] = 0; } $count_by_user_then_status[$user][$last_status] += 1; if (array_key_exists($last_status, $count_by_user_then_status["TOTAL"]) == false) { $count_by_user_then_status["TOTAL"][$last_status] = 0; } $count_by_user_then_status["TOTAL"][$last_status] += 1; } } // Move total line to the bottom $total_row = $count_by_user_then_status["TOTAL"]; unset($count_by_user_then_status["TOTAL"]); $count_by_user_then_status["TOTAL"] = $total_row; $params["user_status_count"] = $count_by_user_then_status; return $params; }
/** * show diff * * @author Andreas Gohr <*****@*****.**> * @param string $text - compare with this text with most current version * @param bool $intr - display the intro text */ function html_diff($text = '', $intro = true, $type = null) { global $ID; global $REV; global $lang; global $conf; if (!$type) { $type = $_REQUEST['difftype']; } if ($type != 'inline') { $type = 'sidebyside'; } // we're trying to be clever here, revisions to compare can be either // given as rev and rev2 parameters, with rev2 being optional. Or in an // array in rev2. $rev1 = $REV; if (is_array($_REQUEST['rev2'])) { $rev1 = (int) $_REQUEST['rev2'][0]; $rev2 = (int) $_REQUEST['rev2'][1]; if (!$rev1) { $rev1 = $rev2; unset($rev2); } } else { $rev2 = (int) $_REQUEST['rev2']; } $r_minor = ''; $l_minor = ''; if ($text) { // compare text to the most current revision $l_rev = ''; $l_text = rawWiki($ID, ''); $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . ' ' . dformat((int) @filemtime(wikiFN($ID))) . '</a> ' . $lang['current']; $r_rev = ''; $r_text = cleanText($text); $r_head = $lang['yours']; } else { if ($rev1 && $rev2) { // two specific revisions wanted // make sure order is correct (older on the left) if ($rev1 < $rev2) { $l_rev = $rev1; $r_rev = $rev2; } else { $l_rev = $rev2; $r_rev = $rev1; } } elseif ($rev1) { // single revision given, compare to current $r_rev = ''; $l_rev = $rev1; } else { // no revision was given, compare previous to current $r_rev = ''; $revs = getRevisions($ID, 0, 1); $l_rev = $revs[0]; $REV = $l_rev; // store revision back in $REV } // when both revisions are empty then the page was created just now if (!$l_rev && !$r_rev) { $l_text = ''; } else { $l_text = rawWiki($ID, $l_rev); } $r_text = rawWiki($ID, $r_rev); list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev); } $df = new Diff(explode("\n", htmlspecialchars($l_text)), explode("\n", htmlspecialchars($r_text))); if ($type == 'inline') { $tdf = new InlineDiffFormatter(); } else { $tdf = new TableDiffFormatter(); } if ($intro) { print p_locale_xhtml('diff'); } if (!$text) { ptln('<div class="diffoptions">'); $form = new Doku_Form(array('action' => wl())); $form->addHidden('id', $ID); $form->addHidden('rev2[0]', $l_rev); $form->addHidden('rev2[1]', $r_rev); $form->addHidden('do', 'diff'); $form->addElement(form_makeListboxField('difftype', array('sidebyside' => $lang['diff_side'], 'inline' => $lang['diff_inline']), $type, $lang['diff_type'], '', '', array('class' => 'quickselect'))); $form->addElement(form_makeButton('submit', 'diff', 'Go')); $form->printForm(); $diffurl = wl($ID, array('do' => 'diff', 'rev2[0]' => $l_rev, 'rev2[1]' => $r_rev, 'difftype' => $type)); ptln('<p><a class="wikilink1" href="' . $diffurl . '">' . $lang['difflink'] . '</a></p>'); ptln('</div>'); } ?> <table class="diff diff_<?php echo $type; ?> "> <tr> <th colspan="2" <?php echo $l_minor; ?> > <?php echo $l_head; ?> </th> <th colspan="2" <?php echo $r_minor; ?> > <?php echo $r_head; ?> </th> </tr> <?php echo $tdf->format($df); ?> </table> <?php }
/** * Returns a list of available revisions of a given wiki page * * @author Michael Klier <*****@*****.**> */ function pageVersions($id, $first) { $id = $this->resolvePageId($id); if (auth_quickaclcheck($id) < AUTH_READ) { throw new RemoteAccessDeniedException('You are not allowed to read this page', 111); } global $conf; $versions = array(); if (empty($id)) { throw new RemoteException('Empty page ID', 131); } $revisions = getRevisions($id, $first, $conf['recent'] + 1); if (count($revisions) == 0 && $first != 0) { $first = 0; $revisions = getRevisions($id, $first, $conf['recent'] + 1); } if (count($revisions) > 0 && $first == 0) { array_unshift($revisions, ''); // include current revision array_pop($revisions); // remove extra log entry } if (count($revisions) > $conf['recent']) { array_pop($revisions); // remove extra log entry } if (!empty($revisions)) { foreach ($revisions as $rev) { $file = wikiFN($id, $rev); $time = @filemtime($file); // we check if the page actually exists, if this is not the // case this can lead to less pages being returned than // specified via $conf['recent'] if ($time) { $info = getRevisionInfo($id, $time, 1024); if (!empty($info)) { $data['user'] = $info['user']; $data['ip'] = $info['ip']; $data['type'] = $info['type']; $data['sum'] = $info['sum']; $data['modified'] = $this->api->toDate($info['date']); $data['version'] = $info['date']; array_push($versions, $data); } } } return $versions; } else { return array(); } }
/** * Saves a wikitext by calling io_writeWikiPage. * Also directs changelog and attic updates. * * @author Andreas Gohr <*****@*****.**> * @author Ben Coburn <*****@*****.**> */ function saveWikiText($id, $text, $summary, $minor = false) { /* Note to developers: This code is subtle and delicate. Test the behavior of the attic and changelog with dokuwiki and external edits after any changes. External edits change the wiki page directly without using php or dokuwiki. */ global $conf; global $lang; global $REV; // ignore if no changes were made if ($text == rawWiki($id, '')) { return; } $file = wikiFN($id); $old = @filemtime($file); // from page $wasRemoved = empty($text); $wasCreated = !@file_exists($file); $wasReverted = $REV == true; $newRev = false; $oldRev = getRevisions($id, -1, 1, 1024); // from changelog $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]); if (!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old >= $oldRev) { // add old revision to the attic if missing saveOldRevision($id); // add a changelog entry if this edit came from outside dokuwiki if ($old > $oldRev) { addLogEntry($old, $id); // send notify mails notify($id, 'admin', $oldRev, '', false); notify($id, 'subscribers', $oldRev, '', false); // remove soon to be stale instructions $cache = new cache_instructions($id, $file); $cache->removeCache(); } } if ($wasRemoved) { // pre-save deleted revision @touch($file); clearstatcache(); $newRev = saveOldRevision($id); // remove empty file @unlink($file); // remove old meta info... $mfiles = metaFiles($id); $changelog = metaFN($id, '.changes'); foreach ($mfiles as $mfile) { // but keep per-page changelog to preserve page history if (@file_exists($mfile) && $mfile !== $changelog) { @unlink($mfile); } } $del = true; // autoset summary on deletion if (empty($summary)) { $summary = $lang['deleted']; } // remove empty namespaces io_sweepNS($id, 'datadir'); io_sweepNS($id, 'mediadir'); } else { // save file (namespace dir is created in io_writeWikiPage) io_writeWikiPage($file, $text, $id); // pre-save the revision, to keep the attic in sync $newRev = saveOldRevision($id); $del = false; } // select changelog line type $extra = ''; $type = 'E'; if ($wasReverted) { $type = 'R'; $extra = $REV; } else { if ($wasCreated) { $type = 'C'; } else { if ($wasRemoved) { $type = 'D'; } else { if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = 'e'; } } } } //minor edits only for logged in users addLogEntry($newRev, $id, $type, $summary, $extra); // send notify mails notify($id, 'admin', $old, $summary, $minor); notify($id, 'subscribers', $old, $summary, $minor); // update the purgefile (timestamp of the last time anything within the wiki was changed) io_saveFile($conf['cachedir'] . '/purgefile', time()); }
/** * display recent changes * * @author Andreas Gohr <*****@*****.**> * @author Matthias Grimm <*****@*****.**> * @author Ben Coburn <*****@*****.**> * @author Kate Arzamastseva <*****@*****.**> */ function html_recent($first = 0, $show_changes = 'both') { global $conf; global $lang; global $ID; /* we need to get one additionally log entry to be able to * decide if this is the last page or is there another one. * This is the cheapest solution to get this information. */ $flags = 0; if ($show_changes == 'mediafiles' && $conf['mediarevisions']) { $flags = RECENTS_MEDIA_CHANGES; } elseif ($show_changes == 'pages') { $flags = 0; } elseif ($conf['mediarevisions']) { $show_changes = 'both'; $flags = RECENTS_MEDIA_PAGES_MIXED; } $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags); if (count($recents) == 0 && $first != 0) { $first = 0; $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags); } $hasNext = false; if (count($recents) > $conf['recent']) { $hasNext = true; array_pop($recents); // remove extra log entry } print p_locale_xhtml('recent'); if (getNS($ID) != '') { print '<div class="level1"><p>' . sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent')) . '</p></div>'; } $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes')); $form->addHidden('sectok', null); $form->addHidden('do', 'recent'); $form->addHidden('id', $ID); if ($conf['mediarevisions']) { $form->addElement('<div class="changeType">'); $form->addElement(form_makeListboxField('show_changes', array('pages' => $lang['pages_changes'], 'mediafiles' => $lang['media_changes'], 'both' => $lang['both_changes']), $show_changes, $lang['changes_type'], '', '', array('class' => 'quickselect'))); $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply'])); $form->addElement('</div>'); } $form->addElement(form_makeOpenTag('ul')); foreach ($recents as $recent) { $date = dformat($recent['date']); if ($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) { $form->addElement(form_makeOpenTag('li', array('class' => 'minor'))); } else { $form->addElement(form_makeOpenTag('li')); } $form->addElement(form_makeOpenTag('div', array('class' => 'li'))); if (!empty($recent['media'])) { $form->addElement(media_printicon($recent['id'])); } else { $icon = DOKU_BASE . 'lib/images/fileicons/file.png'; $form->addElement('<img src="' . $icon . '" alt="' . $recent['id'] . '" class="icon" />'); } $form->addElement(form_makeOpenTag('span', array('class' => 'date'))); $form->addElement($date); $form->addElement(form_makeCloseTag('span')); $diff = false; $href = ''; if (!empty($recent['media'])) { $diff = count(getRevisions($recent['id'], 0, 1, 8192, true)) && @file_exists(mediaFN($recent['id'])); if ($diff) { $href = media_managerURL(array('tab_details' => 'history', 'mediado' => 'diff', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&'); } } else { $href = wl($recent['id'], "do=diff", false, '&'); } if (!empty($recent['media']) && !$diff) { $form->addElement('<img src="' . DOKU_BASE . 'lib/images/blank.gif" width="15" height="11" alt="" />'); } else { $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href))); $form->addElement(form_makeTag('img', array('src' => DOKU_BASE . 'lib/images/diff.png', 'width' => 15, 'height' => 11, 'title' => $lang['diff'], 'alt' => $lang['diff']))); $form->addElement(form_makeCloseTag('a')); } if (!empty($recent['media'])) { $href = media_managerURL(array('tab_details' => 'history', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&'); } else { $href = wl($recent['id'], "do=revisions", false, '&'); } $form->addElement(form_makeOpenTag('a', array('class' => 'revisions_link', 'href' => $href))); $form->addElement(form_makeTag('img', array('src' => DOKU_BASE . 'lib/images/history.png', 'width' => 12, 'height' => 14, 'title' => $lang['btn_revs'], 'alt' => $lang['btn_revs']))); $form->addElement(form_makeCloseTag('a')); if (!empty($recent['media'])) { $href = media_managerURL(array('tab_details' => 'view', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&'); $class = file_exists(mediaFN($recent['id'])) ? 'wikilink1' : ($class = 'wikilink2'); $form->addElement(form_makeOpenTag('a', array('class' => $class, 'href' => $href))); $form->addElement($recent['id']); $form->addElement(form_makeCloseTag('a')); } else { $form->addElement(html_wikilink(':' . $recent['id'], useHeading('navigation') ? null : $recent['id'])); } $form->addElement(form_makeOpenTag('span', array('class' => 'sum'))); $form->addElement(' – ' . htmlspecialchars($recent['sum'])); $form->addElement(form_makeCloseTag('span')); $form->addElement(form_makeOpenTag('span', array('class' => 'user'))); if ($recent['user']) { $form->addElement('<bdi>' . editorinfo($recent['user']) . '</bdi>'); if (auth_ismanager()) { $form->addElement(' <bdo dir="ltr">(' . $recent['ip'] . ')</bdo>'); } } else { $form->addElement('<bdo dir="ltr">' . $recent['ip'] . '</bdo>'); } $form->addElement(form_makeCloseTag('span')); $form->addElement(form_makeCloseTag('div')); $form->addElement(form_makeCloseTag('li')); } $form->addElement(form_makeCloseTag('ul')); $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav'))); $last = $first + $conf['recent']; if ($first > 0) { $first -= $conf['recent']; if ($first < 0) { $first = 0; } $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-prev'))); $form->addElement(form_makeTag('input', array('type' => 'submit', 'name' => 'first[' . $first . ']', 'value' => $lang['btn_newer'], 'accesskey' => 'n', 'title' => $lang['btn_newer'] . ' [N]', 'class' => 'button show'))); $form->addElement(form_makeCloseTag('div')); } if ($hasNext) { $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-next'))); $form->addElement(form_makeTag('input', array('type' => 'submit', 'name' => 'first[' . $last . ']', 'value' => $lang['btn_older'], 'accesskey' => 'p', 'title' => $lang['btn_older'] . ' [P]', 'class' => 'button show'))); $form->addElement(form_makeCloseTag('div')); } $form->addElement(form_makeCloseTag('div')); html_form('recent', $form); }
/** * Shows difference between two revisions of file * * @author Kate Arzamastseva <*****@*****.**> */ function media_diff($image, $ns, $auth, $fromajax = false) { global $lang; global $conf; global $INPUT; if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) { return ''; } $rev1 = $INPUT->int('rev'); $rev2 = $INPUT->ref('rev2'); if (is_array($rev2)) { $rev1 = (int) $rev2[0]; $rev2 = (int) $rev2[1]; if (!$rev1) { $rev1 = $rev2; unset($rev2); } } else { $rev2 = $INPUT->int('rev2'); } if ($rev1 && !file_exists(mediaFN($image, $rev1))) { $rev1 = false; } if ($rev2 && !file_exists(mediaFN($image, $rev2))) { $rev2 = false; } if ($rev1 && $rev2) { // two specific revisions wanted // make sure order is correct (older on the left) if ($rev1 < $rev2) { $l_rev = $rev1; $r_rev = $rev2; } else { $l_rev = $rev2; $r_rev = $rev1; } } elseif ($rev1) { // single revision given, compare to current $r_rev = ''; $l_rev = $rev1; } else { // no revision was given, compare previous to current $r_rev = ''; $revs = getRevisions($image, 0, 1, 8192, true); if (file_exists(mediaFN($image, $revs[0]))) { $l_rev = $revs[0]; } else { $l_rev = ''; } } // prepare event data $data[0] = $image; $data[1] = $l_rev; $data[2] = $r_rev; $data[3] = $ns; $data[4] = $auth; $data[5] = $fromajax; // trigger event return trigger_event('MEDIA_DIFF', $data, '_media_file_diff', true); }
/** * request with too large offset and range */ function test_requesttoolargenumberrevs() { $first = 50; $num = 50; $revsexpected = array(); $revs = getRevisions($this->pageid, $first, $num, $chunk_size = 8192, $media = false); $this->assertEquals($revsexpected, $revs); }
/** * Add recent changed pages to a feed object * * @author Andreas Gohr <*****@*****.**> * @param FeedCreator $rss the FeedCreator Object * @param array $data the items to add * @param array $opt the feed options */ function rss_buildItems(&$rss, &$data, $opt) { global $conf; global $lang; /* @var auth_basic $auth */ global $auth; $eventData = array('rss' => &$rss, 'data' => &$data, 'opt' => &$opt); $event = new Doku_Event('FEED_DATA_PROCESS', $eventData); if ($event->advise_before(false)) { foreach ($data as $ditem) { if (!is_array($ditem)) { // not an array? then only a list of IDs was given $ditem = array('id' => $ditem); } $item = new FeedItem(); $id = $ditem['id']; if (!$ditem['media']) { $meta = p_get_metadata($id); } else { $meta = array(); } // add date if ($ditem['date']) { $date = $ditem['date']; } elseif ($ditem['media']) { $date = @filemtime(mediaFN($id)); } elseif (@file_exists(wikiFN($id))) { $date = @filemtime(wikiFN($id)); } elseif ($meta['date']['modified']) { $date = $meta['date']['modified']; } else { $date = 0; } if ($date) { $item->date = date('r', $date); } // add title if ($conf['useheading'] && $meta['title']) { $item->title = $meta['title']; } else { $item->title = $ditem['id']; } if ($conf['rss_show_summary'] && !empty($ditem['sum'])) { $item->title .= ' - ' . strip_tags($ditem['sum']); } // add item link switch ($opt['link_to']) { case 'page': if ($ditem['media']) { $item->link = media_managerURL(array('image' => $id, 'ns' => getNS($id), 'rev' => $date), '&', true); } else { $item->link = wl($id, 'rev=' . $date, true, '&'); } break; case 'rev': if ($ditem['media']) { $item->link = media_managerURL(array('image' => $id, 'ns' => getNS($id), 'rev' => $date, 'tab_details' => 'history'), '&', true); } else { $item->link = wl($id, 'do=revisions&rev=' . $date, true, '&'); } break; case 'current': if ($ditem['media']) { $item->link = media_managerURL(array('image' => $id, 'ns' => getNS($id)), '&', true); } else { $item->link = wl($id, '', true, '&'); } break; case 'diff': default: if ($ditem['media']) { $item->link = media_managerURL(array('image' => $id, 'ns' => getNS($id), 'rev' => $date, 'tab_details' => 'history', 'mediado' => 'diff'), '&', true); } else { $item->link = wl($id, 'rev=' . $date . '&do=diff', true, '&'); } } // add item content switch ($opt['item_content']) { case 'diff': case 'htmldiff': if ($ditem['media']) { $revs = getRevisions($id, 0, 1, 8192, true); $rev = $revs[0]; $src_r = ''; $src_l = ''; if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)), 300)) { $more = 'w=' . $size[0] . '&h=' . $size[1] . 't=' . @filemtime(mediaFN($id)); $src_r = ml($id, $more); } if ($rev && ($size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)), 300))) { $more = 'rev=' . $rev . '&w=' . $size[0] . '&h=' . $size[1]; $src_l = ml($id, $more); } $content = ''; if ($src_r) { $content = '<table>'; $content .= '<tr><th width="50%">' . $rev . '</th>'; $content .= '<th width="50%">' . $lang['current'] . '</th></tr>'; $content .= '<tr align="center"><td><img src="' . $src_l . '" alt="" /></td><td>'; $content .= '<img src="' . $src_r . '" alt="' . $id . '" /></td></tr>'; $content .= '</table>'; } } else { require_once DOKU_INC . 'inc/DifferenceEngine.php'; $revs = getRevisions($id, 0, 1); $rev = $revs[0]; if ($rev) { $df = new Diff(explode("\n", rawWiki($id, $rev)), explode("\n", rawWiki($id, ''))); } else { $df = new Diff(array(''), explode("\n", rawWiki($id, ''))); } if ($opt['item_content'] == 'htmldiff') { // note: no need to escape diff output, TableDiffFormatter provides 'safe' html $tdf = new TableDiffFormatter(); $content = '<table>'; $content .= '<tr><th colspan="2" width="50%">' . $rev . '</th>'; $content .= '<th colspan="2" width="50%">' . $lang['current'] . '</th></tr>'; $content .= $tdf->format($df); $content .= '</table>'; } else { // note: diff output must be escaped, UnifiedDiffFormatter provides plain text $udf = new UnifiedDiffFormatter(); $content = "<pre>\n" . hsc($udf->format($df)) . "\n</pre>"; } } break; case 'html': if ($ditem['media']) { if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)))) { $more = 'w=' . $size[0] . '&h=' . $size[1] . 't=' . @filemtime(mediaFN($id)); $src = ml($id, $more); $content = '<img src="' . $src . '" alt="' . $id . '" />'; } else { $content = ''; } } else { if (@filemtime(wikiFN($id)) === $date) { $content = p_wiki_xhtml($id, '', false); } else { $content = p_wiki_xhtml($id, $date, false); } // no TOC in feeds $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s', '', $content); // add alignment for images $content = preg_replace('/(<img .*?class="medialeft")/s', '\\1 align="left"', $content); $content = preg_replace('/(<img .*?class="mediaright")/s', '\\1 align="right"', $content); // make URLs work when canonical is not set, regexp instead of rerendering! if (!$conf['canonical']) { $base = preg_quote(DOKU_REL, '/'); $content = preg_replace('/(<a href|<img src)="(' . $base . ')/s', '$1="' . DOKU_URL, $content); } } break; case 'abstract': default: if ($ditem['media']) { if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)))) { $more = 'w=' . $size[0] . '&h=' . $size[1] . 't=' . @filemtime(mediaFN($id)); $src = ml($id, $more); $content = '<img src="' . $src . '" alt="' . $id . '" />'; } else { $content = ''; } } else { $content = $meta['description']['abstract']; } } $item->description = $content; //FIXME a plugin hook here could be senseful // add user # FIXME should the user be pulled from metadata as well? $user = @$ditem['user']; // the @ spares time repeating lookup $item->author = ''; if ($user && $conf['useacl'] && $auth) { $userInfo = $auth->getUserData($user); if ($userInfo) { switch ($conf['showuseras']) { case 'username': $item->author = $userInfo['name']; break; default: $item->author = $user; break; } } else { $item->author = $user; } if ($userInfo && !$opt['guardmail']) { $item->authorEmail = $userInfo['mail']; } else { //cannot obfuscate because some RSS readers may check validity $item->authorEmail = $user . '@' . $ditem['ip']; } } elseif ($user) { // this happens when no ACL but some Apache auth is used $item->author = $user; $item->authorEmail = $user . '@' . $ditem['ip']; } else { $item->authorEmail = 'anonymous@' . $ditem['ip']; } // add category if (isset($meta['subject'])) { $item->category = $meta['subject']; } else { $cat = getNS($id); if ($cat) { $item->category = $cat; } } // finally add the item to the feed object, after handing it to registered plugins $evdata = array('item' => &$item, 'opt' => &$opt, 'ditem' => &$ditem, 'rss' => &$rss); $evt = new Doku_Event('FEED_ITEM_ADD', $evdata); if ($evt->advise_before()) { $rss->addItem($item); } $evt->advise_after(); // for completeness } } $event->advise_after(); }
/** * show diff * * @author Andreas Gohr <*****@*****.**> */ function html_diff($text = '', $intro = true) { require_once DOKU_INC . 'inc/DifferenceEngine.php'; global $ID; global $REV; global $lang; global $conf; if ($text) { $df = new Diff(explode("\n", htmlspecialchars(rawWiki($ID, ''))), explode("\n", htmlspecialchars(cleanText($text)))); $left = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . ' ' . date($conf['dformat'], @filemtime(wikiFN($ID))) . '</a>' . $lang['current']; $right = $lang['yours']; } else { if ($REV) { $r = $REV; } else { //use last revision if none given $revs = getRevisions($ID, 0, 1); $r = $revs[0]; } if ($r) { $df = new Diff(explode("\n", htmlspecialchars(rawWiki($ID, $r))), explode("\n", htmlspecialchars(rawWiki($ID, '')))); $left = '<a class="wikilink1" href="' . wl($ID, "rev={$r}") . '">' . $ID . ' ' . date($conf['dformat'], $r) . '</a>'; } else { $df = new Diff(array(''), explode("\n", htmlspecialchars(rawWiki($ID, '')))); $left = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . '</a>'; } $right = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . ' ' . date($conf['dformat'], @filemtime(wikiFN($ID))) . '</a> ' . $lang['current']; } $tdf = new TableDiffFormatter(); if ($intro) { print p_locale_xhtml('diff'); } ?> <table class="diff"> <tr> <th colspan="2"> <?php echo $left; ?> </th> <th colspan="2"> <?php echo $right; ?> </th> </tr> <?php echo $tdf->format($df); ?> </table> <?php }
/** * Builds page edit summary using the config option */ function _buildPageSummary($page_counts, $type) { static $link_opt; static $urls = array(); static $urlparam_tmpl = array('diff' => 'do%%5Bdiff%%5D,rev2%%5B%%5D=%s,rev2%%5B%%5D=%s', 'rev' => 'rev=%s', 'revlist' => 'do=revisions', 'current' => ''); if (is_null($link_opt)) { $link_opt = $this->getConf('link_page'); } $output = $item_count = null; if (!isset($urlparam_tmpl[$link_opt])) { // without url info $output_tmpl = "%4s. %s (%s)\n"; foreach ($page_counts as $id => $edit_count) { $output .= sprintf($output_tmpl, ++$item_count, $id, $edit_count); } } elseif (in_array($link_opt, array('revlist', 'current'))) { // with fixed url $output_tmpl = "%4s. %s (%s)\n%4s %s\n"; foreach ($page_counts as $id => $edit_count) { if (!isset($urls[$type][$id])) { $urls[$type][$id] = wl($id, $urlparam_tmpl[$link_opt], 'absolute', '&'); } $output .= sprintf($output_tmpl, ++$item_count, $id, $edit_count, '', $urls[$type][$id]); } } elseif (in_array($link_opt, array('diff', 'rev'))) { // with varied url $output_tmpl = "%4s. %s (%s)\n%4s %s\n"; foreach ($page_counts as $id => $edit_count) { if (!isset($urls[$type][$id])) { // scan base revision (prev_last) for the current period $curr_last = $prev_last = null; foreach (getRevisions($id, -1, 0) as $rev) { if (is_null($curr_last) && $rev <= $this->_end) { $curr_last = $rev; } elseif (is_null($prev_last) && $rev < $this->_start[$type]) { $prev_last = $rev; break; } } if (is_null($curr_last)) { $curr_last = 1; } // something wrong, but go on if (is_null($prev_last)) { $prev_last = 1; } // compare newly created page with rev 1970-01-01 $urlparam = sprintf($urlparam_tmpl[$link_opt], $curr_last, $prev_last); $urls[$type][$id] = wl($id, $urlparam, 'absolute', '&'); } $output .= sprintf($output_tmpl, ++$item_count, $id, $edit_count, '', $urls[$type][$id]); } } return isset($output) ? rtrim($this->getLang('note_edit_count') . $output) : ''; }
/** * Rewrite the text of a page according to the recorded moves, the rewritten text is saved * * @param string $id The id of the page that shall be rewritten * @param string|null $text Old content of the page. When null is given the content is loaded from disk. * @return string The rewritten content */ public function execute_rewrites($id, $text = null) { $meta = $this->getMoveMeta($id); if($meta && (isset($meta['moves']) || isset($meta['media_moves']))) { if(is_null($text)) $text = rawWiki($id); $moves = isset($meta['moves']) ? $meta['moves'] : array(); $media_moves = isset($meta['media_moves']) ? $meta['media_moves'] : array(); $old_text = $text; $text = $this->rewrite_content($text, $id, $moves, $media_moves); $changed = ($old_text != $text); $file = wikiFN($id, '', false); if(is_writable($file) || !$changed) { if ($changed) { // Wait a second if page has just been saved $oldRev = getRevisions($id, -1, 1, 1024); // from changelog if ($oldRev == time()) sleep(1); saveWikiText($id, $text, $this->getLang('linkchange')); } unset($meta['moves']); unset($meta['media_moves']); p_set_metadata($id, array('plugin_move' => $meta), false, true); } else { // FIXME: print error here or fail silently? msg('Error: Page '.hsc($id).' needs to be rewritten because of page renames but is not writable.', -1); } } return $text; }
/** * Send a digest mail * * Sends a digest mail showing a bunch of changes. * * @param string $subscriber_mail The target mail address * @param array $id The ID * @param int $lastupdate Time of the last notification * * @author Adrian Lang <*****@*****.**> */ function subscription_send_digest($subscriber_mail, $id, $lastupdate) { $n = 0; do { $rev = getRevisions($id, $n++, 1); $rev = count($rev) > 0 ? $rev[0] : null; } while (!is_null($rev) && $rev > $lastupdate); $replaces = array('NEWPAGE' => wl($id, '', true, '&'), 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')); if (!is_null($rev)) { $subject = 'changed'; $replaces['OLDPAGE'] = wl($id, "rev={$rev}", true, '&'); $df = new Diff(explode("\n", rawWiki($id, $rev)), explode("\n", rawWiki($id))); $dformat = new UnifiedDiffFormatter(); $replaces['DIFF'] = $dformat->format($df); } else { $subject = 'newpage'; $replaces['OLDPAGE'] = 'none'; $replaces['DIFF'] = rawWiki($id); } subscription_send($subscriber_mail, $replaces, $subject, $id, 'subscr_digest'); }
/** * Add recent changed pages to a feed object * * @author Andreas Gohr <*****@*****.**> * @param object $rss - the FeedCreator Object * @param array $data - the items to add * @param array $opt - the feed options */ function rss_buildItems(&$rss, &$data, $opt) { global $conf; global $lang; global $auth; $eventData = array('rss' => &$rss, 'data' => &$data, 'opt' => &$opt); $event = new Doku_Event('FEED_DATA_PROCESS', $eventData); if ($event->advise_before(false)) { foreach ($data as $ditem) { if (!is_array($ditem)) { // not an array? then only a list of IDs was given $ditem = array('id' => $ditem); } $item = new FeedItem(); $id = $ditem['id']; $meta = p_get_metadata($id); // add date if ($ditem['date']) { $date = $ditem['date']; } elseif ($meta['date']['modified']) { $date = $meta['date']['modified']; } else { $date = @filemtime(wikiFN($id)); } if ($date) { $item->date = date('r', $date); } // add title if ($conf['useheading'] && $meta['title']) { $item->title = $meta['title']; } else { $item->title = $ditem['id']; } if ($conf['rss_show_summary'] && !empty($ditem['sum'])) { $item->title .= ' - ' . strip_tags($ditem['sum']); } // add item link switch ($opt['link_to']) { case 'page': $item->link = wl($id, 'rev=' . $date, true, '&'); break; case 'rev': $item->link = wl($id, 'do=revisions&rev=' . $date, true, '&'); break; case 'current': $item->link = wl($id, '', true, '&'); break; case 'diff': default: $item->link = wl($id, 'rev=' . $date . '&do=diff', true, '&'); } // add item content switch ($opt['item_content']) { case 'diff': case 'htmldiff': require_once DOKU_INC . 'inc/DifferenceEngine.php'; $revs = getRevisions($id, 0, 1); $rev = $revs[0]; if ($rev) { $df = new Diff(explode("\n", htmlspecialchars(rawWiki($id, $rev))), explode("\n", htmlspecialchars(rawWiki($id, '')))); } else { $df = new Diff(array(''), explode("\n", htmlspecialchars(rawWiki($id, '')))); } if ($opt['item_content'] == 'htmldiff') { $tdf = new TableDiffFormatter(); $content = '<table>'; $content .= '<tr><th colspan="2" width="50%">' . $rev . '</th>'; $content .= '<th colspan="2" width="50%">' . $lang['current'] . '</th></tr>'; $content .= $tdf->format($df); $content .= '</table>'; } else { $udf = new UnifiedDiffFormatter(); $content = "<pre>\n" . $udf->format($df) . "\n</pre>"; } break; case 'html': $content = p_wiki_xhtml($id, $date, false); // no TOC in feeds $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s', '', $content); // make URLs work when canonical is not set, regexp instead of rerendering! if (!$conf['canonical']) { $base = preg_quote(DOKU_REL, '/'); $content = preg_replace('/(<a href|<img src)="(' . $base . ')/s', '$1="' . DOKU_URL, $content); } break; case 'abstract': default: $content = $meta['description']['abstract']; } $item->description = $content; //FIXME a plugin hook here could be senseful // add user # FIXME should the user be pulled from metadata as well? $user = null; $user = @$ditem['user']; // the @ spares time repeating lookup $item->author = ''; if ($user && $conf['useacl'] && $auth) { $userInfo = $auth->getUserData($user); $item->author = $userInfo['name']; if ($userInfo && !$opt['guardmail']) { $item->authorEmail = $userInfo['mail']; } else { //cannot obfuscate because some RSS readers may check validity $item->authorEmail = $user . '@' . $ditem['ip']; } } elseif ($user) { // this happens when no ACL but some Apache auth is used $item->author = $user; $item->authorEmail = $user . '@' . $ditem['ip']; } else { $item->authorEmail = 'anonymous@' . $ditem['ip']; } // add category if ($meta['subject']) { $item->category = $meta['subject']; } else { $cat = getNS($id); if ($cat) { $item->category = $cat; } } // finally add the item to the feed object, after handing it to registered plugins $evdata = array('item' => &$item, 'opt' => &$opt, 'ditem' => &$ditem, 'rss' => &$rss); $evt = new Doku_Event('FEED_ITEM_ADD', $evdata); if ($evt->advise_before()) { $rss->addItem($item); } $evt->advise_after(); // for completeness } } $event->advise_after(); }
/** * Checks if the current page is a translation of a page * in the default language. Displays a notice when it is * older than the original page. Tries to lin to a diff * with changes on the original since the translation */ function checkage() { global $ID; global $INFO; if (!$this->getConf('checkage')) { return; } if (!$INFO['exists']) { return; } $lng = $this->getLangPart($ID); if ($lng == $this->defaultlang) { return; } $rx = '/^' . $this->tns . '((' . join('|', $this->trans) . '):)?/'; $idpart = preg_replace($rx, '', $ID); // compare modification times list($orig, $name) = $this->buildTransID($this->defaultlang, $idpart); $origfn = wikiFN($orig); if ($INFO['lastmod'] >= @filemtime($origfn)) { return; } // get revision from before translation $orev = 0; $revs = getRevisions($orig, 0, 100); foreach ($revs as $rev) { if ($rev < $INFO['lastmod']) { $orev = $rev; break; } } // see if the found revision still exists if ($orev && !page_exists($orig, $orev)) { $orev = 0; } // build the message and display it $orig = cleanID($orig); $msg = sprintf($this->getLang('outdated'), wl($orig)); if ($orev) { $msg .= sprintf(' ' . $this->getLang('diff'), wl($orig, array('do' => 'diff', 'rev' => $orev))); } echo '<div class="notify">' . $msg . '</div>'; }
/** * Saves a wikitext by calling io_writeWikiPage. * Also directs changelog and attic updates. * * @author Andreas Gohr <*****@*****.**> * @author Ben Coburn <*****@*****.**> */ function saveWikiText($id, $text, $summary, $minor = false) { /* Note to developers: This code is subtle and delicate. Test the behavior of the attic and changelog with dokuwiki and external edits after any changes. External edits change the wiki page directly without using php or dokuwiki. */ global $conf; global $lang; global $REV; // ignore if no changes were made if ($text == rawWiki($id, '')) { return; } $file = wikiFN($id); $old = @filemtime($file); // from page $wasRemoved = trim($text) == ''; // check for empty or whitespace only $wasCreated = !@file_exists($file); $wasReverted = $REV == true; $newRev = false; $oldRev = getRevisions($id, -1, 1, 1024); // from changelog $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]); if (!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old >= $oldRev) { // add old revision to the attic if missing saveOldRevision($id); // add a changelog entry if this edit came from outside dokuwiki if ($old > $oldRev) { addLogEntry($old, $id, DOKU_CHANGE_TYPE_EDIT, $lang['external_edit'], '', array('ExternalEdit' => true)); // remove soon to be stale instructions $cache = new cache_instructions($id, $file); $cache->removeCache(); } } if ($wasRemoved) { // Send "update" event with empty data, so plugins can react to page deletion $data = array(array($file, '', false), getNS($id), noNS($id), false); trigger_event('IO_WIKIPAGE_WRITE', $data); // pre-save deleted revision @touch($file); clearstatcache(); $newRev = saveOldRevision($id); // remove empty file @unlink($file); // don't remove old meta info as it should be saved, plugins can use IO_WIKIPAGE_WRITE for removing their metadata... // purge non-persistant meta data p_purge_metadata($id); $del = true; // autoset summary on deletion if (empty($summary)) { $summary = $lang['deleted']; } // remove empty namespaces io_sweepNS($id, 'datadir'); io_sweepNS($id, 'mediadir'); } else { // save file (namespace dir is created in io_writeWikiPage) io_writeWikiPage($file, $text, $id); // pre-save the revision, to keep the attic in sync $newRev = saveOldRevision($id); $del = false; } // select changelog line type $extra = ''; $type = DOKU_CHANGE_TYPE_EDIT; if ($wasReverted) { $type = DOKU_CHANGE_TYPE_REVERT; $extra = $REV; } else { if ($wasCreated) { $type = DOKU_CHANGE_TYPE_CREATE; } else { if ($wasRemoved) { $type = DOKU_CHANGE_TYPE_DELETE; } else { if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = DOKU_CHANGE_TYPE_MINOR_EDIT; } } } } //minor edits only for logged in users addLogEntry($newRev, $id, $type, $summary, $extra); // send notify mails notify($id, 'admin', $old, $summary, $minor); notify($id, 'subscribers', $old, $summary, $minor); // update the purgefile (timestamp of the last time anything within the wiki was changed) io_saveFile($conf['cachedir'] . '/purgefile', time()); // if useheading is enabled, purge the cache of all linking pages if (useHeading('content')) { $pages = ft_backlinks($id); foreach ($pages as $page) { $cache = new cache_renderer($page, wikiFN($page), 'xhtml'); $cache->removeCache(); } } }
/** * Returns all statuses for all revisions of a given page * * { * "111111": ["check"], * "222222": ["check"], * "333333": ["review"], * "444444": ["review", "discuss"], * "555555": ["publish"] * } * * @param string $page_id The page to read revisions from * * @return an array containing the page status revisions */ function getStatusesForPage($page_id) { $statuses_by_revision = array(); // Get all revisions for this page. $revision_ids = getRevisions($page_id, 0, 10000); // Reverse the array so that it goes least-recent to most-recent $revision_ids = array_reverse($revision_ids); // Push the current revision onto the stack. array_push($revision_ids, ""); // Extract status from each revision foreach ($revision_ids as $revision_id) { $status_tags = getStatusTags($page_id, $revision_id); $statuses_by_revision[$revision_id] = $status_tags; } return $statuses_by_revision; }
/** * Send a digest mail * * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff() * but determines the last known revision first * * @author Adrian Lang <*****@*****.**> * * @param string $subscriber_mail The target mail address * @param array $id The ID * @param int $lastupdate Time of the last notification * @return bool */ protected function send_digest($subscriber_mail, $id, $lastupdate) { $n = 0; do { $rev = getRevisions($id, $n++, 1); $rev = count($rev) > 0 ? $rev[0] : null; } while (!is_null($rev) && $rev > $lastupdate); return $this->send_diff($subscriber_mail, 'subscr_digest', $id, $rev); }
/** * show diff * * @author Andreas Gohr <*****@*****.**> */ function html_diff($text = '', $intro = true) { require_once DOKU_INC . 'inc/DifferenceEngine.php'; global $ID; global $REV; global $lang; global $conf; // we're trying to be clever here, revisions to compare can be either // given as rev and rev2 parameters, with rev2 being optional. Or in an // array in rev2. $rev1 = $REV; if (is_array($_REQUEST['rev2'])) { $rev1 = (int) $_REQUEST['rev2'][0]; $rev2 = (int) $_REQUEST['rev2'][1]; if (!$rev1) { $rev1 = $rev2; unset($rev2); } } else { $rev2 = (int) $_REQUEST['rev2']; } if ($text) { // compare text to the most current revision $l_rev = ''; $l_text = rawWiki($ID, ''); $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . ' ' . strftime($conf['dformat'], @filemtime(wikiFN($ID))) . '</a> ' . $lang['current']; $r_rev = ''; $r_text = cleanText($text); $r_head = $lang['yours']; } else { if ($rev1 && $rev2) { // two specific revisions wanted // make sure order is correct (older on the left) if ($rev1 < $rev2) { $l_rev = $rev1; $r_rev = $rev2; } else { $l_rev = $rev2; $r_rev = $rev1; } } elseif ($rev1) { // single revision given, compare to current $r_rev = ''; $l_rev = $rev1; } else { // no revision was given, compare previous to current $r_rev = ''; $revs = getRevisions($ID, 0, 1); $l_rev = $revs[0]; } // when both revisions are empty then the page was created just now if (!$l_rev && !$r_rev) { $l_text = ''; } else { $l_text = rawWiki($ID, $l_rev); } $r_text = rawWiki($ID, $r_rev); if (!$l_rev) { $l_head = '—'; } else { $l_info = getRevisionInfo($ID, $l_rev, true); if ($l_info['user']) { $l_user = editorinfo($l_info['user']); if (auth_ismanager()) { $l_user .= ' (' . $l_info['ip'] . ')'; } } else { $l_user = $l_info['ip']; } $l_user = '******' . $l_user . '</span>'; $l_sum = $l_info['sum'] ? '<span class="sum">' . hsc($l_info['sum']) . '</span>' : ''; if ($l_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) { $l_minor = 'class="minor"'; } $l_head = '<a class="wikilink1" href="' . wl($ID, "rev={$l_rev}") . '">' . $ID . ' [' . strftime($conf['dformat'], $l_rev) . ']</a>' . '<br />' . $l_user . ' ' . $l_sum; } if ($r_rev) { $r_info = getRevisionInfo($ID, $r_rev, true); if ($r_info['user']) { $r_user = editorinfo($r_info['user']); if (auth_ismanager()) { $r_user .= ' (' . $r_info['ip'] . ')'; } } else { $r_user = $r_info['ip']; } $r_user = '******' . $r_user . '</span>'; $r_sum = $r_info['sum'] ? '<span class="sum">' . hsc($r_info['sum']) . '</span>' : ''; if ($r_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) { $r_minor = 'class="minor"'; } $r_head = '<a class="wikilink1" href="' . wl($ID, "rev={$r_rev}") . '">' . $ID . ' [' . strftime($conf['dformat'], $r_rev) . ']</a>' . '<br />' . $r_user . ' ' . $r_sum; } elseif ($_rev = @filemtime(wikiFN($ID))) { $_info = getRevisionInfo($ID, $_rev, true); if ($_info['user']) { $_user = editorinfo($_info['user']); if (auth_ismanager()) { $_user .= ' (' . $_info['ip'] . ')'; } } else { $_user = $_info['ip']; } $_user = '******' . $_user . '</span>'; $_sum = $_info['sum'] ? '<span class="sum">' . hsc($_info['sum']) . '</span>' : ''; if ($_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) { $r_minor = 'class="minor"'; } $r_head = '<a class="wikilink1" href="' . wl($ID) . '">' . $ID . ' [' . strftime($conf['dformat'], $_rev) . ']</a> ' . '(' . $lang['current'] . ')' . '<br />' . $_user . ' ' . $_sum; } else { $r_head = '— (' . $lang['current'] . ')'; } } $df = new Diff(explode("\n", htmlspecialchars($l_text)), explode("\n", htmlspecialchars($r_text))); $tdf = new TableDiffFormatter(); if ($intro) { print p_locale_xhtml('diff'); } ?> <table class="diff"> <tr> <th colspan="2" <?php echo $l_minor; ?> > <?php echo $l_head; ?> </th> <th colspan="2" <?php echo $r_minor; ?> > <?php echo $r_head; ?> </th> </tr> <?php echo $tdf->format($df); ?> </table> <?php }
/** * Convenience function to get just the status tags from a page revision. * * @param array $params The parameters given by the user * @return array An updated $params array with data filled in */ function handleActivityByNamespaceReport($params) { global $cache_revinfo; // Validate parameters $params = validateNamespace($params); $params = validateStartDate($params); $params = validateEndDate($params); $namespace = $params["namespace"]; $params["report_title"] = "Activity by Namespace for " . $namespace . " from " . $params["start_date"] . " to " . $params["end_date"]; // Find all pages in the namespace // Example page attributes are: // id: en:bible:notes:rom:01:01 // rev: 1437676066 // mtime: 1437676066 // size: 3485 $pages = getAllPagesInNamespace($namespace); $params["debug_num_pages_in_ns"] = count($pages); $sub_namespaces = array(); $num_revisions_within_dates = 0; $count_by_sub_namespace_then_status = array(); $count_by_sub_namespace_then_status["TOTAL"] = array(); foreach ($pages as $page) { // Ignore any pages that haven't been changed since the begin date. if ($page["rev"] < $params["start_timestamp"]) { continue; } // Clear the Dokuwiki revision cache. This is potentially fragile, but // we don't do this, the DokuWiki cache for this script will grow until // it blows out the memory for this thread. $cache_revinfo = array(); // Because we searched for all pages in $namespace, we can make the // assumption that every page begins with the namespace. Thus by // removing the namespace from every page we get the path of the page // within that namespace. We use stren() + 1 to also catch the colon at // the end of the parent namespace. $page_id = $page["id"]; $local_page_id = substr($page_id, strlen($namespace) + 1); $local_page_id_parts = explode(":", $local_page_id); // The sub-namespace corresponds to the "book" if using a namespace like // "en:bible:notes". $sub_namespace = $local_page_id_parts[0]; if (in_array($sub_namespace, $sub_namespaces) == false) { array_push($sub_namespaces, $sub_namespace); } // Get all revisions for this page. $revision_ids = getRevisions($page_id, 0, 10000); // Reverse the array so that it goes least-recent to most-recent $revision_ids = array_reverse($revision_ids); // Push the current revision onto the stack. array_push($revision_ids, $page["rev"]); $current_revision_id = $page["rev"]; // Count number of revisions for debugging $num_revisions += count($revision_ids); // Consider each revision $prev_status_tags = null; $prev_revision_id = null; foreach ($revision_ids as $revision_id) { if ($revision_id >= $params["start_timestamp"] and $revision_id <= $params["end_timestamp"]) { // Count number of revisions for debugging $num_revisions_within_dates += 1; // Load status tags for previous version if necessary. This // happens when the current version is within the time window // but the previous version was not. if ($prev_revision_id != null and $prev_status_tags == null) { $prev_status_tags = getStatusTags($page_id, $prev_revision_id); } // Get status tags (we have to make the call differently based on // whether or not this is the current revision) if ($revision_id == $current_revision_id) { $status_tags = getStatusTags($page_id, ""); } else { $status_tags = getStatusTags($page_id, $revision_id); } // Compare status tags to see if they've changed. if ($prev_status_tags != null and $status_tags != $prev_status_tags) { // Status tags have changed. // Create sub-namespace in report if needed. if (array_key_exists($sub_namespace, $count_by_sub_namespace_then_status) == false) { $count_by_sub_namespace_then_status[$sub_namespace] = array(); } foreach ($status_tags as $status_tag) { // Create status count if it doesn't already exist. if (array_key_exists($status_tag, $count_by_sub_namespace_then_status[$sub_namespace]) == false) { $count_by_sub_namespace_then_status[$sub_namespace][$status_tag] = 0; } // Increment status count. $count_by_sub_namespace_then_status[$sub_namespace][$status_tag] += 1; // Create total status count if it doesn't already exist. if (array_key_exists($status_tag, $count_by_sub_namespace_then_status["TOTAL"]) == false) { $count_by_sub_namespace_then_status["TOTAL"][$status_tag] = 0; } // Increment total status count. $count_by_sub_namespace_then_status["TOTAL"][$status_tag] += 1; } } } // Remember previous revision $prev_revision_id = $revision_id; $prev_status_tags = $status_tags; } } // Move total line to the bottom $total_row = $count_by_sub_namespace_then_status["TOTAL"]; unset($count_by_sub_namespace_then_status["TOTAL"]); $count_by_sub_namespace_then_status["TOTAL"] = $total_row; $params["debug_num_revisions_in_ns"] = $num_revisions; $params["debug_num_revisions_within_dates"] = $num_revisions_within_dates; $params["count_by_sub_namespace_then_status"] = $count_by_sub_namespace_then_status; return $params; }
/** * Send digest and list mails for all subscriptions which are in effect for the * current page * * @author Adrian Lang <*****@*****.**> */ function sendDigest() { echo 'sendDigest(): started' . NL; global $ID; global $conf; if (!$conf['subscribers']) { echo 'sendDigest(): disabled' . NL; return false; } $subscriptions = subscription_find($ID, array('style' => '(digest|list)', 'escaped' => true)); /** @var auth_basic $auth */ global $auth; global $lang; global $conf; global $USERINFO; // remember current user info $olduinfo = $USERINFO; $olduser = $_SERVER['REMOTE_USER']; foreach ($subscriptions as $id => $users) { if (!subscription_lock($id)) { continue; } foreach ($users as $data) { list($user, $style, $lastupdate) = $data; $lastupdate = (int) $lastupdate; if ($lastupdate + $conf['subscribe_time'] > time()) { // Less than the configured time period passed since last // update. continue; } // Work as the user to make sure ACLs apply correctly $USERINFO = $auth->getUserData($user); $_SERVER['REMOTE_USER'] = $user; if ($USERINFO === false) { continue; } if (substr($id, -1, 1) === ':') { // The subscription target is a namespace $changes = getRecentsSince($lastupdate, null, getNS($id)); } else { if (auth_quickaclcheck($id) < AUTH_READ) { continue; } $meta = p_get_metadata($id); $changes = array($meta['last_change']); } // Filter out pages only changed in small and own edits $change_ids = array(); foreach ($changes as $rev) { $n = 0; while (!is_null($rev) && $rev['date'] >= $lastupdate && ($_SERVER['REMOTE_USER'] === $rev['user'] || $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) { $rev = getRevisions($rev['id'], $n++, 1); $rev = count($rev) > 0 ? $rev[0] : null; } if (!is_null($rev) && $rev['date'] >= $lastupdate) { // Some change was not a minor one and not by myself $change_ids[] = $rev['id']; } } if ($style === 'digest') { foreach ($change_ids as $change_id) { subscription_send_digest($USERINFO['mail'], $change_id, $lastupdate); } } elseif ($style === 'list') { subscription_send_list($USERINFO['mail'], $change_ids, $id); } // TODO: Handle duplicate subscriptions. // Update notification time. subscription_set($user, $id, $style, time(), true); } subscription_unlock($id); } // restore current user info $USERINFO = $olduinfo; $_SERVER['REMOTE_USER'] = $olduser; echo 'sendDigest(): finished' . NL; return true; }