function showDiffPage($diffOnly = false) { global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol; wfProfileIn(__METHOD__); # Allow frames except in certain special cases $wgOut->allowClickjacking(); # If external diffs are enabled both globally and for the user, # we'll use the application/x-external-editor interface to call # an external diff tool like kompare, kdiff3, etc. if ($wgUseExternalEditor && $wgUser->getOption('externaldiff')) { global $wgCanonicalServer, $wgScript, $wgLang; $wgOut->disable(); header("Content-type: application/x-external-editor; charset=UTF-8"); $url1 = $this->mTitle->getCanonical(array('action' => 'raw', 'oldid' => $this->mOldid)); $url2 = $this->mTitle->getCanonical(array('action' => 'raw', 'oldid' => $this->mNewid)); $special = $wgLang->getNsText(NS_SPECIAL); $control = <<<CONTROL \t\t\t[Process] \t\t\tType=Diff text \t\t\tEngine=MediaWiki \t\t\tScript={$wgCanonicalServer}{$wgScript} \t\t\tSpecial namespace={$special} \t\t\t[File] \t\t\tExtension=wiki \t\t\tURL={$url1} \t\t\t[File 2] \t\t\tExtension=wiki \t\t\tURL={$url2} CONTROL; echo $control; wfProfileOut(__METHOD__); return; } $wgOut->setArticleFlag(false); if (!$this->loadRevisionData()) { // Sounds like a deleted revision... Let's see what we can do. $t = $this->mTitle->getPrefixedText(); $d = wfMsgExt('missingarticle-diff', array('escape'), $this->deletedIdMarker($this->mOldid), $this->deletedIdMarker($this->mNewid)); $wgOut->setPagetitle(wfMsg('errorpagetitle')); $wgOut->addWikiMsg('missing-article', "<nowiki>{$t}</nowiki>", "<span class='plainlinks'>{$d}</span>"); wfProfileOut(__METHOD__); return; } wfRunHooks('DiffViewHeader', array($this, $this->mOldRev, $this->mNewRev)); if ($this->mNewRev->isCurrent()) { $wgOut->setArticleFlag(true); } # mOldid is false if the difference engine is called with a "vague" query for # a diff between a version V and its previous version V' AND the version V # is the first version of that article. In that case, V' does not exist. if ($this->mOldid === false) { $this->showFirstRevision(); $this->renderNewRevision(); // should we respect $diffOnly here or not? wfProfileOut(__METHOD__); return; } $oldTitle = $this->mOldPage->getPrefixedText(); $newTitle = $this->mNewPage->getPrefixedText(); if ($oldTitle == $newTitle) { $wgOut->setPageTitle($newTitle); } else { $wgOut->setPageTitle($oldTitle . ', ' . $newTitle); } if ($this->mNewPage->equals($this->mOldPage)) { $wgOut->setSubtitle(wfMsgExt('difference', array('parseinline'))); } else { $wgOut->setSubtitle(wfMsgExt('difference-multipage', array('parseinline'))); } $wgOut->setRobotPolicy('noindex,nofollow'); if (!$this->mOldPage->userCanRead() || !$this->mNewPage->userCanRead()) { $wgOut->loginToUse(); $wgOut->output(); $wgOut->disable(); wfProfileOut(__METHOD__); return; } $sk = $wgUser->getSkin(); if (method_exists($sk, 'suppressQuickbar')) { $sk->suppressQuickbar(); } // Check if page is editable $editable = $this->mNewRev->getTitle()->userCan('edit'); if ($editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed('rollback')) { $wgOut->preventClickjacking(); $rollback = '   ' . $sk->generateRollback($this->mNewRev); } else { $rollback = ''; } // Prepare a change patrol link, if applicable if ($wgUseRCPatrol && $this->mTitle->userCan('patrol')) { // If we've been given an explicit change identifier, use it; saves time if ($this->mRcidMarkPatrolled) { $rcid = $this->mRcidMarkPatrolled; $rc = RecentChange::newFromId($rcid); // Already patrolled? $rcid = is_object($rc) && !$rc->getAttribute('rc_patrolled') ? $rcid : 0; } else { // Look for an unpatrolled change corresponding to this diff $db = wfGetDB(DB_SLAVE); $change = RecentChange::newFromConds(array('rc_user_text' => $this->mNewRev->getRawUserText(), 'rc_timestamp' => $db->timestamp($this->mNewRev->getTimestamp()), 'rc_this_oldid' => $this->mNewid, 'rc_last_oldid' => $this->mOldid, 'rc_patrolled' => 0), __METHOD__); if ($change instanceof RecentChange) { $rcid = $change->mAttribs['rc_id']; $this->mRcidMarkPatrolled = $rcid; } else { // None found $rcid = 0; } } // Build the link if ($rcid) { $wgOut->preventClickjacking(); $token = $wgUser->editToken($rcid); $patrol = ' <span class="patrollink">[' . $sk->link($this->mTitle, wfMsgHtml('markaspatrolleddiff'), array(), array('action' => 'markpatrolled', 'rcid' => $rcid, 'token' => $token), array('known', 'noclasses')) . ']</span>'; } else { $patrol = ''; } } else { $patrol = ''; } # Carry over 'diffonly' param via navigation links if ($diffOnly != $wgUser->getBoolOption('diffonly')) { $query['diffonly'] = $diffOnly; } # Make "previous revision link" $query['diff'] = 'prev'; $query['oldid'] = $this->mOldid; # Cascade unhide param in links for easy deletion browsing if ($this->unhide) { $query['unhide'] = 1; } if (!$this->mOldRev->getPrevious()) { $prevlink = ' '; } else { $prevlink = $sk->link($this->mTitle, wfMsgHtml('previousdiff'), array('id' => 'differences-prevlink'), $query, array('known', 'noclasses')); } # Make "next revision link" $query['diff'] = 'next'; $query['oldid'] = $this->mNewid; # Skip next link on the top revision if ($this->mNewRev->isCurrent()) { $nextlink = ' '; } else { $nextlink = $sk->link($this->mTitle, wfMsgHtml('nextdiff'), array('id' => 'differences-nextlink'), $query, array('known', 'noclasses')); } $oldminor = ''; $newminor = ''; if ($this->mOldRev->isMinor()) { $oldminor = ChangesList::flag('minor'); } if ($this->mNewRev->isMinor()) { $newminor = ChangesList::flag('minor'); } # Handle RevisionDelete links... $ldel = $this->revisionDeleteLink($this->mOldRev); $rdel = $this->revisionDeleteLink($this->mNewRev); $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $this->mOldtitle . '</strong></div>' . '<div id="mw-diff-otitle2">' . $sk->revUserTools($this->mOldRev, !$this->unhide) . '</div>' . '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment($this->mOldRev, !$diffOnly, !$this->unhide) . $ldel . '</div>' . '<div id="mw-diff-otitle4">' . $prevlink . '</div>'; $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $this->mNewtitle . '</strong></div>' . '<div id="mw-diff-ntitle2">' . $sk->revUserTools($this->mNewRev, !$this->unhide) . " {$rollback}</div>" . '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment($this->mNewRev, !$diffOnly, !$this->unhide) . $rdel . '</div>' . '<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>'; # Check if this user can see the revisions $allowed = $this->mOldRev->userCan(Revision::DELETED_TEXT) && $this->mNewRev->userCan(Revision::DELETED_TEXT); # Check if one of the revisions is deleted/suppressed $deleted = $suppressed = false; if ($this->mOldRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // old revisions text is hidden if ($this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; } // also suppressed } if ($this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // new revisions text is hidden if ($this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; } // also suppressed } # If the diff cannot be shown due to a deleted revision, then output # the diff header and links to unhide (if available)... if ($deleted && (!$this->unhide || !$allowed)) { $this->showDiffStyle(); $multi = $this->getMultiNotice(); $wgOut->addHTML($this->addHeader('', $oldHeader, $newHeader, $multi)); if (!$allowed) { $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff'; # Give explanation for why revision is not visible $wgOut->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg)); } else { # Give explanation and add a link to view the diff... $link = $this->mTitle->getFullUrl(array('diff' => $this->mNewid, 'oldid' => $this->mOldid, 'unhide' => 1)); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; $wgOut->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg, $link)); } # Otherwise, output a regular diff... } else { # Add deletion notice if the user is viewing deleted content $notice = ''; if ($deleted) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; $notice = "<div id='mw-{$msg}' class='mw-warning plainlinks'>\n" . wfMsgExt($msg, 'parseinline') . "</div>\n"; } $this->showDiff($oldHeader, $newHeader, $notice); if (!$diffOnly) { $this->renderNewRevision(); } } wfProfileOut(__METHOD__); }
protected function formatRevisionRow($row, $earliestLiveTime, $remaining) { $rev = Revision::newFromArchiveRow($row, array('title' => $this->mTargetObj)); $revTextSize = ''; $ts = wfTimestamp(TS_MW, $row->ar_timestamp); // Build checkboxen... if ($this->mAllowed) { if ($this->mInvert) { if (in_array($ts, $this->mTargetTimestamp)) { $checkBox = Xml::check("ts{$ts}"); } else { $checkBox = Xml::check("ts{$ts}", true); } } else { $checkBox = Xml::check("ts{$ts}"); } } else { $checkBox = ''; } // Build page & diff links... $user = $this->getUser(); if ($this->mCanView) { $titleObj = $this->getPageTitle(); # Last link if (!$rev->userCan(Revision::DELETED_TEXT, $this->getUser())) { $pageLink = htmlspecialchars($this->getLanguage()->userTimeAndDate($ts, $user)); $last = $this->msg('diff')->escaped(); } elseif ($remaining > 0 || $earliestLiveTime && $ts > $earliestLiveTime) { $pageLink = $this->getPageLink($rev, $titleObj, $ts); $last = Linker::linkKnown($titleObj, $this->msg('diff')->escaped(), array(), array('target' => $this->mTargetObj->getPrefixedText(), 'timestamp' => $ts, 'diff' => 'prev')); } else { $pageLink = $this->getPageLink($rev, $titleObj, $ts); $last = $this->msg('diff')->escaped(); } } else { $pageLink = htmlspecialchars($this->getLanguage()->userTimeAndDate($ts, $user)); $last = $this->msg('diff')->escaped(); } // User links $userLink = Linker::revUserTools($rev); // Minor edit $minor = $rev->isMinor() ? ChangesList::flag('minor') : ''; // Revision text size $size = $row->ar_len; if (!is_null($size)) { $revTextSize = Linker::formatRevisionSize($size); } // Edit summary $comment = Linker::revComment($rev); // Tags $attribs = array(); list($tagSummary, $classes) = ChangeTags::formatSummaryRow($row->ts_tags, 'deletedhistory'); if ($classes) { $attribs['class'] = implode(' ', $classes); } // Revision delete links $revdlink = Linker::getRevDeleteLink($user, $rev, $this->mTargetObj); $revisionRow = $this->msg('undelete-revision-row')->rawParams($checkBox, $revdlink, $last, $pageLink, $userLink, $minor, $revTextSize, $comment, $tagSummary)->escaped(); return Xml::tags('li', $attribs, $revisionRow) . "\n"; }
public function showDiffPage($diffOnly = false) { # Allow frames except in certain special cases $out = $this->getOutput(); $out->allowClickjacking(); $out->setRobotPolicy('noindex,nofollow'); if (!$this->loadRevisionData()) { $this->showMissingRevision(); return; } $user = $this->getUser(); $permErrors = $this->mNewPage->getUserPermissionsErrors('read', $user); if ($this->mOldPage) { # mOldPage might not be set, see below. $permErrors = wfMergeErrorArrays($permErrors, $this->mOldPage->getUserPermissionsErrors('read', $user)); } if (count($permErrors)) { throw new PermissionsError('read', $permErrors); } $rollback = ''; $query = array(); # Carry over 'diffonly' param via navigation links if ($diffOnly != $user->getBoolOption('diffonly')) { $query['diffonly'] = $diffOnly; } # Cascade unhide param in links for easy deletion browsing if ($this->unhide) { $query['unhide'] = 1; } # Check if one of the revisions is deleted/suppressed $deleted = $suppressed = false; $allowed = $this->mNewRev->userCan(Revision::DELETED_TEXT, $user); $revisionTools = array(); # mOldRev is false if the difference engine is called with a "vague" query for # a diff between a version V and its previous version V' AND the version V # is the first version of that article. In that case, V' does not exist. if ($this->mOldRev === false) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; $oldHeader = ''; } else { Hooks::run('DiffViewHeader', array($this, $this->mOldRev, $this->mNewRev)); if ($this->mNewPage->equals($this->mOldPage)) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; } else { $out->setPageTitle($this->msg('difference-title-multipage', $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText())); $out->addSubtitle($this->msg('difference-multipage')); $samePage = false; } if ($samePage && $this->mNewPage->quickUserCan('edit', $user)) { if ($this->mNewRev->isCurrent() && $this->mNewPage->userCan('rollback', $user)) { $rollbackLink = Linker::generateRollback($this->mNewRev, $this->getContext()); if ($rollbackLink) { $out->preventClickjacking(); $rollback = '   ' . $rollbackLink; } } if (!$this->mOldRev->isDeleted(Revision::DELETED_TEXT) && !$this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $undoLink = Html::element('a', array('href' => $this->mNewPage->getLocalURL(array('action' => 'edit', 'undoafter' => $this->mOldid, 'undo' => $this->mNewid)), 'title' => Linker::titleAttrib('undo')), $this->msg('editundo')->text()); $revisionTools['mw-diff-undo'] = $undoLink; } } # Make "previous revision link" if ($samePage && $this->mOldRev->getPrevious()) { $prevlink = Linker::linkKnown($this->mOldPage, $this->msg('previousdiff')->escaped(), array('id' => 'differences-prevlink'), array('diff' => 'prev', 'oldid' => $this->mOldid) + $query); } else { $prevlink = ' '; } if ($this->mOldRev->isMinor()) { $oldminor = ChangesList::flag('minor'); } else { $oldminor = ''; } $ldel = $this->revisionDeleteLink($this->mOldRev); $oldRevisionHeader = $this->getRevisionHeader($this->mOldRev, 'complete'); $oldChangeTags = ChangeTags::formatSummaryRow($this->mOldTags, 'diff'); $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader . '</strong></div>' . '<div id="mw-diff-otitle2">' . Linker::revUserTools($this->mOldRev, !$this->unhide) . '</div>' . '<div id="mw-diff-otitle3">' . $oldminor . Linker::revComment($this->mOldRev, !$diffOnly, !$this->unhide) . $ldel . '</div>' . '<div id="mw-diff-otitle5">' . $oldChangeTags[0] . '</div>' . '<div id="mw-diff-otitle4">' . $prevlink . '</div>'; if ($this->mOldRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // old revisions text is hidden if ($this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; // also suppressed } } # Check if this user can see the revisions if (!$this->mOldRev->userCan(Revision::DELETED_TEXT, $user)) { $allowed = false; } } # Make "next revision link" # Skip next link on the top revision if ($samePage && !$this->mNewRev->isCurrent()) { $nextlink = Linker::linkKnown($this->mNewPage, $this->msg('nextdiff')->escaped(), array('id' => 'differences-nextlink'), array('diff' => 'next', 'oldid' => $this->mNewid) + $query); } else { $nextlink = ' '; } if ($this->mNewRev->isMinor()) { $newminor = ChangesList::flag('minor'); } else { $newminor = ''; } # Handle RevisionDelete links... $rdel = $this->revisionDeleteLink($this->mNewRev); # Allow extensions to define their own revision tools Hooks::run('DiffRevisionTools', array($this->mNewRev, &$revisionTools, $this->mOldRev, $user)); $formattedRevisionTools = array(); // Put each one in parentheses (poor man's button) foreach ($revisionTools as $key => $tool) { $toolClass = is_string($key) ? $key : 'mw-diff-tool'; $element = Html::rawElement('span', array('class' => $toolClass), $this->msg('parentheses')->rawParams($tool)->escaped()); $formattedRevisionTools[] = $element; } $newRevisionHeader = $this->getRevisionHeader($this->mNewRev, 'complete') . ' ' . implode(' ', $formattedRevisionTools); $newChangeTags = ChangeTags::formatSummaryRow($this->mNewTags, 'diff'); $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' . '<div id="mw-diff-ntitle2">' . Linker::revUserTools($this->mNewRev, !$this->unhide) . " {$rollback}</div>" . '<div id="mw-diff-ntitle3">' . $newminor . Linker::revComment($this->mNewRev, !$diffOnly, !$this->unhide) . $rdel . '</div>' . '<div id="mw-diff-ntitle5">' . $newChangeTags[0] . '</div>' . '<div id="mw-diff-ntitle4">' . $nextlink . $this->markPatrolledLink() . '</div>'; if ($this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // new revisions text is hidden if ($this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; // also suppressed } } # If the diff cannot be shown due to a deleted revision, then output # the diff header and links to unhide (if available)... if ($deleted && (!$this->unhide || !$allowed)) { $this->showDiffStyle(); $multi = $this->getMultiNotice(); $out->addHTML($this->addHeader('', $oldHeader, $newHeader, $multi)); if (!$allowed) { $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff'; # Give explanation for why revision is not visible $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg)); } else { # Give explanation and add a link to view the diff... $query = $this->getRequest()->appendQueryValue('unhide', '1'); $link = $this->getTitle()->getFullURL($query); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg, $link)); } # Otherwise, output a regular diff... } else { # Add deletion notice if the user is viewing deleted content $notice = ''; if ($deleted) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; $notice = "<div id='mw-{$msg}' class='mw-warning plainlinks'>\n" . $this->msg($msg)->parse() . "</div>\n"; } $this->showDiff($oldHeader, $newHeader, $notice); if (!$diffOnly) { $this->renderNewRevision(); } } }
/** * Returns a row from the history printout. * * @todo document some more, and maybe clean up the code (some params redundant?) * * @param stdClass $row The database row corresponding to the previous line. * @param mixed $next The database row corresponding to the next line * (chronologically previous) * @param bool|string $notificationtimestamp * @param bool $latest Whether this row corresponds to the page's latest revision. * @param bool $firstInList Whether this row corresponds to the first * displayed on this history page. * @return string HTML output for the row */ function historyLine($row, $next, $notificationtimestamp = false, $latest = false, $firstInList = false) { $rev = new Revision($row); $rev->setTitle($this->getTitle()); if (is_object($next)) { $prevRev = new Revision($next); $prevRev->setTitle($this->getTitle()); } else { $prevRev = null; } $curlink = $this->curLink($rev, $latest); $lastlink = $this->lastLink($rev, $next); $curLastlinks = $curlink . $this->historyPage->message['pipe-separator'] . $lastlink; $histLinks = Html::rawElement('span', array('class' => 'mw-history-histlinks'), $this->msg('parentheses')->rawParams($curLastlinks)->escaped()); $diffButtons = $this->diffButtons($rev, $firstInList); $s = $histLinks . $diffButtons; $link = $this->revLink($rev); $classes = array(); $del = ''; $user = $this->getUser(); // Show checkboxes for each revision if ($user->isAllowed('deleterevision')) { $this->preventClickjacking(); // If revision was hidden from sysops, disable the checkbox if (!$rev->userCan(Revision::DELETED_RESTRICTED, $user)) { $del = Xml::check('deleterevisions', false, array('disabled' => 'disabled')); // Otherwise, enable the checkbox... } else { $del = Xml::check('showhiderevisions', false, array('name' => 'ids[' . $rev->getId() . ']')); } // User can only view deleted revisions... } elseif ($rev->getVisibility() && $user->isAllowed('deletedhistory')) { // If revision was hidden from sysops, disable the link if (!$rev->userCan(Revision::DELETED_RESTRICTED, $user)) { $del = Linker::revDeleteLinkDisabled(false); // Otherwise, show the link... } else { $query = array('type' => 'revision', 'target' => $this->getTitle()->getPrefixedDBkey(), 'ids' => $rev->getId()); $del .= Linker::revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), false); } } if ($del) { $s .= " {$del} "; } $lang = $this->getLanguage(); $dirmark = $lang->getDirMark(); $s .= " {$link}"; $s .= $dirmark; $s .= " <span class='history-user'>" . Linker::revUserTools($rev, true) . "</span>"; $s .= $dirmark; if ($rev->isMinor()) { $s .= ' ' . ChangesList::flag('minor'); } # Sometimes rev_len isn't populated if ($rev->getSize() !== null) { # Size is always public data $prevSize = isset($this->parentLens[$row->rev_parent_id]) ? $this->parentLens[$row->rev_parent_id] : 0; $sDiff = ChangesList::showCharacterDifference($prevSize, $rev->getSize()); $fSize = Linker::formatRevisionSize($rev->getSize()); $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "{$fSize} {$sDiff}"; } # Text following the character difference is added just before running hooks $s2 = Linker::revComment($rev, false, true); if ($notificationtimestamp && $row->rev_timestamp >= $notificationtimestamp) { $s2 .= ' <span class="updatedmarker">' . $this->msg('updatedmarker')->escaped() . '</span>'; $classes[] = 'mw-history-line-updated'; } $tools = array(); # Rollback and undo links if ($prevRev && $this->getTitle()->quickUserCan('edit', $user)) { if ($latest && $this->getTitle()->quickUserCan('rollback', $user)) { // Get a rollback link without the brackets $rollbackLink = Linker::generateRollback($rev, $this->getContext(), array('verify', 'noBrackets')); if ($rollbackLink) { $this->preventClickjacking(); $tools[] = $rollbackLink; } } if (!$rev->isDeleted(Revision::DELETED_TEXT) && !$prevRev->isDeleted(Revision::DELETED_TEXT)) { # Create undo tooltip for the first (=latest) line only $undoTooltip = $latest ? array('title' => $this->msg('tooltip-undo')->text()) : array(); $undolink = Linker::linkKnown($this->getTitle(), $this->msg('editundo')->escaped(), $undoTooltip, array('action' => 'edit', 'undoafter' => $prevRev->getId(), 'undo' => $rev->getId())); $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; } } // Allow extension to add their own links here wfRunHooks('HistoryRevisionTools', array($rev, &$tools)); if ($tools) { $s2 .= ' ' . $this->msg('parentheses')->rawParams($lang->pipeList($tools))->escaped(); } # Tags list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'history'); $classes = array_merge($classes, $newClasses); if ($tagSummary !== '') { $s2 .= " {$tagSummary}"; } # Include separator between character difference and following text if ($s2 !== '') { $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2; } wfRunHooks('PageHistoryLineEnding', array($this, &$row, &$s, &$classes)); $attribs = array(); if ($classes) { $attribs['class'] = implode(' ', $classes); } return Xml::tags('li', $attribs, $s) . "\n"; }
function showDiffPage($diffOnly = false) { wfProfileIn(__METHOD__); # Allow frames except in certain special cases $out = $this->getOutput(); $out->allowClickjacking(); $out->setRobotPolicy('noindex,nofollow'); if (!$this->loadRevisionData()) { $this->showMissingRevision(); wfProfileOut(__METHOD__); return; } $user = $this->getUser(); $permErrors = $this->mNewPage->getUserPermissionsErrors('read', $user); if ($this->mOldPage) { # mOldPage might not be set, see below. $permErrors = wfMergeErrorArrays($permErrors, $this->mOldPage->getUserPermissionsErrors('read', $user)); } if (count($permErrors)) { wfProfileOut(__METHOD__); throw new PermissionsError('read', $permErrors); } # If external diffs are enabled both globally and for the user, # we'll use the application/x-external-editor interface to call # an external diff tool like kompare, kdiff3, etc. if (ExternalEdit::useExternalEngine($this->getContext(), 'diff')) { //TODO: come up with a good solution for non-text content here. // at least, the content format needs to be passed to the client somehow. // Currently, action=raw will just fail for non-text content. $urls = array('File' => array('Extension' => 'wiki', 'URL' => $this->mNewPage->getCanonicalURL(array('action' => 'raw', 'oldid' => $this->mOldid))), 'File2' => array('Extension' => 'wiki', 'URL' => $this->mNewPage->getCanonicalURL(array('action' => 'raw', 'oldid' => $this->mNewid)))); $externalEditor = new ExternalEdit($this->getContext(), $urls); $externalEditor->execute(); wfProfileOut(__METHOD__); return; } $rollback = ''; $undoLink = ''; $query = array(); # Carry over 'diffonly' param via navigation links if ($diffOnly != $user->getBoolOption('diffonly')) { $query['diffonly'] = $diffOnly; } # Cascade unhide param in links for easy deletion browsing if ($this->unhide) { $query['unhide'] = 1; } # Check if one of the revisions is deleted/suppressed $deleted = $suppressed = false; $allowed = $this->mNewRev->userCan(Revision::DELETED_TEXT, $user); # mOldRev is false if the difference engine is called with a "vague" query for # a diff between a version V and its previous version V' AND the version V # is the first version of that article. In that case, V' does not exist. if ($this->mOldRev === false) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; $oldHeader = ''; } else { wfRunHooks('DiffViewHeader', array($this, $this->mOldRev, $this->mNewRev)); $sk = $this->getSkin(); if (method_exists($sk, 'suppressQuickbar')) { $sk->suppressQuickbar(); } if ($this->mNewPage->equals($this->mOldPage)) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; } else { $out->setPageTitle($this->msg('difference-title-multipage', $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText())); $out->addSubtitle($this->msg('difference-multipage')); $samePage = false; } if ($samePage && $this->mNewPage->quickUserCan('edit', $user)) { if ($this->mNewRev->isCurrent() && $this->mNewPage->userCan('rollback', $user)) { $out->preventClickjacking(); $rollback = '   ' . Linker::generateRollback($this->mNewRev, $this->getContext()); } if (!$this->mOldRev->isDeleted(Revision::DELETED_TEXT) && !$this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $undoLink = ' ' . $this->msg('parentheses')->rawParams(Html::element('a', array('href' => $this->mNewPage->getLocalUrl(array('action' => 'edit', 'undoafter' => $this->mOldid, 'undo' => $this->mNewid)), 'title' => Linker::titleAttrib('undo')), $this->msg('editundo')->text()))->escaped(); } } # Make "previous revision link" if ($samePage && $this->mOldRev->getPrevious()) { $prevlink = Linker::linkKnown($this->mOldPage, $this->msg('previousdiff')->escaped(), array('id' => 'differences-prevlink'), array('diff' => 'prev', 'oldid' => $this->mOldid) + $query); } else { $prevlink = ' '; } if ($this->mOldRev->isMinor()) { $oldminor = ChangesList::flag('minor'); } else { $oldminor = ''; } $ldel = $this->revisionDeleteLink($this->mOldRev); $oldRevisionHeader = $this->getRevisionHeader($this->mOldRev, 'complete'); $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader . '</strong></div>' . '<div id="mw-diff-otitle2">' . Linker::revUserTools($this->mOldRev, !$this->unhide) . '</div>' . '<div id="mw-diff-otitle3">' . $oldminor . Linker::revComment($this->mOldRev, !$diffOnly, !$this->unhide) . $ldel . '</div>' . '<div id="mw-diff-otitle4">' . $prevlink . '</div>'; if ($this->mOldRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // old revisions text is hidden if ($this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; // also suppressed } } # Check if this user can see the revisions if (!$this->mOldRev->userCan(Revision::DELETED_TEXT, $user)) { $allowed = false; } } # Make "next revision link" # Skip next link on the top revision if ($samePage && !$this->mNewRev->isCurrent()) { $nextlink = Linker::linkKnown($this->mNewPage, $this->msg('nextdiff')->escaped(), array('id' => 'differences-nextlink'), array('diff' => 'next', 'oldid' => $this->mNewid) + $query); } else { $nextlink = ' '; } if ($this->mNewRev->isMinor()) { $newminor = ChangesList::flag('minor'); } else { $newminor = ''; } # Handle RevisionDelete links... $rdel = $this->revisionDeleteLink($this->mNewRev); $newRevisionHeader = $this->getRevisionHeader($this->mNewRev, 'complete') . $undoLink; $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' . '<div id="mw-diff-ntitle2">' . Linker::revUserTools($this->mNewRev, !$this->unhide) . " {$rollback}</div>" . '<div id="mw-diff-ntitle3">' . $newminor . Linker::revComment($this->mNewRev, !$diffOnly, !$this->unhide) . $rdel . '</div>' . '<div id="mw-diff-ntitle4">' . $nextlink . $this->markPatrolledLink() . '</div>'; if ($this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // new revisions text is hidden if ($this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; } // also suppressed } # If the diff cannot be shown due to a deleted revision, then output # the diff header and links to unhide (if available)... if ($deleted && (!$this->unhide || !$allowed)) { $this->showDiffStyle(); $multi = $this->getMultiNotice(); $out->addHTML($this->addHeader('', $oldHeader, $newHeader, $multi)); if (!$allowed) { $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff'; # Give explanation for why revision is not visible $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg)); } else { # Give explanation and add a link to view the diff... $link = $this->getTitle()->getFullUrl($this->getRequest()->appendQueryValue('unhide', '1', true)); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg, $link)); } # Otherwise, output a regular diff... } else { # Add deletion notice if the user is viewing deleted content $notice = ''; if ($deleted) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; $notice = "<div id='mw-{$msg}' class='mw-warning plainlinks'>\n" . $this->msg($msg)->parse() . "</div>\n"; } $this->showDiff($oldHeader, $newHeader, $notice); if (!$diffOnly) { $this->renderNewRevision(); } } wfProfileOut(__METHOD__); }
/** * Generates each row in the contributions list. * * Contributions which are marked "top" are currently on top of the history. * For these contributions, a [rollback] link is shown for users with sysop * privileges. The rollback link restores the most recent version that was not * written by the target user. * * @todo This would probably look a lot nicer in a table. * @param $row * @return string */ function formatRow( $row ) { wfProfileIn( __METHOD__ ); $page = Title::makeTitle( $row->ar_namespace, $row->ar_title ); $rev = new Revision( array( 'title' => $page, 'id' => $row->ar_rev_id, 'comment' => $row->ar_comment, 'user' => $row->ar_user, 'user_text' => $row->ar_user_text, 'timestamp' => $row->ar_timestamp, 'minor_edit' => $row->ar_minor_edit, 'deleted' => $row->ar_deleted, ) ); $undelete = SpecialPage::getTitleFor( 'Undelete' ); $logs = SpecialPage::getTitleFor( 'Log' ); $dellog = Linker::linkKnown( $logs, $this->messages['deletionlog'], array(), array( 'type' => 'delete', 'page' => $page->getPrefixedText() ) ); $reviewlink = Linker::linkKnown( SpecialPage::getTitleFor( 'Undelete', $page->getPrefixedDBkey() ), $this->messages['undeleteviewlink'] ); $user = $this->getUser(); if ( $user->isAllowed( 'deletedtext' ) ) { $last = Linker::linkKnown( $undelete, $this->messages['diff'], array(), array( 'target' => $page->getPrefixedText(), 'timestamp' => $rev->getTimestamp(), 'diff' => 'prev' ) ); } else { $last = $this->messages['diff']; } $comment = Linker::revComment( $rev ); $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $user ); $date = htmlspecialchars( $date ); if ( !$user->isAllowed( 'undelete' ) || !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { $link = $date; // unusable link } else { $link = Linker::linkKnown( $undelete, $date, array( 'class' => 'mw-changeslist-date' ), array( 'target' => $page->getPrefixedText(), 'timestamp' => $rev->getTimestamp() ) ); } // Style deleted items if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { $link = '<span class="history-deleted">' . $link . '</span>'; } $pagelink = Linker::link( $page, null, array( 'class' => 'mw-changeslist-title' ) ); if ( $rev->isMinor() ) { $mflag = ChangesList::flag( 'minor' ); } else { $mflag = ''; } // Revision delete link $del = Linker::getRevDeleteLink( $user, $rev, $page ); if ( $del ) { $del .= ' '; } $tools = Html::rawElement( 'span', array( 'class' => 'mw-deletedcontribs-tools' ), $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( array( $last, $dellog, $reviewlink ) ) )->escaped() ); $separator = '<span class="mw-changeslist-separator">. .</span>'; $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment}"; # Denote if username is redacted for this edit if ( $rev->isDeleted( Revision::DELETED_USER ) ) { $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>"; } $ret = Html::rawElement( 'li', array(), $ret ) . "\n"; wfProfileOut( __METHOD__ ); return $ret; }
/** * Generates each row in the contributions list. * * Contributions which are marked "top" are currently on top of the history. * For these contributions, a [rollback] link is shown for users with roll- * back privileges. The rollback link restores the most recent version that * was not written by the target user. * * @todo This would probably look a lot nicer in a table. * @param $row * @return string */ function formatRow($row) { wfProfileIn(__METHOD__); $ret = ''; $classes = array(); /* * There may be more than just revision rows. To make sure that we'll only be processing * revisions here, let's _try_ to build a revision out of our row (without displaying * notices though) and then trying to grab data from the built object. If we succeed, * we're definitely dealing with revision data and we may proceed, if not, we'll leave it * to extensions to subscribe to the hook to parse the row. */ wfSuppressWarnings(); $rev = new Revision($row); $validRevision = $rev->getParentId() !== null; wfRestoreWarnings(); if ($validRevision) { $classes = array(); $page = Title::newFromRow($row); $link = Linker::link($page, htmlspecialchars($page->getPrefixedText()), array('class' => 'mw-contributions-title'), $page->isRedirect() ? array('redirect' => 'no') : array()); # Mark current revisions $topmarktext = ''; $user = $this->getUser(); if ($row->rev_id == $row->page_latest) { $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>'; # Add rollback link if (!$row->page_is_new && $page->quickUserCan('rollback', $user) && $page->quickUserCan('edit', $user)) { $this->preventClickjacking(); $topmarktext .= ' ' . Linker::generateRollback($rev, $this->getContext()); } } # Is there a visible previous revision? if ($rev->userCan(Revision::DELETED_TEXT, $user) && $rev->getParentId() !== 0) { $difftext = Linker::linkKnown($page, $this->messages['diff'], array(), array('diff' => 'prev', 'oldid' => $row->rev_id)); } else { $difftext = $this->messages['diff']; } $histlink = Linker::linkKnown($page, $this->messages['hist'], array(), array('action' => 'history')); if ($row->rev_parent_id === null) { // For some reason rev_parent_id isn't populated for this row. // Its rumoured this is true on wikipedia for some revisions (bug 34922). // Next best thing is to have the total number of bytes. $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . Linker::formatRevisionSize($row->rev_len) . ' <span class="mw-changeslist-separator">. .</span> '; } else { $parentLen = isset($this->mParentLens[$row->rev_parent_id]) ? $this->mParentLens[$row->rev_parent_id] : 0; $chardiff = ' <span class="mw-changeslist-separator">. .</span> ' . ChangesList::showCharacterDifference($parentLen, $row->rev_len, $this->getContext()) . ' <span class="mw-changeslist-separator">. .</span> '; } $lang = $this->getLanguage(); $comment = $lang->getDirMark() . Linker::revComment($rev, false, true); $date = $lang->userTimeAndDate($row->rev_timestamp, $user); if ($rev->userCan(Revision::DELETED_TEXT, $user)) { $d = Linker::linkKnown($page, htmlspecialchars($date), array('class' => 'mw-changeslist-date'), array('oldid' => intval($row->rev_id))); } else { $d = htmlspecialchars($date); } if ($rev->isDeleted(Revision::DELETED_TEXT)) { $d = '<span class="history-deleted">' . $d . '</span>'; } # Show user names for /newbies as there may be different users. # Note that we already excluded rows with hidden user names. if ($this->contribs == 'newbie') { $userlink = ' . . ' . Linker::userLink($rev->getUser(), $rev->getUserText()); $userlink .= ' ' . $this->msg('parentheses')->rawParams(Linker::userTalkLink($rev->getUser(), $rev->getUserText()))->escaped() . ' '; } else { $userlink = ''; } if ($rev->getParentId() === 0) { $nflag = ChangesList::flag('newpage'); } else { $nflag = ''; } if ($rev->isMinor()) { $mflag = ChangesList::flag('minor'); } else { $mflag = ''; } $del = Linker::getRevDeleteLink($user, $rev, $page); if ($del !== '') { $del .= ' '; } $diffHistLinks = $this->msg('parentheses')->rawParams($difftext . $this->messages['pipe-separator'] . $histlink)->escaped(); $ret = "{$del}{$d} {$diffHistLinks}{$chardiff}{$nflag}{$mflag} {$link}{$userlink} {$comment} {$topmarktext}"; # Denote if username is redacted for this edit if ($rev->isDeleted(Revision::DELETED_USER)) { $ret .= " <strong>" . $this->msg('rev-deleted-user-contribs')->escaped() . "</strong>"; } # Tags, if any. list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'contributions'); $classes = array_merge($classes, $newClasses); $ret .= " {$tagSummary}"; } // Let extensions add data wfRunHooks('ContributionsLineEnding', array($this, &$ret, $row, &$classes)); $classes = implode(' ', $classes); $ret = "<li class=\"{$classes}\">{$ret}</li>\n"; wfProfileOut(__METHOD__); return $ret; }
/** * Generates each row in the contributions list. * * Contributions which are marked "top" are currently on top of the history. * For these contributions, a [rollback] link is shown for users with roll- * back privileges. The rollback link restores the most recent version that * was not written by the target user. * * @todo This would probably look a lot nicer in a table. * @param object $row * @return string */ function formatRow($row) { $ret = ''; $classes = []; /* * There may be more than just revision rows. To make sure that we'll only be processing * revisions here, let's _try_ to build a revision out of our row (without displaying * notices though) and then trying to grab data from the built object. If we succeed, * we're definitely dealing with revision data and we may proceed, if not, we'll leave it * to extensions to subscribe to the hook to parse the row. */ MediaWiki\suppressWarnings(); try { $rev = new Revision($row); $validRevision = (bool) $rev->getId(); } catch (Exception $e) { $validRevision = false; } MediaWiki\restoreWarnings(); if ($validRevision) { $classes = []; $page = Title::newFromRow($row); $link = Linker::link($page, htmlspecialchars($page->getPrefixedText()), ['class' => 'mw-contributions-title'], $page->isRedirect() ? ['redirect' => 'no'] : []); # Mark current revisions $topmarktext = ''; $user = $this->getUser(); if ($row->rev_id === $row->page_latest) { $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>'; $classes[] = 'mw-contributions-current'; # Add rollback link if (!$row->page_is_new && $page->quickUserCan('rollback', $user) && $page->quickUserCan('edit', $user)) { $this->preventClickjacking(); $topmarktext .= ' ' . Linker::generateRollback($rev, $this->getContext()); } } # Is there a visible previous revision? if ($rev->userCan(Revision::DELETED_TEXT, $user) && $rev->getParentId() !== 0) { $difftext = Linker::linkKnown($page, $this->messages['diff'], [], ['diff' => 'prev', 'oldid' => $row->rev_id]); } else { $difftext = $this->messages['diff']; } $histlink = Linker::linkKnown($page, $this->messages['hist'], [], ['action' => 'history']); if ($row->rev_parent_id === null) { // For some reason rev_parent_id isn't populated for this row. // Its rumoured this is true on wikipedia for some revisions (bug 34922). // Next best thing is to have the total number of bytes. $chardiff = ' <span class="mw-changeslist-separator">. .</span> '; $chardiff .= Linker::formatRevisionSize($row->rev_len); $chardiff .= ' <span class="mw-changeslist-separator">. .</span> '; } else { $parentLen = 0; if (isset($this->mParentLens[$row->rev_parent_id])) { $parentLen = $this->mParentLens[$row->rev_parent_id]; } $chardiff = ' <span class="mw-changeslist-separator">. .</span> '; $chardiff .= ChangesList::showCharacterDifference($parentLen, $row->rev_len, $this->getContext()); $chardiff .= ' <span class="mw-changeslist-separator">. .</span> '; } $lang = $this->getLanguage(); $comment = $lang->getDirMark() . Linker::revComment($rev, false, true); $date = $lang->userTimeAndDate($row->rev_timestamp, $user); if ($rev->userCan(Revision::DELETED_TEXT, $user)) { $d = Linker::linkKnown($page, htmlspecialchars($date), ['class' => 'mw-changeslist-date'], ['oldid' => intval($row->rev_id)]); } else { $d = htmlspecialchars($date); } if ($rev->isDeleted(Revision::DELETED_TEXT)) { $d = '<span class="history-deleted">' . $d . '</span>'; } # Show user names for /newbies as there may be different users. # Note that we already excluded rows with hidden user names. if ($this->contribs == 'newbie') { $userlink = ' . . ' . $lang->getDirMark() . Linker::userLink($rev->getUser(), $rev->getUserText()); $userlink .= ' ' . $this->msg('parentheses')->rawParams(Linker::userTalkLink($rev->getUser(), $rev->getUserText()))->escaped() . ' '; } else { $userlink = ''; } $flags = []; if ($rev->getParentId() === 0) { $flags[] = ChangesList::flag('newpage'); } if ($rev->isMinor()) { $flags[] = ChangesList::flag('minor'); } $del = Linker::getRevDeleteLink($user, $rev, $page); if ($del !== '') { $del .= ' '; } $diffHistLinks = $this->msg('parentheses')->rawParams($difftext . $this->messages['pipe-separator'] . $histlink)->escaped(); # Tags, if any. list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'contributions', $this->getContext()); $classes = array_merge($classes, $newClasses); Hooks::run('SpecialContributions::formatRow::flags', [$this->getContext(), $row, &$flags]); $templateParams = ['del' => $del, 'timestamp' => $d, 'diffHistLinks' => $diffHistLinks, 'charDifference' => $chardiff, 'flags' => $flags, 'articleLink' => $link, 'userlink' => $userlink, 'logText' => $comment, 'topmarktext' => $topmarktext, 'tagSummary' => $tagSummary]; # Denote if username is redacted for this edit if ($rev->isDeleted(Revision::DELETED_USER)) { $templateParams['rev-deleted-user-contribs'] = $this->msg('rev-deleted-user-contribs')->escaped(); } $templateParser = new TemplateParser(); $ret = $templateParser->processTemplate('SpecialContributionsLine', $templateParams); } // Let extensions add data Hooks::run('ContributionsLineEnding', [$this, &$ret, $row, &$classes]); // TODO: Handle exceptions in the catch block above. Do any extensions rely on // receiving empty rows? if ($classes === [] && $ret === '') { wfDebug("Dropping Special:Contribution row that could not be formatted\n"); return "<!-- Could not format Special:Contribution row. -->\n"; } // FIXME: The signature of the ContributionsLineEnding hook makes it // very awkward to move this LI wrapper into the template. return Html::rawElement('li', ['class' => $classes], $ret) . "\n"; }
/** * Returns a row from the history printout. * * @todo document some more, and maybe clean up the code (some params redundant?) * * @param $row Object: the database row corresponding to the previous line. * @param $next Mixed: the database row corresponding to the next line. (chronologically previous) * @param $notificationtimestamp * @param $latest Boolean: whether this row corresponds to the page's latest revision. * @param $firstInList Boolean: whether this row corresponds to the first displayed on this history page. * @return String: HTML output for the row */ function historyLine($row, $next, $notificationtimestamp = false, $latest = false, $firstInList = false) { $rev = new Revision($row); $rev->setTitle($this->getTitle()); if (is_object($next)) { $prevRev = new Revision($next); $prevRev->setTitle($this->getTitle()); } else { $prevRev = null; } $curlink = $this->curLink($rev, $latest); $lastlink = $this->lastLink($rev, $next); $diffButtons = $this->diffButtons($rev, $firstInList); $histLinks = Html::rawElement('span', array('class' => 'mw-history-histlinks'), '(' . $curlink . $this->historyPage->message['pipe-separator'] . $lastlink . ') '); $s = $histLinks . $diffButtons; $link = $this->revLink($rev); $classes = array(); $del = ''; $user = $this->getUser(); // Show checkboxes for each revision if ($user->isAllowed('deleterevision')) { $this->preventClickjacking(); // If revision was hidden from sysops, disable the checkbox if (!$rev->userCan(Revision::DELETED_RESTRICTED, $user)) { $del = Xml::check('deleterevisions', false, array('disabled' => 'disabled')); // Otherwise, enable the checkbox... } else { $del = Xml::check('showhiderevisions', false, array('name' => 'ids[' . $rev->getId() . ']')); } // User can only view deleted revisions... } elseif ($rev->getVisibility() && $user->isAllowed('deletedhistory')) { // If revision was hidden from sysops, disable the link if (!$rev->userCan(Revision::DELETED_RESTRICTED, $user)) { $cdel = Linker::revDeleteLinkDisabled(false); // Otherwise, show the link... } else { $query = array('type' => 'revision', 'target' => $this->getTitle()->getPrefixedDbkey(), 'ids' => $rev->getId()); $del .= Linker::revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), false); } } if ($del) { $s .= " {$del} "; } $lang = $this->getLanguage(); $dirmark = $lang->getDirMark(); $s .= " {$link}"; $s .= $dirmark; $s .= " <span class='history-user'>" . Linker::revUserTools($rev, true) . "</span>"; $s .= $dirmark; if ($rev->isMinor()) { $s .= ' ' . ChangesList::flag('minor'); } # Size is always public data $prevSize = $prevRev ? $prevRev->getSize() : 0; $sDiff = ChangesList::showCharacterDifference($prevSize, $rev->getSize()); $fSize = Linker::formatRevisionSize($rev->getSize()); $s .= " . . {$fSize} {$sDiff} . . "; $s .= Linker::revComment($rev, false, true); if ($notificationtimestamp && $row->rev_timestamp >= $notificationtimestamp) { $s .= ' <span class="updatedmarker">' . $this->msg('updatedmarker')->escaped() . '</span>'; } $tools = array(); # Rollback and undo links if ($prevRev && !count($this->getTitle()->getUserPermissionsErrors('edit', $this->getUser()))) { if ($latest && !count($this->getTitle()->getUserPermissionsErrors('rollback', $this->getUser()))) { $this->preventClickjacking(); $tools[] = '<span class="mw-rollback-link">' . Linker::buildRollbackLink($rev) . '</span>'; } if (!$rev->isDeleted(Revision::DELETED_TEXT) && !$prevRev->isDeleted(Revision::DELETED_TEXT)) { # Create undo tooltip for the first (=latest) line only $undoTooltip = $latest ? array('title' => $this->msg('tooltip-undo')->text()) : array(); $undolink = Linker::linkKnown($this->getTitle(), $this->msg('editundo')->escaped(), $undoTooltip, array('action' => 'edit', 'undoafter' => $prevRev->getId(), 'undo' => $rev->getId())); $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; } } if ($tools) { $s .= ' (' . $lang->pipeList($tools) . ')'; } # Tags list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'history'); $classes = array_merge($classes, $newClasses); $s .= " {$tagSummary}"; wfRunHooks('PageHistoryLineEnding', array($this, &$row, &$s, &$classes)); $attribs = array(); if ($classes) { $attribs['class'] = implode(' ', $classes); } return Xml::tags('li', $attribs, $s) . "\n"; }
/** * Renders an item in the feed * @param MWTimestamp $ts The time the edit occurred * @param string $diffLink url to the diff for the edit * @param string $username The username of the user that made the edit (absent if anonymous) * @param string $comment The edit summary * @param Title|null $title The title of the page that was edited * @param bool $isAnon Is the edit anonymous? * @param int|null $bytes Net number of bytes changed or null if not applicable * @param bool $isMinor Is the edit minor? * @return string HTML code * * @todo FIXME: use an array as an argument? */ protected function renderFeedItemHtml($ts, $diffLink = '', $username = '', $comment = '', $title = null, $isAnon = false, $bytes = 0, $isMinor = false) { $output = $this->getOutput(); $user = $this->getUser(); $lang = $this->getLanguage(); if ($isAnon) { $usernameClass = MobileUI::iconClass('anonymous', 'before', 'icon-16px mw-mf-user mw-mf-anon'); } else { $usernameClass = MobileUI::iconClass('user', 'before', 'icon-16px mw-mf-user'); } $html = Html::openElement('li', array('class' => 'page-summary')); if ($diffLink) { $html .= Html::openElement('a', array('href' => $diffLink, 'class' => 'title')); } else { $html .= Html::openElement('div', array('class' => 'title')); } if ($title) { $html .= Html::element('h3', array(), $title->getPrefixedText()); } if ($username && $this->showUsername) { $html .= Html::element('p', array('class' => $usernameClass), $username); } $html .= Html::element('p', array('class' => 'edit-summary component truncated-text multi-line two-line'), $comment); if ($isMinor) { $html .= ChangesList::flag('minor'); } $html .= Html::openElement('div', array('class' => 'list-thumb')) . Html::element('p', array('class' => 'timestamp'), $lang->userTime($ts, $user)); if ($bytes) { $formattedBytes = $lang->formatNum($bytes); if ($bytes > 0) { $formattedBytes = '+' . $formattedBytes; $bytesClass = 'mw-mf-bytesadded'; } else { $bytesClass = 'mw-mf-bytesremoved'; } $html .= Html::element('p', array('class' => $bytesClass, 'dir' => 'ltr'), $formattedBytes); } $html .= Html::closeElement('div'); if ($diffLink) { $html .= Html::closeElement('a'); } else { $html .= Html::closeElement('div'); } $html .= Html::closeElement('li'); $output->addHtml($html); }
/** * Generates each row in the contributions list. * * Contributions which are marked "top" are currently on top of the history. * For these contributions, a [rollback] link is shown for users with roll- * back privileges. The rollback link restores the most recent version that * was not written by the target user. * * @todo This would probably look a lot nicer in a table. */ function formatRow($row) { global $wgUser, $wgLang, $wgContLang; wfProfileIn(__METHOD__); $sk = $this->getSkin(); $rev = new Revision($row); $classes = array(); $page = Title::newFromRow($row); $page->resetArticleId($row->rev_page); // use process cache $link = $sk->link($page, htmlspecialchars($page->getPrefixedText()), array(), $page->isRedirect() ? array('redirect' => 'no') : array()); # Mark current revisions $difftext = $topmarktext = ''; if ($row->rev_id == $row->page_latest) { $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>'; # Add rollback link if (!$row->page_is_new && $page->quickUserCan('rollback') && $page->quickUserCan('edit')) { $this->preventClickjacking(); $topmarktext .= ' ' . $sk->generateRollback($rev); } } # Is there a visible previous revision? if ($rev->userCan(Revision::DELETED_TEXT) && $rev->getParentId() !== 0) { $difftext = $sk->linkKnown($page, $this->messages['diff'], array(), array('diff' => 'prev', 'oldid' => $row->rev_id)); } else { $difftext = $this->messages['diff']; } $histlink = $sk->linkKnown($page, $this->messages['hist'], array(), array('action' => 'history')); $comment = $wgContLang->getDirMark() . $sk->revComment($rev, false, true); $date = $wgLang->timeanddate(wfTimestamp(TS_MW, $row->rev_timestamp), true); if ($rev->isDeleted(Revision::DELETED_TEXT)) { $d = '<span class="history-deleted">' . $date . '</span>'; } else { $d = $sk->linkKnown($page, htmlspecialchars($date), array(), array('oldid' => intval($row->rev_id))); } if ($this->target == 'newbies') { $userlink = ' . . ' . $sk->userLink($row->rev_user, $row->rev_user_text); $userlink .= ' ' . wfMsg('parentheses', $sk->userTalkLink($row->rev_user, $row->rev_user_text)) . ' '; } else { $userlink = ''; } if ($rev->getParentId() === 0) { $nflag = ChangesList::flag('newpage'); } else { $nflag = ''; } if ($rev->isMinor()) { $mflag = ChangesList::flag('minor'); } else { $mflag = ''; } // Don't show useless link to people who cannot hide revisions $canHide = $wgUser->isAllowed('deleterevision'); if ($canHide || $rev->getVisibility() && $wgUser->isAllowed('deletedhistory')) { if (!$rev->userCan(Revision::DELETED_RESTRICTED)) { $del = $this->mSkin->revDeleteLinkDisabled($canHide); // revision was hidden from sysops } else { $query = array('type' => 'revision', 'target' => $page->getPrefixedDbkey(), 'ids' => $rev->getId()); $del = $this->mSkin->revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), $canHide); } $del .= ' '; } else { $del = ''; } $diffHistLinks = '(' . $difftext . $this->messages['pipe-separator'] . $histlink . ')'; $ret = "{$del}{$d} {$diffHistLinks} {$nflag}{$mflag} {$link}{$userlink} {$comment} {$topmarktext}"; # Denote if username is redacted for this edit if ($rev->isDeleted(Revision::DELETED_USER)) { $ret .= " <strong>" . wfMsgHtml('rev-deleted-user-contribs') . "</strong>"; } # Tags, if any. list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'contributions'); $classes = array_merge($classes, $newClasses); $ret .= " {$tagSummary}"; // Let extensions add data wfRunHooks('ContributionsLineEnding', array(&$this, &$ret, $row)); $classes = implode(' ', $classes); $ret = "<li class=\"{$classes}\">{$ret}</li>\n"; wfProfileOut(__METHOD__); return $ret; }
/** * Generates each row in the contributions list. * * Contributions which are marked "top" are currently on top of the history. * For these contributions, a [rollback] link is shown for users with sysop * privileges. The rollback link restores the most recent version that was not * written by the target user. * * @todo This would probably look a lot nicer in a table. */ function formatRow($row) { global $wgUser, $wgLang; wfProfileIn(__METHOD__); $sk = $this->getSkin(); $rev = new Revision(array('id' => $row->ar_rev_id, 'comment' => $row->ar_comment, 'user' => $row->ar_user, 'user_text' => $row->ar_user_text, 'timestamp' => $row->ar_timestamp, 'minor_edit' => $row->ar_minor_edit, 'deleted' => $row->ar_deleted)); $page = Title::makeTitle($row->ar_namespace, $row->ar_title); $undelete = SpecialPage::getTitleFor('Undelete'); $logs = SpecialPage::getTitleFor('Log'); $dellog = $sk->linkKnown($logs, $this->messages['deletionlog'], array(), array('type' => 'delete', 'page' => $page->getPrefixedText())); $reviewlink = $sk->linkKnown(SpecialPage::getTitleFor('Undelete', $page->getPrefixedDBkey()), $this->messages['undeleteviewlink']); if ($wgUser->isAllowed('deletedtext')) { $last = $sk->linkKnown($undelete, $this->messages['diff'], array(), array('target' => $page->getPrefixedText(), 'timestamp' => $rev->getTimestamp(), 'diff' => 'prev')); } else { $last = $this->messages['diff']; } $comment = $sk->revComment($rev); $date = htmlspecialchars($wgLang->timeanddate($rev->getTimestamp(), true)); if (!$wgUser->isAllowed('undelete') || !$rev->userCan(Revision::DELETED_TEXT)) { $link = $date; // unusable link } else { $link = $sk->linkKnown($undelete, $date, array(), array('target' => $page->getPrefixedText(), 'timestamp' => $rev->getTimestamp())); } // Style deleted items if ($rev->isDeleted(Revision::DELETED_TEXT)) { $link = '<span class="history-deleted">' . $link . '</span>'; } $pagelink = $sk->link($page); if ($rev->isMinor()) { $mflag = ChangesList::flag('minor'); } else { $mflag = ''; } // Revision delete link $canHide = $wgUser->isAllowed('deleterevision'); if ($canHide || $rev->getVisibility() && $wgUser->isAllowed('deletedhistory')) { if (!$rev->userCan(Revision::DELETED_RESTRICTED)) { $del = $this->mSkin->revDeleteLinkDisabled($canHide); // revision was hidden from sysops } else { $query = array('type' => 'archive', 'target' => $page->getPrefixedDbkey(), 'ids' => $rev->getTimestamp()); $del = $this->mSkin->revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), $canHide) . ' '; } } else { $del = ''; } $tools = Html::rawElement('span', array('class' => 'mw-deletedcontribs-tools'), wfMsg('parentheses', $wgLang->pipeList(array($last, $dellog, $reviewlink)))); $ret = "{$del}{$link} {$tools} . . {$mflag} {$pagelink} {$comment}"; # Denote if username is redacted for this edit if ($rev->isDeleted(Revision::DELETED_USER)) { $ret .= " <strong>" . wfMsgHtml('rev-deleted-user-contribs') . "</strong>"; } $ret = Html::rawElement('li', array(), $ret) . "\n"; wfProfileOut(__METHOD__); return $ret; }
/** * Returns a row from the history printout. * * @todo document some more, and maybe clean up the code (some params redundant?) * * @param $row Object: the database row corresponding to the previous line. * @param $next Mixed: the database row corresponding to the next line. * @param $notificationtimestamp * @param $latest Boolean: whether this row corresponds to the page's latest revision. * @param $firstInList Boolean: whether this row corresponds to the first displayed on this history page. * @return String: HTML output for the row */ function historyLine($row, $next, $notificationtimestamp = false, $latest = false, $firstInList = false) { global $wgUser, $wgLang; $rev = new Revision($row); $rev->setTitle($this->title); $curlink = $this->curLink($rev, $latest); $lastlink = $this->lastLink($rev, $next); $diffButtons = $this->diffButtons($rev, $firstInList); $histLinks = Html::rawElement('span', array('class' => 'mw-history-histlinks'), '(' . $curlink . $this->historyPage->message['pipe-separator'] . $lastlink . ') '); $s = $histLinks . $diffButtons; $link = $this->revLink($rev); $classes = array(); $del = ''; // Show checkboxes for each revision if ($wgUser->isAllowed('deleterevision')) { $this->preventClickjacking(); // If revision was hidden from sysops, disable the checkbox if (!$rev->userCan(Revision::DELETED_RESTRICTED)) { $del = Xml::check('deleterevisions', false, array('disabled' => 'disabled')); // Otherwise, enable the checkbox... } else { $del = Xml::check('showhiderevisions', false, array('name' => 'ids[' . $rev->getId() . ']')); } // User can only view deleted revisions... } else { if ($rev->getVisibility() && $wgUser->isAllowed('deletedhistory')) { // If revision was hidden from sysops, disable the link if (!$rev->userCan(Revision::DELETED_RESTRICTED)) { $cdel = $this->getSkin()->revDeleteLinkDisabled(false); // Otherwise, show the link... } else { $query = array('type' => 'revision', 'target' => $this->title->getPrefixedDbkey(), 'ids' => $rev->getId()); $del .= $this->getSkin()->revDeleteLink($query, $rev->isDeleted(Revision::DELETED_RESTRICTED), false); } } } if ($del) { $s .= " {$del} "; } $s .= " {$link}"; $s .= " <span class='history-user'>" . $this->getSkin()->revUserTools($rev, true) . "</span>"; if ($rev->isMinor()) { $s .= ' ' . ChangesList::flag('minor'); } if (!is_null($size = $rev->getSize()) && !$rev->isDeleted(Revision::DELETED_TEXT)) { $s .= ' ' . $this->getSkin()->formatRevisionSize($size); } $s .= $this->getSkin()->revComment($rev, false, true); if ($notificationtimestamp && $row->rev_timestamp >= $notificationtimestamp) { $s .= ' <span class="updatedmarker">' . wfMsgHtml('updatedmarker') . '</span>'; } $tools = array(); # Rollback and undo links if (!is_null($next) && is_object($next)) { if ($latest && $this->title->userCan('rollback') && $this->title->userCan('edit')) { $this->preventClickjacking(); $tools[] = '<span class="mw-rollback-link">' . $this->getSkin()->buildRollbackLink($rev) . '</span>'; } if ($this->title->quickUserCan('edit') && !$rev->isDeleted(Revision::DELETED_TEXT) && !$next->rev_deleted & Revision::DELETED_TEXT) { # Create undo tooltip for the first (=latest) line only $undoTooltip = $latest ? array('title' => wfMsg('tooltip-undo')) : array(); $undolink = $this->getSkin()->link($this->title, wfMsgHtml('editundo'), $undoTooltip, array('action' => 'edit', 'undoafter' => $next->rev_id, 'undo' => $rev->getId()), array('known', 'noclasses')); $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; } } if ($tools) { $s .= ' (' . $wgLang->pipeList($tools) . ')'; } # Tags list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow($row->ts_tags, 'history'); $classes = array_merge($classes, $newClasses); $s .= " {$tagSummary}"; wfRunHooks('PageHistoryLineEnding', array($this, &$row, &$s, &$classes)); $attribs = array(); if ($classes) { $attribs['class'] = implode(' ', $classes); } return Xml::tags('li', $attribs, $s) . "\n"; }
public static function contributionsLineEndingProcess(ContribsPager &$contribsPager, &$ret, $row) { wfProfileIn(__METHOD__); $rev = new Revision($row); $page = $rev->getTitle(); $page->resetArticleId($row->rev_page); $wfMsgOptsBase = self::getMessageOptions(null, $row); $isThread = $wfMsgOptsBase['isThread']; $isNew = $wfMsgOptsBase['isNew']; // Don't show useless link to people who cannot hide revisions $del = Linker::getRevDeleteLink($contribsPager->getUser(), $rev, $page); if ($del !== '') { $del .= ' '; } else { $del = ''; } // VOLDEV-40: remove html messages $ret = $del; $ret .= Linker::linkKnown($page, $contribsPager->getLanguage()->userTimeAndDate($row->rev_timestamp, $contribsPager->getUser()), [], ['oldid' => $row->rev_id]) . ' ('; if ($isNew) { $ret .= $contribsPager->msg('diff')->escaped(); } else { $ret .= Linker::linkKnown($page, $contribsPager->msg('diff')->escaped(), [], ['diff' => 'prev', 'oldid' => $row->rev_id]); } $wallMessage = new WallMessage($page); $threadId = $wallMessage->getMessagePageId(); $threadTitle = Title::newFromText($threadId, NS_USER_WALL_MESSAGE); $ret .= ' | ' . Linker::linkKnown($threadTitle, $contribsPager->msg('hist')->escaped(), [], ['action' => 'history']) . ') '; if ($isThread && $isNew) { $ret .= ChangesList::flag('newpage') . ' '; } if (MWNamespace::getSubject($row->page_namespace) === NS_WIKIA_FORUM_BOARD && empty($wfMsgOptsBase['articleTitleVal'])) { $wfMsgOptsBase['articleTitleTxt'] = $contribsPager->msg('forum-recentchanges-deleted-reply-title')->text(); } $prefix = MWNamespace::getSubject($row->page_namespace) === NS_WIKIA_FORUM_BOARD ? 'forum' : 'wall'; $ret .= $contribsPager->msg($prefix . '-contributions-line')->params($wfMsgOptsBase['articleTitle'])->rawParams(htmlspecialchars($wfMsgOptsBase['articleTitleTxt']))->params($wfMsgOptsBase['wallTitleTxt'], $wfMsgOptsBase['wallPageName'])->parse(); if (!$isNew) { $summary = $rev->getComment(); if (empty($summary)) { $msg = Linker::commentBlock($contribsPager->msg(static::getMessagePrefix($row->page_namespace) . '-edit')->inContentLanguage()->text()); } else { $msg = Linker::revComment($rev, false, true); } $ret .= ' ' . $contribsPager->getLanguage()->getDirMark() . $msg; } wfProfileOut(__METHOD__); return true; }
/** * Render the header of a diff page including: * Name with url to page * Bytes added/removed * Day and time of edit * Edit Comment */ function showHeader() { $title = $this->targetTitle; if ($this->prevRev) { $bytesChanged = $this->rev->getSize() - $this->prevRev->getSize(); } else { $bytesChanged = $this->rev->getSize(); } if ($bytesChanged > 0) { $changeMsg = 'mobile-frontend-diffview-bytesadded'; $sizeClass = MobileUI::iconClass('bytesadded', 'before', 'icon-12px meta mw-mf-bytesadded'); } elseif ($bytesChanged === 0) { $changeMsg = 'mobile-frontend-diffview-bytesnochange'; $sizeClass = MobileUI::iconClass('bytesneutral', 'before', 'icon-12px meta mw-mf-bytesneutral'); } else { $changeMsg = 'mobile-frontend-diffview-bytesremoved'; $sizeClass = MobileUI::iconClass('bytesremoved', 'before', 'icon-12px meta mw-mf-bytesremoved'); $bytesChanged = abs($bytesChanged); } if ($this->rev->isMinor()) { $minor = ChangesList::flag('minor'); } else { $minor = ''; } if ($this->rev->getComment() !== '') { $comment = Linker::formatComment($this->rev->getComment(), $title); } else { $comment = $this->msg('mobile-frontend-changeslist-nocomment')->escaped(); } $ts = new MWTimestamp($this->rev->getTimestamp()); $this->getOutput()->addHtml(Html::openElement('div', array('id' => 'mw-mf-diff-info', 'class' => 'page-summary')) . Html::openElement('h2', array()) . Html::element('a', array('href' => $title->getLocalURL()), $title->getPrefixedText()) . Html::closeElement('h2') . $this->msg('mobile-frontend-diffview-comma')->rawParams(Html::element('span', array('class' => $sizeClass), $this->msg($changeMsg)->numParams($bytesChanged)->text()), Html::element('span', array('class' => 'mw-mf-diff-date meta'), $ts->getHumanTimestamp()))->text() . Html::closeElement('div') . $minor . Html::rawElement('div', array('id' => 'mw-mf-diff-comment'), $comment)); }
/** * Achievement List * * @access public * @param array Array of watch lists for the user. * @param string Cut Off Link HTML * @param array Array of HTML links to hide or show items. * @param array Filter options in reverse boolean. True or 1 = Hide. * @param integer Seconds into the past to allow. * @param array Information of visible site keys. * @param object SpecialGobalWatchList special page. * @return string Built HTML */ public function globalWatchlist($globalWatchlist, $pageCount, $cutOffLinks, $showHideLinks, $filterOptions, $secondsFilter, $visibleSites, $specialPage) { global $wgShowUpdatedMarker; $settingsPage = Title::newFromText('Special:GlobalWatchlist/settings'); $settingsURL = $settingsPage->getFullURL(); if ($secondsFilter == 0) { $timeFilter = 7 * 24; } else { $timeFilter = $secondsFilter / 3600; } $HTML = "\n\t\t\t\t<div id='contentSub'>" . wfMessage('gwl_for')->escaped() . " {$specialPage->wgUser->getName()} <span class='mw-watchlist-toollinks'>(<a title='{$settingsPage->getText()}' href='{$settingsURL}'>" . wfMessage('gwl_edit_settings')->escaped() . "</a>)</span></div>\n\t\t\t\t<p>" . wfMessage('watchlist-details', $pageCount)->parse() . "</p>\n\t\t\t\t<form action='{$this->urlPrefix}/Special:GlobalWatchlist' id='mw-watchlist-form' method='get' name='mw-watchlist-form'>\n\t\t\t\t\t<fieldset id='mw-watchlist-options'>\n\t\t\t\t\t\t<legend>" . wfMessage('globalwatchlist_options')->escaped() . "</legend>\n\t\t\t\t\t\t" . wfMessage($timeFilter >= 24 ? 'below_changes_in_days' : 'below_changes_in', $timeFilter >= 24 ? $timeFilter / 24 : $timeFilter, $specialPage->getLanguage()->userDate(time(), $specialPage->getUser()), $specialPage->getLanguage()->userTime(time(), $specialPage->getUser())) . "<br>\n\t\t\t\t\t\t{$cutOffLinks}<br>\n\t\t\t\t\t\t" . implode(' | ', $showHideLinks) . "\n\t\t\t\t\t\t<hr>\n\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t<label for='site'>" . wfMessage('wiki')->escaped() . ":</label> <select class='siteselector' id='site' name='site'>\n\t\t\t\t\t\t\t\t<option value=''" . ($specialPage->wgRequest->getVal('site') == '' ? " selected='selected'" : null) . ">" . wfMessage('gwl_all_sites') . "</option>"; if (is_array($visibleSites) && count($visibleSites)) { //Loop over and output visible sites to choose from. foreach ($visibleSites as $siteKey => $name) { if (empty($siteKey)) { continue; } $HTML .= "\n\t\t\t\t\t\t\t\t<option value='{$siteKey}'" . ($specialPage->wgRequest->getVal('site') == $siteKey ? " selected='selected'" : null) . ">{$name}</option>"; } } $HTML .= "\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t<label for='namespace'>" . wfMessage('namespace')->escaped() . "</label> <select class='namespaceselector' id='namespace' name='namespace'>\n\t\t\t\t\t\t\t\t<option value='all'" . ($specialPage->wgRequest->getVal('namespace') == 'all' ? " selected='selected'" : null) . ">" . wfMessage('gwl_all_ns') . "</option>\n\t\t\t\t\t\t\t\t<option value='custom'" . ($specialPage->wgRequest->getVal('namespace') == 'custom' ? " selected='selected'" : null) . ">" . wfMessage('gwl_custom_ns') . "</option>"; $namespaces = $specialPage->getLanguage()->getFormattedNamespaces(); if (is_array($namespaces) && count($namespaces)) { //Loop over and output namespaces. foreach ($namespaces as $key => $name) { if ($key < 0) { continue; } if ($key === 0) { $name = wfMessage('blanknamespace')->escaped(); } $HTML .= "\n\t\t\t\t\t\t\t\t<option value='{$key}'" . ($specialPage->wgRequest->getVal('namespace') == $key && $specialPage->wgRequest->getVal('namespace') != 'all' && $specialPage->wgRequest->getVal('namespace') != 'custom' ? " selected='selected'" : null) . ">{$name}</option>"; } } $HTML .= "\n\t\t\t\t\t\t\t</select> <input id='nsinvert' name='invert' title='" . wfMessage('tooltip-invert')->escaped() . "' type='checkbox' value='1'" . ($specialPage->wgRequest->getInt('invert') == 1 ? " checked='checked'" : null) . "> " . "<label for='nsinvert' title='" . wfMessage('tooltip-invert')->escaped() . "'>" . wfMessage('invert')->escaped() . "</label> <input id='nsassociated' name='associated' title='" . wfMessage('tooltip-namespace_association')->escaped() . "' type='checkbox' value='1'" . ($specialPage->wgRequest->getInt('associated') == 1 ? " checked='checked'" : null) . "> " . "<label for='nsassociated' title='" . wfMessage('tooltip-namespace_association')->escaped() . "'>" . wfMessage('namespace_association')->escaped() . "</label> <input type='submit' value='" . wfMessage('go')->escaped() . "'>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<div id='collapse_expand_all'>\n\t\t\t\t\t\t\t<a href=\"#\" id='expand_all'>" . wfMessage('expand_all')->escaped() . "</a> / <a href=\"#\" id='collapse_all'>" . wfMessage('collapse_all')->escaped() . "</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</fieldset>\n\t\t\t\t</form>"; if (is_array($globalWatchlist) && count($globalWatchlist)) { $oldTimestamp = null; if ($secondsFilter > 0) { $oldTimestamp = time() - $secondsFilter; } $counter = 0; foreach ($globalWatchlist as $siteKey => $articles) { if (strlen($siteKey) !== 32 || !is_array($articles)) { //This usually indicates a major error. continue; } if (strlen($specialPage->wgRequest->getVal('site')) == 32 && $specialPage->wgRequest->getVal('site') != $siteKey) { //They filtered to view only one site. continue; } $_temp = current($articles); $site = $_temp['site']; $grl = globalRevisionList::newFromSite($siteKey); if (!$grl) { continue; } $revisions = $grl->getList(); $previousDate = null; $innerHTML = false; foreach ($revisions as $revision) { if (!is_object($revision)) { continue; } $foundArticle = $specialPage->searchByKeyValue($articles, ['article'], $revision->getTitle()->getText()); if ($foundArticle === false) { continue; } else { if (count($foundArticle) == 2) { //Both the subject namespace and talk namespace pages were found. Select the correct one per the revision's namespace. if ($revision->getTitle()->getNamespace() == $foundArticle[0]['article']['mNamespace']) { $article = $foundArticle[0]; } else { $article = $foundArticle[1]; } } else { $article = current($foundArticle); } } $title = $revision->getTitle(); $filterNamespaces[] = $specialPage->wgRequest->getVal('namespace'); if ($specialPage->wgRequest->getInt('associated') == 1) { if ($specialPage->wgRequest->getVal('namespace') % 2 > 0) { $extraNamespace = $specialPage->wgRequest->getVal('namespace') - 1; } else { $extraNamespace = $specialPage->wgRequest->getVal('namespace') + 1; } $filterNamespaces[] = $extraNamespace; } //Check filtering. if ($revision->isMinor() && $filterOptions['hideMinor'] || $revision->getRawUser() > 0 && $filterOptions['hideLiu'] || $revision->getRawUser() < 1 && $filterOptions['hideAnons'] || $revision->getRawUserText() == $specialPage->getUser()->getName() && $filterOptions['hideOwn'] || wfTimestamp(TS_UNIX, $revision->getTimestamp()) <= $oldTimestamp && $oldTimestamp !== null || is_numeric($specialPage->wgRequest->getVal('namespace')) && !in_array($title->getNamespace(), $filterNamespaces) || $specialPage->wgRequest->getVal('namespace') == 'custom' && $title->getNamespace() < 100) { continue; } $newDate = $specialPage->getLanguage()->userDate($revision->getTimestamp(), $specialPage->getUser()); if ($newDate != $previousDate) { $previousDate = $specialPage->getLanguage()->userDate($revision->getTimestamp(), $specialPage->getUser()); if ($ulStarted == true) { $innerHTML .= "\n\t\t\t\t\t\t\t</ul>"; $ulStarted = false; } $innerHTML .= "\n\t\t\t\t\t\t\t<h4>" . $previousDate . "</h4>"; $innerHTML .= "\n\t\t\t\t\t\t\t<ul class='special'>"; $ulStarted = true; } //This should include any trailing slashes. $siteUrlPrefix = $site['url_prefix'] . '/'; // Sort of hacky way of getting prefixed URL from a title serialized from another wiki. // Prefixed text is cached but prefixed URL is not, so we jump through a few hoops to convert one to another. // TODO someday write a RemoteTitle superclass to handle some of this stuff automatically? $titlePrefixedUrl = str_replace($title->getText(), $title->getDBkey(), $title->getPrefixedText()); $titlePrefixedUrl = wfUrlencode(str_replace(' ', '_', $titlePrefixedUrl)); $innerHTML .= "\n\t\t\t\t\t\t\t\t<li class='" . ($counter % 2 == 0 ? "mw-line-even" : "mw-line-odd") . "'>\n\t\t\t\t\t\t\t\t\t(<a href='{$siteUrlPrefix}{$titlePrefixedUrl}?diff={$revision->getId()}&oldid={$revision->getParentId()}' tabindex='{$counter}' title='{$title->getPrefixedText()}' target='_blank'>diff</a> | <a href='{$siteUrlPrefix}{$titlePrefixedUrl}?curid={$revision->getPage()}&action=history' title='{$title->getPrefixedText()}' target='_blank'>hist</a>) <span class='mw-changeslist-separator'>. .</span> " . ($revision->isMinor() ? ChangesList::flag('minor') : null) . ($revision->getParentId() === 0 ? ChangesList::flag('newpage') : null) . " <span class='mw-title'><a class='mw-changeslist-title' href='{$siteUrlPrefix}{$titlePrefixedUrl}' title='{$title->getPrefixedText()}' target='_blank'>{$title->getPrefixedText()}</a></span>‎;" . " <span class='mw-changeslist-date'>" . $specialPage->getLanguage()->userTime($revision->getTimestamp(), $specialPage->getUser()) . "</span> <span class='mw-changeslist-separator'>. .</span>" . " <span class='mw-plusminus-pos' dir='ltr' title='{$bytes} bytes after change'>(+/-{$bytesChange})</span>‎ <span class='mw-changeslist-separator'>. .</span> ‎" . "<a class='mw-userlink' href='{$siteUrlPrefix}User:{$revision->getUserText(Revision::RAW)}' title='User:{$revision->getUserText(Revision::RAW)}' target='_blank'>{$revision->getUserText(Revision::RAW)}</a>" . "<span class='mw-usertoollinks'>(<a href='{$siteUrlPrefix}User_talk:{$revision->getUserText(Revision::RAW)}' title='User talk:{$revision->getUserText(Revision::RAW)}' target='_blank'>talk</a> | <a href='{$siteUrlPrefix}Special:Contributions/{$revision->getUserText(Revision::RAW)}' title='Special:Contributions/{$revision->getUserText(Revision::RAW)}' target='_blank'>contribs</a>)</span>‎ " . ($revision->getRawComment() != '' ? "<span class='comment'>({$revision->getRawComment()})</span>" : null) . "\n\t\t\t\t\t\t\t\t</li>"; $counter++; } if ($ulStarted == true) { $innerHTML .= "\n\t\t\t\t\t\t\t</ul>"; $ulStarted = false; } $HTML .= "\n\t\t\t\t<form class='mw-changeslist-site site-{$siteKey}'>\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t\t<legend>{$site['wiki_name']}<span class='view_all'>[<a href='{$siteUrlPrefix}Special:Watchlist' target='_blank'>" . wfMessage('view_all')->escaped() . "</a>]</span><span class='site_expand_collapse'>[ <span title='" . wfMessage('collapse_expand')->escaped() . "'>-</span> ]</span><span>(" . wfMessage('gwl_total_items', $counter)->escaped() . ")</span></legend>"; if ($innerHTML !== false) { $HTML .= "\n\t\t\t\t\t\t<div class='mw-changeslist'>\n\t\t\t\t\t\t\t{$innerHTML}\n\t\t\t\t\t\t</div>"; } else { $HTML .= "\n\t\t\t\t\t\t<div class='mw-changeslist-empty'>\n\t\t\t\t\t\t\t<p>" . wfMessage('gwl_noresult')->escaped() . "</p>\n\t\t\t\t\t\t</div>"; } $HTML .= "\n\t\t\t\t\t</fieldset>\n\t\t\t\t</form>"; } } else { $HTML .= "\n\t\t\t<div class='mw-changeslist'>\n\t\t\t\t<div class='mw-changeslist-empty'>\n\t\t\t\t\t<p>" . wfMessage('gwl_noresult')->escaped() . "</p>\n\t\t\t\t</div>\n\t\t\t</div>"; } return $HTML; }